--- title: "Workshop Outcomes — Planet-Down Cascade" author: qatux workshop: planet-down-cascade status: complete created: 2026-05-01 rounds: 3 decision_refs: [D-194, D-195, D-196, D-197, D-198, D-199, D-200, D-201, D-202, D-203, D-204, D-205, D-206, D-207, D-208, D-209, D-210, D-211, D-212, D-213, D-214, D-215, D-216, D-217, D-218] --- # Planet-Down Cascade — Workshop Outcomes ## What This Workshop Was The planet-down-cascade workshop was a three-round design sprint tasked with answering a single question: **Given the four-layer cascade (Empty World → Population Overlay → City Planning → Street Rendering), what are the concrete algorithms at each layer?** The workshop was convened in response to the generation-cascade workshop's outcomes (docs/workshops/generation-cascade/workshop-outcomes.md), which specified the cascade structure but deferred the algorithm specifications. A consultant review (docs/workshops/planet-down-cascade/consultant-review-planet-down-cascade.md) produced six amendments before Round 1 began; these amendments significantly restructured the problem. **Participants:** Gestalt (systems design), Tyre (technical architecture), Paula (narrative and political), Burnelli-Sheldon (economics), Ozzie (player experience). **Documentation:** Qatux (this document). --- ## What the Workshop Achieved **Five algorithm specifications**, each at pseudocode-level detail sufficient for implementation: 1. **Layer 1 (Empty World):** D8 priority-flood drainage routing from heightmap BLOBs; geographic feature tag extraction (7 tags); sub-biome variant classification. 2. **Layer 2 (Population Overlay):** Five-phase attractor-matching pipeline assigning named cities to geographic attractors; road graph generation; TerritorialStatus derivation per Province; SettlementClass assignment for all settlements. 3. **Layer 3 (City-Level Planning):** Three-component district mix (D-194); FoundingOrientation spatial grid rotation; five explicit political archetype spatial patterns; BlockIrregularity from founding age. 4. **Layer 4 (Street Rendering):** Tile condition derivation from prosperity_current thresholds (0.63/0.43/0.23); chunk condition cache with threshold-crossing invalidation; building fill and street skeleton from BlockSkeleton. **Three data format handoffs per layer:** - Build-time Python → systems.db (heightmaps, name reservations, Province boundary polylines, economics data) - Runtime-background Rust → BodyWorldState LRU cache (Layer 1-2 outputs; reproducible from seed + systems.db) - Runtime-on-demand Rust → never stored (Layer 3-4 generation from BodyWorldState + CityGenerationContext) **26 D-records filed (D-194 through D-219)** — all placeholder candidates claimed and written to `decisions/architecture.md` and `decisions/content.md`. **An implementation ticket dependency chain:** 22+ tickets, ~14.75-day critical path, ~33 dev-days total. **Three pre-implementation data quality blockers** identified (bodies.founding_age_years NULL, system_economy fields NULL, economic_role normalization needed). --- ## Relationship to the Prior Workshop The generation-cascade workshop (documented in docs/workshops/generation-cascade/workshop-outcomes.md) produced the cascade structure itself: four layers, three execution tiers, SeedChain (FNV-1a), CityGenerationContext struct, and D-C1 through D-C17 as Given Facts. It explicitly deferred algorithm design to a follow-on workshop. This workshop is that follow-on. The Given Facts (D-C1 through D-C17) were carried in as fixed axioms and not reopened. **The most significant change from the generation-cascade workshop:** Amendment 1 (from the consultant review) replaced the two-tier Phase 3/Phase 5 model with a three-tier model: build-time Python / runtime-background Rust / runtime-on-demand Rust. Tyre documented this pivot explicitly as "Priority 0: The Architectural Pivot" in his Round 1 file. All five agents absorbed the amendment successfully. Amendment 3 (fully generative placement, markers.json stripped of city positions) eliminated several previously open questions (L1-Q1, L2-Q1, CL-Q4) by making them moot. --- ## All D-Record Candidates ### All D-Records — Filed All workshop D-record candidates have been claimed, written to `decisions/architecture.md` (D-194–D-218) and `decisions/content.md` (D-219), and confirmed. | Real ID | Title | Workshop placeholder(s) | |---------|-------|------------------------| | D-194 | Three-Component District Mix Algorithm | D-C28, GEN-3 | | D-195 | Attractor-Matching Compatibility Matrix | (part of D-C25) | | D-196 | SettlementClass Enum and Active/Ghost Logic | D-C26, GEN-6 | | D-197 | prosperity_baseline Derivation Formula | (new) | | D-198 | Economic Simulation Independence from Layer 1-2 Spatial Data | (part of D-C18) | | D-199 | 6-Field Minimum Economic Read Set for CityGenerationContext | (new) | | D-200 | Three-Tier Execution Model | D-C18 | | D-201 | Spatial Hierarchy — Eight Tiers with Locked Dimensions | D-C21 | | D-202 | Heightmap BLOB Storage Schema | D-C33, ARCH-1 | | D-203 | BodyWorldState Bevy Resource with LRU Cache | ARCH-2, D-C34 | | D-204 | body_radius_km Column on bodies Table | ARCH-4 | | D-205 | Province Boundary Pre-Computation | ARCH-5 | | D-206 | Background Generation Priority Queue | D-C19, GEN-7 | | D-207 | Fully Generative Placement | D-C20 | | D-208 | D8 Priority-Flood Drainage Routing | D-C22, GEN-1 | | D-209 | Geographic Feature Tags — 7 Tags | D-C23 | | D-210 | Sub-Biome Variant Classification | D-C24 | | D-211 | Attractor-Matching Five-Phase Pipeline | D-C25, GEN-2 | | D-212 | TerritorialStatus Derivation | D-C27, GEN-5 | | D-213 | FoundingOrientation Enum and Grid Rotation | D-C29, D-C-FO | | D-214 | AttractorAssignment Enum | D-C-AA | | D-215 | Five Spatial Arrangement Patterns | D-C30, D-C-SA | | D-216 | BlockIrregularity from founding_age | D-C31, GEN-4 | | D-217 | Tile Condition Thresholds | D-C32 | | D-218 | WorldTier Enum Canonical Values | GEN-8 | | D-219 | Naming Registers Lookup Table | D-C-NL | GEN-9 (atlas_feature_names schema) is covered by the schema ticket #903 but does not have a standalone D-record — the DDL is straightforward and doesn't require a design decision. --- ## All Locked Algorithms ### Layer 1: Empty World Generator **D8 Drainage Routing** (Tyre ARCH-1/GEN-1; Gestalt D-C22) - Input: atlas_body_heightmaps BLOB (float32 LE, 512×256) - Algorithm: D8 single-direction steepest descent; priority-flood sink filling; flow accumulation - River threshold: cells with flow_accumulation > 200 are river cells - Outputs: RiverNetwork (river_cells, confluence_nodes, river_mouths), drainage basin boundaries - Performance: ~50ms per body - Specification: tyre-round2.md (ARCH-1), gestalt-round2.md (D-C22), tyre-round3.md (GEN-1) **Geographic Feature Tag Extraction** (Gestalt D-C23) - 7 tags derived per regional cell (64×32 grid) - Derivation rules: RiverConfluence (any confluence in bbox), CoastalHarbor (coastal + roughness<0.3), MountainPass (roughness>0.65, lower cells on two sides), ArablePlain (roughness<0.25 + terrestrial + arable biome), ResourceConcentration (economic_role biome prior), Defensible (roughness>0.5 + ≤2 approach vectors), NaturalBarrier (ocean or roughness>0.85) - Specification: gestalt-round3.md §D-C23 **Sub-Biome Variant Classification** (Gestalt D-C24) - 3-4 variants per biome class, FNV-1a seed-derived, economic_role as probability prior - `terrain_modification_cost: f32` = roughness×0.6 + biome_clearing_cost, clamped [0,1] - Specification: gestalt-round3.md §D-C24 --- ### Layer 2: Population Overlay **Attractor-Matching Five-Phase Pipeline** (D-C25 + D-195) Phase 0 — Score matrix: Build N×M matrix. Apply D-195 compatibility matrix (0–10 scale, 10 roles × 7 attractor types). Hard-zero physically impossible pairings (Paula H1-H4). Phase 1 — Sort cities by constraint tier: - Tier A: extraction/mining (most constrained) - Tier B: manufacturing/transit (moderate) - Tier C: service/commercial (most flexible) Phase 2 — Tier A greedy assignment: fewest-valid-attractors-first order. Phase 3 — Hungarian algorithm on Tier B+C: maximum-weight bipartite matching, O(N³), N≤30. Phase 4 — Synthetic overflow: `SyntheticPlacementReason { PopulationOverflow | PoliticalDecision | CorpExpansion }`. Mismatch classification (lead decision): Soft warning at score < 0.35 (log, proceed). Hard error at score < 0.15 (override to Synthetic { PoliticalDecision }, place at Province centroid, AdminFacing). FoundingOrientation derivation post-assignment (Paula D-C-FO): Geographic attractor type → orientation lookup. Synthetic assignments → AdminFacing. - Specification: gestalt-round2.md (pseudocode), tyre-round3.md (five-phase with MismatchSeverity), paula-round3.md (D-C-AM, D-C-FO), burnelli-sheldon-round3.md (D-195) **TerritorialStatus Priority-Ordered Derivation** (D-C27) ```rust fn derive_territorial_status(province: &ProvinceWorldState) -> TerritorialStatus { if province.placed_at_generation && !province.active { return AbandonedZone; } if !province.placed_at_generation && province.settlement_count == 0 { return WildernessBuffer; } if province.primary_economic_activity == Extraction && province.corporate_presence_score > 0.4 { return ExtractiveZone; } if province.jurisdiction_overlap_score > 0.3 { return ContestZone; } if province.infrastructure_quality > 0.6 && province.corporate_road_maintenance > 0.5 { return CoreTerritory; } FrontierTerritory } ``` `placed_at_generation: bool` is the sole differentiator between AbandonedZone and WildernessBuffer. Immutable; set at Layer 2. - Specification: paula-round2.md, gestalt-round2.md (Rust code), burnelli-sheldon-round3.md (D-197, threshold confirmation) **SettlementClass Enum** (D-196) ```rust enum SettlementClass { NameLocked, // Corp HQ body; always active PopulationBudget, // Active if nearest anchor city health_metric > 0.4 EconomicTriggered { activating_corp_id: String }, // Active if corp health > 0.4 OrganicGrowth, // Active if province avg corp health > 0.5 } ``` Ghost rendering: seed-locked geometry preserved; lighting dark; tile condition Broken; no NPC spawns. - Specification: burnelli-sheldon-round3.md (D-196) --- ### Layer 3: City-Level Planning **Three-Component District Mix** (D-194) Component 1 — Population tier guarantees: | Population | Guaranteed district types | |-----------|--------------------------| | 1–999 | Residential + Mixed | | 1,000–9,999 | Residential + Commercial | | 10,000–99,999 | + Entertainment | | 100,000–499,999 | + Administrative | | 500,000+ | + Industrial/Civic if role-appropriate | Component 2 — Economic role multiplier table (10 roles × 9 types; all rows sum to 100; minimum cell value 3): | economic_role | Res | Com | Ind | Adm | Log | Ent | Mix | Trn | Spe | |--------------|-----|-----|-----|-----|-----|-----|-----|-----|-----| | manufacturing | 18 | 8 | 30 | 6 | 18 | 5 | 8 | 5 | 2 | | agricultural | 22 | 12 | 5 | 10 | 20 | 5 | 15 | 5 | 6 | | extraction | 16 | 7 | 28 | 4 | 24 | 5 | 9 | 5 | 2 | | transit | 10 | 18 | 5 | 5 | 20 | 12 | 14 | 14 | 2 | | research | 16 | 6 | 5 | 14 | 5 | 7 | 10 | 3 | 34 | | commercial | 14 | 30 | 5 | 6 | 10 | 14 | 14 | 5 | 2 | | service_mixed | 20 | 18 | 5 | 10 | 6 | 14 | 18 | 7 | 2 | | mining | 16 | 7 | 22 | 3 | 28 | 6 | 9 | 7 | 2 | | frontier | 26 | 10 | 10 | 5 | 18 | 8 | 16 | 5 | 2 | | energy | 10 | 3 | 18 | 6 | 24 | 3 | 6 | 6 | 24 | Political archetype modifiers (stacked additive, floor at 3 applied after): | political_archetype | Modifiers | |--------------------|-----------| | CompanyTown | Adm −10, Ind +10, Log +5, Res +5 | | AdminCapital | Adm +20, Ent +5, Spe +5, Ind −15 | | FreePort | Com +15, Trn +10, Mix +5, Adm −15 | | Contested | No modifier | | OrganicGrowth | Mix +15, Res +10, Ind −10, Adm −5 | Component 3 — Settlement age character modifier: affects `perimeter_treatment`, `density_pct`, `character_class` on DistrictSkeleton. Does NOT modify district count or type. Applies only at Backwater WorldTier and above. Self-contained: no cross-city queries. Single seed per city. Specification: burnelli-sheldon-round3.md (D-194). **Five Explicit Spatial Arrangement Patterns** (D-C30) | Archetype | Pattern | Player acceptance criterion | |-----------|---------|----------------------------| | CompanyTown | Spine: Industrial/facility at FoundingOrientation terminus; Residential cascades back | Posture visible; city points at its facility | | AdminCapital | Radial: Administrative hub at center (DefenseFacing) or prestige edge (AdminFacing); prosperity decreases outward | Power visible from any approach | | FreePort | Multi-node: 2-3 nodes with distinct character; no dominant center | Player gets productively lost; nodes serve as landmarks | | Contested | Dual-center overlay: two underlying geometric plans; boundary zone Mixed with Checkpoint perimeter | Player reads the conflict in street layout | | OrganicGrowth | Irregular local density: Voronoi around seed attractor points; ±12.5% grid jitter | Most "lived-in" feel; reads as historically authentic | Acceptance criterion (Ozzie): player identifies archetype from 15 seconds of walking without consulting implant UI. Specification: paula-round3.md (D-C-SA with Rust pseudocode), gestalt-round3.md (D-C30), ozzie-round3.md §3. **BlockIrregularity from founding_age** (D-C31) ```rust enum BlockIrregularity { Grid, SlightlyWorn, Irregular, Organic } impl BlockIrregularity { fn from_age_and_archetype(founding_age_years: u32, archetype: SpatialArchetype) -> Self { let step = match archetype { CompanyTown => 40, AdminCapital => 100, FreePort => 60, Contested => 35, OrganicGrowth => 20, }; match founding_age_years / step { 0 => Grid, 1 => SlightlyWorn, 2 => Irregular, _ => Organic } } } ``` Orthogonal to spatial arrangement archetype. Field on DistrictSkeleton. Set at Layer 3; consumed at Layer 4. Minimum irregularity: 0.05 even for brand-new cities (Ozzie). Specification: tyre-round3.md (Q2 resolution, full Rust), gestalt-round3.md (D-C31), paula-round3.md (per-archetype interaction rules), ozzie-round3.md §2. **FoundingOrientation Spatial Grid Rotation** (D-C29) District grid oriented so primary edge faces geographic founding attractor. Orientation → primary edge mapping (Gestalt D-C29). Specification: gestalt-round3.md (D-C29), paula-round3.md (D-C-FO derivation lookup). --- ### Layer 4: Street Rendering **Tile Condition Derivation** (D-C32) | Condition | prosperity_current range | |-----------|-------------------------| | Intact | > 0.63 | | Worn | 0.43–0.63 | | Cracked | 0.23–0.43 | | Broken | < 0.23 | Offset values prevent boundary oscillation (Paula Round 2 proposal; Gestalt adopted). Cache invalidated on threshold crossing (one float comparison per district per economic tick). Ozzie's rendering requirement: "Broken is ruins, not slightly damaged." Rendering spec must differentiate tiers visually with dramatic difference, not subtle variation. Specification: burnelli-sheldon-round3.md (D-197 §tile conditions), gestalt-round3.md (D-C32), ozzie-round3.md §1. **prosperity_baseline Formula** (D-197) ``` prosperity_baseline_i = clamp( economic_tier(body, system) / 5.0 // base [0,1] + ROLE_PROSPERITY_MODIFIER[economic_role] // role offset + (positional_gradient_rank - 0.5) × DISTRIBUTION_INDEX_SCALE[distribution_index] // gradient + topographic_modifier // +0.05 high ground, -0.05 near sea level , 0.05, 0.95) ``` Two distinct fields locked: `prosperity_baseline` (seed-locked, never updated), `prosperity_current` (economics-variable), `prosperity_delta` (derived, never stored). Field naming is locked; any code updating `prosperity_baseline` post-generation is a bug. Specification: burnelli-sheldon-round3.md (D-197). --- ### Infrastructure: Background Generation **Background Generation Architecture** (D-C19 / D-C34 / GEN-7) - Rayon thread pool with `GenerationQueue` (BinaryHeap by priority) - Priority: Immediate (player's current body) → High (body names detected in text by Aho-Corasick) → Medium (gate-adjacent) → Low (all others) - `GenerationCache`: LRU(50 bodies), `Arc`, ~5MB total - `SystemNameIndex`: Aho-Corasick automaton over all body/system names; scans news ticker, NPC dialogue, documents to trigger pre-generation before player travels **Diegetic placeholder tiers when generation incomplete (Ozzie):** 1. Generation complete → full map 2. Heightmap loaded, settlements pending → terrain + coastlines + named points ("settlement survey pending") 3. Only systems.db data → known city names as points, no positions ("Positional survey pending") 4. Nothing → blinking cursor + timestamp + "No survey data" Specification: tyre-round2.md (background thread architecture), tyre-round3.md (GEN-7), gestalt-round3.md (D-C34), ozzie-round3.md §4. --- ## Complete Ticket Dependency Chain ### Critical Path (14.75 dev-days) ``` SCHEMA-heightmaps (0.25d) → PY-heightmap-import (1d) → RS-heightmap-load (0.5d) → RS-body-state (1.5d) → RS-drainage (3d) → RS-attractor-assign (4d) ←─ also needs PY-city-names-authored → RS-district-mix (3d) → RS-tile-conditions (1.5d) → RS-block-irregularity (1d) ``` **Parallel schema foundation (Tier 0 — all start immediately):** BUG-WorldTier (0.5d) | SCHEMA-bodies (0.25d) | SCHEMA-heightmaps (0.25d) | SCHEMA-city-names (0.25d) | SCHEMA-feature-names (0.25d) | SCHEMA-province-bounds (0.25d) **Python pipeline (Tier 1 — after schemas):** PY-heightmap-import (1d) | PY-province-bounds (2d) | PY-city-names-authored (1d) | PY-city-names-corp (1d) | PY-body-radius (0.5d) | PY-economic-role-normalize (0.5d) **Rust types (Tier 2 — after BUG-WorldTier):** RS-types-worldtier (0.5d) → RS-types-settlement (0.5d) | RS-types-district (0.5d) | RS-types-territorial (0.5d) → RS-types-city-ctx (1d) **Rust core (Tier 3):** RS-heightmap-load → RS-body-state → RS-drainage + RS-attractor-types (parallel) **Generation algorithms (Tier 4):** RS-attractor-assign → RS-district-mix → RS-territorial + RS-block-irregularity + RS-tile-conditions (parallel) **Background + UI (Tier 5):** RS-bg-queue → RS-aho-corasick | UI-province-bounds (parallel, depends on PY-province-bounds) **Total effort:** ~33 dev-days. Two-agent parallel (Python/Rust): ~20 calendar days. --- ## Pre-Implementation Data Quality Blockers These three issues in systems.db will prevent correct algorithm execution. They are not blocking workshop closure but are blocking Phase 3 implementation start. | # | Gap | Scope | Fallback available? | |---|-----|-------|---------------------| | 1 | `bodies.founding_age_years` NULL for all 273 inhabited bodies | Content work — wiki authors (Miri) | Yes: `settlement_wave` → age bracket mapping | | 2 | `system_economy.economic_tier` and `distribution_index` NULL for 97% of systems | Content work or confirm fallback is canonical | Yes: population-derived fallback + "moderate" default | | 3 | `bodies.economic_role` values not normalized (5 variants) | Schema migration — `import_economics.py` validation step | Yes: normalization table defined | --- ## Open Questions Carried Forward These were not resolved in the workshop and require lead decisions before specific implementation tickets begin. | Question | Context | Blocking ticket | |----------|---------|----------------| | `corporate_presence_score` derivation formula | TerritorialStatus ExtractiveZone threshold uses this field; derivation undefined | RS-territorial | | L3-Q7: port/station orbital approach direction | Paula designed mechanism (one query, one lookup); Ozzie formally requested for Phase 4; Gestalt/Tyre classified deferred | None (deferred) | | Province watershed algorithm detail | PY-province-bounds needs concrete watershed extraction spec before work begins | PY-province-bounds | | Q4: orbital station as city type | `SpatialArchetype::SpaceStation` as sixth variant? | None (deferred) | | `SyntheticPlacementReason` → political archetype (Ozzie proposal) | Ozzie: CorpExpansion → CompanyTown, PoliticalDecision → AdminCapital, PopulationOverflow → OrganicGrowth | RS-attractor-assign (if adopted) | | `bodies.founding_age_years` content fill | Content work for wiki authors (Miri) | RS-district-mix (fallback acceptable for Phase 1) | | `system_economy` fallback sufficiency | Lead must confirm population-derived tier derivation is acceptable for release | RS-attractor-assign | --- ## D-Records: Claim and File Status All 26 D-records have been claimed and written to `decisions/architecture.md` (D-194–D-218) and `decisions/content.md` (D-219). The placeholder-to-real-ID mapping is in the "All D-Records — Filed" table above. No further claims needed. --- ## Key Design Principles Locked by This Workshop 1. **Geography is political history.** Province boundaries are watershed lines. City positions emerge from terrain. TerritorialStatus is readable from terrain and settlement density without labels. 2. **Every settlement has people; people have needs.** No zero-weight district types. Every settlement above Village tier has Commercial; Entertainment is guaranteed at City scale. The weight table modulates proportion, not presence. 3. **Prosperity baseline is memory; prosperity current is reality.** The gap between what a place was designed to be and what it is now is the most powerful player experience signal in the system. It happens automatically from the two-field model. 4. **Determinism with economic variance.** Geometry is seed-locked. Tile conditions are economics-variable via threshold-crossing cache invalidation. The player can destroy a district economically without the walls moving. 5. **The cascade is the experience.** Each layer correctly sets up the next layer's opening condition. The transition moments (atmosphere entry, Province crossing, district threshold) are where the system pays off — no authored setpieces required. --- *Workshop complete. Three rounds. Five agents. 26 D-records filed (D-194–D-219). All four layer algorithms specified at pseudocode level. Implementation-ready.*