docs(workshops): planet-down cascade workshop + misc stray files

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>
This commit is contained in:
2026-05-03 20:18:30 +02:00
co-authored by Claude Opus 4.6
parent 42ee1f0a0e
commit b9fd75b840
23 changed files with 11548 additions and 0 deletions
@@ -0,0 +1,403 @@
---
title: "Workshop Outcomes — Planet-Down Cascade"
author: qatux
workshop: planet-down-cascade
status: complete
created: 2026-05-01
rounds: 3
---
# 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<BodyWorldState>`, ~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.*