Files
settled-reach/docs/workshops/planet-down-cascade/workshop-outcomes.md
T
jpmschweitzerandClaude Opus 4.8 943f2e7749 docs(workshops): add decision_refs provenance frontmatter (pql migration phase 5)
Workshop -> decision provenance was prose-only. Adds a `decision_refs:` YAML
frontmatter list (the confirmed D-records each workshop-outcomes.md touches,
filtered against the governance decision set) to all 17 workshop outcomes; the two
that lacked frontmatter (commodity-catalog, system-economic-specialization) get a
minimal block. pql indexes the list and `SELECT fm.decision_refs` round-trips it, so
"which workshops touch D-NNN" is answerable via SELECT + filter or `pql search`.

decision_refs is a relevance signal (decisions a workshop discusses/produces), not a
strict authorship claim — historical bare refs aren't disambiguated. Generated wiki
read-only sections are left untouched.

Noted in pql-requirements #5: 1.6.2 has no working DSL operator for frontmatter
list-membership (`~`/`contains` error, `in` matches nothing), so membership queries
need a client-side filter for now.

Reproducible via tooling/pql-migrate/add_workshop_provenance.py (idempotent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 13:06:33 +02:00

22 KiB
Raw Blame History

title, author, workshop, status, created, rounds, decision_refs
title author workshop status created rounds decision_refs
Workshop Outcomes — Planet-Down Cascade qatux planet-down-cascade complete 2026-05-01 3
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-194D-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 (010 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)

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)

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
1999 Residential + Mixed
1,0009,999 Residential + Commercial
10,00099,999 + Entertainment
100,000499,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)

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.430.63
Cracked 0.230.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-194D-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-194D-219). All four layer algorithms specified at pseudocode level. Implementation-ready.