From 8b3baf511fb99f7489c454f913eb6b9f0c295d49 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Tue, 21 Apr 2026 16:00:22 +0200 Subject: [PATCH] =?UTF-8?q?chore(meta):=20plan=20Sprint=2037:=20Sweep=20?= =?UTF-8?q?=E2=80=94=2025-ticket=20spring-clean=20sprint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tickets: - ci (7): #854, #855, #856, #857, #858, #859, #723 - server (9): #853, #860, #862, #863, #874, #877, #878, #789, #847 - client (6): #866, #869, #870, #872, #873, #875 - copy (3): #861, #865, #876 Themes: asset pipeline discipline, PROTOCOL_VERSION removal (D-192), D-167 HeritageRoot purge, D-032 smuggler/detective enum audit, New Game regression fix + merge-path test coverage, copy wiki residue purge, bookmark save-state, generator quality patches. Debt surfaced from Q/D-record sweep: Q-095 Commission name, D-167 and D-032 residue, plus spring-cleaning pulls from stale backlog (#789, #847, #723, #862). #868 cancelled (superseded by #874 + #875). --- docs/backups/settledreach.db.backup | Bin 962560 -> 1044480 bytes docs/sprints/sprint-37/ci.md | 88 ++++++++++++++++++++++++ docs/sprints/sprint-37/client.md | 84 +++++++++++++++++++++++ docs/sprints/sprint-37/copy.md | 65 ++++++++++++++++++ docs/sprints/sprint-37/joint.md | 71 +++++++++++++++++++ docs/sprints/sprint-37/server.md | 102 ++++++++++++++++++++++++++++ 6 files changed, 410 insertions(+) create mode 100644 docs/sprints/sprint-37/ci.md create mode 100644 docs/sprints/sprint-37/client.md create mode 100644 docs/sprints/sprint-37/copy.md create mode 100644 docs/sprints/sprint-37/joint.md create mode 100644 docs/sprints/sprint-37/server.md diff --git a/docs/backups/settledreach.db.backup b/docs/backups/settledreach.db.backup index e15baffa77ba2a1919adb4ef05449fbb6b42fa39..df6af6760a0cae429436d32c6b2aa156a85c1b7a 100644 GIT binary patch delta 52016 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 Mpflk0mqHNpFV=(=I{*Lx delta 5986 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 for full details. + +## Key Decisions + +No domain decisions gate this sprint's work. The asset pipeline epic creates new process conventions documented in `.claude/rules/asset-pipeline.md` (deliverable of #859). + +## Notes + +**#854 — Generator-driven asset pipeline (epic wrapper)** +- This epic establishes the mental model: `server/data/systems.db` is a read-only canonical snapshot, deterministically produced from source files (markers.json, *.toml, generator code). Direct DB edits are forbidden except when mirrored back to source. +- Children: #855 → #856 → #857 and #858 (parallel after #856) → #859 (docs, after #856). +- Driver: Sprint 36 had a binary conflict risk between two branches both committing systems.db changes. This epic prevents the class of problem going forward and establishes the versioning primitives the future savegame system will need. +- #854 itself has no code deliverable — it is the parent epic for tracking purposes. Work happens in children. + +**#855 — systems.db regeneration with versioning awareness** +- Every generator (`import_economics.py`, `generate_atlas.py`, `generate_brands`) must be updated to accept a `--stamp` flag (or equivalent) that writes metadata to the DB after generation. +- The regeneration step should be idempotent: running it twice on the same sources produces identical output. +- Unblocks #856, #857, #858, #859. + +**#856 — Stamp systems.db with generator metadata (meta table)** +- Add a `meta` table to `db/schema.sql`: columns `schema_version`, `generator_sha`, `generated_at`. +- Each generator writes its own row on completion. +- The stamp is what lets a future savegame DB record which canonical snapshot it derives from (migration lineage). +- Blocked by #855 (generators must be updated before stamping is meaningful). + +**#857 — Pre-push git hook for systems.db consistency** +- Hook fires on `git push` and verifies: if `systems.db` is staged, its `meta.generator_sha` matches the current HEAD SHA of the generator source files. +- If not, rejects the push with a message: "systems.db is stale — run `make regen-db` before pushing." +- Install via `make install-hooks`. Document in #859. + +**#858 — Extend /pr-push with rebase + regen + stage** +- The `/pr-push` skill runs before PR creation. Extend it to: (1) rebase on main, (2) run `make regen-db` if any generator source was modified in the branch, (3) stage the updated `systems.db`. +- This prevents the class of binary conflict where two branches both modify generator sources and commit separate DB snapshots. +- Parallel to #857 after #856 is done. + +**#859 — Document the source-canonical rule** +- Deliverables: + 1. `.claude/rules/asset-pipeline.md` — canonical rules file covering: what systems.db is, how to make a DB change, why direct edits are forbidden, the meta table stamp, the pre-push hook, why /pr-push regenerates. + 2. `CLAUDE.md` hook — one-line reference under a new `### Asset pipeline` subsection pointing at the rules file. + 3. `docs/DEVOPS.md` — short section on the pipeline: meta table, `make install-hooks`, regenerate-before-push workflow. +- Blocked by #856 (meta table must exist before documenting it). + +**#723 — Decision-to-ticket coverage report** +- No schema change needed — `tickets.decision_ref` already exists. +- Deliverables: + 1. `make decisions-coverage` — shows each D-record with its implementing ticket(s). + 2. `make decisions-orphan` — shows D-records with no implementing tickets (verify existing target works). + 3. `tooling/db/decision show D-159` — includes linked ticket IDs in output. +- The reverse link (decision → tickets) is a `SELECT` query, not a new column. Avoids fragile two-way sync. +- Standalone, parallel to the asset pipeline chain. + +## Dependency Chain + +``` +#854 (epic) → no code +#855 (regen awareness) → #856 (meta table) → #857 (pre-push hook) + → #858 (pr-push extension) + → #859 (documentation) +#723 → standalone, parallel to all above +``` + +## PR Workflow + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz \ + --title "ci: sprint 37 — asset pipeline discipline, decision coverage report" \ + --description "body" \ + --base main --head sprint-37/ci +``` diff --git a/docs/sprints/sprint-37/client.md b/docs/sprints/sprint-37/client.md new file mode 100644 index 000000000..643923b84 --- /dev/null +++ b/docs/sprints/sprint-37/client.md @@ -0,0 +1,84 @@ +# Sprint 37: Sweep — Client Tasks + +**Goal:** Spring-clean accumulated debt: asset pipeline discipline, PROTOCOL_VERSION removal, D-167/D-032 dead-code purge, New Game regression fix, copy wiki residue, bookmark save-state, and generator quality patches. + +**Branch:** `sprint-37/client` +**Agents:** Stig (dev), Hoshe (QA) + +## New Tickets + +| # | Title | Priority | Blocked by | +|---|-------|----------|------------| +| #872 | New Game flow hangs on 'connecting' after sprint-36 Option A handoff | high | — | +| #873 | Scene-level merge-path UI flow tests (gdUnit4) | high | — | +| #866 | dialogue_box _escape_bbcode chained replace corrupts [lb] escapes | high | — | +| #869 | Fix MetaScreen test helper regression — anti_tedium suite (7 fails) | high | — | +| #870 | Delete or revive 8 parse-error test files | medium | — | +| #875 | Drop PROTOCOL_VERSION on client (D-192) | medium | #874 (server) | + +Use `tooling/db/ticket show ` for full details. + +## Key Decisions + +- `decisions/architecture.md` — D-192 (drop PROTOCOL_VERSION lockstep handshake), D-005 (IPC protocol) + +## Notes + +**#872 — New Game flow hangs on 'connecting' (motivating regression)** +- Manual repro during Sprint 36 PR #134 smoke test: main menu → New Game → loading screen shows 'connecting' and never progresses. +- Likely suspects: + 1. `client/ui/meta/screens/main_menu/main_menu.gd` — `_process()` polls `SimBridge.poll_snapshot()` while `_waiting_for_catalog` is true. If `bookmark_catalog` never arrives (or arrives but the flag isn't cleared), the loop runs forever. + 2. `client/ui/meta/screens/loading/loading_screen.gd` — opaque BG + `MOUSE_FILTER_STOP` means a never-dismissed loading state looks identical to 'hung connecting'. + 3. `client/scripts/protocol/protocol.gd` — `bookmark_catalog` decode landed in v23 (commit 41e89579). Verify server is actually sending `bookmark_catalog` in the first snapshot after connect. + 4. Option A scene handoff race — confirm `main_menu → character_creation` triggers on a signal, not a polled flag. +- Suggested investigation order: add trace prints in `main_menu._process` to confirm `SimBridge.state` and `GameState.bookmark_catalog` contents during the hang. That will disambiguate suspects 1 vs 3 in minutes. + +**#873 — Scene-level merge-path UI flow tests (gdUnit4)** +- Add gdUnit4 scene-level tests for merge-path UI flows so regressions like #872 are caught pre-merge instead of post-merge. +- Scope — one test per flow: + - main menu → new game → character creation → confirm → connected state + - main menu → load game → save picker → selected + - character creation → submit → `sim_bridge` receives correct payload + - bookmark tab → select location → confirm → server gets bookmark action +- Tests must run headless via `tests/run-godot`. Pattern: load scene → simulate input via `_input()` or `button.pressed.emit()` → await signals or poll state with timeout → assert terminal state. +- NOT pixel/screenshot diffing. NOT RPA/xdotool. NOT CI integration (existing `make test-client` already covers this). +- Reference pattern: `test_character_creation_sprint28.gd`. +- These tests are the mechanism the `pr-review` merge-path gate (added Sprint 36 retro) assumes exists. + +**#866 — _escape_bbcode chained replace corruption** +- `dialogue_box.gd:628` chains `.replace('[', '[lb]').replace(']', '[rb]')`. The second replace turns `[lb]` into `[lb[rb]`, corrupting the escape. BBCode injection guard is effectively broken for server-sourced text. +- Fix: escape only `[` (not `]`), since unmatched `]` in RichTextLabel renders as literal. +- Test: `test_escape_bbcode_brackets_in_server_text` in `test_dialogue_sprint18.gd` (currently skipped) — unskip and make it pass. + +**#869 — MetaScreen test helper regression (7 fails)** +- Sprint 36 migrated `bug_report_dialog.gd` from `extends Control` to `extends MetaScreen`. `test_anti_tedium.gd:94` builds the dialog via `Control.new() + set_script(BugReportDialogScript)`, which no longer satisfies the MetaScreen base contract. +- Every call to `dialog.start_capture()` fails with `Nonexistent function in base Control`. 7 failing tests in the anti_tedium suite. +- Fix: instantiate the `.tscn` (preserves the MetaScreen runtime stack) instead of building from script, OR have the test instantiate a MetaScreen-rooted node. + +**#870 — Delete or revive 8 parse-error test files** +- These 8 files fail to parse (not just fail tests), contributing to gdUnit error counts: `test_debug_overlay_sprint19.gd`, `test_entanglement_sprint22.gd`, `test_fog_sprint22.gd`, `test_journal_sprint18.gd`, `test_minimap_sprint18.gd`, `test_session_manager_sprint19.gd`, `test_sprint30.gd`, `test_sprite_integration.gd`. +- They reference removed/renamed APIs from prior sprints. +- Default: delete. File a fresh ticket if/when the underlying coverage is needed again. If any file is worth reviving, rewrite it against the current API. + +**#875 — Drop PROTOCOL_VERSION on client (D-192)** +- Blocked by server ticket #874 — merge server PR first. +- Remove: `version` field read in `client/scripts/protocol/protocol.gd` (`Protocol.decode_snapshot`), the `PROTOCOL_VERSION` constant, and the version-mismatch guard. +- Update any fixture-replay paths that read `version`. +- Keep all field-presence and roundtrip behavioral tests. + +## Dependency Chain + +``` +#874 (server) → #875 (client PROTOCOL_VERSION drop) +#872 (New Game regression fix) → #873 (merge-path tests add coverage for this flow) +#866, #869, #870 → standalone, parallel +``` + +## PR Workflow + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz \ + --title "fix(client): sprint 37 — New Game regression, test cleanup, PROTOCOL_VERSION drop" \ + --description "body" \ + --base main --head sprint-37/client +``` diff --git a/docs/sprints/sprint-37/copy.md b/docs/sprints/sprint-37/copy.md new file mode 100644 index 000000000..2636889cf --- /dev/null +++ b/docs/sprints/sprint-37/copy.md @@ -0,0 +1,65 @@ +# Sprint 37: Sweep — Copy Tasks + +**Goal:** Spring-clean accumulated debt: asset pipeline discipline, PROTOCOL_VERSION removal, D-167/D-032 dead-code purge, New Game regression fix, copy wiki residue, bookmark save-state, and generator quality patches. + +**Branch:** `sprint-37/copy` +**Agents:** Mellanie (author), Paula (narrative), Miri (consistency) + +## New Tickets + +| # | Title | Priority | Blocked by | +|---|-------|----------|------------| +| #876 | decide(content): Commission formal name — canonical long-form designation (Q-095) | medium | — | +| #865 | Purge remaining v0.1 Sova/Van Maanen's residue from wiki | medium | — | +| #861 | Author 112 brand corp wiki stubs to three-layer narrative depth | medium | — | + +Use `tooling/db/ticket show ` for full details. + +## Key Decisions + +- `decisions/questions.md` — Q-095 (Commission formal name — Concord Commission vs. Lattice Commission) +- `decisions/content.md` — D-128 (culture implicit in starting location), D-122 (all NPCs generated) +- `decisions/economics.md` — D-189 (brand layer architecture — 8 categories), D-185 (brands are not commodities) + +## Open Questions to Resolve Early + +- **Q-095: Commission formal name** — Drift between "Concord Commission" and "Lattice Commission" blocks institutional POI signage and tractus fee legal text. Jeroen picks the long-form name; Mellanie/Paula codify everywhere. Resolve in the first session of the sprint before #865 touches faction files. + +## Notes + +**#876 — Commission formal name decision (Q-095)** +- One-conversation resolution. The two drifted names appear in: faction wiki pages, tractus fee descriptions, legal-text authoring guide examples, and institution templates. +- Deliverable: a confirmed D-record (claim an ID with `tooling/db/decision claim D content "Commission formal name"` before writing). Update `decisions/questions.md` to mark Q-095 resolved with the D-record reference. +- Do this first — #865 will touch faction files and should use the canonical name. + +**#865 — Purge v0.1 Sova/Van Maanen's residue from wiki** +- Start by running `grep -rln 'v0\.1\|Sova Transit\|Van Maanen\|Kael Davan\|Sera Venn\|Nils Davan\|the-ring' wiki/` to scope the full hit list. +- Confirm three decision points with Jeroen before bulk editing: + 1. Sova/Transit tree under `wiki/star-systems/GJ-35/sova/` — delete outright, or reparent/retag as a legitimate Vuurkloof station? + 2. Example references in authoring guides/templates — keep as illustrative voice examples, or replace with galactic-scope alternatives? + 3. Decision/lore files citing Kael/Sera/etc. as canonical — rewrite to generic, or delete those sections? +- This is judgment-heavy cleanup, not a mechanical find/replace. Confirm the decision points first; do the bulk editing after. +- Resolve Q-095 (#876) before touching faction files so the correct Commission name is used throughout. + +**#861 — Author 112 brand corp wiki stubs to three-layer narrative depth** +- Sprint 36 PR #133 shipped 112 new corp wiki pages in `wiki/corporations/` as 24-line frontmatter-only placeholders. They need authoring to the canonical three-layer standard. +- Reference template: Calloway, Thrds, VGV, Thalassa stubs (95–125 lines each). Three layers: (1) public identity, (2) actual operation, (3) one concealed fact. +- DoD per page: ≥95 lines, all three layers present, cross-refs populated, corridor/founding-system specificity (not generic boilerplate). +- This is a full sprint's worth of work for the team. Do not attempt to cram into a single session — divide by corridor or category across Mellanie/Paula/Miri and review as you go. +- Paula reviews narrative as authoring progresses. Miri checks cross-ref consistency. + +## Dependency Chain + +``` +#876 (Q-095 Commission name) → #865 (wiki residue purge touches faction files) +#861 → standalone, parallel to both above +``` + +## PR Workflow + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz \ + --title "content(wiki): sprint 37 — brand corp stubs, wiki residue purge, Commission name" \ + --description "body" \ + --base main --head sprint-37/copy +``` diff --git a/docs/sprints/sprint-37/joint.md b/docs/sprints/sprint-37/joint.md new file mode 100644 index 000000000..4a402ce5a --- /dev/null +++ b/docs/sprints/sprint-37/joint.md @@ -0,0 +1,71 @@ +# Sprint 37: Sweep — Joint Overview + +**Sprint goal:** Spring-clean accumulated debt: asset pipeline discipline, PROTOCOL_VERSION removal, D-167/D-032 dead-code purge, New Game regression fix, copy wiki residue, bookmark save-state, and generator quality patches. + +**Sprint 37 is a maintenance and polish sprint.** No new features. Every ticket is either closing a decision-without-follow-through, fixing a regression, establishing infrastructure discipline, or clearing content debt. + +## Pre-Sprint Decisions + +| Decision | Status | Owner | Blocks | +|----------|--------|-------|--------| +| Q-095: Commission formal name | Open — resolve sprint day 1 | copy (Jeroen picks) | #876, #865 | +| D-192: Drop PROTOCOL_VERSION | Confirmed | server then client | #874 → #875 | +| D-167: HeritageRoot removal | Confirmed | server | #877 | +| D-032: Smuggler/detective enum | Confirmed obsolete | server | #878 | + +## Full Ticket Roster + +| Team | # | Title | Priority | +|------|---|-------|----------| +| ci | #854 | Generator-driven asset pipeline (epic) | high | +| ci | #855 | systems.db regeneration with versioning awareness | high | +| ci | #856 | Stamp systems.db with generator metadata (meta table) | high | +| ci | #857 | Pre-push git hook for systems.db consistency | high | +| ci | #858 | Extend /pr-push with rebase + regen + stage | high | +| ci | #859 | Document the source-canonical rule | medium | +| ci | #723 | Add decision-to-ticket coverage report | medium | +| server | #853 | Generator-patch follow-up: dedup, mountains, suffix, compass | medium | +| server | #860 | Resolve 21 raw-commodity / system coverage gate gaps | medium | +| server | #863 | Wire SelectedBookmark into SaveState | medium | +| server | #874 | Drop PROTOCOL_VERSION on server (D-192) | medium | +| server | #877 | Remove HeritageRoot type alias and Heritage variant (D-167) | medium | +| server | #878 | Audit and purge dead smuggler/detective enum (D-032) | medium | +| server | #789 | Storyteller activation_pass log should be DEBUG not WARN | low | +| server | #847 | Determinism smoke test for generate_atlas.py | low | +| server | #862 | Refactor BookmarkPlugin to accept injected registry | low | +| client | #866 | dialogue_box _escape_bbcode chained replace corrupts [lb] | high | +| client | #869 | Fix MetaScreen test helper regression — anti_tedium (7 fails) | high | +| client | #872 | New Game flow hangs on 'connecting' | high | +| client | #873 | Scene-level merge-path UI flow tests (gdUnit4) | high | +| client | #870 | Delete or revive 8 parse-error test files | medium | +| client | #875 | Drop PROTOCOL_VERSION on client (D-192) | medium | +| copy | #861 | Author 112 brand corp wiki stubs to three-layer depth | medium | +| copy | #865 | Purge remaining v0.1 Sova/Van Maanen's residue from wiki | medium | +| copy | #876 | decide(content): Commission formal name (Q-095) | medium | + +**Total: 25 tickets across 4 teams.** + +## Cross-Team Dependencies + +``` +server #874 (PROTOCOL_VERSION drop) → client #875 +copy #876 (Commission name) → copy #865 (faction files use resolved name) +ci #855 → #856 → #857, #858, #859 (asset pipeline chain) +``` + +## Sprint Completion Criteria + +The sprint is done when all of the following are true: + +1. `make economy-db` passes end-to-end with no coverage gate failures (#860 done). +2. `make game` → New Game → character creation completes without hanging (#872 done). +3. Scene-level gdUnit4 tests exist for all four merge-path flows and pass headless (#873 done). +4. `server/src/bridge/types.rs` contains no `PROTOCOL_VERSION` constant and no `version` field in the snapshot envelope (#874 done). +5. `client/scripts/protocol/protocol.gd` contains no version-mismatch guard (#875 done, after #874). +6. `server/src/simulation/generator.rs` contains no `HeritageRoot` type alias and no `Heritage` enum variant (#877 done). +7. `server/data/systems.db` carries a `meta` table with `schema_version`, `generator_sha`, `generated_at` (#856 done). +8. `git push` on a branch with a stale systems.db is rejected by the pre-push hook (#857 done). +9. `.claude/rules/asset-pipeline.md` exists and is referenced from `CLAUDE.md` (#859 done). +10. All 112 new brand corp wiki pages are ≥95 lines with three-layer content (#861 done). +11. Q-095 is resolved: a D-record exists for the Commission formal name (#876 done). +12. `tooling/db/ticket list --sprint 37` shows all 25 tickets as `done`. diff --git a/docs/sprints/sprint-37/server.md b/docs/sprints/sprint-37/server.md new file mode 100644 index 000000000..8912b05bb --- /dev/null +++ b/docs/sprints/sprint-37/server.md @@ -0,0 +1,102 @@ +# Sprint 37: Sweep — Server Tasks + +**Goal:** Spring-clean accumulated debt: asset pipeline discipline, PROTOCOL_VERSION removal, D-167/D-032 dead-code purge, New Game regression fix, copy wiki residue, bookmark save-state, and generator quality patches. + +**Branch:** `sprint-37/server` +**Agents:** Dudley (dev), Tyre (arch), Hoshe (QA) + +## New Tickets + +| # | Title | Priority | Blocked by | +|---|-------|----------|------------| +| #863 | Wire SelectedBookmark into SaveState | medium | — | +| #874 | Drop PROTOCOL_VERSION on server (D-192) | medium | — | +| #853 | Generator-patch follow-up: dedup, mountains, suffix, compass | medium | — | +| #860 | Resolve 21 raw-commodity / system coverage gate gaps | medium | — | +| #877 | Remove HeritageRoot type alias and Heritage enum variant (D-167) | medium | — | +| #878 | Audit and purge dead smuggler/detective character enum (D-032) | medium | — | +| #847 | Determinism smoke test for generate_atlas.py | low | — | +| #789 | fix(simulation): storyteller activation_pass log DEBUG not WARN | low | — | +| #862 | Refactor BookmarkPlugin to accept injected registry | low | — | + +Use `tooling/db/ticket show ` for full details. + +## Key Decisions + +- `decisions/architecture.md` — D-192 (drop PROTOCOL_VERSION lockstep handshake), D-005 (IPC protocol) +- `decisions/content.md` — D-167 (corridors replace heritage roots, 2026-03-24) +- `decisions/perception.md` — D-032 (smuggler/detective enum obsolete post-D-117; D-121 culture-driven voice) + +## Notes + +**#863 — Wire SelectedBookmark into SaveState** +- Add `Serialize`, `Deserialize` derives to `SelectedBookmark` in `server/src/bookmark/mod.rs`. +- Register `SelectedBookmark` as a serializable resource in `server/src/simulation/save_state.rs`. +- Round-trip test: save with bookmark selected → load → `SelectedBookmark` survives. +- Update bookmark spec §4.4 (`docs/architecture/sprint-36-bookmark-spec.md`) — remove the v0.2-deferred scope note. +- Replaces inline TODO at `server/src/bookmark/mod.rs:95-99`. + +**#874 — Drop PROTOCOL_VERSION on server (D-192)** +- Remove: `version` field from snapshot envelope in `server/src/bridge/types.rs`, `PROTOCOL_VERSION` constant, and version stamp in encode path. +- Keep all field-presence and roundtrip behavioral tests. Tautological version-literal assertions were already deleted in Sprint 36. +- Client ticket #875 is blocked by this — server PR merges first. +- After this lands, genuine schema drift surfaces as MessagePack missing-field errors downstream. That is the intended signal. + +**#853 — Generator-patch follow-up** +- Patch `tooling/planet-gen/generate_atlas.py` and `tooling/planet-gen/gemma_naming.py` for 7 systematic issues documented in `docs/design/atlas-generator-refinement-notes.md`: + 1. Cross-body city name dedup (49 collisions — 'Jade Harbor' on 20 bodies, 'Fort Iron' on 10) + 2. Cross-body mountain name dedup (186 bodies with empty names, 'Riverbend' on 39) + 3. Suffix monotony (-rant/-berg clustering) + 4. Compass-direction defaults — forbid 'Eastern X / Western Y' pattern in few-shot + 5. Cultural crossmix gap — thread corridor + cultural-history context into naming prompt + 6. River vocabulary bleeding from navigational terms (blocklist circumflex/contraflow) + 7. Unnamed infrastructure — emit `-` convention for roads/rail at generation time +- Verification: re-run Paula's analysis script after patching; confirm collision counts drop. + +**#860 — Resolve 21 coverage gate gaps** +- `make economy-db` Phase 2 coverage gate fails on 21 raw-commodity/system gaps in tier1 corp tags. The brand layer (V-B01..V-B06) passes cleanly — this is upstream. +- Identify the 21 specific gaps. Either backfill tier1 corp tags or relax the gate if gaps are intentional (e.g. commodity deliberately imported from outside the Reach). +- `make economy-db` must pass end-to-end before Phase 2 demand simulation can run in anger. + +**#877 — Remove HeritageRoot + Heritage variant (D-167)** +- D-167 (2026-03-24) retired the 7 abstract heritage roots in favor of corridors. Two dead stubs remain: + - `pub type HeritageRoot = String;` at `server/src/simulation/generator.rs:76` + - `ZonePaletteModifier::Heritage(HeritageRoot)` variant at `generator.rs:369` +- Delete the type alias, drop the enum variant, remove all Heritage modifier construction sites. +- Verify no external consumer before deleting. Same pattern as D-192 cleanup. + +**#878 — Audit and purge D-032 dead enum** +- D-032 is marked "[Obsolete post-D-117: smuggler/detective eliminated. v0.2 uses culture-driven voice per D-121; this enum is unused.]" +- Known reference sites: `server/src/bridge/types.rs`, `server/src/perception/observer/mod.rs`, `server/tests/archetype_monologue.rs`, and content schemas. +- Audit first — some usages may be load-bearing (e.g. a wider pattern match). Remove confirmed-dead surface only. Do not mass-delete before reading each callsite. + +**#847 — Determinism smoke test for generate_atlas.py** +- ~60 lines of bash or pytest. Place in `tests/` consistent with project test layout. +- Run `generate_atlas.py --body --force --seed 42` twice; diff `markers.json` byte-for-byte. +- Fails if output differs. Guardrail against determinism regressions in terrain analysis, city placement, A*, and naming. + +**#789 — Storyteller log level fix** +- `activation_pass: no Simmering triangles — holding` fires as WARN every few seconds during early gameplay when no NPC relationships have escalated. This is normal state, not an error condition. +- Change to DEBUG or TRACE. One-line fix. + +**#862 — BookmarkPlugin registry injection** +- `BookmarkPlugin::build` calls `register_default_bookmarks(&mut registry)` unconditionally — no hook for a test registry. +- Proposed: `BookmarkPlugin::new(registry)` injection. Default constructor still wires the canonical tycoon registry; injection variant is for tests and future TOML loading. +- File: `server/src/bookmark/mod.rs:107-118`. Flagged in PR #132 review; accepted as follow-up. + +## Dependency Chain + +``` +#874 (server PROTOCOL_VERSION drop) → unblocks #875 (client) +#877, #878, #789, #847, #862 → standalone, parallel +#863, #853, #860 → standalone, parallel +``` + +## PR Workflow + +```bash +tea pr create --repo jpmschweitzer/settled-reach --login schweitz \ + --title "refactor(server): sprint 37 — dead code purge, protocol cleanup, generator patches" \ + --description "body" \ + --base main --head sprint-37/server +```