Planet-down cascade workshop (3 rounds, 5 agents): layer-by-layer generation from empty world through population overlay, city planning, and street rendering. Includes consultant review by Troblum. Also commits: pre-Sprint-35 DB backup, Claude Code team-mode tmux test log (team-test.md). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
460 lines
39 KiB
Markdown
460 lines
39 KiB
Markdown
---
|
||
title: "Workshop Brief — Planet-Down Cascade"
|
||
description: "Design the multi-layered planet-down generation algorithms where each layer's output feeds the next: empty world → population overlay → city planning → street rendering"
|
||
type: workshop
|
||
status: active
|
||
workshop: planet-down-cascade
|
||
agent: ""
|
||
round: 0
|
||
created: 2026-04-30
|
||
---
|
||
|
||
# Planet-Down Cascade Workshop Brief
|
||
|
||
**Goal:** Design the full generation cascade from planetary heightmap to walkable street-level tiles. Each layer receives the previous layer's output as input. The workshop must produce algorithm specifications, data format handoffs, and implementation decisions for each layer — resolving open questions that domain specialists have surfaced but cannot close alone.
|
||
|
||
**Ticket:** (to be assigned at sprint planning)
|
||
**Priority:** HIGH — this is the architectural design that drives Phase 3 and Phase 5 implementation
|
||
**Participants:** Gestalt (systems design + fun factor), Tyre (architecture + feasibility), Paula (narrative + political depth), Burnelli-Sheldon (economics + simulation modeling)
|
||
**Source:** Lead directive, generation-cascade workshop Rounds 1-4 (2026-04-24 to 2026-04-30)
|
||
|
||
---
|
||
|
||
## The Lead's Cascade Vision
|
||
|
||
*This is the authoritative design. Reproduce it verbatim here so all participants work from the same framing.*
|
||
|
||
**1. EMPTY WORLD** — Start from heightmap. Water simulation: river drainage from heightmap datapoints + seed to calculate river presence. Scatter lakes with biome/river dependency. Refine sub-biomes into actionable regional areas (jungle with clearings, hills, swamp). The existing heightmap rivers are overrides/labels — real rivers come from seeded drainage patterns. Keep names, re-place based on water sim. ALL DETERMINISTIC down to player scale. Result: complete natural world, no civilization.
|
||
|
||
**2. POPULATION OVERLAY** — Layer economics onto natural world. Where rivers meet, where rivers hit the sea, where arable land is — settlements anchor. Felled forests become farmland. Flattened hilly terrain for cities (terraced, no longer craggy). Anchored by wiki population counts and markers.json. Wider area: entire city with surrounding lands, village with farms rolling into farmland and forest. Road/rail networks inter and intra regional. Sub-settlements INCLUDED. MaintenanceAuthority on roads INCLUDED.
|
||
|
||
**3. CITY-LEVEL PLANNING** — "Cities Skylines" with topography. Plot farmland, wilderness, suburbs, industrial areas, commercial districts, ports, airports, train stations, highways, cultural districts. All if appropriate within size and cultural parameters. ENTIRE PHASE on its own.
|
||
|
||
**4. STREET-LEVEL RENDERING** — Take city-level data, render streets, buildings, scatter civilization.
|
||
|
||
**CRITICAL DETERMINISM RULE:** Economic sim's rolling state affects RENDERING (prosperity, repair, wealth state) NOT LAYOUT (streets, buildings locked by seed). User cannot leave and return to find different street plan because GDP changed. The same streets and buildings, just richer or poorer.
|
||
|
||
**CRITICAL LoD/ZOOM RULE:** First pass = automatic parameter generation. Then cascaded world render: user spawns in village X, code "zooms in" on planet, traces all cascade steps to where user sits, and with those patterns in hand (including cascades for chunks/districts around them) fills in actual street levels. Smart: react to economics flags, parse wealth/repairedness on rolling economics. DO NOT influence determinism.
|
||
|
||
**BUILDING LoD RULE:** Door-per-edge as boundary. Descriptor + catalog of what's behind the door. No spatial rendering of building interiors until player crosses threshold.
|
||
|
||
---
|
||
|
||
## Context
|
||
|
||
The generation-cascade workshop (Rounds 1-4, 2026-04-30) produced the foundational architecture and resolved the key data-model decisions. That workshop examined the pipeline from two directions: bottom-up (what exists in the codebase today) and top-down (what the lead's vision requires). The conclusion: the existing architecture (DistrictSkeleton, SeedChain, chunk streaming) maps cleanly to Layers 3 and 4 of this cascade, but Layers 1 and 2 were not designed in detail.
|
||
|
||
This workshop begins where the last one ended. The given facts below are locked. This workshop designs the open questions.
|
||
|
||
---
|
||
|
||
## Given Facts — Carry Forward Without Revisiting
|
||
|
||
### Pipeline Architecture
|
||
|
||
| Decision | Detail |
|
||
|----------|--------|
|
||
| Phase 3 = Python tooling + systems.db | All Layer 1 and Layer 2 generation runs offline in `generate_regional.py` and commits to systems.db. |
|
||
| Phase 5 = Rust runtime + seed-derived | Layer 3 and Layer 4 generation runs in the simulation server at runtime, seed-derived, never stored. |
|
||
| These two phases are parallelizable | Phase 5 can stub `CityGenerationContext` and wire to systems.db when Phase 3 is done. |
|
||
| `generate_regional.py` is the third pipeline generator | Runs after `generate_atlas.py` in `make regen-db`. Produces Phase 3 systems.db tables. Has its own meta stamp. |
|
||
| Atlas markers are the last stored layer | Everything from Layer 3 down is seed-derived at runtime. Save files store: `world_seed` + markers snapshot hash + `ChunkMutations`. |
|
||
| No markers.json extension | City attributes are read from systems.db bodies table by cross-reference on `body_id`. markers.json stays as a pure spatial + naming store. |
|
||
| Atlas coordinates are UI-only | The 512×256 pixel grid is a display coordinate system. Walkable world uses city-local sim-tile coordinates from (0,0). No pixel-to-sim-tile formula. |
|
||
|
||
### Seed Chain
|
||
|
||
All randomness below the atlas layer uses FNV-1a `child_seed`:
|
||
|
||
```rust
|
||
fn child_seed(parent: u64, discriminant: u64) -> u64 {
|
||
let mut h = parent ^ 0xcbf29ce484222325;
|
||
for byte in discriminant.to_le_bytes() {
|
||
h ^= byte as u64;
|
||
h = h.wrapping_mul(0x100000001b3);
|
||
}
|
||
h
|
||
}
|
||
|
||
district_seed = child_seed(world_seed, district_id)
|
||
chunk_seed = child_seed(district_seed, (cx as u64) << 32 | (cy as u64))
|
||
```
|
||
|
||
Any district or chunk can be generated in isolation without generating neighbors first. Complies with D-010.
|
||
|
||
### Phase 3 → Phase 5 Handoff
|
||
|
||
```rust
|
||
pub struct CityGenerationContext {
|
||
pub city_id: String,
|
||
pub political_archetype: PoliticalArchetype,
|
||
pub prosperity_baseline: f32, // seed-locked Layer 3 output
|
||
pub surrounding_biome: BiomeClass,
|
||
pub road_entry_directions: Vec<CardinalDirection>,
|
||
pub footprint_radius_km: f32,
|
||
pub founding_orientation: FoundingOrientation,
|
||
pub world_tier: WorldTier,
|
||
}
|
||
```
|
||
|
||
This struct is populated from systems.db at game startup. Phase 5 reads it as input to district skeleton generation. `prosperity_baseline` is the seed-locked economic baseline. The runtime economic simulation produces a separate `prosperity_current` field — these are two distinct data fields and must not be conflated in implementation.
|
||
|
||
### City Decomposition (D-C4)
|
||
|
||
- District count: `max(1, floor(city.population / 50_000))`, log-scaled for large cities
|
||
- WorldTier district ceilings: Epicenter uncapped | Regional 8 | Backwater 4 | Passage 2 | Waypoint 1
|
||
- Capital cities: +1 bonus district
|
||
- Domed/cave settlement: fixed 1 district
|
||
- Districts positioned in city-local sim tiles: district at grid (col, row) → origin `(col × 512, row × 512)`
|
||
|
||
### District Skeleton (Phase 1 Minimum Scope)
|
||
|
||
- **Stage 1 (mandatory):** Classification — WorldTier, ComplexityTier, SettingType, DistrictType, LayoutMode, `prosperity_baseline`, `perimeter_treatment`
|
||
- **Stage 2 (mandatory):** Block grid — 4×4 BlockSkeletons with ZoningType, `density_pct`, seed
|
||
- **Stages 3-5 deferred:** Reservations, social sites, guarantee audit — enrichment for post-walkable world
|
||
|
||
### WorldTier Enum (corrected)
|
||
|
||
```rust
|
||
enum WorldTier { Epicenter, Regional, Backwater, Passage, Waypoint }
|
||
// NOT: Peripheral, Connected, Core — those are wrong and must be replaced
|
||
```
|
||
|
||
### GeneratorChunkData Upgrade
|
||
|
||
```rust
|
||
pub struct GeneratorChunkData { pub tiles: Vec<TileEntry> } // 64×64 = 4,096 entries
|
||
pub struct TileEntry { pub tile_id: TileId, pub walkable: bool }
|
||
// Replaces: pub type GeneratorChunkData = Vec<bool>
|
||
```
|
||
|
||
Minimum tile vocabulary: `floor_street`, `floor_interior`, `wall`.
|
||
|
||
### Economics: 6-Field Minimum Read Set and 10×9 Weight Table
|
||
|
||
Six fields queryable from systems.db per city, sufficient to produce a spatially differentiated world:
|
||
|
||
| # | Field | Source table |
|
||
|---|-------|-------------|
|
||
| 1 | `economic_role` | `bodies` |
|
||
| 2 | `settlement_pattern` | `bodies` |
|
||
| 3 | `economic_tier` | `system_economy` |
|
||
| 4 | `distribution_index` | `system_economy` |
|
||
| 5 | `corp_presence` count | `corp_presence` |
|
||
| 6 | `headquarters_system/body` match | `corporations` |
|
||
|
||
10×9 DistrictType weight table (economic_role → DistrictType prior):
|
||
|
||
| economic_role | Res | Com | Ind | Adm | Log | Ent | Mix | Trn | Spe |
|
||
|--------------|-----|-----|-----|-----|-----|-----|-----|-----|-----|
|
||
| manufacturing | 25 | 5 | 35 | 5 | 20 | 2 | 5 | 3 | 0 |
|
||
| agricultural | 30 | 15 | 5 | 10 | 25 | 3 | 10 | 2 | 0 |
|
||
| extraction | 20 | 5 | 30 | 5 | 30 | 0 | 5 | 5 | 0 |
|
||
| transit | 10 | 20 | 5 | 5 | 25 | 10 | 15 | 10 | 0 |
|
||
| research | 20 | 5 | 5 | 15 | 5 | 5 | 10 | 0 | 35 |
|
||
| commercial | 15 | 35 | 5 | 5 | 10 | 15 | 15 | 0 | 0 |
|
||
| service_mixed | 25 | 20 | 5 | 10 | 5 | 15 | 20 | 0 | 0 |
|
||
| mining | 20 | 5 | 25 | 0 | 35 | 0 | 10 | 5 | 0 |
|
||
| frontier | 35 | 10 | 10 | 5 | 20 | 5 | 15 | 0 | 0 |
|
||
| energy | 10 | 0 | 20 | 5 | 30 | 0 | 5 | 5 | 25 |
|
||
|
||
`political_archetype` modifiers stack on top of these weights.
|
||
|
||
### Sub-Settlement Hierarchy (lead inclusion)
|
||
|
||
| Tier | Population | Treatment | Placement trigger |
|
||
|------|-----------|-----------|------------------|
|
||
| City | 10,000+ | Full district decomposition (Layer 3) | markers.json anchor + geography |
|
||
| Town | 1,000–10,000 | Single-district equivalent | Road network + economic role |
|
||
| Outpost | 50–1,000 | Purpose-typed single node | Corp presence + extraction trigger |
|
||
| Waypoint | <50 | Infrastructure node, no district | Road midpoint formula |
|
||
| Rural cluster | Isolated | Type-tagged scattered nodes | settlement_pattern + economic role |
|
||
| Ruin | 0 | Historical footprint | Abandoned tether condition |
|
||
|
||
**Placement triggers:**
|
||
- Mining camps: `economic_role ∈ {extraction, mining}` + corp_presence with qualifying commodity → 1 camp per 2 corps
|
||
- Trade waypoints: long roads (pixel_count > threshold) → waypoint at geometric midpoint
|
||
- Agricultural nodes: `settlement_pattern ∈ {dispersed, dispersed_rural}` + `economic_role = agricultural` → clusters at interval ∝ `1/economic_tier`
|
||
- Shadow nodes: `collection_efficiency < 0.6` + shadow-viable corps → 1 informal settlement at city fringe
|
||
|
||
### Dynamic Settlements Are Latent
|
||
|
||
All economically plausible settlement positions are placed at Layer 2 generation time (seed-derived). Whether each is ACTIVE is determined by the economic simulation's rolling state. An inactive settlement exists spatially as ruins/empty structures — it is dark, unmaintained, and depopulated. Ghost towns are generated, not scripted.
|
||
|
||
### Determinism Boundary (per layer)
|
||
|
||
| Layer | Seed-locked (never changes) | Economics-variable (runtime updates) |
|
||
|-------|-----------------------------|--------------------------------------|
|
||
| 1 (Empty World) | Heightmap, river network, biome grid, pass locations | Nothing — terrain is fixed |
|
||
| 2 (Population) | Settlement positions, road topology, sub-settlement placement, PoliticalTether | Road condition (edge case: abandoned roads degrade further) |
|
||
| 3 (City Planning) | District positions, district types, block grid, `prosperity_baseline`, `perimeter_treatment` | `prosperity_current` (rolling sim update); building availability |
|
||
| 4 (Street-Level) | Street positions, building footprints, door positions, all tile layout | Tile condition states (Intact/Worn/Cracked/Broken); building activity signals |
|
||
|
||
**The felled-forests exception:** "Felled forests → farmland" is Layer 2 regional land-use (coarse biome-cell resolution), not Layer 4 tile mutation. Regional land-use CAN update at biome-cell resolution as economics shifts. City-internal street/building geometry is permanently seed-locked.
|
||
|
||
### LoD Zoom-In Cascade
|
||
|
||
When a player spawns, the code traces the full cascade from planet level down to the player's tile position. The cascade is lazy — only what the player needs is computed:
|
||
|
||
```
|
||
Planet in systems.db (Layers 1+2 data committed)
|
||
│
|
||
├── Player within planet rendering range?
|
||
│ → Regional biome overlay activates for atlas UI
|
||
│
|
||
├── Player within city footprint range?
|
||
│ → Generate city district grid (Layer 3 / Phase 1)
|
||
│ → Build DistrictMap in memory
|
||
│
|
||
├── Player within district range (chunk load radius)?
|
||
│ → load_chunk() fires per chunk in radius
|
||
│ → Layer 4 / Phase 2 generates tile data from BlockSkeleton + chunk_seed
|
||
│
|
||
└── Player at building door threshold?
|
||
→ Interior descriptor + catalog loaded (door-per-edge)
|
||
→ Interior tiles generated (Phase 4+)
|
||
```
|
||
|
||
The `DistrictMap` resource is built when the player enters a city's footprint radius. Cities outside player range have no `DistrictMap` — only their `CityGenerationContext` in memory from startup DB read.
|
||
|
||
---
|
||
|
||
## Workshop Sections — What Needs Design
|
||
|
||
### Section 1: Layer 1 — Empty World
|
||
|
||
**Inputs:** 512×256 heightmap (from `generate_atlas.py`), 64×32 regional biome grid (from `generate_regional.py` Phase 3 Layer B), body definitions from systems.db.
|
||
|
||
**Required outputs:**
|
||
- Sub-biome variant tag per regional cell (`sub_biome_variant` column on `atlas_regional_biomes`): 3-4 variants per biome class (e.g., `tropical` → `jungle_clearing | swamp | riverine_forest | lowland_marsh`)
|
||
- `terrain_modification_cost: f32` per regional cell — effort required to settle here (high = forest/hills; low = plains/coast)
|
||
- River network at regional resolution: where major rivers run, confluence points, river mouths
|
||
- River confluence identification as stored point features (settlement attractors for Layer 2)
|
||
- Mountain pass identification: regional cells with high `terrain_roughness` adjacent to significantly lower cells
|
||
- Geographic feature tag layer for settlement placement guidance (Paula): `RiverConfluence`, `CoastalHarbor`, `MountainPass`, `ArablePlain`, `ResourceConcentration`, `Defensible`, `NaturalBarrier`
|
||
|
||
**Questions to design:**
|
||
|
||
**L1-Q1 (Architecture):** The existing heightmap rivers (markers.json `rivers` entries) are authored overrides and labels. The lead says real rivers come from seeded drainage patterns. What is the relationship between authored river markers and the generated drainage network? Options: (a) authored rivers are replaced entirely — generated network takes over and re-places names; (b) authored rivers are anchors — the generated network is seeded to match them where they exist and fills gaps elsewhere; (c) two separate systems at different resolutions — authored for atlas display, generated for walkable world. **This must be resolved before Layer 1 can be implemented.**
|
||
|
||
**L1-Q2 (Resolution):** At what resolution does the river network get stored? The 64×32 regional grid gives coarse river paths sufficient for Layer 2 settlement placement. If rivers are walkable obstacles at street level (Phase 5), their exact courses need to be at tile precision. Concrete choice: (a) regional-resolution only (64×32) — rivers are navigation constraints, not tile-level obstacles; (b) stored at intermediate resolution (e.g., 512×256 pixel path like atlas roads) — sufficient for both; (c) tile-resolution derived by SeedChain at Phase 5 time — keep stored data coarse. **Answer determines whether the river data belongs in systems.db or is seed-derived at Phase 5.**
|
||
|
||
**L1-Q3 (Stored vs. derived):** The generated river network is fully deterministic from the heightmap + seed. Does it get committed to systems.db (like the 64×32 biome grid), or is it derived at runtime (like districts)? Core tension: the river confluence points that anchor Layer 2 settlement placement must be queryable before any game session starts. This implies at minimum the confluence points are stored, even if the full river course is seed-derived.
|
||
|
||
**L1-Q4 (Biome prior):** Burnelli-Sheldon proposes using `bodies.economic_role` as a soft biome probability prior in sub-biome refinement — an agricultural world gets more arable cells, an extraction world gets more rugged terrain. Is this prior applied at sub-biome resolution (nudges the `sub_biome_variant` distribution) or left to `planet_class` alone? Low-risk to include; risk is self-contradictory worlds if excluded.
|
||
|
||
**Minimum viable Layer 1:**
|
||
- `sub_biome_variant` column populated on `atlas_regional_biomes`
|
||
- `terrain_modification_cost` column populated on `atlas_regional_biomes`
|
||
- River confluence points stored as point features in systems.db (new `atlas_geographic_features` table)
|
||
- Mountain pass identification (candidate pass cells tagged)
|
||
- Geographic feature tags (Paula's taxonomy) attached to regional cells
|
||
- Defer: tile-resolution river routing, exact lake polygons, authored river vs. generated network reconciliation
|
||
|
||
---
|
||
|
||
### Section 2: Layer 2 — Population Overlay
|
||
|
||
**Inputs:** Layer 1 output (regional biome + geographic feature tags + river network), atlas_cities (markers.json population anchors), atlas_roads/railroads (authored infrastructure), systems.db bodies + economics tables.
|
||
|
||
**Required outputs:**
|
||
- `land_use` column on all regional cells: Urban | Agricultural | Industrial | Wilderness | Corridor | Ruins | Ocean | Impassable
|
||
- `terrain_modification` signal per cell: what was here before settlement, what it became
|
||
- Sub-settlement positions (all latent dynamic settlements placed, seed-derived)
|
||
- Road graph (nodes + edges with `MaintenanceAuthority`): already designed in Phase 3 Layer C
|
||
- `TerritorialStatus` per regional cell: CoreTerritory | FrontierTerritory | ExtractiveZone | ContestZone | WildernessBuffer | AbandonedZone
|
||
- `PoliticalTether` per sub-city settlement: Administered | EconomicallyDependent | Contested | Independent | Corporate | Abandoned
|
||
- `FoundingOrientation` per settlement: derived from geographic trigger that caused placement
|
||
- `political_archetype` per city: derived from geographic trigger + economic data (override column for named cities)
|
||
- Hinterland characterization per body: how the space between cities is filled
|
||
|
||
**Questions to design:**
|
||
|
||
**L2-Q1 (Architecture, lead must resolve):** Does "geography determines shape" mean city positions in markers.json are re-evaluated by Layer 2, or do they stay fixed as anchors with Layer 2 explaining them? The generator's behavior differs significantly: (a) positions anchored — `generate_regional.py` cannot write back to markers.json, only extends systems.db with geographic context; (b) positions re-derived — generator produces settlement positions from geographic attractors, uses markers.json population counts only, may place cities at different pixel positions. The lead's phrasing ("anchored by wiki population counts and markers.json") implies (a). But "where rivers meet, where rivers hit the sea, where arable land is — settlements anchor" implies the generator knows WHY each city is where it is. **Position: anchored with geographic explanation attached — but this must be confirmed.**
|
||
|
||
**L2-Q2 (Sub-settlement placement):** Given a body with `economic_role = extraction` and 3 qualifying corps, where exactly do the mining camps sit? The algorithm needs spatial precision for seeded placement. Proposed algorithm: camp_position = closest regional cell to corp's `location_id` asset that has `land_use ∈ {Industrial, Wilderness}`, at seeded distance from tether city within [15%–40%] of body scale. Does this hold? What's the camp count formula?
|
||
|
||
**L2-Q3 (Road algorithm):** The lead says "road/rail networks inter and intra regional." markers.json has authored road pixel-paths for named highway routes. Does Layer 2 generate additional roads (for sub-settlement connections) or does it only interpret existing authored roads? Proposed resolution: authored roads remain for named routes (setting decisions — Gate Corp built this highway); Layer 2 generates local roads connecting sub-settlements to their tether cities using the road graph algorithm from Phase 3 Layer C. Do participants agree?
|
||
|
||
**L2-Q4 (Hinterland shapes):** Burnelli-Sheldon proposes per-`economic_role` hinterland fill (agricultural: farmland + irrigation + processing nodes; extraction: access corridors + stockpile areas; transit: dense road/rail + relay stations). How is the spatial shape of farmland determined? Proposed rule: agricultural cells cluster around agricultural-role cities + along river corridors, at density inversely proportional to `terrain_modification_cost` from Layer 1. Rough terrain stays wilderness; gentle terrain becomes farmland. Is this sufficient, or does it need a Voronoi-style region-growing algorithm?
|
||
|
||
**L2-Q5 (TerritorialStatus derivation):** Paula establishes TerritorialStatus as a post-processing output of settlement placement + road generation. Confirm the derivation algorithm: CoreTerritory = high settlement density + well-maintained roads throughout; FrontierTerritory = settlements placed + roads thin at distance; ExtractiveZone = settlement at ResourceConcentration + corporate presence + excellent extraction corridor + absent off-corridor; ContestZone = two settlement clusters with competing road networks in overlapping territory; WildernessBuffer = no settlement placed; AbandonedZone = settlement placed but economic basis flagged as exhausted. What are the quantitative thresholds for each?
|
||
|
||
**L2-Q6 (Latent settlement flag storage):** Burnelli-Sheldon raises OQ-R4-B1: where does the settlement active/ghost flag live? Options: (a) as a sim-readable component on each settlement entity in the ECS; (b) in a systems.db Phase 3 output table (settlement positions + trigger conditions + activating corp/condition reference); (c) computed entirely at runtime from `corp_presence` + `corp_financial_state.health_metric`. Recommended answer is (b) — Phase 3 stores latent settlement positions + trigger conditions; sim reads them alongside `corp_financial_state`. This integrates with `generate_regional.py` naturally.
|
||
|
||
**Minimum viable Layer 2:**
|
||
- `land_use` column on all regional cells
|
||
- Sub-settlement placement: mining camps + trade waypoints (highest frequency, most game-relevant)
|
||
- Road graph with `MaintenanceAuthority` per edge (Phase 3 Layer C already designed)
|
||
- `TerritorialStatus` per regional cell
|
||
- `political_archetype` per city (derived, override column)
|
||
- `FoundingOrientation` per city (derived from geographic trigger)
|
||
- Defer: exact agricultural node positions (land_use tag sufficient for minimum), shadow node placement, authored vs. generated road resolution
|
||
|
||
---
|
||
|
||
### Section 3: Layer 3 — City-Level Planning
|
||
|
||
**Inputs:** `CityGenerationContext` struct (from systems.db at game startup), Layer 1+2 regional data via systems.db, `world_seed`.
|
||
|
||
**Required outputs:**
|
||
- N-district grid per city (district positions in city-local sim tiles)
|
||
- `DistrictSkeleton` Phase 1 Stages 1-2 for each district:
|
||
- Stage 1: WorldTier, ComplexityTier, SettingType, DistrictType, LayoutMode, `prosperity_baseline`, `perimeter_treatment`
|
||
- Stage 2: 4×4 BlockSkeleton grid with ZoningType, `density_pct`, seed
|
||
- District spatial arrangement shaped by `FoundingOrientation` + `political_archetype`
|
||
- Prosperity gradient direction and magnitude across districts
|
||
|
||
**Questions to design:**
|
||
|
||
**L3-Q1 (District grid orientation):** The district count formula (D-C4) places N districts in a grid. `FoundingOrientation` implies the grid should be oriented: a PortFacing city has its LogisticsHub/Transit districts at the water-facing edge; a DefenseFacing city has its Administrative district at the elevated center. Does `FoundingOrientation` modify the district placement grid (spatial rotation/alignment), or does it only modify the prosperity gradient direction without changing grid positions? Gestalt identifies this as an open question — the brief should scope it: (a) spatial orientation change (more complex, more legible); (b) gradient direction only (simpler, less visually obvious).
|
||
|
||
**L3-Q2 (Political archetype as spatial arrangement):** Paula and Gestalt both flag that `political_archetype` should shape spatial ARRANGEMENT, not just type frequency. CompanyTown = spine (facility at one end, housing radiating back). AdminCapital = center (administrative hub, residential radiating outward). FreePort = multiple nodes (no single center). Does Layer 3 implement these spatial arrangement patterns explicitly, or do they emerge from the weight table and `FoundingOrientation` rules without explicit arrangement logic? Explicit arrangement produces more consistent legibility; emergent arrangement is cheaper to implement. The brief should specify which the workshop is designing.
|
||
|
||
**L3-Q3 (Arterial road layout):** Does City-Level Planning produce an explicit arterial road layout within the city, or are district boundary positions the implicit road network? Explicit arterials: requires a road placement algorithm as a Layer 3 output; produces a more realistic city; consumed by Layer 4 tile generation for street positioning. Implicit streets: district boundaries imply roads exist; Layer 4 generates streets at those positions; simpler. For the minimum slice: district boundary = implicit street. For richer cities: arterials first. The brief should state which is in scope for Round 1 of this workshop.
|
||
|
||
**L3-Q4 (Topographic constraints on zoning):** Burnelli-Sheldon proposes that topographic constraints from Layer 1 modify the 10×9 weight table: industrial zones weighted toward flat terrain near logistics access; residential zones weighted against flood plains; LogisticsHub always adjacent to road/rail entry points. How does Layer 3 access terrain roughness at city-local resolution? The Layer 1 data is at 64×32 regional resolution — each regional cell covers multiple districts. Is sampling the regional roughness at the city's center position sufficient, or does Layer 3 need finer terrain data per district?
|
||
|
||
**L3-Q5 (prosperity_baseline derivation formula):** Burnelli-Sheldon's formula:
|
||
```
|
||
base = economic_tier / 5.0
|
||
role_modifier: extraction -0.1, research +0.15, service_mixed +0.1, frontier -0.2
|
||
if stratified: district_i = base + role_modifier + (prestige_rank(i) - 0.5) × 0.7
|
||
if moderate: district_i = clamp(N(base + role_modifier, 0.1), 0.2, 0.8)
|
||
```
|
||
`distribution_index` controls gradient magnitude; `FoundingOrientation` controls gradient direction. Does topography also contribute (hilltop districts = historically higher prosperity)? Burnelli-Sheldon recommends: topography sets gradient direction, `distribution_index` sets gradient magnitude. Do all participants agree?
|
||
|
||
**L3-Q6 (Sub-settlement depth — OQ-R4-G6):** The sub-settlement hierarchy includes Towns (1,000–10,000 population, "single-district equivalent"). Does a town go through the same DistrictSkeleton generation path as a city district, or does it get a simplified code path? A unified code path is architecturally cleaner; a simplified path is cheaper for its lower content budget. If unified: the town's `CityGenerationContext` has `district_count = 1`. If simplified: separate Rust codepath with a reduced struct.
|
||
|
||
**L3-Q7 (Port/station direction):** Burnelli-Sheldon proposes Transit and LogisticsHub districts are placed at the city edge closest to the orbital station (if one exists). Does Layer 3 receive directional information about nearby orbital stations? This requires knowing the orbital station's atlas pixel position relative to the city's position — feasible from systems.db. Is this in scope for the minimum viable slice, or deferred?
|
||
|
||
**Minimum viable Layer 3:**
|
||
- District count + grid positions (D-C4 formula)
|
||
- Phase 1 Stage 1 classification per district (WorldTier, ComplexityTier, DistrictType from weight table, LayoutMode, `prosperity_baseline`, `perimeter_treatment`)
|
||
- Phase 1 Stage 2 block grid (4×4 BlockSkeletons)
|
||
- `political_archetype` consumed from systems.db (Layer 2 output)
|
||
- Defer: `FoundingOrientation` spatial grid orientation, explicit arterial layout, topographic constraints per district, Stages 3-5
|
||
|
||
---
|
||
|
||
### Section 4: Layer 4 — Street-Level Rendering
|
||
|
||
**Inputs:** `DistrictSkeleton` (Layer 3 output), `district_seed` (from SeedChain), `prosperity_current` (runtime economic state), `chunk_coord`.
|
||
|
||
**Required outputs:**
|
||
- 64×64 `TileEntry` grids (`GeneratorChunkData`) per chunk, on demand
|
||
- 3 canonical tile types: `floor_street`, `floor_interior`, `wall`
|
||
- Tile condition state per tile: `Intact | Worn | Cracked | Broken` (economics-variable, NOT seed-locked)
|
||
- Building footprints with door positions (door-per-edge boundary)
|
||
|
||
**Questions to design:**
|
||
|
||
**L4-Q1 (Economics-variable rendering mechanism — OQ-R4-G2):** The determinism rule says tile conditions are economics-variable. Three implementation options: (a) bake `prosperity_baseline` into tile conditions at generation time, update on `prosperity_current` change event; (b) compute tile conditions live from `prosperity_current` each render frame — no cache; (c) cache condition state per chunk, invalidate when `prosperity_current` crosses a threshold. Performance and implementation complexity differ significantly. The brief needs to choose: the workshop must specify which mechanism, including whether condition states are cached per chunk or computed live.
|
||
|
||
**L4-Q2 (Condition update trigger):** What triggers a tile condition recalculation? Options: (a) every time `prosperity_current` changes (potentially every tick); (b) when `prosperity_current` crosses defined thresholds (e.g., Intact ≥ 0.6, Worn 0.4–0.6, Cracked 0.2–0.4, Broken < 0.2); (c) periodic recalculation (e.g., every 100 ticks). For performance: threshold crossings are correct — most districts are stable most of the time and require no recalculation.
|
||
|
||
**L4-Q3 (Phase 2 tile algorithm — confirmed):** Street skeleton first (door-per-block-edge defines connectivity), then building fill (density-driven rectangles within block). `density_pct` from BlockSkeleton drives building coverage. High density = packed buildings with minimal street width; low density = open space. This is confirmed from Rounds 2-3. The workshop should design the algorithm parameters: minimum street corridor width, maximum building rectangle dimensions, gap rules at block edges.
|
||
|
||
**L4-Q4 (Interior generation trigger — OQ-R4-G5 partial):** Door-per-edge is the confirmed building boundary. What triggers interior tile generation? Options: (a) when player entity is within N tiles of any door — pre-generates the interior before the player crosses; (b) when player entity crosses the door threshold — generates on entry; (c) pre-generated with the exterior chunk but not rendered until entered. Option (a) avoids a generation stall at the door but wastes compute for doors the player passes. Option (b) is simplest. The brief should specify.
|
||
|
||
**L4-Q5 ("Scatter civilization" scope):** The lead says "scatter civilization into tiles." Beyond floor/wall canonical types, what does Layer 4 place? Candidates: (a) prop spawn points (furniture, vehicles, crates) — entity system populates at runtime from these points; (b) surface decals (signage, paint) — tile variants or overlay tiles; (c) entity spawn points for NPCs. The brief should specify which of these Layer 4 is responsible for vs. what is runtime entity system placement.
|
||
|
||
**L4-Q6 (prosperity_delta architecture — Paula):** Paula proposes that `prosperity_index` must have two components to enable the "ghost city effect": a seed-locked `prosperity_baseline` (what the district was planned as) and a sim-driven `prosperity_delta` (what current economic state says relative to the plan). The effective prosperity = `prosperity_baseline + prosperity_delta` (clamped to [0.0, 1.0]). The gap between these two values IS the narrative: planned-for-wealth but now-poor; planned-for-rough but now-thriving. This requires the rendering system to have access to BOTH values. Does the workshop adopt this two-field model explicitly?
|
||
|
||
**Minimum viable Layer 4:**
|
||
- 3 tile types generating a walkable, navigable space
|
||
- Seed-locked layout from `district_seed` via FNV-1a
|
||
- Chunk streaming hookup: `load_chunk()` calls tile generation via `DistrictMap`
|
||
- `prosperity_baseline` drives initial building density selection
|
||
- Defer: tile condition overlays (economics-variable rendering) — implement after walkable world
|
||
- Defer: building interior generation beyond door-per-edge boundary
|
||
- Defer: prop/decal scatter, entity spawn points
|
||
|
||
---
|
||
|
||
### Section 5: Cross-Layer Architecture
|
||
|
||
**Questions that span multiple layers:**
|
||
|
||
**CL-Q1 (`prosperity_baseline` vs. `prosperity_current` naming — Gestalt OQ-R4-G3):** The workshop must confirm these are TWO DISTINCT FIELDS with distinct names, never conflated:
|
||
- `prosperity_baseline: f32` — Layer 3 output, seed-locked, stored in `CityGenerationContext` / atlas tables. What the district was economically designed for.
|
||
- `prosperity_current: f32` — Runtime simulation state. What the district's current economic health is. Updated by the economic simulation.
|
||
- `prosperity_delta: f32` — Optional derived field (`prosperity_current - prosperity_baseline`). Enables the ghost city effect.
|
||
|
||
**CL-Q2 (Regional land-use evolution vs. city-geometry lock):** Burnelli-Sheldon identifies this as OQ-R4-B2: when agricultural corp expands and new farmland appears, does this register as (a) a ChunkMutations entry in the save file, or (b) a runtime update to `regional_land_use` in the biome grid at coarse resolution (not a full regen-db)? The proposed answer: Layer 2 regional land-use changes at biome-cell resolution are a runtime parameter read by the Layer 4 renderer to determine what large-scale tile type fills undeveloped hinterland. This is NOT ChunkMutations (which are reserved for player-caused or explicit-event damage at tile precision). The brief needs to confirm this resolution boundary.
|
||
|
||
**CL-Q3 (Ruin lifecycle):** Paula proposes AbandonedZone settlements follow a deterministic decay path: prosperity_delta drops sharply → perimeter treatment degrades (Checkpoint → Fenced → Open) → landmark condition deteriorates. When does the economic simulation flag a settlement for ruination — (a) when `corp_financial_state.health_metric` drops below threshold (emergent); (b) when a lead-authored event fires (override); or (c) both, with sim as default and authored events as override? All three domain files suggest (c).
|
||
|
||
**CL-Q4 (Authored rivers vs. drainage network — L1-Q1 elevated):** This question spans Layers 1 and 2 and must be resolved before either layer can be designed fully. The existing markers.json `rivers` arrays are authored polylines. The lead wants rivers from seeded drainage patterns with names re-placed based on water sim. This requires a reconciliation pass. The workshop should specify: (a) what the drainage algorithm produces; (b) how authored river names are matched to generated courses; (c) whether markers.json rivers are ever modified by `generate_regional.py` or whether the generated drainage is parallel data in systems.db only.
|
||
|
||
---
|
||
|
||
## Workshop Format
|
||
|
||
**3 rounds:**
|
||
|
||
### Round 1 — Inventory and Framing
|
||
|
||
Each participant examines all open questions from their domain perspective. For each layer, state: what algorithm exists or can be reused, what is genuinely novel design work, what is a data/schema question only. Identify which questions are prerequisite (later questions depend on earlier answers) and which are independent.
|
||
|
||
Write findings to `docs/workshops/planet-down-cascade/{agent}-round1.md`.
|
||
|
||
### Round 2 — Algorithm Proposals
|
||
|
||
Based on Round 1, each participant proposes concrete algorithms for their domain questions. Proposals must specify: inputs, outputs, data format, whether code goes in Python tooling or Rust runtime. For any question where participants disagree, state the disagreement clearly — do not average positions.
|
||
|
||
Write proposals to `docs/workshops/planet-down-cascade/{agent}-round2.md`.
|
||
|
||
### Round 3 — Convergence and D-Records
|
||
|
||
Review all proposals. Lock algorithm choices. Produce D-record candidates. Identify remaining open questions that need lead resolution. Produce the implementation ticket dependency chain.
|
||
|
||
Write final positions to `docs/workshops/planet-down-cascade/{agent}-round3.md`.
|
||
|
||
---
|
||
|
||
## Required Reading
|
||
|
||
Before Round 1, all participants must read:
|
||
|
||
**Workshop outputs (carry-forwards):**
|
||
- `docs/workshops/generation-cascade/round-1-notes.md` — layer inventory and consensus from prior workshop
|
||
- `docs/workshops/generation-cascade/round-2-notes.md` — D-record candidates (D-C1 through D-C8 proposals)
|
||
- `docs/workshops/generation-cascade/tyre-round3.md` — Phase 3 technical layers (generate_regional.py design)
|
||
|
||
**Code (what exists):**
|
||
- `tooling/planet-gen/planet_simulation.py` — terrain simulation: elevation, temperature, moisture, biomes, rivers
|
||
- `tooling/planet-gen/generate_atlas.py` — city placement, infrastructure, output format
|
||
- `server/src/simulation/generator.rs` — DistrictSkeleton and all types (stubs)
|
||
- `server/src/simulation/chunk_streaming.rs` — chunk load/unload architecture
|
||
|
||
**Decisions:**
|
||
- `decisions/architecture.md` — D-010 (determinism rule), D-020 (client-server), D-031 (time system)
|
||
- `CLAUDE.md` §Development Cascade — the phase definitions and cascade order
|
||
|
||
**Domain requirements (Round 4 inputs):**
|
||
- `docs/workshops/generation-cascade/gestalt-round4.md` — systems design questions per layer
|
||
- `docs/workshops/generation-cascade/paula-round4.md` — narrative requirements, TerritorialStatus, ghost city effect
|
||
- `docs/workshops/generation-cascade/burnelli-sheldon-round4.md` — economics integration, latent settlements, determinism split
|
||
|
||
---
|
||
|
||
## Expected Outputs
|
||
|
||
1. **Algorithm specifications** for each of the 4 cascade layers — sufficient detail for a developer to implement without further design work
|
||
2. **Data format handoffs** per layer — what goes in systems.db (Python tooling), what is seed-derived at runtime (Rust), what is a runtime simulation variable
|
||
3. **D-records** in `decisions/` for all locked decisions (targeting D-C1 through D-C12 candidates from prior workshop plus new ones from this workshop)
|
||
4. **Implementation ticket dependency chain** — what to build first, what is blocked behind what, which prior tickets (#899 et al.) need rescoping against the new layer model
|
||
5. **Resolved open questions** — specifically: L1-Q1 (authored vs. drainage rivers), L2-Q1 (anchor vs. re-derive city positions), L3-Q1 (FoundingOrientation spatial effect), L4-Q1 (economics rendering mechanism), CL-Q1 (prosperity_baseline vs. current naming), CL-Q2 (regional land-use evolution resolution boundary)
|
||
|
||
---
|
||
|
||
## Participant Roles
|
||
|
||
| Participant | Primary domain | Layer focus |
|
||
|-------------|---------------|-------------|
|
||
| **Gestalt** | Systems design, fun factor, mechanical decisions | Cross-layer — evaluates each layer for interesting player choices and mechanical coherence |
|
||
| **Tyre** | Technical architecture, feasibility, algorithm design | All layers — owns algorithm concreteness, data formats, Python vs. Rust split, performance |
|
||
| **Paula** | Narrative legibility, political depth | Layers 1-2 (geographic triggers → political structure) + the ghost city effect at Layer 4 |
|
||
| **Burnelli-Sheldon** | Economics simulation, quantitative modeling | Layers 2-3 (economics onto geography, zoning weights) + determinism/rendering split |
|