feat(simulation): FillChunk shell derivation — Void/Wall/FloorSlab/Roof from cached tags (T-987)

Implement the D-230 on-demand derive phase (data-production half). New
atlas/shell.rs derives a sparse building shell for one 64 m chunk from the
plan-phase BuildingPropertyTags: rectangle-containment (footprint ∩ chunk) ×
z-range (per-floor voxel bands from FloorExtent), in a quarter-ground z frame
(D-110). Walls on the footprint perimeter, FloorSlab on interior floor bases,
Roof above the top floor; interior air is Void and never stored.

- gen_queue.rs: GenWorkItem::FillChunk carries pre-resolved block_tags +
  block_pos + sub_chunk (run_work_item stays cache-free, mirroring
  GenerateSkeleton); GenCompletion::ChunkFilled carries Box<FilledChunk>; the
  arm calls shell::fill_chunk; pure build_fill_chunk_item(&QuarterWorldState,..)
  added.
- plugin.rs: ChunkFilled handler accepts the shell (trace-only — the Phase-5
  rendering consumer and the on-demand streaming dispatch trigger are deferred;
  do not build on legacy chunk_streaming.rs before Phase 5, gated by T-962).

Tests: determinism, sparsity-vs-dense-volume, wall/floor/roof correctness,
basement z-origin, sub-chunk clipping, full submit→drain→ChunkFilled round-trip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 23:34:46 +02:00
co-authored by Claude Opus 4.8
parent 85311f716c
commit 5f85dc55d5
6 changed files with 630 additions and 13 deletions
+15
View File
@@ -2616,3 +2616,18 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP5N42SSGW300FEDTS3M', 'status', 'in_progress', 'review', NULL, '2026-06-17 07:48:14', '2026-06-17 07:48:14.920', '2026-06-17 07:48:14.920', NULL, 'e7115e66b6bd755538db65e42fbc162d', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPQ0381QWDAQABZAY7MRD8M', 'status', 'review', 'done', NULL, '2026-06-17 08:14:43', '2026-06-17 08:14:43.302', '2026-06-17 08:14:43.302', NULL, 'f90f10ea8bfed4aceb71c55b391bc153', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBPTXP5N42SSGW300FEDTS3M', 'status', 'review', 'done', NULL, '2026-06-17 08:14:43', '2026-06-17 08:14:43.306', '2026-06-17 08:14:43.306', NULL, '1f6b136fdd32b361532caee73a6df1c7', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYAE6MPE7720360WM4', 'status', 'backlog', 'in_progress', NULL, '2026-06-27 19:17:27', '2026-06-27 19:17:27.762', '2026-06-27 19:17:27.762', NULL, 'e7130599a19541fca24b52697172b604', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYAE6MPE7720360WM4', 'description', 'On-demand derive phase: read cached DistrictWorldState; per tag, shell_derive(seed, footprint, extent, z) -> Void|Wall|FloorSlab|Roof per voxel. Pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Budget ~0.4-0.6ms/chunk.', 'On-demand derive phase: read cached DistrictWorldState; per tag, shell_derive(seed, footprint, extent, z) -> Void|Wall|FloorSlab|Roof per voxel. Pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Budget ~0.4-0.6ms/chunk.
---
Implemented 2026-06-27 (branch fillchunk-shell-derive) as the DATA-PRODUCTION half of the D-230 on-demand derive phase:
- server/src/atlas/shell.rs (new): ShellVoxel{Void,Wall,FloorSlab,Roof} (repr(u8), append-only D-010) + FilledChunk (sparse BTreeMap, only non-Void stored) + pure fill_chunk(). Rectangle-containment (footprint ∩ chunk) × z-range (per-floor voxel bands from FloorExtent). Quarter-ground z origin (D-110: ground floor bottom = 0, basements negative). Walls on footprint perimeter, FloorSlab on interior floor bases, Roof one voxel above top floor.
- gen_queue.rs: GenWorkItem::FillChunk now carries pre-resolved block_tags + block_pos + sub_chunk (run_work_item is cache-free, mirrors GenerateSkeleton); GenCompletion::ChunkFilled carries Box<FilledChunk>; arm calls fill_chunk; added pure build_fill_chunk_item(&QuarterWorldState,..).
- plugin.rs: ChunkFilled handler accepts the shell (trace-only — no Phase-5 consumer yet).
SCOPE DECISION (deviates from the stale T-959 refinement note): the on-demand streaming DISPATCH TRIGGER ("wire chunk_streaming.rs to enqueue FillChunk") is DEFERRED to Phase 5. chunk_streaming.rs is the legacy Phase-5 rendering path (32-tile WalkabilityMap chunks); CLAUDE.md forbids building on the in-world rendering code before Phase 5, and the trigger is gated by T-962. fill_chunk is re-derivable (D-227) so no storage/consumer is required now.
OUT OF SCOPE (other T-959 children): interstitial street/open-space fill + D-235 surface material vocabulary (WallMaterial/RoofForm/StreetSurface) = T-988; condition overlay = T-999.
Tests: determinism, sparsity-vs-dense-volume (budget proxy — no flaky wall-clock asserts), wall/floor/roof correctness, basement z-origin, sub-chunk clipping, full submit→drain→ChunkFilled round-trip. cargo test (1571 lib) + clippy -D warnings + fmt all green.', NULL, '2026-06-27 21:34:18', '2026-06-27 21:34:18.369', '2026-06-27 21:34:18.369', NULL, '35133a94263116cb59fcaee570ea4630', 2) ON CONFLICT(hash) DO NOTHING;
+15
View File
@@ -3533,3 +3533,18 @@ CURRENT LOCATIONS: derive_basin_direction = chunk_context.rs:453-483 (false comm
RESOLUTION (D-239 §8 BINDS the D8 thalweg → flow_accumulation alone is insufficient; use the true fdir): in run_layer1 (where fdir is still alive, before it''s discarded), aggregate the dominant D8 direction PER DISTRICT and carry that small per-district direction map on Layer1Output — which composes directly with T-1044''s Layer1Output caching. Thread it through derive_all_districts → derive_district_profile → DistrictProfile.basin_direction; derive_chunk_context reads district.basin_direction. Fix the false doc comment + module contract either way. Do NOT expose the full 131KB fdir grid on DrainageResult — aggregate to per-district direction in the pass.
COMPOSE WITH T-1044 (same batch, same layer1.rs/cascade.rs flow). Acceptance: chunk-scale channels respect the covering district''s D8 thalweg (flow downhill, connect across the chunk/region-seed boundary); false comment corrected; golden-seed determinism preserved.', 'done', 'medium', NULL, 'server', 'D-239', '2026-06-12 10:40:58', '2026-06-17 08:14:43.306', NULL, 'e221e440c3be0db6d0de73a2544dfb9d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYAE6MPE7720360WM4', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'FillChunk shell derivation <5ms (Void/Wall/FloorSlab/Roof from cached tags)', 'On-demand derive phase: read cached DistrictWorldState; per tag, shell_derive(seed, footprint, extent, z) -> Void|Wall|FloorSlab|Roof per voxel. Pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Budget ~0.4-0.6ms/chunk.', 'in_progress', 'high', NULL, 'server', 'D-230', '2026-05-25 19:11:17', '2026-06-27 19:17:27.756', NULL, 'c3f6dbae1f1cc01e40bfb703411b170f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYAE6MPE7720360WM4', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'FillChunk shell derivation <5ms (Void/Wall/FloorSlab/Roof from cached tags)', 'On-demand derive phase: read cached DistrictWorldState; per tag, shell_derive(seed, footprint, extent, z) -> Void|Wall|FloorSlab|Roof per voxel. Pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Budget ~0.4-0.6ms/chunk.
---
Implemented 2026-06-27 (branch fillchunk-shell-derive) as the DATA-PRODUCTION half of the D-230 on-demand derive phase:
- server/src/atlas/shell.rs (new): ShellVoxel{Void,Wall,FloorSlab,Roof} (repr(u8), append-only D-010) + FilledChunk (sparse BTreeMap, only non-Void stored) + pure fill_chunk(). Rectangle-containment (footprint ∩ chunk) × z-range (per-floor voxel bands from FloorExtent). Quarter-ground z origin (D-110: ground floor bottom = 0, basements negative). Walls on footprint perimeter, FloorSlab on interior floor bases, Roof one voxel above top floor.
- gen_queue.rs: GenWorkItem::FillChunk now carries pre-resolved block_tags + block_pos + sub_chunk (run_work_item is cache-free, mirrors GenerateSkeleton); GenCompletion::ChunkFilled carries Box<FilledChunk>; arm calls fill_chunk; added pure build_fill_chunk_item(&QuarterWorldState,..).
- plugin.rs: ChunkFilled handler accepts the shell (trace-only — no Phase-5 consumer yet).
SCOPE DECISION (deviates from the stale T-959 refinement note): the on-demand streaming DISPATCH TRIGGER ("wire chunk_streaming.rs to enqueue FillChunk") is DEFERRED to Phase 5. chunk_streaming.rs is the legacy Phase-5 rendering path (32-tile WalkabilityMap chunks); CLAUDE.md forbids building on the in-world rendering code before Phase 5, and the trigger is gated by T-962. fill_chunk is re-derivable (D-227) so no storage/consumer is required now.
OUT OF SCOPE (other T-959 children): interstitial street/open-space fill + D-235 surface material vocabulary (WallMaterial/RoofForm/StreetSurface) = T-988; condition overlay = T-999.
Tests: determinism, sparsity-vs-dense-volume (budget proxy — no flaky wall-clock asserts), wall/floor/roof correctness, basement z-origin, sub-chunk clipping, full submit→drain→ChunkFilled round-trip. cargo test (1571 lib) + clippy -D warnings + fmt all green.', 'in_progress', 'high', NULL, 'server', 'D-230', '2026-05-25 19:11:17', '2026-06-27 21:34:18.368', NULL, '558c202a0f38cd43859bd807a5793265', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);