Anchor-Repair Service¶
Status: 📐 Design-only — Fully unbuilt and honestly marked 📐 Design-only: no anchor_repair_service, no Phase-11 anchor-check or placement-helper symbols resolve, no repair events … (impl audit 2026-06-16)
📐 Design-only. No service code is committed yet; this page is the prescriptive runtime spec. Canonical decision in ADR-0053 WR12. Anchor placement is set at galaxy generation in Phase 11 per ./galaxy-generator-design.md.
Galaxy generation Phase 11 anchors four critical structures per region: the Capital welcome planet (TERRA), the Class-1 commerce station, SpaceDock #1 (starter-cluster anchor), and SpaceDock #2 (frontier anchor). These are load-bearing for new-player onboarding, faction-rep economics, and ship-construction services. Phase 11 is worldgen-time only — no runtime mechanism detects-and-repairs a missing anchor (e.g., destroyed in late-game faction war, or never injected due to a Phase 11 transient failure that wasn't caught by Phase 13 validation).
This service is the daily-cadence safety net that detects missing anchors and re-injects them.
Cadence and scan target¶
Cadence: daily at UTC midnight (alongside the ./region-lifecycle.md WR9 cron — same scheduler tick).
Scan target: each Region with status = 'active'. Skipped for pending, suspended, grace, terminated, generation_corrupt, attachment_pending. Skipped for Central Nexus (it has its own anchor model).
Anchor checks per region¶
For each active region, verify all four Phase-11 anchors:
| Anchor | Check |
|---|---|
| Capital welcome planet | EXISTS row in Planet with region_id = region.id, sector_number = region.capital_sector_number, planet_type = 'TERRA' |
| Class-1 commerce station | EXISTS row in Station with region_id = region.id, station_class = CLASS_1, sector_number IN (region.capital_sector_number + 1, ...starter-cluster fallback) |
| SpaceDock #1 (starter-cluster anchor) | EXISTS row in Station with region_id = region.id, is_spacedock = true, region_assignment_role = 'starter_cluster', in starter cluster |
| SpaceDock #2 (frontier anchor) | EXISTS row in Station with region_id = region.id, is_spacedock = true, region_assignment_role = 'frontier', in Frontier zone |
Each check is a single existence query. Total per-region cost: 4 SQL queries; very small.
Action on missing anchor¶
For each missing anchor:
- Re-inject via Phase 11 placement logic. The placement code lives in
services/gameserver/src/services/galaxy_service.py:GalaxyGenerator._place_phase_11_anchors(or equivalent helper). The service imports it directly — no duplication. - Idempotent placement: the helper picks the canonical anchor sector, but if that sector is occupied by player infrastructure (e.g., a player station has been built in the Capital sector + 1 since worldgen), the placement falls back per the same Phase 11 fallback rules (next eligible sector within the cluster).
- On success: emit
region_anchor_repairedrealtime event with the anchor type and the new placement sector. Operator dashboard shows the repair in the audit log. - On failure (no eligible sector available, or other validation rule blocks it): emit
region_anchor_repair_failedrealtime event + admin alert (email/Slack). The region remains active but with the missing anchor — manual ops intervention required.
Idempotency¶
- A region with all four anchors intact is a no-op pass (4 quick existence queries, 0 actions).
- Running the service twice on a region with a missing anchor is safe — the second pass finds the anchor already injected by the first pass.
- A missing anchor that fails injection is logged and alerted, but the service does not retry within the same daily run; the next day's pass tries again.
Why daily, not faster¶
Anchor destruction is rare. Capital welcome planets, Class-1 stations, and SpaceDocks are heavily defended (citadel-level structures + faction patrol presence). Players generally cannot destroy them; the failure mode is mostly "Phase 11 had a transient bug that Phase 13 missed, and the anchor was never injected" — a worldgen-time issue caught at the next daily scan.
A more frequent cadence (hourly, every-15-min) would burn cycles re-checking unchanged regions for the rare destruction event. Daily catches the realistic failure modes without overhead.
Failure modes¶
| Mode | Detection | Handling |
|---|---|---|
| Service down for > 1 day | Heartbeat absent for > 24h | On restart, runs immediately; backlogged repairs surface. |
| Re-injection fails (sector occupied + fallback exhausted) | Placement helper returns failure | Emit region_anchor_repair_failed + admin alert. Region operates with missing anchor until manual ops fix. |
| Re-injection succeeds but the new anchor is in an unexpected location | Placement-helper picked a fallback | Emit region_anchor_repaired with the chosen sector in the payload; operator can review and decide if a manual relocation is warranted. |
| Multiple service instances scanning concurrently | Two instances both attempt re-injection on the same region | The placement helper uses a per-region advisory lock (pg_advisory_xact_lock(hashtext('region:' || region_id))), per the SK18 pattern from ADR-0050. One instance wins; the other no-ops. |
Source map¶
| Concern | Path (target) |
|---|---|
| Service entry point | services/gameserver/src/services/anchor_repair_service.py:run_daily_scan |
| Cron / scheduler | services/gameserver/src/scheduling/cron_jobs.py:anchor_repair_daily |
| Phase 11 placement helper (reused) | services/gameserver/src/services/galaxy_service.py:GalaxyGenerator._place_phase_11_anchors |
| Per-region advisory lock | Shared utility (per ADR-0050 SK18 pattern) |
| Realtime event emission | Existing realtime_bus.emit for region_anchor_repaired / region_anchor_repair_failed |
Related¶
../ADR/0053-batch6-runtime-services.md— canonical decision (WR12)../galaxy-generator-design.md— Phase 11 anchor placement that this service repairs../region-lifecycle.md— daily cron entry;Region.statusvalues that this service skips.../ADR/0050-batch3-provisioning-lifecycle-hardening.md— SK18 advisory-lock pattern reused.