diff --git a/governance/README.md b/governance/README.md index 3a1b86c0c..ab9603c2a 100644 --- a/governance/README.md +++ b/governance/README.md @@ -318,6 +318,7 @@ line in place — keep the Q-record for the audit trail rather than deleting it. - [D-250: Wall cutaway is client-side camera presentation, decoupled from fog-of-perception](decisions/perception.md#d-250-wall-cutaway-is-client-side-camera-presentation-decoupled-from-fog-of-perception) — _perception_ - [D-251: Character asset route, 2026 reconfirmation — Quaternius rig, in-house wardrobe, purchased animation tiers](decisions/content.md#d-251-character-asset-route-2026-reconfirmation--quaternius-rig-in-house-wardrobe-purchased-animation-tiers) — _content_ - [D-252: Facing is view-only — movement no longer writes Facing; NPC gaze is intent](decisions/architecture.md#d-252-facing-is-view-only--movement-no-longer-writes-facing-npc-gaze-is-intent) — _architecture_ +- [D-253: Region transient state model — seasonal/tidal/weather/snow phase functions (resolves Q-105)](decisions/architecture.md#d-253-region-transient-state-model--seasonaltidalweathersnow-phase-functions-resolves-q-105) — _architecture_ ## Open questions @@ -385,9 +386,7 @@ line in place — keep the Q-record for the audit trail rather than deleting it. - [Q-093: Tile-based exploration map in player insert (Google Maps for the implant)](questions/architecture.md#q-093-tile-based-exploration-map-in-player-insert-google-maps-for-the-implant) — _architecture_ - [Q-097: Strip "What They Don't Talk About" from corporation pages](questions/content.md#q-097-strip-what-they-dont-talk-about-from-corporation-pages) — _content_ - [Q-099: Mod content catalog — body rows / terrain_reference overlay for systems.db](questions/architecture.md#q-099-mod-content-catalog--body-rows--terrain-reference-overlay-for-systemsdb) — _architecture_ -- [Q-102: Cohesion-matrix algorithm — seam-free continuous variation](questions/architecture.md#q-102-cohesion-matrix-algorithm--seam-free-continuous-variation) — _architecture_ - [Q-103: Tile-mutator op schema](questions/architecture.md#q-103-tile-mutator-op-schema) — _architecture_ -- [Q-105: Region seasonal/clock state — the shared cheap-dynamism source](questions/architecture.md#q-105-region-seasonalclock-state--the-shared-cheap-dynamism-source) — _architecture_ - [Q-107: Wiki → Atlas content-set consolidation](questions/architecture.md#q-107-wiki--atlas-content-set-consolidation) — _architecture_ - [Q-108: Subterranean / domed / sealed-habitat settlement morphology — does the built-world fill model need a surface-vs-enclosed branch](questions/architecture.md#q-108-subterranean--domed--sealed-habitat-settlement-morphology--does-the-built-world-fill-model-need-a-surface-vs-enclosed-branch) — _architecture_ - [Q-109: Cascade generation-source dispatch — planetary / station / mod-DLC-forked / save-only](questions/architecture.md#q-109-cascade-generation-source-dispatch--planetary--station--mod-dlc-forked--save-only) — _architecture_ @@ -443,7 +442,9 @@ line in place — keep the Q-record for the audit trail rather than deleting it. - [Q-098: Persistence of generated river/city mapping outputs](questions/architecture.md#q-098-persistence-of-generated-rivercity-mapping-outputs) — _architecture_ - [Q-100: Biome authority — Python sim vs Rust cascade](questions/architecture.md#q-100-biome-authority--python-sim-vs-rust-cascade) — _architecture_ - [Q-101: Refinement / derivation contract — coarse hint → fine geometry + morphology algorithm family](questions/architecture.md#q-101-refinement--derivation-contract--coarse-hint--fine-geometry--morphology-algorithm-family) — _architecture_ +- [Q-102: Cohesion-matrix algorithm — seam-free continuous variation](questions/architecture.md#q-102-cohesion-matrix-algorithm--seam-free-continuous-variation) — _architecture_ - [Q-104: Floor-index ↔ absolute voxel-z coordinate mapping](questions/architecture.md#q-104-floor-index--absolute-voxel-z-coordinate-mapping) — _architecture_ +- [Q-105: Region seasonal/clock state — the shared cheap-dynamism source](questions/architecture.md#q-105-region-seasonalclock-state--the-shared-cheap-dynamism-source) — _architecture_ - [Q-106: Era-band stacking depth for layered architecture-flavor](questions/architecture.md#q-106-era-band-stacking-depth-for-layered-architecture-flavor) — _architecture_ - [Q-110: Region/chunk physical-scale anchoring — what is a region in metres, and what anchors a heightmap pixel?](questions/architecture.md#q-110-regionchunk-physical-scale-anchoring--what-is-a-region-in-metres-and-what-anchors-a-heightmap-pixel) — _architecture_ diff --git a/governance/decisions/architecture.md b/governance/decisions/architecture.md index 85c9ae8e4..9b8649454 100644 --- a/governance/decisions/architecture.md +++ b/governance/decisions/architecture.md @@ -1966,4 +1966,34 @@ Technical foundation decisions that constrain implementation: engine, client-ser --- -*106 decisions (D-001 through D-252, excluding gaps). Last updated: 2026-07-06 (D-252 — Facing is view-only: movement no longer writes Facing, NPC gaze becomes AI intent, bump-to-turn retired; resolves the Q-084 walk-vs-aim split with T-1088 play evidence).* +### D-253: Region transient state model — seasonal/tidal/weather/snow phase functions (resolves Q-105) + +- **Date:** 2026-07-08 +- **Resolves:** [Q-105](../questions/architecture.md#q-105). +- **Decision:** The region ([D-243](#d-243)) is the single scale at which **transient (clock-bound) surface state** is resolved — "computed once per region per phase, inherited by every district and tile inside" ([D-243](#d-243) §3, the cheap-dynamism source). This record pins that model: **what** transient state a region carries, **how** its clock advances and recomputes, **how** tiles inherit it, and its **determinism** — but explicitly **not** the gameplay/rendering that later consumes it. + + **(a) The transient bundle — four phase terms + their derived surface scalars.** A region carries a memoized `RegionPhase` (proposed carrier name) built from **four clock terms, one per natural clock rate** (fastest → slowest), each a pure function of the in-game clock ([D-031](#d-031)) and the region's static params: + - **Diurnal** — day/night, from the [D-031](#d-031) day-phase clock; amplitude = the atmosphere heat-retention already defined in [D-239](#d-239) §2 (thick air → small swing). *This term is the one [D-239](#d-239) §2 already owns for temperature; D-253 folds it into the same region phase model as the other three, rather than leaving it a separate mechanism.* + - **Tidal** — from the lunar/day clock; amplitude from the body's satellite config; **absent (zero) when the body has no moon**. Drives the tidal component of water-height. + - **Weather** — a coarse, seeded, deterministic precipitation/condition term (a bounded pseudo-sequence keyed on `(region, weather-bucket)` — **not** a simulated advecting weather system; deliberately minimal to stay "nearly free"). Drives puddles + general conditions. + - **Seasonal** — from the year clock, **phased continuously by latitude** (amplitude → 0 at the equator and sign inverting across it — a continuous function of latitude, not a binary hemisphere flag, so the equatorial seam never steps). The slow temperature term + the seasonal component of water-height + the snow/crop drivers. + - From these terms the region derives its **transient surface scalars** (region-level, not per-tile): **temperature(time)** = the T-1078 / [D-240](#d-240) static baseline **+** seasonal offset **+** diurnal offset (this makes [D-239](#d-239) §2's "temperature(time)" concrete — the static baseline T-1078 ships is the mean-phase value); **water-height(time)** = mean water level **+** seasonal term **+** tidal term; **snow/ice depth** = `f(temperature(time), moisture)` feeding the **existing T-1030 scattered band** ([D-239](#d-239) §3) — D-253 supplies only the *depth over time*, T-1030 owns *where* the band scatters; **weather intensity/category**; and the **crop-cycle phase** (sown → growing → ripe → harvested → fallow) = `f(seasonal)`, the cadence clock for [D-228](#d-228)'s managed-`Vegetation` farmland override (D-253 owns the *cadence*; the settlement/economic layer owns crop *type* and farmland *placement*). + + **(b) Phase-stepping — recompute on bucket rollover, never per tick, never integrated.** Each term declares a **clock-bucket size** (diurnal ≈ day-phase; tidal ≈ day-phase; weather ≈ a multi-phase block; seasonal ≈ a season step). `RegionPhase` is **memoized with the clock-buckets it was computed for**; a query recomputes a term **only when its bucket rolls over** — the "nearly free" requirement, and [D-226](#d-226)'s dynamic-state path. Crucially, every scalar is evaluated from the **absolute clock value** (`state = f(clock)`), **never integrated step-by-step** (`state += Δ`): absolute evaluation is drift-free, reproducible, and hands [D-226](#d-226) its **frozen-phase inspection** for free (freeze the clock → the whole world's transient state is stable and re-derivable). Default bucket sizes are source-canonical + tunable (the `climate_constants.toml` precedent), provisional pending calibration. + + **(c) Inheritance — region computes the scalar, the tile realizes it locally, edge-fuzzed.** Districts and tiles **never compute their own phase** — they read the region scalars, blended across region neighbours by the **same [D-243](#d-243) §4 edge-fuzz** the static climate baseline uses (warp-perturbed bilinear across region centres), so a weather front / seasonal gradient / thaw line never steps on the ~205 km grid (and the equatorial seasonal seam is continuous by construction, per (a)). The **per-tile realization is a local comparison**, not an inherited value: a tile is **flooded** iff `region-water-height(time) > tile.elevation`; **snowed** to a depth capped by the region snow-depth, scattered by the T-1030 band and gated on its own material/moisture; **puddled** iff the weather term is wet and the tile sits in a micro-relief low (the T-1081 `voxel_relief` hollows) that diurnal evaporation has not burned off; its farmland shows the crop stage the region phase dictates. So floodplain / tidal-flat / seasonal-river **emerge** ([D-228](#d-228)) — the static world stays static; only the region scalars carry the clock. This directly discharges T-1082's deferred TidalFlat wet/dry alternation (`Shallow`/`Dry` = tidal water-height vs local elevation). + + **(d) Determinism ([D-010](#d-010)).** Every term and scalar is **integer fixed-point** (phase positions as basis-points of their cycle; depths/heights in integer mm/cm; temperature offsets on the existing integer scale) and a **seeded pure function** of `(absolute clock, region params, body seed)`. **No per-tick float accumulation exists anywhere in the model** — see (b). Weather's pseudo-sequence is a seeded hash of `(region, weather-bucket)`, so it replays identically. Frozen clock → byte-identical state on every platform and every reload; this is what lets the transient overlay live in the [D-227](#d-227) derive-don't-store tier (the memo is **not persisted** — a save records only the clock and the state re-derives). + + **(e) Scope boundary — this is the STATE MODEL, not its consumers.** D-253 defines *derivable transient state and the functions that produce it* — Phase-4-appropriate, viewable as an Atlas layer like every other cascade output, and completing the forward-contracts left open by T-1030 / T-1078 / T-1082. It does **NOT** build: weather/flood/snow **gameplay** (movement, perception, passability effects — [D-239](#d-239) §9 names the passability hook but its wiring is later), the **farming/crop simulation**, snow/water **rendering** (Phase 5), or any **verb** that reads this state. Those are later phases and inherit a fixed contract here. Adding a fifth term or a consumer is a future amendment, not a reinterpretation of this record. + +- **Rationale:** The model is cheap *because* the four transient terms line up with the four natural clock rates and reuse the existing temperature primitive — temperature(time) and water-height(time) are just a static baseline plus the same seasonal/diurnal/tidal offsets, so **one clock model drives temperature, flooding, tides, snow, puddles, weather, and crops** with no per-system simulation. The single load-bearing choice — **absolute-clock evaluation rather than step integration** — satisfies three requirements at once: determinism ([D-010](#d-010), no drift), cheapness (recompute only on bucket rollover, and per-region not per-tile → O(regions)), and inspectability ([D-226](#d-226) frozen phase). Computing at the region and realizing per-tile by comparison is what keeps the dynamism O(regions) while still producing per-tile flood/snow/puddle/crop extent — the "computed once, inherited" contract [D-243](#d-243) §3 promised. +- **Raised by:** Jeroen (Q-105 model constraints — clock + hemisphere bound, moon-gated tides, computed-once-per-region, "nearly free", 2026-05-25) + Tyre (T-1057 triage — the four-term phase-function model, the absolute-clock / memoized-by-bucket / edge-fuzzed-inheritance pins, 2026-07-08). **Authored as a proposed design for PR review** — the flagged judgment calls (below) are open for the lead/user to adjust. +- **Design choices flagged for review:** (1) **diurnal promoted to a first-class term** — Q-105 enumerated three terms (seasonal/tidal/weather); D-253 adds diurnal as the fourth, since [D-239](#d-239) §2 already owns it for temperature and T-1030's dawn-frost already depends on it (a coherence unification, but it extends Q-105's framing). (2) **weather kept deliberately minimal** (a seeded coarse precipitation/condition term, no advecting fronts) — the depth is a taste call. (3) **recompute-bucket granularities** (diurnal/tidal ≈ day-phase, weather ≈ multi-phase, seasonal ≈ season-step) are provisional/tunable, not hard-pinned. (4) **memoized-not-persisted** reconciles D-228's "region property computed once per phase" with [D-227](#d-227) derive-don't-store (the region *caches* the bundle keyed by bucket; it is never durable state). (5) **"phased by hemisphere" read as continuous-in-latitude** (amplitude→0 at equator) rather than a north/south sign flag, so the seam edge-fuzzes cleanly. +- **Implementation:** Phase 4+ (epic [T-750](../../.pql)), the forward contract from T-1030 (transient freeze/snow depth), T-1078 (region climate stack — shipped the static baseline + region-clock *structure*; D-253 adds the transient terms), and T-1082 (tidal-flat wet/dry). Proposed home: a `RegionPhase` carrier alongside `RegionProfile` in `BodyWorldState` ([D-203](#d-203) / [D-239](#d-239) §10 region storage), memoized by clock-bucket; proposed derivations `derive_region_phase(clock, region)` + per-tile realization helpers (flood / snow / puddle / crop) — names non-binding, the impl ticket fixes them. Constants in a source-canonical TOML (`climate_constants.toml` precedent). A new implementation ticket lands under T-750. +- **Cross-reference:** [D-243](#d-243) (region = climate/weather/season lockdown scale; §3 computed-once-inherited; §4 edge-fuzz — the inheritance contract), [D-228](#d-228) (the seasonal water-height / snow-cover overlay / crop cycle this model drives — its open water-height sub-question resolved here), [D-239](#d-239) §2 (temperature(time) primitive — made concrete) / §3 (T-1030 scattered freeze-snow — given its clock) / §9 (seasonal passability — a later consumer), [D-240](#d-240) (`planet_class` temperature envelope — the baseline the seasonal/diurnal terms offset), [D-226](#d-226) (dynamic-state frozen-phase inspection), [D-031](#d-031) (game clock / day phases — the clock source), [D-010](#d-010) (determinism), [D-203](#d-203) (BodyWorldState region cache — the memo home), [D-227](#d-227) (derive-don't-store — the memo is not persisted). Tickets: [T-1057](../../.pql) (this triage), T-1030 / T-1078 / T-1082 (the discharged forward-contracts), T-1081 (`voxel_relief` micro-lows — puddle/flood sites). +- **Dissent:** None + +--- + +*107 decisions (D-001 through D-253, excluding gaps). Last updated: 2026-07-08 (D-253 — region transient state model: four clock-terms (diurnal/tidal/weather/seasonal), memoized-by-bucket absolute-clock evaluation, edge-fuzzed per-tile realization; resolves Q-105).* diff --git a/governance/questions/architecture.md b/governance/questions/architecture.md index aa989d146..6a7c6e238 100644 --- a/governance/questions/architecture.md +++ b/governance/questions/architecture.md @@ -370,15 +370,17 @@ Technical foundation questions: engine, protocols, data structures, performance, --- ### Q-102: Cohesion-matrix algorithm — seam-free continuous variation -- **Status:** Open — deferred from D-228 (2026-05-25) +- **Status:** Resolved 2026-07-08 (T-1057) — resolved-in-practice by [D-246](../decisions/architecture.md#d-246) + [D-228](../decisions/architecture.md#d-228) (with T-1080 / T-1077); no dedicated cohesion-matrix D-record is needed. +- **Resolution:** All five sub-questions are answered by shipped work, so the cohesion matrix needs no separate decision. **(1) noise basis + (2) position-keying + (3) threshold mapping + (4) determinism** are exactly [D-246](../decisions/architecture.md#d-246)'s `voxel_mosaic()`: an enveloped-fBm **value-noise** band (the T-1081 `detail_scatter` machinery, `[64, 32, 16, 8]` m octaves) keyed on **absolute world position** under a body-global `SeedDomain::VoxelMosaic` (never the chunk index — so it is continuous across every chunk/region boundary), whose field value indexes the class palette's **cumulative weights** to select a micro-habitat (the threshold map), all integer-only per [D-010](../decisions/architecture.md#d-010). The precondition that there is *something* for the field to vary is the [D-243](../decisions/architecture.md#d-243) ladder's per-district climate/moisture gradient — T-1080 (meso-scale moisture-gradient fix: a flat body constant became a latitude/elevation/continentality gradient) over T-1077 (the D-243 ladder wiring) — so the noise modulates a real gradient, not a uniform slab. **(5) authored linear features composite without bleeding** is [D-228](../decisions/architecture.md#d-228)'s categorical `FloorMaterial`/`Vegetation` **override stack** (wild/natural → economic/managed → user): the topmost *present* layer wins and layers are **never blended**, so an authored road/plaza/field-edge (economic layer) sits on the continuous wild scatter with a hard categorical edge — D-228's "a straight line must always have a placed cause." Seam-free variation (blended noise) and crisp authored lines (categorical, unblended override) are therefore **two distinct mechanisms**, not one algorithm — which is precisely why a standalone cohesion-matrix decision is unnecessary. - **Question:** D-228 mandates that intra-region material / sub-biome scatter come from a **global, position-keyed continuous noise field** so chunk/region boundaries never read as grid seams, while authored straight lines (roads, plazas) stay crisp. Concretely: the noise basis (value / Perlin / simplex / worley); keying on world position (not chunk index) to stay continuous across boundaries; how material / sub-biome thresholds map onto it; integer-only / determinism (D-010); and how authored linear features composite over the continuous field without bleeding. - **Context:** The anti-squaring principle pushed down to the material layer. -- **Cross-reference:** [D-228](../decisions/architecture.md#d-228), [D-227](../decisions/architecture.md#d-227), [D-010](../decisions/architecture.md#d-010) +- **Cross-reference:** [D-228](../decisions/architecture.md#d-228), [D-246](../decisions/architecture.md#d-246), [D-227](../decisions/architecture.md#d-227), [D-010](../decisions/architecture.md#d-010), [D-243](../decisions/architecture.md#d-243); tickets T-1080, T-1077, T-1081, T-1084 --- ### Q-103: Tile-mutator op schema -- **Status:** Open — deferred from D-227 (2026-05-25) +- **Status:** Open — deferred from D-227 (2026-05-25); re-triaged 2026-07-08 (T-1057) → Phase-5 save-system kickoff. +- **Triage (2026-07-08, T-1057):** DEFERRED, scope unchanged. The question's own text scopes it to Phase 5+ — it is the save-format op vocabulary, and mutators are [D-227](../decisions/architecture.md#d-227)'s sole persisted state, so it cannot be settled without a concrete save serializer to answer against. Confirmed an **empirical non-blocker** for the Phase-4 tile-fill spine: T-987 (child of T-959) shipped the Layer-5 shell derivation (`server/src/atlas/shell.rs` — `Void`/`Wall`/`FloorSlab`/`Roof`) with **no mutator layer at all** — the derive phase is a pure function of `(seed, tags, z)`, and `ChunkMutations`/`TileOverride` already exist as the frozen-base overlay a mutator log will later stack on. Revisit at Phase-5 save-system kickoff, where the typed-op-vs-full-override fork, replay ordering / conflict semantics, and cross-re-derivation validity are decided together. - **Question:** D-227 makes tile mutators the sole persisted state (save = `seed + mutator log`). What does one mutator record — a full per-voxel override, or typed ops (`Dig`, `Build(FloorMaterial)`, `Place(object)`, `Destroy(object)`, `SetMaterial`)? Typed ops are compact, semantic, replayable, but need a fixed op vocabulary; full overrides are simple but heavier and lose intent. Also: replay ordering / conflict semantics, and confirmation that a position-keyed mutator stays valid across re-derivation (determinism guarantees an identical base — to be pinned). - **Context:** Needed when the save system is built (Phase 5+). - **Cross-reference:** [D-227](../decisions/architecture.md#d-227), [D-010](../decisions/architecture.md#d-010) @@ -395,10 +397,11 @@ 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) +- **Status:** Resolved 2026-07-08 (T-1057) by [D-253](../decisions/architecture.md#d-253) +- **Resolution:** Resolved by [D-253](../decisions/architecture.md#d-253) — a **four-term region transient-state model** (diurnal · tidal · weather · seasonal, one term per natural clock rate) whose derived surface scalars are **temperature(time)** (= the T-1078/D-240 static baseline + seasonal + diurnal offsets), **water-height(time)** (= mean + seasonal + tidal), **snow/ice depth**, **weather**, and the **crop-cycle phase**. The bundle is **memoized on the region keyed by clock-bucket**, recomputed **on bucket rollover, never per tick and never integrated** (state = `f(absolute clock, region)`, so it is drift-free, reproducible per [D-010](../decisions/architecture.md#d-010), and gives D-226 frozen-phase inspection for free). Districts/tiles **never compute their own phase** — they read the region scalars, **edge-fuzzed** by the D-243 §4 blend, and **realize per-tile state by local comparison** (flooded iff water-height > tile elevation; snowed via the existing T-1030 scatter band; puddled in T-1081 micro-lows; crop stage from the region phase). D-253 supplies the clock the three shipped punts were forward-contracts to (T-1030 transient freeze/snow depth, T-1082 tidal-flat wet/dry, T-1078 region-clock structure), and is a **state model only** — the gameplay/rendering consumers are Phase 5+. - **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](../decisions/architecture.md#d-228), [D-227](../decisions/architecture.md#d-227), [D-226](../decisions/architecture.md#d-226) (dynamic-state inspection), [D-010](../decisions/architecture.md#d-010) +- **Cross-reference:** [D-253](../decisions/architecture.md#d-253) (the resolving model), [D-228](../decisions/architecture.md#d-228), [D-243](../decisions/architecture.md#d-243) (region = lockdown scale), [D-239](../decisions/architecture.md#d-239) §2/§3 (temperature primitive; T-1030 scatter band), [D-227](../decisions/architecture.md#d-227), [D-226](../decisions/architecture.md#d-226) (dynamic-state inspection), [D-010](../decisions/architecture.md#d-010) --- @@ -427,7 +430,8 @@ Technical foundation questions: engine, protocols, data structures, performance, --- ### Q-109: Cascade generation-source dispatch — planetary / station / mod-DLC-forked / save-only -- **Status:** Open — raised by Jeroen (2026-06-05, during T-957 zone-selection authoring) +- **Status:** Open — raised by Jeroen (2026-06-05, during T-957 zone-selection authoring); re-triaged 2026-07-08 (T-1057) → revisit before stations / mods / save-only bodies are prioritized (Phase 5+). +- **Triage (2026-07-08, T-1057):** DEFERRED. The **planetary path is the only live generation source** today, so the dispatch discriminator has exactly one branch to route to and nothing yet exercises the fork. The station-only interim is already carved by the [D-229](../decisions/architecture.md#d-229) amendment (2026-06-05): T-957's `(ZoningType × economic_role × setting)` zone-selection table deliberately excludes the station-only ids (`residential_station` / `extraction_space` / `port_space` / `rural_orbital`), reserving them for a separate station cascade — so the seam is *documented* without the dispatcher being built. Revisit before stations, mod/DLC-forked bodies, or save-only `player_base` bodies are prioritized (Phase 5+), when the discriminator's home (`SettingType` vs a dedicated `GenerationSource`), the mod/DLC external-code resolution, and the save-only cache-bypass (a `player_base` body must never enqueue an `AnalyzeBody` — [D-225](../decisions/architecture.md#d-225)/[D-203](../decisions/architecture.md#d-203)) are decided together with the savegame model. - **Question:** The generation cascade currently has a single implicit path: every body runs the planetary generator (`run_cascade` → Layers 0–5). But a body's `SettingType` (or a sibling `GenerationSource` discriminator) should **dispatch at the cascade entry** to one of several generation sources: (a) **planetary** — the default terrain→quarter→tile cascade (T-955/T-956/T-957…); (b) **station / orbital** — a *separate* cascade that owns the station-only zone types (`residential_station`, `extraction_space`, `port_space`, `rural_orbital`) and its own layout model, rather than branching the planetary code (this is why T-957's zone-selection table deliberately excludes those ids — see [D-229](../decisions/architecture.md#d-229) amendment 2026-06-05); (c) **mod / DLC-forked** — a value that routes generation to externally-provided code/templates (a mod or DLC name), so third-party content can supply a body's built form without patching core; (d) **save-only / `player_base`** — disables auto-generation entirely and loads the body's built world from a save file (player-constructed bases must persist, not regenerate). Where does this discriminator live — on `SettingType` itself (it already carries `Station`/`Orbital` variants) or a dedicated `GenerationSource` enum at the `run_cascade` entry? How does the mod/DLC fork resolve to external code (registry? trait object? content-pack manifest?)? And how does save-only compose with the D-203 hot cache and the D-225 compute-on-demand proxy (a `player_base` body must never enqueue an `AnalyzeBody`)? - **Context:** Surfaced while authoring T-957's `(ZoningType × economic_role × setting)` zone-selection table: `setting` is the right *tweaker* within the planetary path, but it is also the natural *dispatch* point above it — the two roles are distinct and only the tweaker belongs in T-957. The dispatch is a cross-cutting routing seam above any single layer; capturing it here so T-957 stays scoped to the planetary path. Not blocking — the planetary path is the only live source today. Wants Tyre (cascade architecture) + a look at modding/DLC strategy and the savegame model (Phase 5+) before it's decided. - **Cross-reference:** [D-200](../decisions/architecture.md#d-200) (runtime cascade), [D-225](../decisions/architecture.md#d-225) (compute-on-demand proxy — save-only must bypass), [D-203](../decisions/architecture.md#d-203) (hot cache), [D-229](../decisions/architecture.md#d-229) (zone-selection — excludes station ids for this reason), D-222 (Quarter/spatial tiers), and the development cascade (Phase 5 player control + savegame)