docs(decisions): economic-built-world workshop — fill-seam records + trait-template flavor model
Rounds 2-3 of the economic & built-world workshop (Phase 4 fill seam). New records: D-229 building-property-tag schema (resolves Q-104 via FloorExtent), D-230 FillChunk two-phase execution, D-231 DoorSpec/InteriorDescriptor (the Phase-6 interior seed), D-232 architecture-flavor as an economically-gated / seed-drawn / wiki-biased trait-template catalog, D-233 economic block-fill (BulkClass x ProductionUbiquity), D-234 morphology->street/footprint constraints, D-235 exterior visual grammar + asset fallback hierarchy. Amended D-097/D-198/D-199/D-184; superseded in part D-101-A/D-104/D-105/D-107. Q-104 + Q-106 resolved; Q-107 (wiki->Atlas content consolidation) opened. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+122
-3
@@ -266,6 +266,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- **Raised by:** Gestalt (tier structure + assassin lens integration), Tyre (GuaranteeAuditResult struct). Full team sign-off.
|
||||
- **Dissent:** None.
|
||||
- **Cross-reference:** D-103 (assassin lens guarantees A-1 through A-4), D-102 (horizon view corridor — Tier 2 coastal)
|
||||
- **Amended 2026-05-25 (D-229/D-231 — guarantee-audit engine hook):** The audit runs AFTER step-3 fill completes for a district, before the skeleton is marked "complete"; on a failed mandatory check for the district's `world_tier`, fill regenerates with adjusted parameters. Signature: `run_guarantee_audit(skeleton: &DistrictSkeleton, building_tags: &[BuildingPropertyTag], street_graph: &StreetGraph, seed: SeedChain) -> GuaranteeAuditResult`. Tier-1 checks (all inhabited): `social_hub_present` (≥1 building with entry_class ∈ {Public, Commercial} AND zone ∈ entertainment/market/transit); `informal_zone_present` (≥1 Organic + Public, or open ground in an Organic district); `encounter_corridor` (street graph has a continuous Public traversal path crossing the district). Tier-2 checks (full-complexity): `traffic_chokepoint` (a bottleneck node whose removal disconnects the graph); `institutional_space` (admin/judicial/checkpoint, Restricted); `insider_space` (Restricted + Hidden or non-primary-street door); `economic_node` (≥1 Commercial); `breach_only_zone` (≥1 BreachOnly). Tier-3 / A-1..A-4 (pure graph/geometry over step-3 tags + street graph — NO interior generation): A-1 elevated vantage (max above_ground ≥3 with unobstructed LOS to a chokepoint); A-2 egress multiplicity (≥2 distinct unique-street connections across the perimeter); A-3 temporal opacity (≥1 TemporalWindow door → reduced traffic in closed hours); A-4 non-institutional route (a Public→insider-space path exists that avoids all institutional buildings). `GuaranteeAuditResult` gains a bool per check above, retaining its existing `rooftop_discovery_zone` (D-106) and `horizon_view_corridor` (D-102) fields. Raised by: Gestalt, economic-built-world workshop round 2.
|
||||
|
||||
### D-099: WallBackside / TileBehindState — Dual Classification
|
||||
- **Date:** 2026-02-27
|
||||
@@ -293,6 +294,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- **Dissent:** None.
|
||||
- **Cross-reference:** D-102 (horizon view corridor — T5 coastal water is the referenced terrain type), D-104 (heritage grammar overlay — modifier axis A)
|
||||
- **Superseded (partial):** Modifier axis A (HeritageRoot) superseded by [D-167](content.md#d-167-heritage-roots-superseded--corridor-cultural-system-replaces-abstract-roots) (2026-03-24). Cultural palette modifiers are now authored per-system via the corridor framework; abstract heritage root IDs no longer drive axis A.
|
||||
- **Modifier axis A (`HeritageRoot`) superseded 2026-05-25 by D-232** → replaced by `FlavorTagFilter`; axes B/C and faction/climate/condition/season unchanged.
|
||||
|
||||
### D-102: Horizon View Corridor as Coastal Guarantee
|
||||
- **Date:** 2026-02-27
|
||||
@@ -832,6 +834,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- **Ticket:** #915 (CityGenerationContext reads economic snapshot)
|
||||
- **Raised by:** Generation cascade workshop (#897)
|
||||
- **Cross-reference:** D-026 (simulation tiers), D-200 (CityGenerationContext), D-206 (background generation queue)
|
||||
- **Amended 2026-05-25 (D-233 — structural-fill / condition-overlay two-pass split):** The "Phase 2 condition application" language is replaced by a formal two-pass model. **Pass 1 — Structural fill** (frozen; reads t=0 initial-economics only; re-derivable from `seed + initial_economics_snapshot`): zone-type per block, building footprint shape+position, building-type vocabulary tags (`BuildingTag`), `founded_era`, operations-surface extent (bulk industries), and the labor-demand signal feeding adjacent residential blocks. **Pass 2 — Condition overlay** (the sanctioned rolling-economy consumer; a paint layer OVER the frozen fill; refreshable on a cadence): `BuildingConditionState = New|Maintained|Worn|Derelict|Abandoned`; `OccupancyState = Full|Partial|Vacant`; `VegetationEncroachment` (Abandoned in appropriate sub-biomes, D-210); feeds the D-100 tile `DamageOverlay`. **Hard wall:** the condition overlay CANNOT change a `BuildingTag` (a mine plant becomes an *abandoned* mine plant, never an office). The fill generator's signature is `fill_chunk(ctx: CityGenerationContext, seed: SeedChain)` — NO access to `PressureState`, price signals, or tâtonnement output. The condition overlay is a SEPARATE Bevy system, triggered by `EconEvent` (D-180), with its own read set. Cross-ref D-233, D-100, D-180, D-210, D-217. Raised by: Burnelli/Tyre/Gestalt, economic-built-world workshop round 2.
|
||||
|
||||
### D-199: 6-Field Minimum Economic Read Set for City Generation Context
|
||||
- **Date:** 2026-05-01
|
||||
@@ -848,6 +851,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- **Ticket:** #915 (CityGenerationContext implementation)
|
||||
- **Raised by:** Generation cascade workshop (#897)
|
||||
- **Cross-reference:** D-196 (SettlementClass), D-197 (prosperity_baseline), D-198 (economic simulation independence), D-200 (CityGenerationContext struct)
|
||||
- **Amended 2026-05-25 (D-229/D-232/D-233):** `CityGenerationContext` gains `morphology_zone`, `flavor_profile`/`architecture_flavors`, `dominant_bulk_class`, `dominant_production_ubiquity` (the record already permits >6 fields).
|
||||
|
||||
### D-200: Three-Tier Execution Model (Build-Time / Runtime-Background / Runtime-On-Demand)
|
||||
- **Date:** 2026-05-01
|
||||
@@ -1537,7 +1541,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
- **Volumetric, in voxels.** Unit vocabulary, fixed here: a **voxel** is the 1 m cube (the 3-D framing of D-222's tile); a **subvoxel** is its 0.5 m subdivision — eight per voxel, the finest derivation/dig granularity, in all three axes. Derivation is 3-D — surface *and* subsurface — over this grid. The heightmap/atlas sets the top; below it a deterministic geology model (strata by depth → bedrock; per-region lithology; ore / aquifer / cave as 3-D noise) fills downward. Digging reveals `derive(...)` for newly-exposed subvoxels — never "generated on dig"; the hole merely stops hiding seed-math.
|
||||
- **Floors are semantic and variable-height, not a fixed voxel count.** A *floor* — a walkable story, the z-level of D-049 / D-110 — groups voxels and **defaults to 3 voxels (3 m)** but is **not locked**: a cathedral nave or a hangar is one ~10 m floor, and that must be expressible. The voxel/subvoxel substrate is uniform and continuous in z; a floor exists wherever the volume is **walkable void**, and that void can be **built** (buildings), **excavated** (basements, mines), or **natural** (caves, lava tubes, caverns — derived in the geology model as 3-D voids). A cave is as much a floor as a basement; only truly solid rock is floorless. z-level addressing indexes stories whose real height in voxels varies.
|
||||
- **Vertical extent is physical, not a floor count.** The old "±50 floors" framing was a *stored-grid* artifact (each z-level was a filled, stored layer with memory/fill cost). Derive-don't-store eliminates that cost, so: **down** is bound by the body's own geology (crust → bedrock → pressure/heat-impassable interior, a per-body depth tied to radius / `max_elevation`); **up** is bound by *max built-structure height* (towers are built/authored or player-placed, not natural-derived) over free-to-derive air. This **supersedes any ±50 cap** — none was ever recorded as a D-record; it survived only as a 50-floor-skyscraper *example* in the generator-architecture workshop.
|
||||
- **Persistence = tile mutators only.** A save is `seed + a sparse log of tile mutators` — the additions and destructions the player or simulation made (pave a road, build a wall, dig a shaft, fell a tree, demolish a structure). Load = re-derive the base, then replay mutators over it. Saves are tiny regardless of world size. (Mutator op schema → Q-103.)
|
||||
- **Persistence has two parts, and only one is mutators.** (a) **Tile mutators** capture *direct physical gameplay* — the player or sim physically altering the world (pave a road, build a wall, dig a strip-mine, rocket-launcher the bank, fell a tree). Save = `seed + sparse mutator log`; load = re-derive base + replay. (b) **Live simulation state** (the rolling economy now; NPC / storyteller state later) is *history*, not seed-derivable, so it **is saved — but as sim-state, not as mutators.** The *derived* economic base is handled by **cache expiration** — recomputed on expiry, and **force-evicted on an event** (e.g. the player tanks a system's economy) — while the rolling state persists alongside the save. The physical world stays re-derivable; the living state is the small saved delta on top. (Mutator op schema → Q-103.)
|
||||
- **Determinism reclassified safety-critical.** Because mutators reference derived state, any derivation drift (a non-deterministic algorithm, an f32 comparison/ordering, `HashMap` iteration) desyncs the whole save — not merely a cosmetic difference. D-010's integer-only + ordered-collection discipline is load-bearing for *saves*, not just for golden tests.
|
||||
- **Rationale:** A world that stores its tiles cannot scale to body-sized 3-D volumes and bloats saves; a pure-function world with a transient cache + a sparse mutator log scales to any size, makes saves trivially small, and is the only model under which "dig anywhere, to any depth" is free (the subsurface was always computable — digging just reveals it). It also forces the determinism discipline the whole cascade needs anyway. The downward floor cap fell because it was solving a problem — per-layer storage cost — that derive-don't-store eliminates.
|
||||
- **Open sub-questions:** the geology-model fidelity (simple depth-horizon stack vs tectonic-grade folding/faults) and how far `FloorMaterial` is derived now vs deferred to the city layers (both tracked in D-228 / Q-101); the mutator op schema (Q-103).
|
||||
@@ -1551,7 +1555,7 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
### D-228: Composite tile schema — orthogonal axes, derived shape, region-level morphology
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** A tile's "type" is **not a flat enum** but a small bundle of **orthogonal axes** (all *derived* per D-227 — this is the cache's value type, not stored state). Named types ("fjord wall", "mountain pass", "river bank") are **derived display labels, never stored**: a fjord wall and a sea cliff are identical to the simulation (`Cliff + Rock + DeepWater`), differing only in name.
|
||||
- **Per-subtile axes:** `TerrainMaterial` (natural ground — Soil / Sand / Gravel / Rock / Wetland / Snow / Ice / Lava) · `FloorMaterial` (built surface over the ground — None / Concrete / Pavement / Carpet / Metal / …) · `Vegetation` (ground cover — Barren / Grass / Scrub / Thicket / Forest / Crop / Cleared / …) · `Water` (local depth *state* — Dry / Shallow / Deep) · `elevation` (scalar, metres). The `FloorMaterial` / `Vegetation` vocabularies stay open, extended by the layers that own them.
|
||||
- **Per-subtile axes:** `TerrainMaterial` (**permanent** natural ground — Soil / Sand / Gravel / Rock / Wetland / Lava) · `FloorMaterial` (built surface over the ground — None / Concrete / Pavement / Carpet / Metal / …) · `Vegetation` (ground cover — Barren / Grass / Scrub / Thicket / Forest / Crop / Cleared / …) · `Water` (local depth *state* — Dry / Shallow / Deep) · `elevation` (scalar, metres). **`Snow` and `Ice` are not `TerrainMaterial`** — they are seasonally dependent, so they live in the seasonal **cover** overlay (Q-105), present only when the region's seasonal state puts them there (permanent only where climate never melts them — poles, glaciers). The `FloorMaterial` / `Vegetation` vocabularies stay open, extended by the layers that own them.
|
||||
- **`FloorMaterial` and `Vegetation` are override stacks, not single derivations.** Each resolves in precedence **wild/natural (biome + climate) → economic (managed: farmland, plantation, paving, clearance — from the settlement layer) → user (mutators: chopped, planted, built, demolished)**; the topmost present layer wins — the same derive-don't-store + mutator pattern (D-227) as the rest of the world. `Vegetation` carries cover/concealment, the movement sound profile, *and* economic yield (timber / crops) on one axis — and managed crops follow the region seasonal cycle (sown → growing → ripe → harvested → fallow), so farmland visibly turns over with the year (Q-105).
|
||||
- **`Water` is a dynamic depth state, plus derived flow.** The axis is depth/wetness only; the water *feature semantics* (river / ocean / lake / sea / tidal-flat / delta) are the **region morphology zone** (below), not a per-tile value, and **flow direction** derives from the D8 network where moving water applies. Depth is **time-varying** via a deliberately cheap, deterministic, **clock-bound** water-height — a **seasonal** term phased by hemisphere (latitude sign) and a **tidal** term that exists only if the body has a moon (no satellite → no tide), amplitudes as body parameters. The water-height is a **region property computed once per phase** (not per tile, not per frame); per-tile flood state is then just `region-water-height vs local elevation`. So **floodplain / tidal-flat / seasonal-river *emerge*** rather than being placed — a low `alluvial-plain` floods at high water and recedes at low. The static world stays static; only this region water-height carries the clock overlay (sim-time as the derivation input), recomputed on phase change — the dynamic-state path D-226 anticipates. Passability derives from `Water` + `elevation` + `material` together. (Model → Q-105.)
|
||||
- **Derived, not stored:** **shape / geometry** (flat / angled / drop / cliff) = `f(elevation-step × material)` — sand slumps to an angle of repose, rock breaks to a vertical face on the same 1 m step; drives the rendered tile geometry. **Tactical form** (crest / hollow / channel / bank — cover, sightline, concealment) read from elevation curvature + water adjacency on demand. Storing either would mean updating 8 neighbours per elevation change, and both are clean functions of data the tile already holds.
|
||||
@@ -1567,4 +1571,119 @@ Technical foundation decisions that constrain implementation: engine, client-ser
|
||||
|
||||
---
|
||||
|
||||
*88 decisions (D-001 through D-228, excluding gaps). Last updated: 2026-05-25 (D-227 deterministic-rebuild world model, D-228 composite tile schema — atlas-derivation workshop)*
|
||||
### D-229: Building-property-tag schema — the step-3 fill output
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** Define `BuildingPropertyTag`, the typed step-3 output that replaces the `String` stubs (`era`, `society_profile`, `zone_palette`, `chunk_layout`) on the skeleton. **One tag per building footprint** placed in a block; **written once** at plan time (inside the extended `GenerateSkeleton`, D-230), **read-only thereafter** by three consumers — FillChunk (D-230), the guarantee audit (D-097), and the Phase-6 interior generator (D-231). Fields:
|
||||
- `zone_type_id: ZoneTypeId` (`Box<str>` matching a RON `id`) — *what the building is*; the wire to the 31 D-142 zone-type RON files. Refined from `district_mix.rs`'s per-block `ZoningType` via a generator lookup `(ZoningType + economic_role + seed) → ZoneTypeId`.
|
||||
- `footprint: TileRect` — integer tile-space rect within the 128-tile block (D-010 integer-only).
|
||||
- `extent: FloorExtent` — floor/basement extent; **resolves Q-104** (below).
|
||||
- `entry_class: BuildingEntryClass` — physical access character: `Public | Commercial | Restricted | BreachOnly`. **Named `BuildingEntryClass`, not "access tier"**, to avoid colliding with D-028's relational dialogue layers. Derived from `zone_type × layout_mode (D-096) × prosperity (D-197)`: Commission-Grid → formal/logged/corporate-or-authority credentials; Organic → social/reputation/unlogged credentials. U-curve degrade: low prosperity on a normally-Commercial zone → `BreachOnly` (derelict).
|
||||
- `flavor_ref: ArchitectureFlavorRef { flavor_index: u8 }` — index into the body's flavor profile (D-232); deterministic `(seed + zone_type) → index`; no rolling-economy read. (The selection mechanism is D-232's weighted `allow`/`block` filter; the tag records the chosen index for Phase-6 to read cold.)
|
||||
- `era: ConstructionEra` (`Founding | Established | Modern | Derelict`) + `era_cause: EraCause` — feeds ZonePalette modifier axis C (D-101) and sets the D-217 condition floor. Derived from `founding_age_years + prosperity_baseline + seed`; a body carries mixed-era buildings (founding period anchors the distribution; seed scatters outliers).
|
||||
- `initial_condition: TileCondition` — frozen-amber snapshot from `prosperity_baseline` (D-197/D-217). The rolling condition overlay (D-198) paints *over* this; it never mutates the tag.
|
||||
- **`FloorExtent { base_floor: i8, floor_count: u8, heights: FloorHeightProfile }`** where `FloorHeightProfile = Uniform(u8) | Variable(Vec<u8>)`. **Q-104 resolution (the D-110 ↔ D-227 bridge):** two pure functions — `floor_at_voxel_z(z) -> Option<i8>` and `voxel_range_for_floor(f) -> Option<(i32,i32)>` — map D-110 floor-index addressing onto D-227 physical voxel-z. Default `Uniform(3)` (3 voxels ≈ 3 m/floor, per Jeroen); a cathedral/hangar is `Uniform(10)`; a mixed-use stack is `Variable([5,3,3,3,3])`. The `Variable` branch carries per-floor memory only when floors actually differ.
|
||||
- **Rationale:** A typed tag is the single contract that lets the atlas render a building, the guarantee audit validate a district, and Phase 6 seed an interior — all from one frozen object. String stubs cannot carry any of that. Orthogonal fields (what / where / how-tall / who-may-enter / cultural / era / condition) compose without a combinatorial enum explosion, matching D-228's axis discipline at the building scale.
|
||||
- **Implementation:** Phase 4+ (epic forthcoming). Replaces the stub fields on `BlockSkeleton`/`DistrictSkeleton` in `server/src/simulation/generator.rs`.
|
||||
- **Raised by:** Tyre (schema + FloorExtent/Q-104), Gestalt (BuildingEntryClass, derivation), Miri (flavor_ref), economic-built-world workshop round 2, 2026-05-25.
|
||||
- **Cross-reference:** [D-142](content.md#d-142) (zone-type taxonomy), [D-096](#d-096) (layout mode), [D-197](#d-197) (prosperity baseline), [D-217](#d-217) (tile condition), [D-110](#d-110) (signed z-levels), [D-227](#d-227) (voxel substrate), [D-101](#d-101) (ZonePalette), [D-028](#d-028) (dialogue access — name disambiguated), [D-230](#d-230), [D-231](#d-231), [D-232](#d-232), [Q-104](questions-architecture.md#q-104) (resolved here)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
### D-230: FillChunk two-phase execution model — background-plan / on-demand-derive
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** Building fill runs in **two phases**, split by latency budget. **Plan phase (background, no budget):** building-property tags + footprints are produced *inside* the extended `GenerateSkeleton` Rayon task — it already holds the full `CityGenerationContext`, so footprint subdivision + tag assignment stay contiguous with the skeleton work. Output: `DistrictWorldState { skeleton: DistrictSkeleton, block_tags: BTreeMap<(u8,u8), Vec<BuildingPropertyTag>> }`, stored in `BodyWorldState.districts: BTreeMap<DistrictId, DistrictWorldState>` — `BTreeMap` everywhere for D-010 determinism. **Derive phase (`FillChunk`, on-demand, <5 ms):** pure geometric shell derivation — read the cached `DistrictWorldState`, and for each tag `shell_derive(seed, footprint, extent, z) → {Void | Wall | FloorSlab | Roof}` per voxel; fill interstitial tiles (street / open space) from `density_pct` + the D-215 pattern; apply `initial_condition`. Cost estimate: ~40 k voxels/chunk × ~10–15 ns = **0.4–0.6 ms**, well under budget even for tall skyscrapers. **Pre-condition:** `FillChunk` is only dispatched after `GenCompletion::SkeletonGenerated` for that district has been processed; if `block_tags` is absent, re-enqueue at `High` and warn — never a blocking read on the main thread.
|
||||
- **Rationale:** The expensive, variable planning work (district mix, tag assignment, footprint layout) has no place under a per-chunk latency budget; doing it once in the background and reducing `FillChunk` to cache-read + rectangle-containment + z-range lookup is what makes on-demand fill trivially fast and re-derivable (D-227).
|
||||
- **Code-gap flagged (→ implementation ticket, independent of this workshop):** `GenCompletion::SkeletonGenerated` currently returns only `city_id: u64`; it must carry the `DistrictWorldState` back so the main thread can insert it into `BodyWorldState.districts`. The completion routing is incomplete today regardless.
|
||||
- **Implementation:** Phase 4+. Amends D-200 (extends the runtime-background execution tier). `FillChunk`/`ChunkGenWorker` are no-op stubs today (`gen_queue.rs`, `workers/stubs.rs`).
|
||||
- **Raised by:** Tyre, economic-built-world workshop round 2, 2026-05-25.
|
||||
- **Cross-reference:** [D-200](#d-200) (three-tier execution — amended), [D-203](#d-203) (LRU cache), [D-225](#d-225) (layer-stream proxy), [D-227](#d-227) (derive-don't-store), [D-215](#d-215) (arrangement patterns), [D-220](#d-220) (density), [D-010](#d-010) (determinism), [D-229](#d-229)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
### D-231: DoorSpec and InteriorDescriptor — the step3→step4 boundary and the Phase-6 seed
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** The **door is the boundary descriptor** between the generated exterior (step 3) and the lazy interior (step 4, Phase 6) — *not yet an interactive object* (that is Phase 5). Each building tag carries `doors: SmallVec<[DoorSpec; 4]>` (≥ 1). `DoorSpec { facing: CardinalDirection, door_class: Main|Service|Emergency|Hidden, entry_class: BuildingEntryClass, initial_state: Open|Closed|Locked|Sealed, credential: None|TemporalWindow(hours)|Corporate(corp)|Resident(block)|Authority|Social(f32), connects_to: Street(id)|AdjacentBuilding(block)|Interstitial, interior_descriptor: InteriorDescriptor }`. **Door-count derivation:** 1 `Main` minimum; `Service` if the zone has a logistical function; `Emergency` if `floor_extent.above_ground ≥ 2`; `Hidden` seeded by zone_type (research ~60% / admin ~20% / residential ~5%) — `Hidden` carries the D-106 Rooftop-Bar discovery layer. **Initial-state derivation:** Public → Open/Closed, Commercial → `TemporalWindow(zone hours)`, Restricted → Locked + credential, BreachOnly → Sealed. **Mutation semantics (D-227):** initial state is frozen-amber derived; runtime changes (faction lockdown, a picked lock) are **tile mutators over the frozen base**, never edits to the tag — the base is always re-derivable. **`InteriorDescriptor { zone_type_id, entry_class, floor_extent, era, flavor_ref, prosperity, layout_mode }` is the complete Phase-6 seed:** a future interior generator produces a deterministic floor plan from this descriptor + `SeedChain` with **no other system queried**. The voxel at a door position is a conditionally-passable solid — solid until Phase 6 activates the interior, then void.
|
||||
- **Rationale:** Phase 6 (D-166) defers interiors, but its seed must be fixed *now* or step 3 cannot guarantee a generable interior later. Carrying the descriptor on the door (the exact threshold where generation will fire) keeps the lazy-interior contract local and self-sufficient, matching the planet-down-cascade "descriptor + catalog behind the door" rule.
|
||||
- **Implementation:** `DoorSpec`/`InteriorDescriptor` structs land in Phase 4 (planned on the tag); the door-open *fill* is Phase 6. `ChunkMutations`/`TileOverride` already exist as the mutator layer.
|
||||
- **Raised by:** Gestalt, economic-built-world workshop round 2, 2026-05-25.
|
||||
- **Cross-reference:** [D-229](#d-229), [D-097](#d-097) (guarantee audit reads connectivity), [D-106](#d-106) (rooftop clause / hidden door), [D-110](#d-110), [D-227](#d-227) (mutators), [D-142](content.md#d-142), [D-217](#d-217), [D-166](scope.md#d-166) (Phase-6 deferral)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
### D-232: Architecture-flavor — trait-template catalog (economically gated, seed-drawn, wiki-biased)
|
||||
- **Date:** 2026-05-25 (round 3 — supersedes the round-2 per-body-Gemma-array draft of this record)
|
||||
- **Decision:** The **architecture-domain completion of D-167** (which retired the 7 abstract heritage roots and made the wiki the cultural register but never built the wiki→generator mapping). Cultural flavor is **economically founded, seed-randomized from a shared trait-template catalog, with the wiki biasing the draw for hero bodies** (Jeroen's model). This *replaces* both the round-2 per-body Gemma arrays and the rejected dimensional per-axis draw.
|
||||
- **The catalog (holistic templates, not per-axis traits).** A single shared `architecture_trait_catalog.toml` → `trait_templates` table. Each template is a **coherent bundle** — a template defines a *relationship* between its axes, so it is never decomposed: `{ tag, label, cultural_description, allow: [ObjectTag], block: [ObjectTag], zone_affinity: {DistrictType→weight}, era_scope, eligibility (two-tier, below), base_weight, visual_bundle (→ D-235) }`. Per-axis mixing is forbidden (it produces incoherent grammar, e.g. stone walls + flat roof).
|
||||
- **Two-tier eligibility — and role is per-layer, not global.** *Hard gates* (`bulk_class`, `prosperity`, `production_ubiquity`) exclude a template from the pool when failed. *Weight modifiers* (`economic_role`, `dominant_faction`, `founding_age`, `geographic_sector`, `morphology_zone`) multiply `base_weight` but never exclude. **Key principle: the same input plays different roles at different layers.** `morphology_zone` is a *hard gate* on street geometry (D-234 — a fjord cannot have radial streets) yet only a *soft weight* on cultural-template eligibility. Economics *founds and hard-gates* what a building **is** (D-233) but only *weights* how it is **characterized**. **`geographic_sector` (corridor) is always a soft weight, never a gate** — "corridors are tendencies, not borders"; a hard corridor gate collapses the pool to ~7 templates/corridor → ~100 % within-corridor collision.
|
||||
- **All eligibility numbers are integer basis-points** (`min_prosperity_bps` etc.), never `f32` — D-010 determinism, now save-critical under D-227.
|
||||
- **The draw (three phases).** (1) *Body vocabulary* (background, at skeleton time): hard-gate filter → weight (mods × wiki-bias) → `SeedChain`-seeded weighted draw of **K** templates → stored as `trait_selection: Vec<String>` on the skeleton. **K is locked to `complexity_tier`: Full = 5, Moderate = 3, Minimal = 1, Empty = 0** (Nigel's birthday math: K=4 fails within-corridor uniqueness at a 35-template catalog; K is *not* a range and *not* keyed to prosperity). The draw is **coverage-aware** — it must cover the body's actual district-type mix, not draw 5 templates that all starve the civic district. (2) *District-dominant* (at fill): each district draws **one** dominant template from the body's vocabulary, weighted by `zone_affinity`, applied **whole** to that district (Araminta's composition rule); secondaries surface on outbuildings/secondary streets and in their own high-affinity districts. The body vocabulary is **closed by default** (a mining town's civic hall looks like *that town's* civic — a feature). (3) *Within-template* variation: the fill seed picks within each template's `allow`-lists per axis.
|
||||
- **Deviation system — closed by default, rare tasty swerves.** On top of the closed vocabulary, a **rare per-building wildcard** can draw a *coherent whole* template from outside the body's vocabulary (the Shinto temple in Amsterdam — a complete foreign building, never an axis-scramble). The swerve is **cultural only**; the building's function still passes the normal economic hard gates. **Three sources, two opposed active drivers:** *foreign import* (another corridor's grammar) driven **up** by cosmopolitanism / centrality / transit / Epicenter tier; *heritage callback* (the body's own corridor **heritage pool**) driven **up** by remoteness / isolation / conservatism (the cut-off Latin world that reaches back to haciendas); and the passive *past-vogue holdover* (just old, see era below). The **sparsity escape hatch is the same mechanism** triggered by necessity rather than dice — when the closed vocabulary genuinely cannot serve a district, it reaches the full catalog.
|
||||
- **Corridor = a two-part pool.** Each corridor is authored as a **baseline** character (its default cohesive look — e.g. East-Asian: cyberpunk density + utilitarian industry) **plus a heritage sub-pool** (deep-history callbacks — East-Asian: fishing-village/temple enclaves; Iberian/Latin: haciendas, colonial-revival) tagged so the remoteness dial draws specifically from it. A **shared cross-corridor pool** feeds the foreign-import swerves.
|
||||
- **Era = maintenance/wear, NOT a material-tech ladder.** The Reach is post-space-travel throughout; there is no stone→concrete→glass progression — fashion cycles fast and arbitrarily. A building's construction era (D-229, from distance to the founding origin) reads primarily as **age/wear**, realized through the condition layer (D-217 / D-198 — maintenance modulates how much wear shows). The "different-looking old building" is just the **past-vogue holdover** — the *temporal* sibling of the spatial swerve, one deviation system with sources in *elsewhere* and *elsewhen*. (This retires the round-2 era-band material-progression and the `era_fallback`-as-tech-ladder; `era_fallback` survives only as the asset-resolution fallback chain, below.)
|
||||
- **Logical catalog vs visual-asset layer (the incremental-content split).** The **logical catalog** — tags, eligibility, `allow`/`block`, token *references* — is cheap data, **authored complete and frozen at launch**; the draw reads only this, so derivation is deterministic and stable forever **with no catalog versioning needed**. The **visual-asset layer** is a **fallback hierarchy**: every specific texture/material token declares a generic parent it degrades to (`temple_wall_wood` → generic `wood_wall` placeholder until the specific art ships, then it **upgrades in place**). Themes/textures are patched in incrementally behind stable tokens — a shipped world's *structure* is frozen at birth (the temple was always a temple), only its render *fidelity* sharpens. Versioning re-enters only if a genuinely **new logical template** is added post-launch (minimized by authoring the catalog generously up front).
|
||||
- **Wiki bias (hero bodies only, ~30–40).** A sparse `atlas_body_trait_bias` row: `pin` (mandatory, **counts toward K**), `boost` (≤ 3× weight), `suppress` (≥ 0.33×, never 0 — preserves second-playthrough surprise). Bias is **per-body, never per-corridor**. Non-hero bodies (the ~240 remainder) run the identical algorithm with no bias — economics + corridor weight + seed.
|
||||
- **CI guardrails (Nigel, build-time validation):** ≥ 5 templates eligible after hard gates per economic class; no single template > 60 % of pool weight after modifiers; bias is per-body; the catalog grows ≥ 1 template/dimension per new corridor or archetype.
|
||||
- **Catalog population:** ~25–35 templates at floor, 40–45 target. Core hand-curated (Miri authors cultural meaning + eligibility; Araminta authors the matching `visual_bundle`), then a *bounded* Gemma corpus-distillation pass (one read of all wiki, propose new templates, human-gated) — not per-body generation.
|
||||
- **Channel separation from D-233 (held):** D-233 decides **what** a building is (vocabulary, coverage); D-232 decides **how** it is characterized. Same inputs (e.g. `bulk_class`) used non-conflictingly — D-233 as hard function gate, D-232 as soft cultural weight. They compose at fill.
|
||||
- **Supersedes (architecture/generator domain only):** **D-104** (`HeritageGrammarOverlay` + per-root data → the catalog + `allow`/`block`), **D-105** (heritage-root→informal-zone lookup → flavor-filtered selection; the three zone *types* survive), **D-101 modifier axis A** (`HeritageRoot` → catalog draw; axes B/C + faction/climate/condition/season unchanged), **D-107** (per-root trauma decay → per-template/condition; the "trauma intensifies culture" principle survives). Also retires the round-2 `atlas_body_culture` / `atlas_body_culture_era` tables.
|
||||
- **Storage:** `trait_templates` (the catalog) + `atlas_body_trait_bias` (sparse, hero bodies). The per-body draw result lives as `trait_selection: Vec<String>` on the skeleton — **re-derivable** from catalog + economics + bias + `SeedChain`, no Gemma in the hot path. `CityGenerationContext` carries `trait_selection` + `morphology_zone` (replacing the round-2 `flavor_profile`; D-199 amend).
|
||||
- **Source-location deferred:** the human-authored *source* home (catalog file + `bias.json`) rides on **Q-107** (wiki → Atlas content-set consolidation). The generator-facing tables are invariant to it, so the fill seam is unblocked regardless.
|
||||
- **Implementation:** Phase 4+ (mechanism + sparse catalog); visual themes fill in across Phase 5+ and post-launch behind the fallback chain. `ObjectTag`/material vocabulary is Miri + Araminta co-maintained.
|
||||
- **Raised by:** Jeroen (the trait-template / economically-founded / wiki-biased model + the swerve, corridor-pool, era-as-maintenance, and logical/asset-split refinements), with Miri (lead synthesis), Burnelli (eligibility), Nigel (draw + variety math + guardrails), Araminta (visual bundle + composition), economic-built-world workshop rounds 2–3, 2026-05-25.
|
||||
- **Cross-reference:** [D-167](content.md#d-167) (corridor cultural system — completed here), [D-223](content.md#d-223) (Gemma naming pipeline + corridor mixing), [D-142](content.md#d-142) (zone types — the function baseline filtered by templates), [D-228](#d-228) (morphology zone), [D-233](#d-233) (economic channel — what vs how), [D-234](#d-234) (morphology→street — the hard-gate layer), [D-235](#d-235) (visual bundle + fallback hierarchy), [D-217](#d-217)/[D-198](#d-198) (condition — where era's wear lands), [D-224](#d-224) (SeedChain — the draw PRNG), [D-199](#d-199) (read-set — extended), [D-101](#d-101)/[D-104](content.md#d-104)/[D-105](content.md#d-105)/[D-107](content.md#d-107) (superseded in part), [Q-106](questions-architecture.md#q-106) (era reframed), [Q-107](questions-architecture.md#q-107) (source location)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
### D-233: Economic signal → block-fill vocabulary — `BulkClass` × `ProductionUbiquity`
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** Within a block of a given district type (D-194), the building vocabulary, coverage density, and interstitial character derive from two commodity signals for the settlement's **dominant commodity**, both read from the `systems.db` commodity catalog (D-184) at build time — **seed-independent, no D-199 tier split, no rolling-economy touch:**
|
||||
- `BulkClass = BulkSolid | BulkLiquid | PrecisionDense | Perishable | NonPhysical`
|
||||
- `ProductionUbiquity = Ubiquitous | Common | Specialist | MonopolySource`
|
||||
- **Coverage:** `BulkClass` sets a roofed-coverage fraction (NonPhysical 0.85–0.95 → PrecisionDense 0.75–0.90 → Perishable 0.50–0.65 → BulkSolid 0.25–0.40 → BulkLiquid 0.20–0.35), scaled by `density_class` (D-220) and `prosperity_baseline` (D-197). For bulk industries the non-roofed remainder is **operations surface** (haul roads, ore pads, conveyor runs, tank berms) — tagged *built economic infrastructure*, **not** interstitial open space.
|
||||
- **Vocabulary:** each `(BulkClass × production_tier)` draws from a **frozen** building-vocabulary pool (e.g. `mine_head`/`conveyor_run`/`tailings_area`; `tank_farm`/`flare_stack`; `cleanroom_facility`/`qc_lab`; `field_shed`/`cold_store`; `office_tower`/`civic_hall`). A mine block places mine buildings, full stop.
|
||||
- **Concentration/dispersion rule:** `ProductionUbiquity` sets the **spatial spread** of zone-type blocks, not per-block weight — `Ubiquitous` scatters small instances through mixed-use (the settlement doesn't read as "a water town"); `MonopolySource` concentrates contiguous block groups (the mine *is* the settlement, everything else is support).
|
||||
- **Residential follow-on:** every non-residential production block emits a labor-demand signal sizing adjacent residential blocks (BulkSolid extraction ~3.0× → BulkLiquid ~2.5× → Perishable ~2.5× → PrecisionDense ~2.0× → NonPhysical ~1.5×); housing character from `prosperity_baseline` (>0.7 market-rate → 0.4–0.7 standard worker → <0.4 company barracks/squatter).
|
||||
- **Frozen-amber:** the vocabulary is t=0-derived and immutable; the condition overlay (D-198) paints Maintained/Worn/Abandoned but **cannot change a `BuildingTag`** — a mine processing plant becomes an *abandoned* mine plant, never an office.
|
||||
- **Amends D-199** (adds `dominant_bulk_class`, `dominant_production_ubiquity`, `morphology_zone` to `CityGenerationContext`) and **D-184** (back-fills the `bulk_class` enum values it named but left open). Enforcement: `fill_chunk(ctx, seed)` holds no `PressureState` reference.
|
||||
- **Rationale:** Floor-area-to-output ratio is the capital structure of an industry, not a style choice — services maximize floor, bulk extraction is mostly open operations surface. Driving coverage and vocabulary from `BulkClass` makes settlements that share an economic *role* look physically distinct by *what they make*, and the ubiquity rule gives the settlement its identity (a MonopolySource town vs background infrastructure).
|
||||
- **Implementation:** Phase 4+. `bulk_class`/`production_ubiquity` lookups added to the economics import; consumed in the plan phase (D-230).
|
||||
- **Raised by:** Burnelli, economic-built-world workshop round 2, 2026-05-25.
|
||||
- **Cross-reference:** [D-194](#d-194) (district mix — upstream), [D-184](content.md#d-184) (commodity catalog — amended), [D-199](#d-199) (read-set — amended), [D-220](#d-220) (density), [D-197](#d-197) (prosperity), [D-142](content.md#d-142) (zone types — downstream), [D-176](#d-176), [D-180](#d-180) (EconEvent — overlay trigger), [D-198](#d-198) (condition overlay — amended), [D-229](#d-229)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
### D-234: Morphology zone → street and footprint constraints
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** The D-228 region **morphology zone** constrains street geometry and footprint subdivision so terrain shapes settlement form — the variety guardrail that stops the generator placing a radial-core city in a fjord. Two rules:
|
||||
- **(a) Permitted street patterns by morphology** — a lookup that gates the D-215 archetype arrangement patterns: fjord/canyon → ribbon or hub-and-spoke only (streets linear along the terrain axis, radial impossible); delta/braided → hub-and-spoke following channels (bridges as forced nodes); alluvial-plain → any pattern; island → hub-and-spoke (perimeter access priority); mountain-pass → ribbon only (elevation steps as block boundaries).
|
||||
- **(b) Waterfront footprint rule** — any block adjacent to a water morphology feature uses **pier/quay geometry** on the water-facing edge (no standard setback, dock-orthogonal subdivision, access priority toward the water) and standard street frontage on inland edges. Applies uniformly wherever a block touches a water feature — body-independent, zone-independent. This is what makes port towns present to the quay differently than to the street.
|
||||
- Street geometry obeys the ±45° pathfinding cap (D-096) — no curves; straight lines appear only where authored (the D-228 cohesion rule). Fills the Step-1 `chunk_layout` / `corridors` / `access_points` stubs.
|
||||
- **Rationale:** Morphology zone and `architecture_flavor` were added to the context (D-199) as *inputs without consumers* — two economically-similar bodies otherwise produce topologically identical street networks and identical block subdivision (Nigel's same-y-grid failure). These two rules give morphology structural teeth so the fjord port and the delta port are categorically different cities, not reskins.
|
||||
- **Implementation:** Phase 4+. Consumed in the plan phase (D-230) when laying streets and subdividing blocks.
|
||||
- **Raised by:** Nigel, economic-built-world workshop round 2, 2026-05-25.
|
||||
- **Cross-reference:** [D-228](#d-228) (morphology zone), [D-215](#d-215) (arrangement patterns — gated), [D-096](#d-096) (layout mode / ±45° cap), [D-220](#d-220) (density), [D-229](#d-229), [Q-106](questions-architecture.md#q-106)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
### D-235: Building exterior visual grammar and material vocabulary
|
||||
- **Date:** 2026-05-25
|
||||
- **Decision:** The visible-form layer over the tags. `(template × zone_type × density) → BuildingExteriorTag { wall_material, roof_form, setback_tier, facade_rhythm, color_range }`, where `template` is the district's dominant trait template (D-232), derived in three steps: **(1)** the template's `allow`/`block` filters the available material + roof token set (a template is a *coherent bundle* — its axes are chosen together, never mixed across templates); **(2)** `zone_type` biases within the filtered set (probabilistic — the template can override, e.g. an `allow: [stone_base]` industrial block stays stone, not corrugated metal); **(3)** `density` sets `setback_tier` (Dense → `zero_lot` → … → Frontier → `campus`), which drives **interstitial type** (`void / court / garden / plaza / dock_slip / market_pad / open_lawn`). The fill seed then picks within each axis's allow-list. **Era is NOT a material filter here** (D-232 reframe): the Reach is post-space-travel throughout, with no stone→concrete→glass tech ladder — material choice comes from the template (fashion), and era reads as **maintenance/wear** via the condition layer (D-217), with the occasional out-of-vogue building handled as a deviation (D-232's past-vogue holdover), not a material rule.
|
||||
- **Exterior vocabulary** (extends D-228's FloorMaterial axis): `WallMaterial` (stone_cut/stone_rough/fired_brick/clay_render/heavy_timber/pile_timber/timber_frame/reinforced_concrete/corrugated_metal/steel_panel/composite_panel/precision_glass/smart_facade), `RoofForm` (pitched_steep/pitched_shallow/flat/composite_curved/corrugate_shed/dome), `FacadeRhythm` (bay_window/grid_panel/solid_punched/arcade/open_front/blind_wall), `StreetSurface` (cobble/packed_earth/poured_slab/elevated_boardwalk/dock_plank/rail_embedded — derives from district + density + template, the same filter as buildings, for a consistent world). **Color** is template-bounded (cultural palette cue), seed-selected within range — always within the template's register.
|
||||
- **Fallback hierarchy (the incremental-content mechanism, D-232).** Every specific texture/material token declares a **generic parent it degrades to**: `temple_wall_wood` → generic `wood_wall` placeholder until the specific asset ships, then it **upgrades in place**. The *logical* token a building uses is fixed at generation (deterministic, frozen); only its rendered *fidelity* sharpens as themes/textures are patched in. This is where incremental content lives — an asset-resolution concern, not a generation one — so no catalog versioning is needed. (`era_fallback` from the round-2 draft survives only in this generalized form — a fallback chain, not a tech ladder.)
|
||||
- Worked example: a fjord port (template `fjord_maritime` → stone base + steep roof + zero-lot + solid-punched + cobble) and a delta port (template `river_delta` → pile-timber + shallow roof + arcade + boardwalk) share density and zone types yet read as completely different cities.
|
||||
- **Formally retires** Araminta's generator-architecture Round-4 `D-READY-9` ten-root heritage-modifier TOML system — superseded by the D-232 trait-template catalog (body-specific draw, allow/block-bounded, no root taxonomy).
|
||||
- **Rationale:** The template is the differentiator, but it needs a concrete visual vocabulary to act on, filtered consistently across walls, roofs, facades, *and* streets or the world reads incoherent. The "old quarter vs new development" texture comes from **wear + occasional past-vogue holdover** (D-232), not from a material-technology ladder — because in a post-space-travel setting there is no such ladder. The fallback hierarchy lets the logical world be complete and frozen at launch while the art catches up over patches.
|
||||
- **Implementation:** Phase 4+ (the token logic + Atlas-level data); textured render + the bulk of the theme library are Phase 5+ and post-launch, behind the fallback chain. `ObjectTag`/material vocabulary is Miri + Araminta co-maintained.
|
||||
- **Raised by:** Araminta, economic-built-world workshop round 2, 2026-05-25.
|
||||
- **Cross-reference:** [D-228](#d-228) (composite tile / FloorMaterial — extended), [D-232](#d-232) (flavor — the filter), [D-217](#d-217) (condition / heritage markers), [D-142](content.md#d-142) (zone types), [D-220](#d-220) (density → setback), [D-106](#d-106) (height tiers)
|
||||
- **Dissent:** None
|
||||
|
||||
---
|
||||
|
||||
*95 decisions (D-001 through D-235, excluding gaps). Last updated: 2026-05-25 (economic-built-world workshop rounds 2–3 — D-229 building-property-tag schema, D-230 FillChunk execution, D-231 door/interior contract, D-232 architecture-flavor as an economically-gated/seed-drawn/wiki-biased trait-template catalog [round-3 rewrite], D-233 economic block-fill, D-234 morphology→street, D-235 exterior visual grammar + asset fallback hierarchy). D-101/D-104/D-105/D-107 superseded in part by D-232.*
|
||||
|
||||
@@ -283,6 +283,7 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
|
||||
- **Dissent:** None.
|
||||
- **Cross-reference:** D-101 (ZonePalette — heritage modifier axis A), D-105 (informal zone typology — heritage correlations), Q-049 (ObjectTag co-maintenance)
|
||||
- **Superseded by:** [D-167](#d-167-heritage-roots-superseded--corridor-cultural-system-replaces-abstract-roots) (2026-03-24). The 7 abstract heritage roots and their grammar overlays are retired. Cultural grammar is now authored per-system using the corridor framework.
|
||||
- **Superseded 2026-05-25 by D-232** (HeritageGrammarOverlay → per-body `ArchitectureFlavorEntry` + `allow`/`block` filter; never wired).
|
||||
|
||||
### D-105: Non-Urban Informal Zone Typology
|
||||
- **Date:** 2026-02-27
|
||||
@@ -293,6 +294,7 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
|
||||
- **Dissent:** None.
|
||||
- **Cross-reference:** D-104 (heritage grammar overlay)
|
||||
- **Superseded by:** [D-167](#d-167-heritage-roots-superseded--corridor-cultural-system-replaces-abstract-roots) (2026-03-24). Heritage root correlations (Frost/Stone/Tide etc.) are retired; informal zone typology must be re-authored against corridor cultural identities.
|
||||
- **Superseded in part 2026-05-25 by D-232** — the three informal-zone *types* survive as a typology; the heritage-root lookup is replaced by flavor-filtered zone selection.
|
||||
|
||||
### D-107: Trauma Events as EraModification Subtypes
|
||||
- **Date:** 2026-02-27
|
||||
@@ -303,6 +305,7 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
|
||||
- **Dissent:** None.
|
||||
- **Cross-reference:** D-100 (DamageOverlay — structural track), D-104 (heritage grammar — cultural baseline), D-109 (LocalOverlay mandate)
|
||||
- **Superseded (partial):** Heritage-root-based decay model superseded by [D-167](#d-167-heritage-roots-superseded--corridor-cultural-system-replaces-abstract-roots) (2026-03-24). Trauma-intensifies-culture principle and structural track remain valid; heritage root baseline references must be re-interpreted via the corridor cultural system.
|
||||
- **Superseded 2026-05-25 by D-232** — per-root trauma decay → per-body `trauma_decay_rate`; the 'trauma intensifies culture' principle survives.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -243,6 +243,7 @@ This domain covers: currency system, commodity taxonomy, shadow economy, corpora
|
||||
- **Raised by:** Full planning team, Sprint 32 Workshop #801.
|
||||
- **Dissent:** None.
|
||||
- **Cross-reference:** [D-173](#d-173-commodity-taxonomy) (taxonomy), [D-182](#d-182-toml-source-of-truth-for-economics-data) (TOML pipeline), [D-185](#d-185-brands-are-not-commodities) (what is excluded from this catalog)
|
||||
- **Amended 2026-05-25 (D-233):** `bulk_class` enum values enumerated — `BulkSolid | BulkLiquid | PrecisionDense | Perishable | NonPhysical`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ Technical foundation questions: engine, protocols, data structures, performance,
|
||||
---
|
||||
|
||||
### Q-100: Biome authority — Python sim vs Rust cascade
|
||||
- **Status:** Open — raised in the atlas-derivation workshop (2026-05-25)
|
||||
- **Status:** Resolved in principle (Jeroen, 2026-05-25) — implementation detail open. The baked heightmap + reliefmap are deterministic *inputs* (gameplay never touches them), so the natural world is `f(reliefmap, heightmap, seed)` and the cascade **derives from** the baked artifacts, never independently re-derives. The biome/climate the reliefmap encodes is therefore **baked as data the Rust cascade reads** (option A below); `subbiome::classify`'s independent re-derivation is reconciled away. Open: exactly which data layers to bake + the Rust read path.
|
||||
- **Question:** Two independent biome systems exist. `planet_simulation.py` (build-time) computes biome from a Whittaker table + sim-derived temperature/moisture and colours `reliefmap.png` — the authoritative *visual*. `subbiome::classify` (Rust, runtime, D-210) re-derives `SubBiomeVariant` from proxies (elevation percentile, slope, river-distance moisture, latitude temperature). They diverge — a cold fjord coast reads `CoastalLowland` in Rust, which lacks the temperature field. Which is canonical? (A) Python canonical → bake temperature + moisture as additional atlas layers the Rust cascade reads (richer; more storage/pipeline; mod-friendly per D-225); (B) Rust canonical → re-render reliefmaps from cascade output at bake time (simpler; loses the atmospheric sim; regenerates ~267 reliefmaps); (C) keep split (pragmatic; contradicts the dual-artifact principle, D-226). Related sub-fork: the Python river-carve (colours the reliefmap) and the Rust D8 network (D-208) also diverge — the dual artifact requires reconciling them.
|
||||
- **Context:** Tyre's top integration snag. Blocks the biome axis of D-228 and the dual-artifact reconciliation. Tyre's lean: A long-term, B for Phase 4, with the expansion path designed in.
|
||||
- **Cross-reference:** [D-228](architecture.md#d-228), [D-226](architecture.md#d-226), [D-210](architecture.md#d-210), [D-208](architecture.md#d-208), [D-225](architecture.md#d-225), `tooling/planet-gen/planet_simulation.py`, `server/src/atlas/subbiome.rs`
|
||||
@@ -380,7 +380,8 @@ Technical foundation questions: engine, protocols, data structures, performance,
|
||||
---
|
||||
|
||||
### Q-104: Floor-index ↔ absolute voxel-z coordinate mapping
|
||||
- **Status:** Open — flagged by Tyre in the atlas-derivation workshop (2026-05-25)
|
||||
- **Status:** Resolved 2026-05-25 by D-229
|
||||
- **Resolution:** Resolved via `FloorExtent { base_floor, floor_count, FloorHeightProfile::{Uniform|Variable} }` + the pure functions `floor_at_voxel_z` / `voxel_range_for_floor`.
|
||||
- **Question:** D-227 introduces an absolute physical z coordinate (metres, continuous over the voxel grid) alongside D-110's floor-*index* addressing (`base_z: i8`, a story-counting integer). The two coexist but the bridge is undefined: "the floor at `base_z = -2`" → which absolute voxel-z values does it span? With variable-height floors (D-227 — default 3 voxels, a cathedral ~10), the mapping is **non-linear** (a floor's voxel-z depends on the heights of the floors below it). What is the mapping function, and where is it owned (a per-building floor table? a region floor-height index?)? Needed before anything queries both systems (Phase 5 building interiors / vertical movement). Gap, not contradiction — D-227 and D-110 do not conflict.
|
||||
- **Context:** Raised in the atlas-derivation workshop (Tyre, Round 2 review of D-227).
|
||||
- **Cross-reference:** [D-227](architecture.md#d-227), [D-110](architecture.md#d-110), [D-049](architecture.md#d-049), [D-222](architecture.md#d-222)
|
||||
@@ -389,10 +390,25 @@ Technical foundation questions: engine, protocols, data structures, performance,
|
||||
|
||||
### Q-105: Region seasonal/clock state — the shared cheap-dynamism source
|
||||
- **Status:** Open — committed in principle (D-228); model details open (2026-05-25)
|
||||
- **Question:** D-228 commits to a deliberately cheap, deterministic, **region-level seasonal/clock state** — computed *once* per region per phase, inherited by its tiles — from which several dynamic overlays derive: **water-height** (floodplain / tidal-flat / seasonal-river), **seasonal snow cover**, **weather**, and the **farmland crop cycle** (sown → growing → ripe → harvested → fallow). The model must stay "nearly free" — simple deterministic functions of the in-game clock + body/region parameters, no per-tile or per-frame simulation. Open: the exact phase functions — a **seasonal** term phased by hemisphere (latitude sign), driven by the year clock; a **tidal** term only when the body has a moon (no satellite → no tide), driven by the lunar/day clock with amplitude from the satellite config; how snow-depth, weather, and the crop cycle each read the same state; the **static-vs-seasonal split** (permanent snow/ice is `TerrainMaterial`, seasonal snow is this dynamic overlay); determinism (state = `f(clock, region)` → reproducible) and its interaction with the cache + the D-226 pause/inspection (a frozen phase for stable inspection); and recompute cadence (per phase-change, not per frame).
|
||||
- **Question:** D-228 commits to a deliberately cheap, deterministic, **region-level seasonal/clock state** — computed *once* per region per phase, inherited by its tiles — from which a family of **transient surface conditions** derive — all computed from the region's seasonal + tidal + weather phase, none of it stored: **flooded tiles** (floodplain / tidal-flat / seasonal-river, where the water-height crosses local elevation), **snow & ice cover**, **puddles** (weather / rain), **weather** generally, and the **farmland crop cycle** (sown → growing → ripe → harvested → fallow). The model must stay "nearly free" — simple deterministic functions of the in-game clock + body/region parameters, no per-tile or per-frame simulation. Open: the exact phase functions — a **seasonal** term phased by hemisphere (latitude sign), driven by the year clock; a **tidal** term only when the body has a moon (no satellite → no tide), driven by the lunar/day clock with amplitude from the satellite config; a **weather** term (shorter-term precipitation → puddles + general conditions); how snow/ice depth, puddles, flood extent, weather, and the crop cycle each read the same state; the **snow/ice cover model** — D-228 removed `Snow`/`Ice` from `TerrainMaterial`, so snow/ice are *entirely* this seasonal cover overlay (permanent only where climate never melts them — poles, glaciers), depth rising/falling with the seasonal phase; determinism (state = `f(clock, region)` → reproducible) and its interaction with the cache + the D-226 pause/inspection (a frozen phase for stable inspection); and recompute cadence (per phase-change, not per frame).
|
||||
- **Context:** Grew from the floodplain dependency — "floodplain is only feasible if water heights move" — into a general cheap-dynamism source. Jeroen's constraints: clock + hemisphere bound, moon-gated tides, computed once per region, simple rules; snow / weather / crop cycle ride the same calculation.
|
||||
- **Cross-reference:** [D-228](architecture.md#d-228), [D-227](architecture.md#d-227), [D-226](architecture.md#d-226) (dynamic-state inspection), [D-010](architecture.md#d-010)
|
||||
|
||||
---
|
||||
|
||||
*54 questions (9 resolved, 1 partially resolved, 44 open). Last updated: 2026-05-25.*
|
||||
### Q-106: Era-band stacking depth for layered architecture-flavor
|
||||
- **Status:** Resolved 2026-05-25 by D-232 (round 3) — the question is dissolved, not answered.
|
||||
- **Question (original):** D-232 (round-2 draft) made flavor a three-layer resolution (body × district × era-band). How many era-bands does a settlement produce as a function of `founding_age_years`? What is the minimum age-gap between bands? How do the flavor slices differ between bands?
|
||||
- **Resolution:** The round-3 rewrite of D-232 **retired the era-band material-progression entirely.** The Reach is post-space-travel throughout — there is no stone→concrete→glass tech ladder, so era is *not* a flavor-stacking axis. A block's construction era (still derived from distance-to-founding-origin, D-229) reads as **maintenance/wear** through the condition layer (D-217/D-198); the occasional out-of-vogue building is handled by D-232's **deviation system** (the "past-vogue holdover" — temporal sibling of the spatial swerve), not by multi-band flavor stacking. So there are no era-bands to count.
|
||||
- **Cross-reference:** [D-232](architecture.md#d-232), [D-229](architecture.md#d-229), [D-217](architecture.md#d-217)/[D-198](architecture.md#d-198) (condition — where era's wear lands)
|
||||
|
||||
---
|
||||
|
||||
### Q-107: Wiki → Atlas content-set consolidation
|
||||
- **Status:** Open — raised by Jeroen (2026-05-25)
|
||||
- **Question:** The wiki pages live semi-outside game scope yet are piped into Atlas content screens AND are now a generator input (D-232 architecture-flavor distilled from wiki prose). Where authored per-body content lives (frontmatter vs companion file vs a consolidated tree) should follow a consolidation of ALL wiki content — pages + heightmaps + markers + architecture-flavor — into one first-class, baked Atlas content set (the "move it into a `gamedata/` subtree" instinct), not a per-feature hack. The generator-facing tables (`trait_templates` + `atlas_body_trait_bias`, D-232) are invariant to the source location, so this does not block the fill seam.
|
||||
- **Cross-reference:** [D-232](architecture.md#d-232), [D-223](architecture.md#d-223), [D-191](architecture.md#d-191) (Atlas/settlement), and the development cascade (Phase 1 wiki + Phase 3 Atlas)
|
||||
|
||||
---
|
||||
|
||||
*56 questions (11 resolved, 1 partially resolved, 44 open). Last updated: 2026-05-25.*
|
||||
|
||||
Reference in New Issue
Block a user