feat(simulation): VoxelColumn walking skeleton (T-1028) #161

Closed
jpmschweitzer wants to merge 0 commits from voxel-walking-skeleton into main
Owner

Summary

T-1028 — the walking skeleton of the lower derivation chain (D-239 §1/§7/§10): RegionProfile → ChunkContext → VoxelColumn → tile, end-to-end, with the AlluvialPlain fallback generator only. The first time generated upper-cascade data becomes actual composed tiles. The 7 specialized family generators are the next ticket (T-1029).

New carriers

  • chunk_context.rsChunkContext (64 m): basin_direction (cardinal) + meander_phase/meander_wavelength_m + active-channel params. Per D-239 §10, no per-tile flow_direction[64×64] (D8 ~152 m/cell is coarser than a chunk); features > 64 m seed from Region-or-higher. New SeedDomain::ChunkContext = 8 (append-only, pinned).
  • voxel.rsVoxelColumn (1 m): composes the D-228 tile axesTerrainMaterial (Soil/Sand/Gravel/Rock/Wetland/Lava), FloorMaterial (None in Phase 4), Vegetation (from the region's vegetation_class), Water (Dry/Shallow/Deep), elevation_m (integer). Snow/Ice are deferred to the seasonal overlay (Q-105), not materials.

Mechanism

  • Derive-on-demand + cache, never persisted (D-227): VoxelCache is an LRU BTreeMap (deterministic eviction tie-break), re-derivation after eviction is identical.
  • Domain warp wired (T-1026): f64 warp → as i32 truncation to the voxel address; D-010 preserved — the meander wavelength f64 is truncated to integer before the Water decision; every material/morphology gate is integer.
  • AlluvialPlain generator: Soil/Wetland terrain, climate-derived vegetation scatter, integer-meander channel (Shallow/Deep) from ChunkContext. The other 7 families are dispatch stubs (canonical terrain material, fall back to AlluvialPlain geometry) — no panic!/unimplemented!; production can't crash. The zone_to_family + match family dispatch is the T-1029 slot-in point.

Tests

29 new (chunk_context 8, voxel 21) + updated seed pin: end-to-end determinism (same seed/pos → identical column), cache hit/miss/eviction/re-derivation, warp-applied, AlluvialPlain material/elevation properties, all 7 family stubs no-panic.

Verification

cargo test 1563 pass, clippy --all-targets -D warnings clean, fmt clean (pre-push gate, incl. cargo test, ran on push). D-010 integer discipline throughout (warp is the only f64).

Ticket: T-1028 (epic T-974, Phase 4). Unblocks T-1029 (specialized geometry generators).

🤖 Generated with Claude Code

## Summary **T-1028** — the walking skeleton of the lower derivation chain (D-239 §1/§7/§10): **RegionProfile → ChunkContext → VoxelColumn → tile**, end-to-end, with the **AlluvialPlain fallback generator only**. The first time generated upper-cascade data becomes actual composed tiles. The 7 specialized family generators are the next ticket (T-1029). ### New carriers - **`chunk_context.rs` — `ChunkContext` (64 m):** `basin_direction` (cardinal) + `meander_phase`/`meander_wavelength_m` + active-channel params. Per D-239 §10, **no per-tile `flow_direction[64×64]`** (D8 ~152 m/cell is coarser than a chunk); features > 64 m seed from Region-or-higher. New `SeedDomain::ChunkContext = 8` (append-only, pinned). - **`voxel.rs` — `VoxelColumn` (1 m):** composes the **D-228 tile axes** — `TerrainMaterial` (Soil/Sand/Gravel/Rock/Wetland/Lava), `FloorMaterial` (None in Phase 4), `Vegetation` (from the region's `vegetation_class`), `Water` (Dry/Shallow/Deep), `elevation_m` (integer). Snow/Ice are deferred to the seasonal overlay (Q-105), not materials. ### Mechanism - **Derive-on-demand + cache, never persisted** (D-227): `VoxelCache` is an LRU `BTreeMap` (deterministic eviction tie-break), re-derivation after eviction is identical. - **Domain warp wired** (T-1026): f64 warp → `as i32` truncation to the voxel address; **D-010** preserved — the meander wavelength f64 is truncated to integer *before* the Water decision; every material/morphology gate is integer. - **AlluvialPlain generator:** Soil/Wetland terrain, climate-derived vegetation scatter, integer-meander channel (Shallow/Deep) from `ChunkContext`. The other 7 families are dispatch stubs (canonical terrain material, fall back to AlluvialPlain geometry) — **no `panic!`/`unimplemented!`**; production can't crash. The `zone_to_family` + `match family` dispatch is the T-1029 slot-in point. ### Tests 29 new (chunk_context 8, voxel 21) + updated seed pin: end-to-end determinism (same seed/pos → identical column), cache hit/miss/eviction/re-derivation, warp-applied, AlluvialPlain material/elevation properties, all 7 family stubs no-panic. ### Verification `cargo test` 1563 pass, `clippy --all-targets -D warnings` clean, `fmt` clean (pre-push gate, incl. `cargo test`, ran on push). D-010 integer discipline throughout (warp is the only f64). Ticket: T-1028 (epic T-974, Phase 4). Unblocks T-1029 (specialized geometry generators). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jpmschweitzer added 1 commit 2026-06-08 14:38:11 +02:00
D-239 §1/§7/§10 — the lower derivation chain end-to-end with the AlluvialPlain
fallback only (specialized families are T-1029).

- chunk_context.rs: ChunkContext (64m) — basin_direction + meander phase/wavelength
  + active-channel params. NO per-tile flow_direction[64x64] (D8 ~152m coarser than
  a chunk, D-239 §10); features >64m seed from Region-or-higher. SeedDomain::ChunkContext=8.
- voxel.rs: VoxelColumn (1m) composing the D-228 tile axes (TerrainMaterial /
  FloorMaterial=None / Vegetation / Water / elevation_m). Derive-on-demand + VoxelCache
  (LRU BTreeMap, never persisted per D-227). Domain warp (T-1026) wired through:
  f64 warp -> as-i32 truncation to the voxel address; all material/morphology gates
  integer (D-010 — the meander wavelength f64 is truncated before the Water decision).
- AlluvialPlain generator: Soil/Wetland terrain, climate vegetation, integer-meander
  channel (Shallow/Deep) from ChunkContext. The other 7 families are dispatch stubs
  (canonical terrain material, fall back to AlluvialPlain geometry) — no panic.

29 new tests incl end-to-end determinism (same seed/pos -> identical column),
cache hit/miss/eviction/re-derivation, warp-applied. cargo test 1563 pass,
clippy -D warnings clean, fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jpmschweitzer added 1 commit 2026-06-08 14:48:29 +02:00
Hoshe REQUEST_CHANGES + Tyre APPROVE:

- Cache key omitted world_seed → a cache reused across worlds could return
  another seed's column. CacheKey is now (world_seed, body_id_hash, voxel_pos).
  (Hoshe #1)
- Sub-chunk voxel seed reused SeedDomain::ChunkContext (tier-collision risk vs
  the region meander seed). Add SeedDomain::Voxel = 9 (append-only, pin test
  updated) and use it. (Hoshe #2, Tyre #1)
- get_or_derive forced callers to pre-compute body_id_hash while fnv1a_64 is
  pub(crate). Drop the param; compute the hash internally. (Tyre #2)
- Fix two inaccurate comments (micro-relief range [-4,+3] not ±2; scatter mask
  is 4-bit [0,15] not bits [4:6]). (Hoshe #3/#4)
- Strengthen the LRU eviction test: add a test-only contains_voxel() helper and
  assert WHICH entry was evicted (refreshed pos survives, LRU pos evicted). (Hoshe #5)
- LRU O(capacity)-scan perf flagged on T-1031 (Tyre #3, deferred to the budget harness).

cargo test passes, clippy -D warnings clean, fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Owner

Review: voxel-walking-skeleton → main (type: code)

Reviewers: Hoshe (code quality) + Tyre (architecture). Source read from the branch worktree. No merge-path surface. Gate green.

Tyre (Architecture): APPROVE

Carrier chain correctly shaped — ChunkContext holds exactly D-239 §10's basin-direction + meander params (no per-tile flow grid); >64 m features seeded at region scale; clean pure-function chain. D-227 solidVoxelCache in-memory only, no serialization path, eviction forces re-derivation. Walking-skeleton scope honoured (7 family stubs fall back, no panic; no Phase-5 drift).

Hoshe (Code Quality): REQUEST_CHANGES → resolved

Structurally sound (no panics in any stub, determinism real + tested, all f64→int via as i32 casts not gates). Five findings.

Findings & resolution (841e2221)

# Reviewer Finding Resolution
1 Hoshe Cache key omitted world_seed — a cache reused across worlds (same body+pos, different seed) would return the wrong column CacheKey is now (world_seed, body_id_hash, voxel_pos).
2 Hoshe + Tyre Sub-chunk voxel seed reused SeedDomain::ChunkContext — domain-collision risk vs the region meander seed (D-224 separation) Added SeedDomain::Voxel = 9 (append-only, pin test updated); sub-chunk seed uses it.
3 Tyre get_or_derive forced callers to pre-compute body_id_hash while fnv1a_64 is pub(crate) — would bite T-1031/T-1032 Dropped the param; the hash is computed internally.
4 Hoshe Two inaccurate comments (micro-relief range, scatter bit-mask) Corrected ([-4,+3] m; 4-bit [0,15] mask).
5 Hoshe LRU eviction test checked only len(), not which entry evicted Added a test-only contains_voxel() helper; the test now asserts the refreshed entry survives and the LRU entry is the one evicted.
Tyre LRU eviction is O(capacity) scan (latent at the budget scale) Deferred to T-1031 (the budget-verification ticket) with the fix sketched in its description.

Verdict: APPROVED (after resolution)

Finding #1 was a real (latent) cache-correctness bug; the rest tightened determinism/separability, API ergonomics, and test rigor. cargo test passes, clippy -D warnings clean, fmt clean. Merging.

🤖 Generated with Claude Code

## Review: voxel-walking-skeleton → main (type: code) Reviewers: **Hoshe** (code quality) + **Tyre** (architecture). Source read from the branch worktree. No merge-path surface. Gate green. ### Tyre (Architecture): APPROVE Carrier chain correctly shaped — `ChunkContext` holds exactly D-239 §10's basin-direction + meander params (no per-tile flow grid); >64 m features seeded at region scale; clean pure-function chain. **D-227 solid** — `VoxelCache` in-memory only, no serialization path, eviction forces re-derivation. Walking-skeleton scope honoured (7 family stubs fall back, no panic; no Phase-5 drift). ### Hoshe (Code Quality): REQUEST_CHANGES → resolved Structurally sound (no panics in any stub, determinism real + tested, all f64→int via `as i32` casts not gates). Five findings. ### Findings & resolution (`841e2221`) | # | Reviewer | Finding | Resolution | |---|----------|---------|------------| | 1 | Hoshe | **Cache key omitted `world_seed`** — a cache reused across worlds (same body+pos, different seed) would return the wrong column | `CacheKey` is now `(world_seed, body_id_hash, voxel_pos)`. | | 2 | Hoshe + Tyre | Sub-chunk voxel seed reused `SeedDomain::ChunkContext` — domain-collision risk vs the region meander seed (D-224 separation) | Added `SeedDomain::Voxel = 9` (append-only, pin test updated); sub-chunk seed uses it. | | 3 | Tyre | `get_or_derive` forced callers to pre-compute `body_id_hash` while `fnv1a_64` is `pub(crate)` — would bite T-1031/T-1032 | Dropped the param; the hash is computed internally. | | 4 | Hoshe | Two inaccurate comments (micro-relief range, scatter bit-mask) | Corrected (`[-4,+3] m`; 4-bit `[0,15]` mask). | | 5 | Hoshe | LRU eviction test checked only `len()`, not *which* entry evicted | Added a test-only `contains_voxel()` helper; the test now asserts the refreshed entry survives and the LRU entry is the one evicted. | | — | Tyre | LRU eviction is O(capacity) scan (latent at the budget scale) | Deferred to **T-1031** (the budget-verification ticket) with the fix sketched in its description. | ### Verdict: APPROVED (after resolution) Finding #1 was a real (latent) cache-correctness bug; the rest tightened determinism/separability, API ergonomics, and test rigor. `cargo test` passes, clippy `-D warnings` clean, fmt clean. Merging. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jpmschweitzer closed this pull request 2026-06-08 15:18:35 +02:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jpmschweitzer/settled-reach#161