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>
39 KiB
title, description, type, status, workshop, agent, round, created
| title | description | type | status | workshop | agent | round | created |
|---|---|---|---|---|---|---|---|
| Workshop Brief — Planet-Down Cascade | Design the multi-layered planet-down generation algorithms where each layer's output feeds the next: empty world → population overlay → city planning → street rendering | workshop | active | planet-down-cascade | 0 | 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:
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
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)
enum WorldTier { Epicenter, Regional, Backwater, Passage, Waypoint }
// NOT: Peripheral, Connected, Core — those are wrong and must be replaced
GeneratorChunkData Upgrade
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_variantcolumn onatlas_regional_biomes): 3-4 variants per biome class (e.g.,tropical→jungle_clearing | swamp | riverine_forest | lowland_marsh) terrain_modification_cost: f32per 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_roughnessadjacent 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_variantcolumn populated onatlas_regional_biomesterrain_modification_costcolumn populated onatlas_regional_biomes- River confluence points stored as point features in systems.db (new
atlas_geographic_featurestable) - 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_usecolumn on all regional cells: Urban | Agricultural | Industrial | Wilderness | Corridor | Ruins | Ocean | Impassableterrain_modificationsignal 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 TerritorialStatusper regional cell: CoreTerritory | FrontierTerritory | ExtractiveZone | ContestZone | WildernessBuffer | AbandonedZonePoliticalTetherper sub-city settlement: Administered | EconomicallyDependent | Contested | Independent | Corporate | AbandonedFoundingOrientationper settlement: derived from geographic trigger that caused placementpolitical_archetypeper 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_usecolumn on all regional cells- Sub-settlement placement: mining camps + trade waypoints (highest frequency, most game-relevant)
- Road graph with
MaintenanceAuthorityper edge (Phase 3 Layer C already designed) TerritorialStatusper regional cellpolitical_archetypeper city (derived, override column)FoundingOrientationper 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)
DistrictSkeletonPhase 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
- Stage 1: WorldTier, ComplexityTier, SettingType, DistrictType, LayoutMode,
- 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_archetypeconsumed from systems.db (Layer 2 output)- Defer:
FoundingOrientationspatial 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
TileEntrygrids (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_seedvia FNV-1a - Chunk streaming hookup:
load_chunk()calls tile generation viaDistrictMap prosperity_baselinedrives 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 inCityGenerationContext/ 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 workshopdocs/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, riverstooling/planet-gen/generate_atlas.py— city placement, infrastructure, output formatserver/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 layerdocs/workshops/generation-cascade/paula-round4.md— narrative requirements, TerritorialStatus, ghost city effectdocs/workshops/generation-cascade/burnelli-sheldon-round4.md— economics integration, latent settlements, determinism split
Expected Outputs
- Algorithm specifications for each of the 4 cascade layers — sufficient detail for a developer to implement without further design work
- 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
- D-records in
decisions/for all locked decisions (targeting D-C1 through D-C12 candidates from prior workshop plus new ones from this workshop) - 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
- 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 |