diff --git a/.project/objectives/README.md b/.project/objectives/README.md
index c63ac1d9..ac7bf86b 100644
--- a/.project/objectives/README.md
+++ b/.project/objectives/README.md
@@ -15,10 +15,10 @@
| Priority | β
| π΅ | π‘ | π΄ | β | β« | Total |
|---|---|---|---|---|---|---|---|
| **P0** | 43 | 0 | 0 | 0 | 0 | 0 | 43 |
-| **P1** | 33 | 1 | 9 | 0 | 17 | 1 | 61 |
-| **P2** | 33 | 0 | 2 | 1 | 7 | 2 | 45 |
+| **P1** | 33 | 1 | 9 | 0 | 19 | 1 | 63 |
+| **P2** | 33 | 0 | 2 | 1 | 9 | 2 | 47 |
| **P3 (oos)** | 3 | 0 | 0 | 0 | 1 | 19 | 23 |
-| **total** | **112** | **1** | **11** | **1** | **25** | **22** | **172** |
+| **total** | **112** | **1** | **11** | **1** | **29** | **22** | **176** |
@@ -26,10 +26,10 @@
| Team Lead | Remaining |
|---|---|
+| [terraformer](../team-leads/terraformer.md) | 9 |
| [warcouncil](../team-leads/warcouncil.md) | 7 |
| [asset-sprite](../team-leads/asset-sprite.md) | 6 |
| [shipwright](../team-leads/shipwright.md) | 5 |
-| [terraformer](../team-leads/terraformer.md) | 5 |
| [combat-dev](../team-leads/combat-dev.md) | 1 |
| [simulator-infra](../team-leads/simulator-infra.md) | 1 |
| [asset-audio](../team-leads/asset-audio.md) | 1 |
@@ -136,9 +136,11 @@
| [p1-44](p1-44-buildings-as-producers.md) | β missing | Buildings produce units, not the city center β per-building production queues | β | 2026-04-29 |
| [p1-45](p1-45-batch-binary-freshness.md) | β missing | "Batch binary freshness: rebuild GDExt before every autoplay batch" | [simulator-infra](../team-leads/simulator-infra.md) | 2026-04-30 |
| [p1-46](p1-46-design-lab-terrain-dimensions.md) | π‘ partial | Terrain Dimensions Lab β fix ridginess, bind 149 flora species, add Whittaker plot | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
-| [p1-47](p1-47-river-hydrology-network.md) | β missing | River hydrology β D6 flow analysis, multi-hex lakes, cross-tile rivers | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
-| [p1-48](p1-48-flora-species-renderer.md) | β missing | Flora species renderer β bind 149 species to world-map tile rendering | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p1-47](p1-47-river-hydrology-network.md) | β missing | River hydrology β D6 flow analysis, hydraulic erosion, multi-hex lakes, cross-tile rivers | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p1-48](p1-48-flora-species-renderer.md) | β missing | Flora species renderer β bind 149 species to world-map tile rendering (single source of truth) | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
| [p1-49](p1-49-fauna-species-renderer.md) | β missing | Fauna species renderer β 61 Game-1 species visible on encounter and lair tiles | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p1-50](p1-50-tectonic-prepass.md) | β missing | Tectonic prepass β voronoi plates + boundary classification seeding elevation | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p1-51](p1-51-worldgen-canonical-design-docs.md) | β missing | Worldgen canonical design docs β author the spec before any Rust | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
| [p2-06](p2-06-export-pipeline.md) | β
done | Export pipeline for Windows / macOS / Linux | [shipwright](../team-leads/shipwright.md) | 2026-04-25 |
| [p2-16](p2-16-audio-assets.md) | π΅ in_progress | Audio assets β in-theme OSS launch pack + source ledger | [asset-audio](../team-leads/asset-audio.md) | 2026-04-27 |
| [p2-22](p2-22-sprite-generation-pipeline.md) | π‘ partial | Sprite generation pipeline β runnable end-to-end | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-25 |
@@ -196,7 +198,9 @@
| [p2-46](p2-46-past-games-archive-replay-viewer.md) | β missing | Past-games archive & replay viewer β `mc-replay` crate, on-disk archive, projection-based playback | [shipwright](../team-leads/shipwright.md) | 2026-04-30 |
| [p2-47](p2-47-in-game-statistics-screens.md) | β missing | In-game statistics screens β Civ-style 5-tab modal (Demographics / Graphs / Rankings / Replay / Histories) | [shipwright](../team-leads/shipwright.md) | 2026-04-30 |
| [p2-48](p2-48-end-of-game-summary-screen.md) | β missing | End-of-game summary screen β outcome banner, standings, score graph, awards, timeline, footer actions | [shipwright](../team-leads/shipwright.md) | 2026-04-30 |
-| [p2-49](p2-49-climate-axes-latitude-continentality.md) | β missing | Climate axes refactor β latitude + continentality as first-class per-hex inputs | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p2-49](p2-49-climate-axes-latitude-continentality.md) | β missing | Climate axes refactor β latitude + continentality + zonal winds as first-class per-hex inputs | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p2-50](p2-50-rng-determinism-pin.md) | β missing | Deterministic RNG + seed-derivation pin across mc-mapgen / mc-climate / mc-ecology | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
+| [p2-51](p2-51-world-shape-knobs.md) | β missing | Player-facing world-shape parameters on new-game screen | [terraformer](../team-leads/terraformer.md) | 2026-04-30 |
## Out of Scope (Game 2 / Game 3)
diff --git a/.project/objectives/p1-51-worldgen-canonical-design-docs.md b/.project/objectives/p1-51-worldgen-canonical-design-docs.md
new file mode 100644
index 00000000..897b9fc5
--- /dev/null
+++ b/.project/objectives/p1-51-worldgen-canonical-design-docs.md
@@ -0,0 +1,145 @@
+---
+id: p1-51
+title: Worldgen canonical design docs β author the spec before any Rust
+priority: p1
+status: missing
+scope: game1
+owner: terraformer
+updated_at: 2026-04-30
+wave: 0
+coordinates_with:
+ - p1-46
+ - p1-47
+ - p1-48
+ - p1-49
+ - p1-50
+ - p2-49
+ - p2-50
+ - p2-51
+---
+
+## Summary
+
+The Terraformer bundle (`p1-46`β¦`p2-51`) is an 8-objective procedural
+terrain pipeline spanning tectonics, hydrology, climate, ecology, RNG
+determinism, world-shape presets, and the design-app Terrain Dimensions
+Lab. Per **Rail 1** (`Rust is the simulation source of truth`,
+CLAUDE.md:13) and the project's three-tier doc system (canonical β
+engineering β JSON, per `.project/designs/README.md`), every Rust crate
+must mechanically implement an authored canonical specification β never
+the reverse.
+
+This Wave-0 objective authors the **7 canonical design docs** at
+`public/games/age-of-dwarves/docs/terrain/` (and `β¦/docs/` for ecology
+binding) that gate all subsequent Wave AβE implementation. Each Rust
+crate's rustdoc references the canonical doc it implements; bridges
+(`api-gdext`, `api-wasm`) and consumers (Godot, design lab) consume
+what the canonical specs declare β not what someone interpolated.
+
+The current TypeScript twins (`floraSpecies.ts`, `hydrology.ts`,
+`faunaSpecies.ts`) under `.project/designs/app/src/utils/worldGen/`
+exist precisely because this stage was skipped. Authoring the canonical
+docs first prevents that recurrence.
+
+## Acceptance
+
+- β» **`WORLDGEN_PIPELINE.md`** at `public/games/age-of-dwarves/docs/terrain/`
+ β master overview + end-to-end data-flow diagram + module map:
+ tectonics β erosion β hydrology β climate β biome classifier β flora
+ binding β fauna binding. Cross-references all 7 Wave-AβE objectives.
+ Single doc that lets any new contributor see the whole shape at once.
+- β» **`TECTONICS.md`** at `β¦/docs/terrain/` β Voronoi plate algorithm,
+ plate-type taxonomy (cratonic / passive / active / volcanic_arc /
+ rift / hotspot), boundary classification, elevation bias logic,
+ exported fields `mountain_proximity`, `coast_proximity`. (Spec for
+ `p1-50`; consumed by `p2-49` rain-shadow.)
+- β» **`HYDROLOGY.md`** at `β¦/docs/terrain/` β Hydraulic erosion
+ pre-pass (Mei et al. simplified solver, ~5 sub-iterations), D6 flow
+ direction, drainage accumulation, Strahler stream order,
+ Planchon-Darboux lake fill, border-outflow rule, coarse-grid
+ resolution strategy for >150Γ150 maps, exported field
+ `riparian_distance`. (Spec for `p1-47`; consumed by `p1-48`/`p1-49`
+ for riparian feedback + aquatic gate.)
+- β» **`CLIMATE.md`** at `β¦/docs/terrain/` β Latitude (signed
+ hemispheric β1..+1), continentality (hex-BFS graph distance not
+ Euclidean), zonal wind bands (trade / westerly / polar by latitude
+ band), rain-shadow logic, west-coast maritime asymmetry as cheap
+ ocean-current proxy, elevation lapse rate, derivations of
+ `mean_temp` / `mean_precip` / `seasonality` / `aridity_index`,
+ T_band / P_band 5-bucket discretisation. (Spec for `p2-49`; consumed
+ by `p1-48`/`p1-49` index keying.)
+- β» **`ECOLOGY_BINDING.md`** at `β¦/docs/` (or extend
+ `ecology-gameplay.md`) β `TerrainFloraIndex` keyed by
+ `(biome_id, T_band, P_band)`; contribution-normalisation rule
+ (rescale by sum, never clip); riparian-preference rule;
+ lineageβglyph map (conifer / temperate-broadleaf /
+ tropical-broadleaf / cactus / palm / aquatic / moss);
+ `TerrainFaunaIndex` with trophic-overlap rule (β€1 apex per tile,
+ predator requires `prey[]` present in self-or-adjacent), aquatic
+ gate via `riparian_distance`, domain-coherence rules
+ (air / land / marine / freshwater). (Spec for `p1-48` + `p1-49`.)
+- β» **`WORLDGEN_RNG.md`** at `β¦/docs/terrain/` β Pinned RNG
+ (`Pcg64` from `rand_pcg`, version pinned in workspace Cargo.toml),
+ `SeedDomain` enum, `seed::derive(map_seed, domain) -> u64` mixing
+ function, save-format pin (RNG version + seed both written to
+ saves), parity-test contract for WASMβGDExt. (Spec for `p2-50`.)
+- β» **`WORLDGEN_PRESETS.md`** at `β¦/docs/terrain/` β World-shape
+ preset catalogue with 5 axes (`landmass`, `climate`, `moisture`,
+ `age`, `sea_level`), JSON schema for
+ `public/games/age-of-dwarves/data/world_shapes/*.json`,
+ preview-thumbnail discipline (rendered from a proof scene, never
+ hand-painted). (Spec for `p2-51`.)
+- β» **Cross-references locked** β each implementation objective
+ (`p1-46`β¦`p2-51`) gets a `canonical_doc:` frontmatter entry
+ pointing at the matching doc above. Implementer agents read the
+ canonical doc before writing Rust.
+- β» **Designs index updated** β
+ `.project/designs/README.md` table extended with rows linking each
+ canonical doc to its companion lab page (current `ForestLab`,
+ future `TectonicsLab` / `ClimateLab` / `HydrologyLab`).
+- β» **CLAUDE.md instruction-router updated** β the "When the task
+ involvesβ¦" table grows entries pointing at the new canonical docs
+ for: tectonic plates / hydrology + erosion / climate axes / ecology
+ binding / RNG determinism / world-shape presets.
+
+## Why this matters (anti-pattern this prevents)
+
+Without doc-first discipline, two consumers diverge: the design lab
+gets a TS implementation, Godot gets a Rust implementation, the
+canonical contract lives in nobody's head. The current TS twins
+(`floraSpecies.ts`, `hydrology.ts`, `faunaSpecies.ts`) at
+`.project/designs/app/src/utils/worldGen/` are exactly that failure
+mode β they're scheduled for deletion by `p1-47` / `p1-48` / `p1-49`
+with the explicit acceptance bullet "Pre-existing TS twin is deleted,
+not ported".
+
+Every doc landed here becomes a 1-to-1 contract that constrains both
+the GDExt and WASM bridges to the same behaviour. Implementation
+agents working on Wave A-E can be given **only** the canonical doc +
+their objective acceptance and produce conforming Rust without
+further coordination.
+
+## Non-goals
+
+- Authoring the Rust code (`mc-mapgen`, `mc-climate`, `mc-ecology`) β
+ that's Wave A-E objectives.
+- Authoring the JSON parameter files (extracted from the docs at
+ Wave A-E implementation time).
+- Capturing canonical screenshots β that's Wave E (`p1-46`).
+- Rewriting `HEX_GEOMETRY.md` or other already-canonical docs β only
+ authoring the missing 7. If existing docs (e.g.
+ `terrain/TERRAIN_SYSTEM.md`, `CREATURE_ECOSYSTEM.md`) overlap, link
+ them rather than duplicate.
+
+## Dependencies
+
+None β this is Wave 0. All other terraformer objectives become
+unblocked by this objective's completion.
+
+## Why status: partial cannot occur here
+
+Either a doc exists with all sections filled in, or it does not. There
+is no "partial doc" β the doc is the contract. Every acceptance bullet
+above is a single deliverable: the file exists at the path, with the
+content sketched above, reviewed for internal consistency. K=N or
+status stays `missing`.
diff --git a/.project/team-leads/terraformer.md b/.project/team-leads/terraformer.md
index 0ed0c6af..b6322595 100644
--- a/.project/team-leads/terraformer.md
+++ b/.project/team-leads/terraformer.md
@@ -8,6 +8,7 @@ objectives:
- p1-48
- p1-49
- p1-50
+ - p1-51
- p2-49
- p2-50
- p2-51
@@ -96,22 +97,31 @@ Files / crates this lead may modify:
## Sequencing
-The naive "single parallel sweep" ordering is wrong. The classifier
-axes change in p2-49, which invalidates flora/fauna `biomes[]` indexes
-built ahead of it. Correct order:
+The naive "single parallel sweep" ordering is wrong on two counts: the
+classifier axes change in p2-49 (which invalidates flora/fauna
+`biomes[]` indexes built ahead of it), AND no Rust code should be
+written before the canonical design docs exist (Rail 1 + the project's
+three-tier doc system). Correct order:
-1. **Wave A (parallel)** β p1-50 (tectonics) + p2-49 (climate axes) +
+1. **Wave 0 (parallel) β DOCS FIRST** β p1-51 authors the 7 canonical
+ design docs at `public/games/age-of-dwarves/docs/terrain/` (plus
+ `ECOLOGY_BINDING.md` at `β¦/docs/`). Every Rust crate in Waves AβE
+ mechanically implements one of these docs. NO IMPLEMENTATION
+ WORK STARTS UNTIL WAVE 0 IS DONE.
+2. **Wave A (parallel)** β p1-50 (tectonics) + p2-49 (climate axes) +
p2-50 (RNG pin). These rewrite the foundation; everything else
depends on stable biome IDs and reproducible seeds.
-2. **Wave B (parallel)** β p1-47 (hydrology + erosion). Independent of
+3. **Wave B (parallel)** β p1-47 (hydrology + erosion). Independent of
climate; only needs the post-tectonic elevation field.
-3. **Wave C (parallel)** β p1-48 (flora) + p1-49 (fauna). Both consume
+4. **Wave C (parallel)** β p1-48 (flora) + p1-49 (fauna). Both consume
stable biome IDs (Wave A) and water topology (Wave B).
-4. **Wave D** β p2-51 (player-facing world-shape knobs). Once the
+5. **Wave D** β p2-51 (player-facing world-shape knobs). Once the
pipeline parameters are stable, expose them on the new-game screen.
-5. **Wave E** β p1-46 (lab as integration / proof). Whittaker plot,
+6. **Wave E** β p1-46 (lab as integration / proof). Whittaker plot,
ridginess fix, screenshot fixtures, all against the post-refactor
- axes.
+ axes β and the lab finally becomes a thin WASM consumer of the
+ Rust core (TS twins deleted in their respective implementation
+ waves).
## Escalation
@@ -141,6 +151,8 @@ built ahead of it. Correct order:
Terraformer does NOT run a cron loop initially. The owned objectives
get a wave-sequenced `/experts-team` parallel sweep per the ordering
-above. Once Wave E (p1-46) lands with proof screenshots in
+above β starting with Wave 0 (p1-51 canonical design docs), then
+proceeding through Waves AβE only after their gating canonical doc
+exists. Once Wave E (p1-46) lands with proof screenshots in
`.project/screenshots/`, the bundle is closed. Subsequent work invoked
on demand.
diff --git a/public/games/age-of-dwarves/data/objectives.json b/public/games/age-of-dwarves/data/objectives.json
index af2d88e2..e7a137cb 100644
--- a/public/games/age-of-dwarves/data/objectives.json
+++ b/public/games/age-of-dwarves/data/objectives.json
@@ -1,13 +1,13 @@
{
- "generated_at": "2026-04-30T22:05:58Z",
+ "generated_at": "2026-04-30T22:33:30Z",
"totals": {
- "in_progress": 1,
- "partial": 11,
- "stub": 1,
"done": 112,
- "missing": 25,
+ "in_progress": 1,
+ "stub": 1,
"oos": 22,
- "total": 172
+ "missing": 29,
+ "partial": 11,
+ "total": 176
},
"objectives": [
{
@@ -918,27 +918,27 @@
"scope": "game1",
"owner": "terraformer",
"updated_at": "2026-04-30",
- "summary": "The Terrain Dimensions Lab at `/world-gen/forest-lab` (lab tab) currently\nclassifies the 17 base biomes and renders cross-tile flora/minerals/fauna\noverlays driven by 4 biome sliders + 3 toggleable overlay layers. Three\nknown gaps:\n\n1. **Ridginess slider has zero effect at default elevation 0.65.** The\n classifier (`terrain.ts:118`) only consults ridginess when\n `elevation > 0.85 β§ ridginess > 0.92` for the volcano case. Anywhere\n else, the slider does nothing β the user verified this on the live\n lab and flagged it.\n2. **The lab loads 0 of 149 flora species.** Trees / shrubs / ground\n cover are generic per terrain. The 149 species in\n `public/resources/ecology/flora/species/*.json` carry rich schema\n (`biomes[]`, `lineage`, `tags` with layer info, `quality_tier`,\n `canopy_contribution`) that the lab ignores entirely.\n3. **No Whittaker TΓP plot inset** β the user can move sliders but has\n no visual map of where they are in biome space."
+ "summary": "The Terrain Dimensions Lab at `/world-gen/forest-lab` (lab tab) currently\nclassifies the 17 base biomes and renders cross-tile flora/minerals/fauna\noverlays driven by 4 biome sliders + 3 toggleable overlay layers. Three\nknown gaps:\n\n1. **Ridginess slider has zero effect at default elevation 0.65.** The\n classifier (`terrain.ts:118`) only consults ridginess when\n `elevation > 0.85 β§ ridginess > 0.92` for the volcano case. Anywhere\n else, the slider does nothing β the user verified this on the live\n lab and flagged it.\n2. **The lab loads 0 of 149 flora species.** Trees / shrubs / ground\n cover are generic per terrain. The 149 species in\n `public/resources/ecology/flora/species/*.json` carry rich schema\n (`biomes[]`, `lineage`, `tags` with layer info, `quality_tier`,\n `canopy_contribution`) that the lab ignores entirely.\n3. **No Whittaker TΓP plot inset** β the user can move sliders but has\n no visual map of where they are in biome space.\n\nThis objective is the **integration / proof surface** of the Wave-E\nterraformer bundle. It must land **after** p1-50, p2-49, p2-50 (Wave A),\np1-47 (Wave B), and p1-48/p1-49 (Wave C). The Whittaker plot, ridginess\nbehaviour, and flora binding all consume the post-refactor axes."
},
{
"id": "p1-47",
- "title": "River hydrology β D6 flow analysis, multi-hex lakes, cross-tile rivers",
+ "title": "River hydrology β D6 flow analysis, hydraulic erosion, multi-hex lakes, cross-tile rivers",
"priority": "p1",
"status": "missing",
"scope": "game1",
"owner": "terraformer",
"updated_at": "2026-04-30",
- "summary": "Water bodies in the current map are per-hex terrain types\n(`ocean`, `coast`, `lake`, `inland_sea`) with no connectivity. Real\ngeographic water is **topological**: rivers are DAGs from headwaters to\nocean, lakes are multi-hex polygons, coastlines are polylines along\nland/water borders.\n\nThe data already encodes this in:\n\n- `public/games/age-of-dwarves/data/terrain/terrain_blends.json` β\n `riverside_forest`, `shore`, `cliff`, `bog_edge` ecotones (cross-tile)\n- 10+ fauna species with `river` / `lake` / `wetland` biomes\n (bald_eagle, alligator, otter, kingfisher, beaver, etc.)\n- 4 riparian flora species: `lotus`, `papyrus`, `giant_water_lily`,\n `pioneer_sedge`\n\nBut there is **no rendering pass** that uses the topology. This\nobjective adds D6 flow-direction analysis on the elevation grid,\ndrainage-area accumulation, lake-basin filling, and a cross-tile\nrenderer that draws rivers as bezier paths through hex edges and lakes\nas multi-hex continuous fills."
+ "summary": "Water bodies in the current map are per-hex terrain types\n(`ocean`, `coast`, `lake`, `inland_sea`) with no connectivity. Real\ngeographic water is **topological**: rivers are DAGs from headwaters to\nocean, lakes are multi-hex polygons, coastlines are polylines along\nland/water borders.\n\nThe data already encodes this in:\n\n- `public/games/age-of-dwarves/data/terrain/terrain_blends.json` β\n `riverside_forest`, `shore`, `cliff`, `bog_edge` ecotones (cross-tile)\n- 10+ fauna species with `river` / `lake` / `wetland` biomes\n (bald_eagle, alligator, otter, kingfisher, beaver, etc.)\n- 4 riparian flora species: `lotus`, `papyrus`, `giant_water_lily`,\n `pioneer_sedge`\n\nBut there is **no rendering pass** that uses the topology. This\nobjective adds:\n\n1. A single hydraulic-erosion iteration that carves valleys before\n drainage analysis (so rivers sit in valleys, not on ridges).\n2. D6 flow-direction analysis on the eroded elevation grid.\n3. Drainage-area accumulation, lake-basin filling.\n4. A cross-tile renderer that draws rivers as bezier paths through\n hex edges and lakes as multi-hex continuous fills.\n\nHydrology computes topology only. Riparian feedback into flora\nselection lives in p1-48; aquatic-domain fauna gating lives in p1-49."
},
{
"id": "p1-48",
- "title": "Flora species renderer β bind 149 species to world-map tile rendering",
+ "title": "Flora species renderer β bind 149 species to world-map tile rendering (single source of truth)",
"priority": "p1",
"status": "missing",
"scope": "game1",
"owner": "terraformer",
"updated_at": "2026-04-30",
- "summary": "`public/resources/ecology/flora/species/*.json` defines **149 species**\nwith rich schema:\n\n- `biomes[]` β which terrain types support the species\n- `tags[]` including layer (`layer_canopy` / `layer_understory` /\n `layer_ground` / `layer_fungal`) and structure (`structure_woody` /\n `conifer` / `broadleaf` / `evergreen`)\n- `lineage` β taxonomic group (`conifers`, `broadleaf_trees`,\n `tropical_broadleaf`, `cacti`, `palms`, `aquatic_plants`,\n `mosses_lichens`, etc.)\n- `quality_tier` (0β10), `canopy_contribution`,\n `undergrowth_contribution`, `fungi_contribution`\n- `drought_tolerance`, `fire_resistance`, `growth_rate`\n\nBut the world-map renderer (`mc-render` or Godot tilemap layer) draws\ngeneric green circles for forest tiles. This objective wires the 149\nspecies data into the canvas / TileMap rendering so a \"forest\" tile in\ntemperate Europe shows oak / beech / birch and a \"forest\" tile in\ntropical climate shows mahogany / teak / strangler_fig β each instance\npicked from the species pool that lists this terrain in its `biomes[]`."
+ "summary": "`public/resources/ecology/flora/species/*.json` defines **149 species**\nwith rich schema:\n\n- `biomes[]` β which terrain types support the species\n- `tags[]` including layer (`layer_canopy` / `layer_understory` /\n `layer_ground` / `layer_fungal`) and structure (`structure_woody` /\n `conifer` / `broadleaf` / `evergreen`)\n- `lineage` β taxonomic group (`conifers`, `broadleaf_trees`,\n `tropical_broadleaf`, `cacti`, `palms`, `aquatic_plants`,\n `mosses_lichens`, etc.)\n- `quality_tier` (0β10), `canopy_contribution`,\n `undergrowth_contribution`, `fungi_contribution`\n- `drought_tolerance`, `fire_resistance`, `growth_rate`\n\nBut the world-map renderer (`mc-render` or Godot tilemap layer) draws\ngeneric green circles for forest tiles. This objective wires the 149\nspecies data into the canvas / TileMap rendering so a \"forest\" tile in\ntemperate climate shows oak / beech / birch and a \"forest\" tile in\ntropical climate shows mahogany / teak / strangler_fig β each instance\npicked from the species pool that lists this terrain in its `biomes[]`.\n\n**Single source of truth (Rail 1):** the selector is implemented ONCE\nin `mc-ecology` (Rust), exposed to Godot via GDExtension and to the\ndesign-lab via WASM. NO TypeScript twin."
},
{
"id": "p1-49",
@@ -950,6 +950,26 @@
"updated_at": "2026-04-30",
"summary": "`public/games/age-of-dwarves/data/manifests/fauna.json` whitelists **61\nGame-1 species** with rich JSON schema in\n`public/resources/ecology/fauna/species/*.json`:\n\n- `domain` (land / air / marine / freshwater)\n- `trophic_level` (apex_predator / predator / herbivore / omnivore)\n- `biomes[]`\n- `prey[]` β actual food-web edges\n- `ecology_tier` (1β10 rarity / strength)\n- `forms_lairs` + `lair_type`\n- `lineage` (canines, ursids, cervids, raptors, etc.)\n- `traits[]` including `size_*`\n\nBut the world-map renderer draws abstract icons for lairs and no\noverlay at all for ambient fauna. This objective wires the species\ndata into the renderer so a wolf lair shows a wolf silhouette, a bear\ncave shows a bear, a harpy nest shows a winged figure."
},
+ {
+ "id": "p1-50",
+ "title": "Tectonic prepass β voronoi plates + boundary classification seeding elevation",
+ "priority": "p1",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "terraformer",
+ "updated_at": "2026-04-30",
+ "summary": "The current `mc-mapgen` elevation field is pure fBm noise. Continents\nare amorphous, mountain ranges are noise-shaped blobs, and there is no\ngeological reason for a peak to be where it is. Real continents have\n**plate boundaries**: mountains arc along convergent edges, rifts and\nmid-ocean ridges along divergent edges, transform faults run linear.\n\nThis objective adds a lo-fi tectonic prepass that runs in <500 ms on a\n200Γ200 map and biases the existing fBm field. Full multi-step plate\nsimulation (`g2-05-tectonics-lithology`) stays deferred to Game 2; this\nis the cheap version that captures 80% of the visual win.\n\nThe prepass also produces `mountain_proximity` and `coast_proximity`\nfields that p2-49 (rain shadow) and p1-47 (drainage divides) consume\nas first-class inputs."
+ },
+ {
+ "id": "p1-51",
+ "title": "Worldgen canonical design docs β author the spec before any Rust",
+ "priority": "p1",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "terraformer",
+ "updated_at": "2026-04-30",
+ "summary": "The Terraformer bundle (`p1-46`β¦`p2-51`) is an 8-objective procedural\nterrain pipeline spanning tectonics, hydrology, climate, ecology, RNG\ndeterminism, world-shape presets, and the design-app Terrain Dimensions\nLab. Per **Rail 1** (`Rust is the simulation source of truth`,\nCLAUDE.md:13) and the project's three-tier doc system (canonical β\nengineering β JSON, per `.project/designs/README.md`), every Rust crate\nmust mechanically implement an authored canonical specification β never\nthe reverse.\n\nThis Wave-0 objective authors the **7 canonical design docs** at\n`public/games/age-of-dwarves/docs/terrain/` (and `β¦/docs/` for ecology\nbinding) that gate all subsequent Wave AβE implementation. Each Rust\ncrate's rustdoc references the canonical doc it implements; bridges\n(`api-gdext`, `api-wasm`) and consumers (Godot, design lab) consume\nwhat the canonical specs declare β not what someone interpolated.\n\nThe current TypeScript twins (`floraSpecies.ts`, `hydrology.ts`,\n`faunaSpecies.ts`) under `.project/designs/app/src/utils/worldGen/`\nexist precisely because this stage was skipped. Authoring the canonical\ndocs first prevents that recurrence."
+ },
{
"id": "p2-06",
"title": "Export pipeline for Windows / macOS / Linux",
@@ -1492,13 +1512,33 @@
},
{
"id": "p2-49",
- "title": "Climate axes refactor β latitude + continentality as first-class per-hex inputs",
+ "title": "Climate axes refactor β latitude + continentality + zonal winds as first-class per-hex inputs",
"priority": "p2",
"status": "missing",
"scope": "game1",
"owner": "terraformer",
"updated_at": "2026-04-30",
- "summary": "`mc-mapgen::sampleCell` (and the design-lab twin in `terrain.ts:213`)\nderives `cold` from\n`abs(row/rows - 0.5) * 2 * (1 - climate)` β an implicit latitude proxy\nthat doesn't expose:\n\n- **Continentality** β distance from ocean, drives seasonal swing and\n base aridity (Siberia vs Ireland)\n- **Seasonality** β latitude amplitude (tropic vs arctic)\n- **Rain shadow** β windward / leeward of mountains (wet Pacific NW\n vs dry Great Basin)\n- **Elevation lapse rate** β ~6.5Β°C/km cooling\n\nThe classifier consumes the collapsed `cold` value, producing biomes\nthat don't differentiate maritime temperate from continental temperate,\nor windward rainforest from leeward rain shadow.\n\nThis objective decomposes the climate input into independent per-hex\nfields β `latitude`, `continentality`, `prevailing_wind_dir` β then\nderives mean T, mean P, seasonality, evapotranspiration deficit. The\nbiome classifier consumes the derived values, exposing\nmaritime / continental and rain-shadow distinctions at last."
+ "summary": "`mc-mapgen::sampleCell` (and the design-lab twin in `terrain.ts:213`)\nderives `cold` from\n`abs(row/rows - 0.5) * 2 * (1 - climate)` β an implicit latitude proxy\nthat doesn't expose:\n\n- **Continentality** β graph distance from ocean, drives seasonal\n swing and base aridity (Siberia vs Ireland)\n- **Seasonality** β latitude amplitude (tropic vs arctic)\n- **Rain shadow** β windward / leeward of mountains (wet Pacific NW\n vs dry Great Basin)\n- **Elevation lapse rate** β ~6.5Β°C/km cooling\n- **West-coast asymmetry** β without ocean current simulation, mid-\n latitude west coasts must still feel maritime; east coasts must\n still feel continental.\n\nThe classifier consumes the collapsed `cold` value, producing biomes\nthat don't differentiate maritime temperate from continental temperate,\nor windward rainforest from leeward rain shadow.\n\nThis objective decomposes the climate input into independent per-hex\nfields β `latitude`, `continentality`, `wind_band` β then derives mean\nT, mean P, seasonality, evapotranspiration deficit. The biome\nclassifier consumes the derived values, exposing maritime / continental\nand rain-shadow distinctions at last."
+ },
+ {
+ "id": "p2-50",
+ "title": "Deterministic RNG + seed-derivation pin across mc-mapgen / mc-climate / mc-ecology",
+ "priority": "p2",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "terraformer",
+ "updated_at": "2026-04-30",
+ "summary": "Every terraformer-owned objective claims \"deterministic from seed\", but\nno objective specifies (a) which RNG, (b) the seed-mixing function,\n(c) the version pin that survives `cargo update`. Today, the worldgen\npipeline calls `rand::thread_rng()` and `StdRng::seed_from_u64(seed)`\ninconsistently across crates. `StdRng` is explicitly documented as\n**not stable across rand versions** β saves break silently on dep\nbumps.\n\nThis objective pins a versioned RNG and a single seed-derivation\nfunction for all worldgen β tectonics, hydrology, climate, species\nselection. Lands as a small infrastructure objective so Wave A of the\nterraformer schedule (p1-50, p2-49) is built on a stable foundation,\nnot retrofitted later."
+ },
+ {
+ "id": "p2-51",
+ "title": "Player-facing world-shape parameters on new-game screen",
+ "priority": "p2",
+ "status": "missing",
+ "scope": "game1",
+ "owner": "terraformer",
+ "updated_at": "2026-04-30",
+ "summary": "The terraformer pipeline now exposes ~15 internal parameters\n(plate count, tectonic strength, fbm octaves, sea level, latitude\ngradient, continentality decay, rain-shadow factor, erosion\niterations, drainage threshold, etc.). Designers tune these in the\nforest lab; **players see none of them**. The new-game screen ships\n\"Map Size\" and not much else.\n\nIndustry baseline (Civ 6, Old World, Songs of Conquest) exposes 4β6\nhigh-level shape knobs. Each knob is a *preset* that derives several\ninternal parameters at once. This objective wires that surface from\nJSON presets through `mc-mapgen` parameters into the Godot game-setup\nscene."
},
{
"id": "g2-01",
|