chore: record Phase 3 results + door-topology decision (Variant A, door_chance 0.5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Parley Hatch 2026-05-28 22:14:47 -06:00
parent 61aedfa0c7
commit 1d003ec5fc
2 changed files with 7 additions and 0 deletions

View file

@ -41,6 +41,12 @@ Direct dispatch + my full code review (skipped separate validator — keystone r
- `Blackboard` HashMap<String,Box<dyn Any>>; wrong-type get→None, take non-destructive. ✓ - `Blackboard` HashMap<String,Box<dyn Any>>; wrong-type get→None, take non-destructive. ✓
- `passes/mod.rs` created with contract doc comment, NO pub mod lines (711 append). - `passes/mod.rs` created with contract doc comment, NO pub mod lines (711 append).
### Phase 3 — five passes ✅ (commits for bsp/room/connect/corridor/door) + adversarial verify
All 5 passes individually PASS (101 tests green, clippy clean). Adversarial verifier found an EMERGENT corridor↔door integration bug (see smell-ledger). Per-pass notes: BSP tiles map exactly; RoomCarver skipped-leaf draws zero rng + Room.cells = room interior; MstConnect is rng-INDEPENDENT in v1 (deterministic from room set; Prim + sorted candidates, no hash order) — N-1 edges, connected under tie/collinear/dup centers; CorridorCarver center-to-center, one Corridor region per edge in edge order, draws one chance(0.5) leg-order coin per edge; DoorPlacer (original) correct in isolation but wall-threshold rule incompatible with pierced corridors.
### DECISION (user) — door topology = Variant A, door_chance 0.5
Backed by 2000-seed experiment (A: 0 orphans, ~50/50 mix; B: 66.5% orphans). DoorPlacer being REVISED to pierce-aware door-at-mouth + DoorConfig{door_chance:0.5 default}. See smell-ledger for the exact model. This is a change to a committed pass (Task 11). Recipe (Task 12) must expose door config + adjust integration expectations (doors exist; edge.at = Some only for doored edges, None for open archways).
## Phase 3 plan (REVISED — no worktrees) ## Phase 3 plan (REVISED — no worktrees)
Run passes 7→8→9→10→11 SEQUENTIALLY in shared tree (forced by no worktree isolation). Each implementer creates its file + appends its own `pub mod`/`pub use` to passes/mod.rs (sequential = no race, like Tasks 2-5 did with lib.rs). No pre-stubbing needed. Sequential also lets later passes (e.g. DoorPlacer) read earlier passes' real cell-population conventions. Then a PARALLEL adversarial verify fan-out (read-only, no isolation) over the 5 passes for algorithm/determinism/bounds bugs. Heavy integration verification deferred to Task 12. Run passes 7→8→9→10→11 SEQUENTIALLY in shared tree (forced by no worktree isolation). Each implementer creates its file + appends its own `pub mod`/`pub use` to passes/mod.rs (sequential = no race, like Tasks 2-5 did with lib.rs). No pre-stubbing needed. Sequential also lets later passes (e.g. DoorPlacer) read earlier passes' real cell-population conventions. Then a PARALLEL adversarial verify fan-out (read-only, no isolation) over the 5 passes for algorithm/determinism/bounds bugs. Heavy integration verification deferred to Task 12.

View file

@ -4,4 +4,5 @@ Patterns surfaced by implementers/reviewers. Format: pattern — where found —
- **range() i32 overflow for astronomically large spans** — rng.rs `range` does `lo + self.below(span) as i32`; spans ≳2³¹ could overflow. UNREACHABLE in a grid-coordinate generator (spans are tiny). Logged, not fixed — fixing would be over-engineering for the domain. Revisit only if a pass ever needs huge ranges. - **range() i32 overflow for astronomically large spans** — rng.rs `range` does `lo + self.below(span) as i32`; spans ≳2³¹ could overflow. UNREACHABLE in a grid-coordinate generator (spans are tiny). Logged, not fixed — fixing would be over-engineering for the domain. Revisit only if a pass ever needs huge ranges.
- **Map equality workaround (RESOLVED)** — map.rs originally compared maps via serialized-JSON strings because Map wasn't Eq. Fixed at source by deriving PartialEq/Eq on Grid<T> + Map (commit 8c4ef2e). Downstream (T6/T12) should use `assert_eq!` on Maps, not reinvent the workaround. - **Map equality workaround (RESOLVED)** — map.rs originally compared maps via serialized-JSON strings because Map wasn't Eq. Fixed at source by deriving PartialEq/Eq on Grid<T> + Map (commit 8c4ef2e). Downstream (T6/T12) should use `assert_eq!` on Maps, not reinvent the workaround.
- **INTEGRATION BUG (Phase 3 adversarial verify) — corridor/door topology conflict** — CorridorCarver carves center→center (pierces room wall ring → guarantees connectivity); DoorPlacer requires a Wall between room-floor and corridor-floor (destroyed by piercing). Result over 120 seeds of full pipeline: 88% edges no door, 36% maps zero doors. All 5 passes individually PASS (BSP perfect tiling; MST deterministic under ties/collinear/dup centers; corridor connectivity; door correct in isolation). Plan-level inconsistency between Task 10 (center-to-center) and Task 11 (wall threshold). RESOLVED by user decision (Variant A) backed by a 2000-seed experiment: A (pierce + door-at-mouth) = 0/2000 orphans, ~50/50 doors/openings at door_chance 0.5; B (stop-short walled, faithful setback=1) = 1330/2000 orphans (66.5%) — B cannot deliver walled-rooms AND connectivity. FIX = revise DoorPlacer to pierce-aware door-at-mouth with DoorConfig{door_chance} default 0.5: a door candidate is the pierced-wall cell (corridor Floor, outside all rooms, with a room-membership neighbor on one orthogonal side and corridor-Floor-outside on the opposite side); per candidate flip rng.chance(door_chance) → Door else leave open archway (Floor); set edge.at to a placed door for that edge's corridor (corridor-region i ↔ graph edge i, append order). Connectivity is via corridor floor regardless of doors → never orphans.
- **Watch in passes**: Point/Tile derive Hash — using a HashSet for flood-fill *visited* membership is fine (determinism only breaks on HashMap/HashSet *iteration order*). If any pass iterates a HashMap/HashSet to produce output, that's a determinism bug — flag it. - **Watch in passes**: Point/Tile derive Hash — using a HashSet for flood-fill *visited* membership is fine (determinism only breaks on HashMap/HashSet *iteration order*). If any pass iterates a HashMap/HashSet to produce output, that's a determinism bug — flag it.