chore(meta): plan Sprint 37: Sweep — 25-ticket spring-clean sprint

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).
This commit is contained in:
2026-04-21 16:00:22 +02:00
parent 2ae836c18a
commit 8b3baf511f
6 changed files with 410 additions and 0 deletions
Binary file not shown.
+88
View File
@@ -0,0 +1,88 @@
# Sprint 37: Sweep — CI 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/ci`
**Agents:** Gestalt (systems), Hoshe (QA)
## New Tickets
| # | Title | Priority | Blocked by |
|---|-------|----------|------------|
| #854 | Generator-driven asset pipeline — regeneration discipline and versioning (epic) | high | — |
| #855 | systems.db regeneration with versioning awareness | high | #854 |
| #856 | Stamp systems.db with generator metadata (meta table) | high | #855 |
| #857 | Pre-push git hook for systems.db consistency | high | #856 |
| #858 | Extend /pr-push with rebase + regen + stage | high | #856 |
| #859 | Document the source-canonical rule (.claude/rules/asset-pipeline.md + DEVOPS) | medium | #856 |
| #723 | Add decision-to-ticket coverage report | medium | — |
Use `tooling/db/ticket show <id>` 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
```
+84
View File
@@ -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 <id>` 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
```
+65
View File
@@ -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 <id>` 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 (95125 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
```
+71
View File
@@ -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`.
+102
View File
@@ -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 <id>` 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 `<City-A>-<City-B>` 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 <small-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
```