From 068df27c7da79eac30fac0d416c5c24ba4418338 Mon Sep 17 00:00:00 2001 From: Parley Hatch Date: Thu, 28 May 2026 21:22:42 -0600 Subject: [PATCH] chore: orchestration bookkeeping through Task 6 (progress, ledger, conventions) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../state/progress.md | 29 +++++++++++++++++-- .../state/smell-ledger.md | 4 ++- .dev/conventions.md | 5 ++-- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.dev/2026-05-28-procgen-map-core/state/progress.md b/.dev/2026-05-28-procgen-map-core/state/progress.md index 59b52f3..11c81de 100644 --- a/.dev/2026-05-28-procgen-map-core/state/progress.md +++ b/.dev/2026-05-28-procgen-map-core/state/progress.md @@ -4,7 +4,7 @@ Orchestrator's external memory. One entry per task as it completes. ## Environment notes - Rust 1.96.0 stable. `~/.cargo/bin` NOT on PATH — prepend `export PATH="$HOME/.cargo/bin:$PATH"` in every cargo shell call. -- Resolved dep versions: _TBD by Task 1 (record rand major here — drives RNG API)._ +- Resolved dep versions: rand 0.10.1, rand_chacha 0.10.0, rand_core 0.10.1, serde 1.0.228 (derive), macroquad 0.4.15. **rand 0.10 line** — RNG task must target 0.10 API (prefer building helpers on RngCore::next_u64). ## Execution schedule - Phase 0: Task 1 (scaffold) — direct dispatch, de-risk toolchain/version resolution. @@ -17,7 +17,32 @@ Orchestrator's external memory. One entry per task as it completes. ## Task log -_(none yet)_ +### Task 1 — Workspace scaffold ✅ (commit 08e99e3) +- Two-crate workspace, resolver "2", edition 2021. core deps: rand/rand_chacha/serde(derive); viz deps: macroquad + reikhelm-core (path). `cargo build`+`cargo test` green (0 tests). lib.rs has only crate doc; main.rs plain `fn main()` stub. `.dev/` plan committed into repo root (fine for greenfield). No rendering dep in core ✓. + +### Tasks 2–5 — core library layer ✅ (commits f707bd1, dbfd44a, 6ffe0cc, 659ef00) +Ran as Phase 1 workflow (sequential 2→3→4→5). All 41 tests green, no warnings. Reviewed all source directly. +- **T2 geometry**: Point/Rect/Line + extras (OFFSETS4/8, offset, right/bottom/is_empty, sign). Fixed iteration orders for determinism. Point derives Hash. Line::cells handles diagonal as connected staircase (documented). +- **T3 grid**: bounds-safe Grid, row-major, OOB read=None/write=no-op. iter_rect avoids Rect::iter borrow lifetime. Now also derives PartialEq/Eq (see fix below). +- **T4 rng**: ChaCha8 wrapper. `fork` mix = splitmix64(basis) → FNV-1a over label bytes → splitmix64 finalizer. NO DefaultHasher. fork(&self) order-independent, doesn't mutate parent. range=half-open w/ rejection sampling; shuffle=hand-rolled Fisher–Yates on next_u64; chance clamps + 53-bit. **Adversarial RNG verify never ran (worktree error) — I reviewed it manually: contract holds.** +- **T5 data model**: Tile{Wall(default),Floor,Door}; RegionId(pub usize) id==index invariant; RegionKind{Room,Corridor}; Region/Edge fields pub; Edge.at=Option; ConnGraph new/add_edge/edges/edges_mut/neighbors; Map all-pub; to_ascii `#`/`.`/`+`. + +### Orchestrator fix — derive PartialEq/Eq on Grid + Map ✅ (commit 8c4ef2e) +T5 deliberately skipped Eq on Map (Grid wasn't Eq) and used a serde-string compare workaround. Added the derives at the data-model layer so Tasks 6 & 12 determinism tests can use `assert_eq!(map_a, map_b)` directly. Purely additive, 41 tests still green. + +### ⚠️ Environment constraint discovered +**Worktree isolation is UNAVAILABLE** (`WorktreeIsolationError: not in a git repository and no WorktreeCreate hooks configured`) — even though `git rev-parse` confirms a valid repo. Do NOT use `isolation: 'worktree'` in workflows or Agent calls. Phase 3 (parallel passes 7–11) must use a different anti-race strategy: orchestrator pre-stubs all 5 pass files + pre-wires passes/mod.rs, commits, then passes are implemented — likely SEQUENTIALLY in the shared tree (or with each agent told to only Write its file + not run concurrent cargo) to avoid build races. Re-decide at Phase 3. + +### Task 6 — generation engine ✅ (commit e0a20f7) +Direct dispatch + my full code review (skipped separate validator — keystone read line-by-line; Task 12 integration tests are the end-to-end backstop). 53 tests green, clippy-clean. +- `Pass::apply(&self, ctx, rng)` exact. `GenContext{tiles,regions,graph,blackboard}` + `add_region`→`RegionId(len())`. `Snapshot{label,tiles,regions,edges}`. `Pipeline::new(w,h)/then/run/run_with_snapshots`. +- **RNG isolation correct**: per-NAME occurrence counter (`HashMap<&str,u32>`), key `"name#occ"`, `root.fork(key)` per pass. Isolation test inserts draw-then-discard X between A,B and proves their streams unchanged. ✓ +- `run`/`run_with_snapshots` share private `run_inner(seed, collect)` → cannot diverge. Snapshots clone-after-apply, RNG-untouched. ✓ +- `Blackboard` HashMap>; wrong-type get→None, take non-destructive. ✓ +- `passes/mod.rs` created with contract doc comment, NO pub mod lines (7–11 append). + +## 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. ## Open concerns - Determinism is the highest-risk surface: RNG fork stability (T4) + pipeline per-pass forking (T6). Both get adversarial verification. diff --git a/.dev/2026-05-28-procgen-map-core/state/smell-ledger.md b/.dev/2026-05-28-procgen-map-core/state/smell-ledger.md index 3efa715..2d5db08 100644 --- a/.dev/2026-05-28-procgen-map-core/state/smell-ledger.md +++ b/.dev/2026-05-28-procgen-map-core/state/smell-ledger.md @@ -2,4 +2,6 @@ Patterns surfaced by implementers/reviewers. Format: pattern — where found — where fixed — where it remains. -_(none yet)_ +- **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 + Map (commit 8c4ef2e). Downstream (T6/T12) should use `assert_eq!` on Maps, not reinvent the workaround. +- **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. diff --git a/.dev/conventions.md b/.dev/conventions.md index 3e3b325..8a077ff 100644 --- a/.dev/conventions.md +++ b/.dev/conventions.md @@ -12,9 +12,10 @@ Ground truth every dispatched agent reads. Plan lives at `.dev/2026-05-28-procge Put this at the front of the same Bash invocation as your cargo command. If you forget it you'll get `command not found: cargo`. - Run all cargo commands from the workspace root `/Users/caspar/Projects/reikhelm`. -## Dependency versions +## Dependency versions (RESOLVED by Task 1) -`rand`/`rand_chacha` are pinned by Task 1 — check `reikhelm-core/Cargo.toml` for the exact major version before writing RNG code. **rand 0.9 changed the API vs 0.8** (`gen_range`→`random_range`, `thread_rng`→`rng`, `SeedableRng` still `from_seed`/`seed_from_u64`). Match the API to the resolved version; `cargo build` is the arbiter. +- `rand` **0.10.1**, `rand_chacha` **0.10.0**, `rand_core` **0.10.1**, `serde` **1.0.228** (derive), `macroquad` **0.4.15**. +- This is the **rand 0.10** line — newer than 0.8/0.9. Verify exact API paths against the compiler: `ChaCha8Rng` implements `SeedableRng` (`seed_from_u64(u64)` / `from_seed([u8;32])`); `rand_core::RngCore` gives `next_u32`/`next_u64`. To minimize exposure to rand's churning trait paths (`SliceRandom`→`IndexedRandom`/`IndexedMutRandom` across 0.8→0.9), prefer building `range`/`chance`/`choose`/`shuffle` directly on `RngCore::next_u64` (e.g. Fisher–Yates shuffle from our own `range`) — also more transparent for determinism. `cargo build` is the arbiter. ## Git