feat(simulation): BuildingExteriorTag grammar + Layer-5 material fill + ops-surface (T-988/T-959/T-1097)

T-988 — BuildingExteriorTag {wall_material, roof_form, facade_rhythm,
setback_tier, color:HsvColor, street_surface}. New trait_exterior.rs:
3-step era-free derivation (visual_bundle filter -> zone-bias weighted
pick -> density->setback), color seed-sampled within the register band.
Resolved at GenerateSkeleton plan time (T-994 precedent), frozen on
BuildingPropertyTag; FillChunk only reads it. Four append-only
integer-discriminant enums (WallMaterial/RoofForm/FacadeRhythm/
StreetSurface), unknown token -> axis Generic + warn. trait_catalog_reader
now parses visual_bundle + the two new tables (OnceLock-cached).
New SeedDomain::TraitExterior (per-axis sub-chains — no cross-field
correlation).

T-959 — FillChunk reads the frozen exterior tag: wall_material/roof_form
onto Wall/Roof shell voxels (FilledChunk.surface_material). Additive,
ShellVoxel untouched, all shell tests pass. (Interstitial-fill-from-setback
split to T-1098 — needs a FillChunk block-metadata + geometry design pass;
nothing consumes FillChunk until Phase 5.)

T-1097 — BlockSkeleton.interstitial_character (OpenSpace|OperationsSurface),
D-233 bulk-driven: bulk-industry blocks' non-roofed remainder tags as built
economic infrastructure, not generic open space.

cargo check --all-targets clean; 1666 lib tests pass; no golden impact
(harnesses top out at CascadeLayer::RoadGraph, unreachable by Layer 4/5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 09:42:20 +02:00
co-authored by Claude Fable 5
parent ba781f9324
commit 204359927a
11 changed files with 1777 additions and 29 deletions
+8
View File
@@ -139,6 +139,13 @@ pub enum SeedDomain {
/// domain so the rare-wildcard roll can never correlate with the zone/era/
/// extent draws sharing that chain.
TraitSwerve = 15,
/// Per-building D-235 exterior-grammar draw (`BuildingExteriorTag`, T-988):
/// wall/roof/facade/street token picks + color HSV sample. Derived off the
/// footprint's own chain (like `TraitSwerve`), keyed per axis (0=wall,
/// 1=roof, 2=facade, 3=street, 4=hue, 5=sat, 6=val) so each axis draws
/// independent entropy — the `assign_block_tags` lesson (distinct
/// sub-chains per field, not one shared roll) applies here too.
TraitExterior = 16,
}
/// A position in the deterministic seed tree (D-224).
@@ -303,6 +310,7 @@ mod tests {
assert_eq!(SeedDomain::TraitVocabulary as u64, 13);
assert_eq!(SeedDomain::TraitDistrict as u64, 14);
assert_eq!(SeedDomain::TraitSwerve as u64, 15);
assert_eq!(SeedDomain::TraitExterior as u64, 16);
}
#[test]