# Phase 2 Wiring Plan (parent integration, executes when A–D return) Workstreams own their files; parent owns everything else. This doc is the exact merge contract so integration is mechanical. ## 1. `lib.rs` additions (parent edits) ```rust pub mod air; pub mod fire; pub mod fluids; pub mod gravity; pub mod laws; pub mod light; pub mod materials; pub mod rope; pub mod rot; pub mod scent; pub mod signal; pub mod sound; pub mod structure; ``` ## 2. `State` additions (parent edits `state.rs` only) - `pub laws: LawLog` — first-time law registry; consulted before pushing any hazard CauseLink (§4.10.5 "no silent rules"). - `pub fall_odo: BTreeMap` — per-actor metres descended, passed `&mut` to `gravity::step_falling_with_mass` every Move tick (total-height damage; fix-C odometer). Clear an actor's entry on teleport; entries for grounded/embedded/dead actors self-reset each call. - `pub sounds: Vec` — Sound phase drains this queue. - `pub lights: Vec` — lantern + carried glow + flora; Light phase consumes. - `pub corpses: BTreeMap<(i32,i32,i32), CorpseClock>` — Rot phase owns. - `pub spillover: Vec` — remainder queue when a phase exceeds its 4k update cap; drained first next tick. Exact `Spill` shape TBD from workstream return signatures — keep per-system spillover INSIDE each module if they already queue it; parent only wires what exists. ## 3. Phase wiring (parent edits `run_phase` only) | Phase | Call | Region scope | |-------|------|--------------| | Move | `gravity::step_falling(&mut actors, &grid, ...)` replaces settle stub | actors | | Fluids | `fluids::step_fluids(grid, region, rng, causes, tick)` | active ±72 vox | | Gas | `air::advect_markers(grid, &self.drafts(), ...)` | active ±72 vox | | Fire | `fire::step_fire(grid, region, rng, causes, tick)` | active ±72 vox | | Signal | `signal::step_signal(grid, &sources, region, ...)` (sources: worldgen live cable; test arena stubs) | active ±72 vox | | Light | `light::propagate_light_capped(grid, &self.lights, dirty_only=true)`; on lamp remove/move/douse call `clear_light_box(old box)` FIRST, then propagate; re-queue when spillover > 0 | dirty cells | | Sound | drain `self.sounds` → `sound::propagate_sound(...)`; AI investigate stub stays (Phase 9/10) | edges+nodes | | Structure | `structure::step_structure(grid, region, ...)` | dirty supports | | Rot | `rot::step_rot(...)` + `scent::step_scent(...)` | region + corpse map | Region = active 3×3×3 chunks around player actor (±72 voxels each axis). Hymns stays stubbed (Phase 8); AETHER_MIST cookoff links already emitted by fire.rs. ## 4. Conflict resolution rules (parent judges by these) 1. Design docs beat implementation: docs/03 §3.3 > subagent API choices. 2. Determinism law is absolute: any HashMap/Instant/threading found at wiring time is rejected back to the workstream, not patched by parent. 3. API mismatches between workstreams (e.g. scent emission paths assumed by fluids vs. scent's real signature) are fixed at the CALLER side in `state.rs`, never by editing a workstream's file without its consent — if the callee's shape is wrong per spec, send it back with the exact diff required. 4. `hash_state()` MUST extend to voxel contents + new maps once phases mutate them (determinism contract). Parent adds: grid contents hash (bounded: hash only loaded chunks, BTreeMap order), corpses, lights. ## 5. Phase 2 gate tests (parent writes `tests/phase2_laws.rs` AFTER wiring) 20 rule-chain integration tests. Canonical list (each asserts the causal story, not just the end state). Reviewer-ordered adjustments folded in (review-audit C/A, xhigh): - `sledge_opens_shortcut_loud` asserts the cause LINK text ("everything hears"-class line), not a `State.sounds` queue entry — no such queue exists yet; the Sound phase translates link→event at wiring. - `supported_lintel_stands` uses `extra == 0` stone (weakened rusted/cracked collapse is design-grounded law 11 and stays: rusted iron and `extra != 0` cracked stone collapse even with support). - Bowling tests place the victim ≤ 0.8 m HORIZONTALLY, expect a single victim with lander-takes-ceil split; damage-0 landings never bowl. - Mid-fall ticks assert empty events + reduced `pos.z` (still-airborne = cross-strata arrival hook, not grounded). - `boulder_down_shaft` / `thrown_potion` expectations use TOTAL-height damage (fix-workstream-c odometer), not final-tick remainder. - Gas-phase tests build `Vec<(u64, u64, f32)>` flow triples via `pack_cell` from edges + drafts + cell→volume map (NOT `drafts()` magnitudes passed raw — `drafts()` returns edge-indexed magnitudes; see MAJOR-4), call the 4-arg `advect_markers`, and re-queue `flows[budget..]` tails. - `step_structure` region args are tight ≤ 64-span dirty bounds, never the whole ±72 active volume (MAX_REGION_SPAN truncation is documented). Canonical list (each asserts the causal story, not just the end state): 1. `oil_under_door_grate_chokes_actor` (the mimic kill, generic actor) 2. `smoke_rises_through_shaft` 3. `chlorine_pools_upward_from_floor` (levitate counter demo hook) 4. `thrown_potion_arcs_and_shatters_below` 5. `boulder_down_shaft_arrives_below` (cross-strata arrival) 6. `coin_throw_lures_loudest_node` (voice-throw) 7. `rope_snaps_when_overloaded` (CHECK GRATES link present) 8. `cut_rope_drops_actor_with_fall_damage` 9. `unsupported_lintel_collapses` / 10. `supported_lintel_stands` 10. `sledge_opens_shortcut_loud` (sound event emitted) 11. `water_douses_fire_to_steam` 12. `oil_ignites_and_spreads` 13. `acid_blood_pits_iron_grate` (cable-rat story beat) 14. `verdigris_eats_copper_wire` 15. `shock_chains_through_water` (tragedy path link) 16. `corpse_bloats_bursts_spawns` (Rustmother link) 17. `wet_iron_rusts_over_time` 18. `flour_reveals_hidden_vent` (SECRET_REVEALED + link) 19. `first_time_law_logged_once` (laws.rs deduplication) Plus proptest material×hazard matrix (`tests/phase2_matrix.rs`, needs `proptest` dev-dep — parent adds AFTER workstreams land to avoid breaking their builds with a lockfile change mid-flight). ## 6. Gate acceptance (checklist Phase 2 gate) - All 12 laws demoable in test arena (`hexwork --arena` lands Phase 5; until then the 20 integration tests ARE the demo, run headless). - Every hazard emits a first-time law log line (assert via LawLog in tests). - `cargo fmt --check`, `clippy -D warnings`, 24+ pre-existing tests still green, 10k-tick soak stable with NEW hash (hash WILL change — voxel contents join it; record new canonical hash in soak test comment). - Tick ≤ 8ms p99 narrative: criterion bench extended to a populated arena (parent adds `benches/arena.rs` in Phase 13 perf harness; for now assert 10k-tick soak wall time < 80s headless as a smoke bound).