DLC entry point for future player construction system. Adds
Modification struct, ModificationType enum, and Modifications
component. Wired into SaveStateV1 with #[serde(default)] for
forward-compatible save format.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New test_world room with two zones (Terminal/Corridor) separated by
a door. Adds ZoneCrossEventQueue resource and detect_zone_crossings
system to fire events when the player crosses zone boundaries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Production startup now spawns 23 Sova NPCs with EntanglementTag
(Flat/Intrigue) based on triangle_membership. Three-phase spawn:
entity creation, cross-reference resolution, and authored triangle
instantiation. Five triangles (3 ActiveFork, 2 PassiveTension per
D-087) with deterministic IDs via FNV-1a hashing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Timer-based storyteller system fires after 1800 ticks (30 game-min).
Sets ContaminationActive resource, applies tension delta to all
ActiveFork triangles, and emits ContaminationEvent for downstream
monologue/behavioral hooks. Q-017 fallback constants in place.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
50-seed randomized testing against 4 structural invariants:
walkable connectivity (BFS), entity bounds, door adjacency,
and minimum tile count floor. Includes generator module for
test-scoped procedural map creation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Register StorytellerPlugin in main.rs — without it, contamination never fires in production
Persist ContaminationActive in SaveStateV1 — add field with #[serde(default)] to prevent double-fire on load
Warnings:
3. Add .before(compute_observer_snapshot) to detect_zone_crossings system ordering
4. Doc comment on z_bands_connected clarifying band indices vs z-level coordinates
5. MIN_WALKABLE_TILES floor not structurally guaranteed
6. Misleading D-111/D-112 citation on modification module
## Review: server → main (type: code)
### Hoshe (Code Quality): REQUEST_CHANGES
Well-structured sprint with solid test coverage. Three warnings.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `modification.rs:1,3` | warning | Doc cites D-111/D-112 but neither describes the modification data model — misleading spec reference |
| 2 | `fuzzy_map.rs:32` | warning | `MIN_WALKABLE_TILES = 200` not structurally guaranteed — a seed outside 0..50 could fail CI |
| 3 | `storyteller/mod.rs:44-46` + `save_state.rs` | warning | `ContaminationActive` not persisted — save at tick 2000, load → contamination fires again (double tension delta) |
| 4 | `contamination.rs:47` | suggestion | Test name says "after 1801 ticks" but activation is at tick 1800 |
| 5 | `generator.rs:497` | suggestion | `BlockSkeleton` has no `Default` impl — future test fixtures will be verbose |
| 6 | `fuzzy_map.rs:277-313` | suggestion | Individual invariant tests duplicate combined test — 5x eval per seed |
| 7 | `zone.rs` | suggestion | No test for zone crossing through unzoned tile (`Some(id) → None`) |
### Tyre (Architecture): REQUEST_CHANGES
Correct D-110/D-112 compliance, clean BTreeMap discipline. Two critical issues.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `main.rs:139-149` | **critical** | `StorytellerPlugin` defined but never registered — contamination never fires in production. D-027 success criterion fails. |
| 2 | `save_state.rs:70` | **critical** | `ContaminationActive` not in `SaveStateV1` — save/load resets timer, double-fires contamination, corrupts triangle tension |
| 3 | `simulation/mod.rs:129` | warning | `detect_zone_crossings` missing `.before(compute_observer_snapshot)` — one-tick lag in audio crossfade at zone boundaries |
| 4 | `generator.rs:347` | warning | `z_bands_connected: Vec<u8>` — needs doc comment clarifying band indices vs z-level coordinates (D-110 compliance) |
| 5 | `contamination.rs:47` | suggestion | Test name mismatches behavior (1801 vs 1800) |
| 6 | `fuzzy_map.rs:32` | suggestion | Comment arithmetic wrong ("5-10 tiles" but means per side) |
| 7 | `storyteller/mod.rs:45` | suggestion | `ContaminationActive` missing `Serialize, Deserialize` derives |
### Verdict: CHANGES REQUESTED
**Critical (must fix):**
1. **Register `StorytellerPlugin` in `main.rs`** — without it, contamination never fires in production
2. **Persist `ContaminationActive` in `SaveStateV1`** — add field with `#[serde(default)]` to prevent double-fire on load
**Warnings:**
3. Add `.before(compute_observer_snapshot)` to `detect_zone_crossings` system ordering
4. Doc comment on `z_bands_connected` clarifying band indices vs z-level coordinates
5. `MIN_WALKABLE_TILES` floor not structurally guaranteed
6. Misleading D-111/D-112 citation on modification module
No new commits since previous review. Original verdict stands: CHANGES REQUESTED.
Unresolved critical issues:
StorytellerPlugin not registered in main.rs — contamination never fires in production. D-027 success criterion fails.
ContaminationActive not in SaveStateV1 — save/load resets timer, double-fires contamination, corrupts triangle tension.
Unresolved warnings:
detect_zone_crossings missing .before(compute_observer_snapshot) — one-tick lag at zone boundaries
z_bands_connected: Vec<u8> needs doc comment clarifying band indices vs z-level coordinates (D-110)
Server team: please push fix commits addressing at minimum the two critical issues before requesting re-review.
## Re-review: server → main (round 2)
No new commits since previous review. **Original verdict stands: CHANGES REQUESTED.**
### Unresolved critical issues:
1. **`StorytellerPlugin` not registered in `main.rs`** — contamination never fires in production. D-027 success criterion fails.
2. **`ContaminationActive` not in `SaveStateV1`** — save/load resets timer, double-fires contamination, corrupts triangle tension.
### Unresolved warnings:
3. `detect_zone_crossings` missing `.before(compute_observer_snapshot)` — one-tick lag at zone boundaries
4. `z_bands_connected: Vec<u8>` needs doc comment clarifying band indices vs z-level coordinates (D-110)
Server team: please push fix commits addressing at minimum the two critical issues before requesting re-review.
The constant was supposed to represent 30 game-minutes but the formula
was wrong (30 × 10 tps × 60s = 1800). Correct derivation: 30 minutes ×
TICKS_PER_GAME_MINUTE (10) = 300. Now uses the canonical constant
directly. Also fixes stale assertion message in integration test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three save/load bugs fixed:
- ContaminationActive not persisted in SaveStateV1 — caused double-fire
of contamination pressure on reload after tick 300.
- Loaded triangle entities missing ActiveSim marker — made them
invisible to escalation and contamination systems after any load.
- Existing triangle entities not despawned before load — created
duplicates, doubling tension escalation per tick.
Also: HashSet → BTreeSet for D-010 compliance, defensive event queue
reset on load, and three regression tests for triangle roundtrip.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Register StorytellerPlugin in main() and dump_schedule_graph() so
contamination system runs in production (critical, rounds 2+3)
- Add doc comment to z_bands_connected clarifying band indices vs
absolute z-levels (D-110)
- Add TODO on hardcoded modifications: vec![] in save_to_file
- Init ContaminationActive in minimal_world() test helper
- Replace ChaCha20Rng with SimRng in fuzzy_map tests (D-010)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
StorytellerPlugin registered in main() (L150) and dump_schedule_graph() (L327)
modifications: vec![] has TODO comment
z_bands_connected has doc comment distinguishing band indices from z-levels
minimal_world() inits ContaminationActive
Tyre (Architecture): APPROVE
Round 3 critical resolved. D-010 determinism, D-087 triangle classification, D-089 no cascade — all respected. CONTAMINATION_DELAY_TICKS = 300 correctly maps to 30 game-minutes per D-027.
Verdict: APPROVED
## Review: server → main (type: code, round 4)
### Hoshe (Code Quality): APPROVE
All four round 3 issues confirmed fixed:
- `StorytellerPlugin` registered in `main()` (L150) and `dump_schedule_graph()` (L327)
- `modifications: vec![]` has TODO comment
- `z_bands_connected` has doc comment distinguishing band indices from z-levels
- `minimal_world()` inits `ContaminationActive`
### Tyre (Architecture): APPROVE
Round 3 critical resolved. D-010 determinism, D-087 triangle classification, D-089 no cascade — all respected. `CONTAMINATION_DELAY_TICKS = 300` correctly maps to 30 game-minutes per D-027.
### Verdict: APPROVED
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Sprint 22 server-side delivery: wires authored Sova content into production and activates the contamination pacing layer.
Test Results
Test Plan
cargo test— full suite greencargo clippy— no new warningsReview: server → main (type: code)
Hoshe (Code Quality): REQUEST_CHANGES
Well-structured sprint with solid test coverage. Three warnings.
modification.rs:1,3fuzzy_map.rs:32MIN_WALKABLE_TILES = 200not structurally guaranteed — a seed outside 0..50 could fail CIstoryteller/mod.rs:44-46+save_state.rsContaminationActivenot persisted — save at tick 2000, load → contamination fires again (double tension delta)contamination.rs:47generator.rs:497BlockSkeletonhas noDefaultimpl — future test fixtures will be verbosefuzzy_map.rs:277-313zone.rsSome(id) → None)Tyre (Architecture): REQUEST_CHANGES
Correct D-110/D-112 compliance, clean BTreeMap discipline. Two critical issues.
main.rs:139-149StorytellerPlugindefined but never registered — contamination never fires in production. D-027 success criterion fails.save_state.rs:70ContaminationActivenot inSaveStateV1— save/load resets timer, double-fires contamination, corrupts triangle tensionsimulation/mod.rs:129detect_zone_crossingsmissing.before(compute_observer_snapshot)— one-tick lag in audio crossfade at zone boundariesgenerator.rs:347z_bands_connected: Vec<u8>— needs doc comment clarifying band indices vs z-level coordinates (D-110 compliance)contamination.rs:47fuzzy_map.rs:32storyteller/mod.rs:45ContaminationActivemissingSerialize, DeserializederivesVerdict: CHANGES REQUESTED
Critical (must fix):
StorytellerPlugininmain.rs— without it, contamination never fires in productionContaminationActiveinSaveStateV1— add field with#[serde(default)]to prevent double-fire on loadWarnings:
3. Add
.before(compute_observer_snapshot)todetect_zone_crossingssystem ordering4. Doc comment on
z_bands_connectedclarifying band indices vs z-level coordinates5.
MIN_WALKABLE_TILESfloor not structurally guaranteed6. Misleading D-111/D-112 citation on modification module
Re-review: server → main (round 2)
No new commits since previous review. Original verdict stands: CHANGES REQUESTED.
Unresolved critical issues:
StorytellerPluginnot registered inmain.rs— contamination never fires in production. D-027 success criterion fails.ContaminationActivenot inSaveStateV1— save/load resets timer, double-fires contamination, corrupts triangle tension.Unresolved warnings:
detect_zone_crossingsmissing.before(compute_observer_snapshot)— one-tick lag at zone boundariesz_bands_connected: Vec<u8>needs doc comment clarifying band indices vs z-level coordinates (D-110)Server team: please push fix commits addressing at minimum the two critical issues before requesting re-review.
Review: server → main (type: code, round 3)
Hoshe (Code Quality): REQUEST_CHANGES
ContaminationActive save/load fixed, CONTAMINATION_DELAY_TICKS corrected. But the #1 critical from round 2 remains unresolved.
StorytellerPluginSTILL not registered in app builder. Contamination never fires in production. Same issue as round 2 — unfixed.modificationshardcoded tovec\![]insave_to_file— no TODO comment for when DLC populates it.z_bands_connected: Vec<u8>still has no doc comment clarifying band indices vs absolute z-levels.Tyre (Architecture): REQUEST_CHANGES
Same blocking issue — StorytellerPlugin absent from production app.
StorytellerPluginnot in app builder ordump_schedule_graph(). Tests pass because they wire systems manually — production never runs them.minimal_world()test helper doesn't initContaminationActive— tests rely onget_resourcefallback.z_bands_connecteddoc comment clarifying band index vs z-level.ChaCha20Rngdirectly instead ofSimRng— inconsistent with D-010.Round 2 issue status:
StorytellerPluginnot registeredContaminationActivenot in SaveStateV1detect_zone_crossingsorderingz_bands_connecteddoc commentVerdict: CHANGES REQUESTED
The
StorytellerPluginregistration has been flagged as critical in two consecutive rounds. One-line fix:app.add_plugins(StorytellerPlugin)inmain().Review: server → main (type: code, round 4)
Hoshe (Code Quality): APPROVE
All four round 3 issues confirmed fixed:
StorytellerPluginregistered inmain()(L150) anddump_schedule_graph()(L327)modifications: vec![]has TODO commentz_bands_connectedhas doc comment distinguishing band indices from z-levelsminimal_world()initsContaminationActiveTyre (Architecture): APPROVE
Round 3 critical resolved. D-010 determinism, D-087 triangle classification, D-089 no cascade — all respected.
CONTAMINATION_DELAY_TICKS = 300correctly maps to 30 game-minutes per D-027.Verdict: APPROVED
Pull request closed