From f1c1d2c8f026fdc006a86919d938f64d42f2a7f5 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 25 May 2026 12:55:57 +0200 Subject: [PATCH] =?UTF-8?q?docs(decisions):=20atlas-to-tile=20derivation?= =?UTF-8?q?=20model=20=E2=80=94=20D-227/D-228=20+=20Q-100..105?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atlas-derivation workshop output (Jeroen + Tyre / Nigel / Burnelli / Gestalt). The foundational world-derivation model: - D-227 deterministic-rebuild world model: world = pure function of seed+atlas, derived on demand + cached, only tile mutators persisted, fully volumetric (voxel / subvoxel), vertical extent physical not floor-count, variable-height floors (cave/basement/built all count). - D-228 composite tile schema: orthogonal axes (TerrainMaterial / FloorMaterial / Vegetation / Water / elevation), derived shape + tactical form, region-level morphology zones, cohesion matrix, dynamic clock-bound region seasonal state (water-height / snow / weather / crop-cycle from one cheap per-region calc). - Q-100..Q-105: biome authority, refinement contract (+ body-class river density + ocean mask), cohesion-matrix algorithm, mutator op schema, floor-to-voxel-z mapping, region seasonal/clock state. - Workshop brief: docs/workshops/atlas-derivation-model.md. Epic #974 + ocean-mask story #975 track implementation (Phase 4 milestone). Co-Authored-By: Claude Opus 4.7 (1M context) --- decisions/architecture.md | 39 ++++++- decisions/questions-architecture.md | 50 +++++++- docs/workshops/atlas-derivation-model.md | 143 +++++++++++++++++++++++ 3 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 docs/workshops/atlas-derivation-model.md diff --git a/decisions/architecture.md b/decisions/architecture.md index d45a56115..75762948c 100644 --- a/decisions/architecture.md +++ b/decisions/architecture.md @@ -1530,4 +1530,41 @@ Technical foundation decisions that constrain implementation: engine, client-ser --- -*86 decisions (D-001 through D-226, excluding gaps). Last updated: 2026-05-24 (D-226 — live-pause inspection harness)* +### D-227: Deterministic-rebuild world model — derive-don't-store, cache, tile mutators, volumetric +- **Date:** 2026-05-25 +- **Decision:** The walkable world is a **pure function of `seed + atlas`**, materialised on demand and cached, never persisted as a tile grid. + - **Derive-don't-store.** `subtile(x,y,z) = derive(seed, atlas, position)` — deterministic (D-010), recomputed on demand, held in a transient by-chunk/region cache for performance, evictable (eviction → recompute, always valid). No generated tile/voxel is ever saved. + - **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.) + - **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). +- **Implementation:** Phase 4+ (epic #750). The caching substrate exists at the atlas level (`BodyWorldStateCache`, D-203/D-225); the tile/voxel cache, mutator overlay, and geology derivation land as the cascade reaches the tile layers. No migration needed pre-save (D-202 `schema_version` lineage covers future drift once saves exist). +- **Raised by:** Jeroen (derive-don't-store, volumetric, drop-the-floor-cap directives) + Claude, atlas-derivation workshop, 2026-05-25. +- **Cross-reference:** [D-010](#d-010) (determinism — now save-critical), [D-222](#d-222) (subtile/tile/chunk hierarchy), [D-110](#d-110) (signed z-levels), [D-225](#d-225) (layer-stream proxy + cache pattern), [D-203](#d-203) (LRU cache tier), [D-224](#d-224) (SeedChain — feeds `derive`), [D-228](#d-228) (composite tile schema — the derived value type), [Q-101](questions-architecture.md#q-101) (refinement contract), [Q-103](questions-architecture.md#q-103) (mutator op schema), [Q-104](questions-architecture.md#q-104) (floor↔voxel-z mapping) +- **Dissent:** None + +--- + +### 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. + - **`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. + - **Region-level (shared by all tiles in a region, not stored per-tile):** **morphology zone** — fjord / delta / meander-reach / **alluvial-plain** / open-ocean / lake / sea / … — the whole-shape decision a single tile cannot see; it (a) governs which tile-tags get laid down, (b) carries affordances a tile can't (this inlet is a sheltered harbour), and (c) **owns the water-feature semantics** (river vs ocean vs lake vs sea) the per-tile `Water` axis deliberately omits. **Morphology resolves top-down (region → tile), never bottom-up** — a tile cannot know it is in a fjord from its neighbours; deriving it per-tile would produce the squaring the model exists to avoid. **Sub-biome, province, and the region's seasonal/clock state are likewise region properties** — the season is computed *once* per region per phase and inherited by its tiles. That single cheap seasonal state drives **water-height (flooding / tides), seasonal snow cover, weather, and the farmland crop cycle** alike — one calculation, many dynamic overlays (→ Q-105). + - **Separate axes:** biome (climate / sub-biome — region-level, authority unresolved, Q-100; distinct from, but informs, the per-tile `Vegetation` cover), resource hints (bitmask: fertile / mineral / timber / groundwater). + - **Cohesion matrix (anti-squaring at the material layer):** intra-region material + sub-biome scatter (a dirt patch in grass, a lone rock in beach sand) comes from a **global, position-keyed continuous noise field — never per-chunk** — so transitions never reveal chunk/grid seams. Straight lines appear **only when authored** (roads, plazas, field edges); a straight line must always have a placed cause, never be a generation-grid artifact. (Algorithm → Q-102.) +- **Rationale:** A flat enum explodes combinatorially (≈5×5×8 ≈ 200 mostly-incoherent variants), can't be queried by axis ("all water-adjacent tiles"), and grows a new variant for every new mechanic. Orthogonal axes are set independently by different cascade passes (material from sub-biome, water from drainage, shape from elevation), independently queryable, cheap to serialize, and compose without explosion. Names are visual/narrative and belong downstream of the simulation. +- **Open sub-questions:** how far the `FloorMaterial` / `Vegetation` vocabularies are enumerated now vs deferred to the settlement layers; the exact morphology-zone vocabulary; the dynamic water-height model (→ Q-105). +- **Implementation:** Phase 4+ (epic #750), as the cascade reaches the tile layers. Today's `TileKind` (D-049 render stack) is the seed of the per-subtile axes; `SubBiomeVariant` (D-210) is the region biome axis. +- **Raised by:** Jeroen (FloorMaterial, shape-from-material, cohesion-matrix directives) + Claude, with the atlas-derivation workshop four — Tyre (composite data-structure), Gestalt (tag model + tactical form), Nigel (morphology-as-character), Burnelli (economic distinctions), 2026-05-25. +- **Cross-reference:** [D-227](#d-227) (derive-don't-store — these axes are the derived value type), [D-010](#d-010), [D-210](#d-210) (sub-biome — region biome axis), [D-222](#d-222) (subtile/tile), [D-049](#d-049) (z-stack render — `TileKind` seed), [D-208](#d-208) (D8 — flow-direction source), [D-226](#d-226) (dynamic-state inspection — the water overlay), [Q-100](questions-architecture.md#q-100) (biome authority), [Q-101](questions-architecture.md#q-101) (refinement contract), [Q-102](questions-architecture.md#q-102) (cohesion-matrix algorithm), [Q-105](questions-architecture.md#q-105) (dynamic water-height) +- **Dissent:** None + +--- + +*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)* diff --git a/decisions/questions-architecture.md b/decisions/questions-architecture.md index 6e0c322da..305e2eff4 100644 --- a/decisions/questions-architecture.md +++ b/decisions/questions-architecture.md @@ -347,4 +347,52 @@ Technical foundation questions: engine, protocols, data structures, performance, --- -*48 questions (9 resolved, 1 partially resolved, 38 open). Last updated: 2026-05-23.* +### Q-100: Biome authority — Python sim vs Rust cascade +- **Status:** Open — raised in the atlas-derivation workshop (2026-05-25) +- **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` + +--- + +### Q-101: Refinement / derivation contract — coarse hint → fine geometry + morphology algorithm family +- **Status:** Open — the atlas-derivation workshop's primary technical output, not yet specified (2026-05-25) +- **Question:** How does a coarse, map-scale hint (a ~78 km Layer-1 cell) plus a seed become fine, ~1 m-voxel coherent geometry — deterministically, without squaring — across ~3 scale jumps (78 km cell → ~1 km region → 64 m chunk → 1 m voxel)? Needs: the inter-layer hint+seed→geometry API (Tyre sketched a `RegionHint`); a **context-driven morphology algorithm family** selected by local context (rivers meander in lowlands / incise in mountains; coasts dune / crag / fjord by slope + lithology + glaciation), ≥6 distinct generators (meander, incised gorge, braided delta, dune strand, cliff coast, fjord, mountain pass); integer-only *structural* decisions (D-010), f64 confined to within-voxel interpolation; clean transitions between adjacent morphology families. **Unowned dependency:** the ocean/lake mask — D-223 stripped the markers.json polygons that D-209 `CoastalAccess`/`LakeShore` extraction referenced, so the mask must instead be derived from the heightmap sea-level threshold or a baked water-bodies layer. Lithology is a required input (promoted load-bearing by the volumetric subsurface, D-227). Also in scope: **body-class parameters** (hydrosphere, tectonic activity, atmosphere) must modulate river-network *density* — the effective river threshold — so an arid body and an oceanic body differ in *how many* rivers, not just where they run (today `RIVER_THRESHOLD` is a global constant, Nigel); and **river flow direction** (upstream/downstream — gates effort vs speed, and feeds the seasonal water model) derives from the D8 network at query time. +- **Context:** The heart of the atlas→tile derivation; surfaces as the cascade builds the region → chunk → voxel layers. +- **Cross-reference:** [D-227](architecture.md#d-227), [D-228](architecture.md#d-228), [D-208](architecture.md#d-208), [D-209](architecture.md#d-209), [D-223](architecture.md#d-223), [D-010](architecture.md#d-010), [D-222](architecture.md#d-222) + +--- + +### Q-102: Cohesion-matrix algorithm — seam-free continuous variation +- **Status:** Open — deferred from D-228 (2026-05-25) +- **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](architecture.md#d-228), [D-227](architecture.md#d-227), [D-010](architecture.md#d-010) + +--- + +### Q-103: Tile-mutator op schema +- **Status:** Open — deferred from D-227 (2026-05-25) +- **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](architecture.md#d-227), [D-010](architecture.md#d-010) + +--- + +### Q-104: Floor-index ↔ absolute voxel-z coordinate mapping +- **Status:** Open — flagged by Tyre in the atlas-derivation workshop (2026-05-25) +- **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) + +--- + +### 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). +- **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.* diff --git a/docs/workshops/atlas-derivation-model.md b/docs/workshops/atlas-derivation-model.md new file mode 100644 index 000000000..65510eac6 --- /dev/null +++ b/docs/workshops/atlas-derivation-model.md @@ -0,0 +1,143 @@ +# Workshop — The Atlas → Tile Derivation Model + +*Driven by the water model, but the water model is only the forcing instance.* + +## Why this, why now + +We are building the generation cascade (Phase 4). The viewer (#960) surfaced a +problem that looks like "lakes" but is actually the **inter-level derivation +contract**: how coarse, map-scale planetary data becomes fine, coherent, +per-tile geometry without squared/blocky artifacts. This is **cascade-first**: +if we bake the wrong derivation model now, every layer below (economic → +settlement → quarters → tiles) inherits it and it becomes very expensive to +unwind. Decide it before those layers exist. Stations are out of scope here — +**bodies first**. + +## The methodology: reverse the derivation + +Do **not** start from "what can the cascade derive?" Start from the target: + +> **Build the complete list of everything a tile on a body must contain.** +> Then work backward: for each item, how do we derive it deterministically from +> (a) a **prebaked atlas dataset** and/or (b) a **seeded algorithm**? + +The requirements list is the spec. The cascade is the mechanism that satisfies +it. We want the list first so the cascade is designed to a known target, not +grown by accretion. + +## The three legs (generalized from the water instance) + +**1. Dual artifact — baked render ↔ semantic model.** +The reliefmap should have lakes/rivers/coastlines **baked in** (authoritative +*visual*, no toggles — yes, that means regenerating reliefmaps). Separately, +there is a **semantic model** (rivers as flow-carrying paths, lakes as regions, +coasts as curves) that is the authoritative *generation input*. The reliefmap is +a render of the model at map scale; the model is what lower levels consume. + +**2. Multi-scale refinement contract — hint, not squared truth.** +A reliefmap pixel is a map-scale rendering, not ground truth to copy downward. A +2-px river (sized for map visibility) is kilometers wide at tile scale; a +land/water pixel edge is a staircase coastline. So coarse data is a **semantic +hint + a seed**, and each lower level **deterministically refines** it into +smooth, ~1-tile-scale geometry that *respects* the hint without copying its +resolution. "Somewhere in this band a coherent coastline passes"; "a river of +roughly this flow threads this corridor." + +**3. Context-driven algorithm family — selection is part of generation.** +Refinement is not one algorithm. The *choice* is driven by local context: +- **Rivers**: low slope → meandering, wide, lazy, floodplains/oxbows; steep → + incised, straighter, gorges, knickpoint waterfalls; (braided/deltaic at the + extremes). +- **Coasts**: low relief + sediment → smooth dune-lined strands; hard rock + + high relief → craggy cliffs; drowned glaciated valleys → fjords. + +Contract: **hint + local context + seed → algorithm selection → geometry.** Same +hint, different terrain, genuinely different feature. + +## The expanding atlas (a key consideration for the brief) + +Satisfying the per-tile list will almost certainly require **more prebaked +planetary layers** than heightmap + reliefmap — plausibly: lithology +distribution, a water-bodies layer, prevailing winds, temperature/precipitation, +glaciation history, biomes. The workshop must take a position on: +- **Which layers are prebaked vs runtime-derived** (storage + determinism + the + modding story — D-225's mod-first intent applies). +- **Context data we have vs owe**: slope/relief derive from the heightmap, but + lithology (crag-vs-dune) and glaciation (fjords) may not exist yet. Decide + whether morphology is selected from proxies we have, or a thin new context + layer is owed (with its determinism + ownership cost). +- **Organizational (future, "at some point")**: moving all wiki content + + heightmaps into a new `gamedata/` atlas subtree sitting next to `server/` and + `client/`. Not a round-1 deliverable, but flag whether the data model implies + it and roughly when. + +## Questions to resolve (each → a D-record or a tracked open question) + +1. **The per-tile content requirements list** — the target (reverse-derivation). +2. **Biome authority** — `planet-gen` reliefmap coloring vs in-cascade + `subbiome::classify`; which is canonical, and what that implies for where + water (and other features) is authored. +3. **The atlas dataset family** — which layers are prebaked; format; determinism. +4. **Dual artifact** — baked reliefmap ↔ semantic model: representation + how they + stay in sync (regen pipeline). +5. **The refinement contract** — the inter-layer hint+seed→geometry API; + anti-squaring; how each layer hands constraints to the next. +6. **Context-driven morphology** — the algorithm families + the context inputs + they require; the have-vs-owe data fork. +7. **Cascade placement + `gamedata/` restructure** — where water/derivation is + authored; restructure scope/timing. +8. **Determinism** — D-010 integer-only must hold across all refinement + + selection (seed → identical geometry). + +## Team angles + +- **Tyre** — architecture: the derivation/refinement contract, dual raster + + semantic data structures, deterministic downscaling, atlas-layer storage, the + `gamedata/` restructure feasibility, performance/memory at tile scale. +- **Nigel** — procedural variety/emergence: does the algorithm family + + reverse-derivation yield rich, sensible, non-repetitive worlds across seeds? + Where does sameness creep in? What's the second-playthrough payoff? +- **Gestalt** — systems meaning: drive the per-tile requirements list from "what + creates interesting decisions / gameplay," and the consequences of morphology + (a fjord is a deep defensible harbor; a meandering river is fertile + + bridgeable; a craggy coast resists landing). +- **Burnelli** — economic layer needs: what the economy reads from the atlas — + water as trade corridor vs barrier, ports/harbors, resource distribution, the + regional scale the economy actually operates at. + +## Round 1 task (each agent) + +Read this brief + the pointed code/decisions, then post an **opening position** +on your angle that includes: +1. Your contribution to the **reverse requirements list** — what must a tile (or + the data feeding it) contain, from your lens. +2. The key **forks/risks** you see in the derivation model. +3. Anything in the current code/decisions that **constrains or contradicts** the + approach. + +This is investigation + position, **not** final design. Flag blockers early +(heartbeat rule below). + +## Reading + +- **Code**: `server/src/atlas/heightmap.rs` (normalized elevation + `sea_level`, + downsample), `server/src/atlas/drainage.rs` (existing `depression_fill`, D8 + routing, rivers/basins, the new Moore boundary trace), `server/src/atlas/layer1.rs` + (`Layer1Output` incl. `grid_w/grid_h`), `server/src/atlas/layer_proxy.rs` + (D-225 proxy). `tooling/planet-gen/` — `render_heightmap.py`, + `planet_renderer.py` (ocean colors, biome coloring), `planet_simulation.py` + (`max_elevation_km`, biome assignment). +- **Decisions**: D-202 (canonical 16-bit heightmap), D-208 (drainage), D-209 / + D-210 (attractors / sub-biomes), D-225 (layer-stream proxy, mod-first), + D-166 (generation-before-player). The cascade phase table in `CLAUDE.md`. +- **Constraints**: D-010 (determinism, integer-only), cascade-first (no detail + from a later phase), the no-squaring principle above. + +## Process + +Round-based. The lead synthesizes after each round and pulls Jeroen in — he +decides when to dismiss. **Close only when D-record(s) + ticket(s) exist** (hard +deliverables). Teammates edit files; the **lead** does all git. + +**Heartbeat rule:** if you work a single task >15 min without progress, message +the lead with what's blocking you. Do not silently retry.