Files
settled-reach/docs/workshops/generation-cascade/burnelli-sheldon-round4.md
T
jpmschweitzerandClaude Opus 4.6 42ee1f0a0e docs(workshops): generation cascade workshop — 4 rounds, D-194 through D-218
Four-round workshop (Gestalt, Tyre, Paula, Burnelli-Sheldon, Miri)
mapping the full generation pipeline from planetary heightmap to
walkable tile. 25 D-records produced. Ticket dependency chain for
Tier 0-4 implementation identified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 20:18:17 +02:00

506 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Round 4 — Burnelli-Sheldon: Economics Integration for Planet-Down Cascade"
description: "Economics domain requirements for the planet-down brief: layer entry points, determinism split, dynamic settlement latency, and per-layer DB field mapping"
type: workshop
status: active
workshop: generation-cascade
agent: burnelli-sheldon
round: 4
created: 2026-04-30
---
# Round 4 — Burnelli-Sheldon: Economics Integration for Planet-Down Cascade
The lead has reframed the cascade as planet-down. This changes the framing, not the
economics. My job this round is to map my accepted contributions from Round 3 into the
new four-layer model and answer the specific questions the lead flagged.
---
## 1. Where Does Economic Data First Enter?
**Answer: Layer 1, weakly. Layer 2, in full.**
### Layer 1 — Empty World (weak influence)
The lead defines Layer 1 as "natural world, no civilization." That is exactly right.
Economics does not shape terrain. But there is one legitimate economics-adjacent
influence at this layer: **biome quality distribution**.
A planet was settled because it was worth settling. The body's `economic_role` in
systems.db is not a cause of its terrain — it is the *outcome* of someone deciding
this terrain was worth exploiting. That selection creates a plausibility constraint:
an `agricultural` world had to have arable soil; an `extraction` world had to have
accessible ore. If the Layer 1 generator produces a volcanic hellscape for a body
with `economic_role = agricultural`, the world is self-contradictory.
The fix is not to have economics drive terrain geometry. It is to use
`bodies.economic_role` + `bodies.planet_class` as a **biome probability prior** in
the sub-biome refinement step:
| economic_role | Sub-biome quality bias |
|---|---|
| agricultural | More temperate/arable cells, gentler slopes, fewer badlands |
| extraction / mining | More rugged terrain, higher mineral-rich designations |
| research | More terrain variety (observable geography = research value) |
| transit | More navigable terrain, natural passes and harbors |
| frontier | Unconstrained — hostile terrain is plausible |
| manufacturing | Near-neutral; manufacturing follows settlement, not terrain |
This is a prior, not a hard constraint. The biome generator can still produce a
difficult agricultural world (thin soil, reclamation history) — but the prior nudges
it toward plausibility. If this is too complicated for Layer 1, the alternative is
simply to trust that `planet_class` encodes this well enough already (`temperate`
planets are already the natural home of `agricultural` worlds) and leave Layer 1
fully physics-driven.
Either approach is defensible. I flag it because the contradiction risk is real and
cheap to address at Layer 1 rather than later.
**Economic fields at Layer 1:**
- `bodies.economic_role` — biome probability prior (optional)
- `bodies.planet_class`, `bodies.atmosphere`, `bodies.surface_gravity` — primary physical inputs
### Layer 2 — Population Overlay (primary entry point)
This is where economics enters in full force and where my Round 3 work connects
most directly. The lead's description — "economics onto geography, settlements anchor
where geography supports them, felled forests → farmland, road/rail networks, anchored
by wiki population counts" — is precisely what the economics data enables.
The `atlas_cities` table provides wiki-anchored city positions and populations. The
`bodies` table provides the economic character. The `corp_presence` table shows which
corporations cluster where. The `atlas_roads` and `atlas_railroads` tables provide
infrastructure. All of this is pre-computed and stored in systems.db.
Layer 2 is not city-internal planning — it is the hinterland: what fills the space
between cities. Economic role determines hinterland character:
| economic_role | Hinterland fill | Notes |
|---|---|---|
| agricultural | Cleared farmland, irrigation networks, processing nodes | "Felled forests → farmland" lives here |
| extraction | Access corridors, mining infrastructure, stockpile areas | Extends to resource deposit locations |
| manufacturing | Industrial fringe, rail freight yards, supply zones | Follows logistics corridors |
| transit | Dense road/rail, relay stations, fuel depots | The infrastructure IS the hinterland |
| research | Exclusion zones, observatory sites, quiet buffer | Sparse, low-density |
| service_mixed | Suburban residential spread, civic infrastructure | Generic low-density |
**Economic fields at Layer 2 (full list):**
From `bodies`: `economic_role`, `settlement_pattern`, `industrial_corridor`, `population`, `planet_class`
From `atlas_*` tables: city positions and populations, road/rail presence, POI kinds
From `system_economy`: `economic_tier` (infrastructure density), `economic_base_primary/secondary`
From `system_gates`: `gate_connections` (trade route intensity → transport infrastructure density)
From `corp_presence`: which corps operate here, `primary_operation` commodity
Dynamic settlement triggers (see §3 below) are also evaluated at Layer 2.
---
## 2. The Determinism Rule — Layout vs. Appearance
The lead's rule is economically clean and I agree with it completely:
> Economic sim's rolling state affects RENDERING (prosperity, repair) NOT LAYOUT
> (streets locked by seed).
This is how real cities work. Detroit's street grid did not change when its industrial
base collapsed. Buenos Aires' Palermo grid was not rezoned during financial crisis.
The buildings aged and emptied; the streets persisted.
Here is the full split as I see it:
### Seed-Locked (Layout) — Generated Once, Never Changed
These are determined by `(world_seed + CityGenerationContext)` at generation time:
- Street grid pattern and block geometry
- Building footprints and lot parcels
- District boundaries
- Perimeter treatment type (Open / Fenced / Walled / Gated / Checkpoint)
- Access point positions
- Settlement positions — including LATENT settlements (see §3)
- Corporate campus footprints (the campus exists even if the corp is failing)
- Port infrastructure physical extent
- Road quality tier (narrow_unpaved through boulevard)
### Economic-Sim-Dynamic (Appearance) — Updated at Runtime
These are driven by the economic simulation's rolling state:
- `prosperity_index` per district — the sim updates this; rendering reads it to
choose tile variants
- Building repair state — prosperity below threshold triggers decay tiles
(ChunkMutations record explicit player damage; general decay is a rendering parameter)
- Active vs. ghost status of latent settlements (see §3)
- Corporate signage and branding presence — corp `health_metric` from
`corp_financial_state` drives whether branded signage renders
- Stockpile visibility in logistics districts — active trade → visible cargo tiles;
depressed trade → empty yard tiles
- Lighting state — prosperity floor affects illumination (powered vs. dark windows)
### The Key Insight: Prosperity Is a Render Parameter, Not a Tile Mutation
The most important implication of this split is that `prosperity_index` should NOT
be implemented as tile-level ChunkMutations for routine decay. It is a single float
per district. The renderer reads it and applies a decay probability to tile selection.
When prosperity drops from 0.6 to 0.3:
- The streets don't change
- The building footprints don't change
- The tile SELECTION changes (cracked pavement variant instead of clean; boarded
window instead of lit; rust on the facade)
- This happens purely in the rendering path, with no world mutation
ChunkMutations remain reserved for player-caused or explicit-event damage — a specific
building explosion, a player-placed barricade. General economic decay is a render
parameter. This distinction matters for performance: prosperity changes don't generate
millions of tile mutations.
### The "Felled Forest → Farmland" Exception
The lead specifically mentioned this as a Layer 2 dynamic. It seems to contradict
the layout-lock rule. Reconciliation:
"Felled forests → farmland" is **regional-scale land use** (Layer 2), not
**city-scale tile layout** (Layer 4). The lock rule applies to city-internal geometry
(streets, buildings). Regional land use — what biome category fills the hinterland
between cities — CAN change as a Layer 2 update when economic conditions shift.
Concretely: if the agriculture corp expands, new farmland clears adjacent to existing
cities. This is a land-use tile change at biome-cell resolution (large tiles covering
hectares), not a building-by-building tile mutation. The distinction:
- Layer 2 regional land use: coarse-resolution, economics-driven, can update
- Layer 4 city street/building layout: fine-resolution, seed-locked, never changes
This is not a contradiction. It is a resolution boundary.
---
## 3. Dynamic Settlements in the Planet-Down Model
In Round 3, I proposed four types of economically-triggered dynamic settlements:
mining camps, trade route waypoints, agricultural dispersed nodes, and shadow economy
nodes. The planet-down model changes how these work.
### The Latency Principle
Under the determinism rule, settlement POSITIONS must be seed-locked at generation
time. But economic conditions change — a mine depletes, a trade route shifts. The
reconciliation is **latent settlements**:
> The generator places ALL economically plausible settlement positions at Layer 2.
> Whether each settlement is ACTIVE is determined by the economic sim's running state.
> An inactive settlement exists spatially — as ruins, as empty structures, as cleared
> ground — but it is dark, unmaintained, and depopulated.
This is economically accurate. Ghost towns exist. The buildings are there; no one
lives in them. The road to the closed mine still exists.
### How This Works Per Settlement Type
**Mining camps:**
- At Layer 2 generation: evaluate `corp_presence` against body. Every body with
`economic_role IN ('extraction', 'mining')` and 2+ corps operating `metallic_ore` or
`rare_minerals` gets N seed-derived camp positions (N = corp count / 2).
- Active status at runtime: `corp_financial_state.health_metric > 0.4` for the
operating corp → camp is active (populated, lit, maintained). Corp distressed or
dissolved → camp becomes ghost state (dark, decayed render variants).
- Why this is seed-locked: the geological deposit that caused the camp to be placed is
geological fact, not economic contingency. The deposit doesn't move when the corp
fails; the camp's position doesn't either.
**Trade route waypoints:**
- At Layer 2 generation: `atlas_roads` entries with `point_count > 5` (long roads) get
a seed-derived waypoint at the geometric midpoint.
- Active status: `gate_links` trade flow proxy (nearby system still connected and
populated) → active. If the terminal system is abandoned, the waypoint becomes a
ruin.
- Seed-locked because: the road was built; the physical clearing was made. Even
abandoned trade routes leave ruins.
**Agricultural dispersed nodes:**
- At Layer 2 generation: `settlement_pattern IN ('dispersed', 'dispersed_rural')` AND
`economic_role = 'agricultural'` → scatter farm cluster positions at interval derived
from `1/economic_tier`.
- Active status: `corp_presence` corp with `primary_operation = 'agricultural_produce'`
health_metric → active or fallow.
- Fallow farms look different from ghost towns: cleared land, overgrown structures,
but the clearing and track remain. These are the richest "economic decay visible in
the world" cases.
**Shadow economy nodes:**
- At Layer 2 generation: `system_fiscal.collection_efficiency < 0.6` on the parent
system → place one informal settlement adjacent to the largest atlas city, position
seed-derived.
- Active status: shadow viability is a structural condition, not a corp health metric.
If collection_efficiency recovers (enforcement crackdown), the settlement is still
there but its character changes (from busy informal market to quiet derelict cluster).
### What This Means for the Layer 2 Implementation
Layer 2 must query the full economics dataset and produce:
1. All atlas-city hinterland characterizations
2. All latent dynamic settlement positions (with type tag and activating corp/condition)
3. Regional land use grid (farmland, industrial fringe, wilderness, etc.)
None of this requires the economic sim to be running. It is all derived from the
snapshot in systems.db at world generation time. The sim then drives the active/ghost
flag as its rolling state changes.
---
## 4. Economic Data Per Cascade Layer — Full Mapping
### Layer 1 — Empty World
| Field | Source table | Usage |
|---|---|---|
| `economic_role` | bodies | Biome probability prior (optional) |
| `planet_class` | bodies | Primary terrain generation input |
| `atmosphere` | bodies | Surface physics input |
| `surface_gravity` | bodies | Terrain height ceiling |
Economics is a soft advisory at this layer. The primary inputs are physical.
### Layer 2 — Population Overlay
| Field | Source table | Usage |
|---|---|---|
| `city_id`, `center_row`, `center_col`, `population`, `kind` | atlas_cities | Settlement anchor positions and sizes |
| `economic_role` | bodies | Hinterland character |
| `settlement_pattern` | bodies | Population distribution mode |
| `industrial_corridor` | bodies | Corridor-specific infrastructure character |
| `economic_tier` | system_economy | Infrastructure density floor |
| `economic_base_primary/secondary` | system_economy | System-level hinterland bias |
| `gate_connections`, `gate_topology` | system_gates | Trade route intensity → transport density |
| `corp_id`, `location_id`, `primary_operation` | corp_presence | Corporate cluster positions |
| `behavioral_archetype` | corporations | HQ layout character |
| `road_*` entries | atlas_roads | Existing road network |
| `railroad_*` entries | atlas_railroads | Rail corridors |
| `collection_efficiency` | system_fiscal | Shadow node trigger condition |
| `health_metric` | corp_financial_state | Settlement active/ghost status at generation |
Dynamic settlement triggers are evaluated from this field set.
### Layer 3 — City-Level Planning (zoning)
This is where the 10×9 matrix applies. The City-Level Planning layer receives a
`CityGenerationContext` (the Phase 3 → Phase 5 handoff struct from Round 3 consensus)
and produces a **zoning map** — land-use assignments per parcel, constrained by
topography from Layer 1 and infrastructure from Layer 2.
| Field | Source | Usage |
|---|---|---|
| `political_archetype` | atlas_cities (Phase 3 derived) | Zoning mix character |
| `prosperity_index` | atlas_cities (Phase 3 derived) | Base wealth gradient for zoning |
| `founding_orientation` | atlas_cities (Phase 3 derived) | Grid orientation, oldest district direction |
| `road_entry_directions` | atlas_road_edges (Phase 3 derived) | Commercial spine anchor |
| `surrounding_biome` | atlas_regional_biomes (Phase 3 derived) | Topographic zoning constraints |
| `district_count` | CityGenerationContext | How many zones to carve |
| `economic_role` | bodies | 10×9 weight matrix row selector |
| `distribution_index` | system_economy | Prosperity gradient shape (stratified vs. moderate) |
| `corp_presence` count | corp_presence (query) | Corporate district intensity |
| `behavioral_archetype` | corporations | District layout shape (Monopolist → homogeneous) |
| `shadow_economy_access` | corporations | Informal district placement trigger |
| `collection_efficiency` | system_fiscal | Informal district size multiplier |
| `currency_zone` | star_systems | Regulatory character (Commission presence) |
| `governance_type`, `dominant_faction` | system_factions | Administrative district character |
**The 10×9 matrix in this context:**
The matrix from Round 3 still applies at Layer 3, but the framing shifts from "assign
district types" to "weight zoning categories." Topography from Layer 1 and
infrastructure from Layer 2 interact with the matrix weights:
- Industrial zones: weighted toward flat terrain near logistics access (road/rail from
Layer 2). If the only flat terrain is already occupied by the commercial spine,
Industrial zones shift to city fringe.
- Residential zones: weighted against flood plains (Layer 1 biome data), toward
terrain above the industrial elevation.
- LogisticsHub: always adjacent to road/rail entry points from Layer 2.
- Specialized (research): weighted toward elevated or secluded terrain.
The matrix provides the prior. Topography and infrastructure provide the constraints.
The product is a zoning map that is economically motivated AND geographically sensible.
**Prosperity gradient at Layer 3:**
`distribution_index = stratified` → the zoning map assigns a monotonic prosperity
gradient across district types. Highest prosperity zones go at the end of the road
spine (historic center); lowest at the industrial fringe. This is the "Cities
Skylines" version of the formula I proposed in Round 3.
`distribution_index = moderate` → zones cluster in the 0.40.6 prosperity band with
less variation. The zoning map is flatter economically.
### Layer 4 — Street-Level Rendering
Layer 4 receives the zoning map from Layer 3 and produces tile grids. Most of Layer 4
is seed-derived (street pattern, building footprints). Economics enters at two points:
**At generation time (seed-locked):**
| Field | Source | Usage |
|---|---|---|
| `bulk_class` | commodities (via corp_presence.primary_operation) | Building archetype (open yard vs. clean room vs. office) |
| `base_premium_multiplier` | brand_products | Landmark building height/prominence |
| `prosperity_index` | Per-district from Layer 3 | Initial tile variant selection |
| `scarcity_class` | brand_products | Frequency of branded signage tiles |
**At runtime (dynamic, no tile mutation):**
| Signal | Source | Render effect |
|---|---|---|
| `prosperity_index` (updated by sim) | Economic simulation | Tile variant selection (clean vs. decayed vs. ruined) |
| `corp_financial_state.health_metric` | Economic simulation | Corporate signage presence, maintenance appearance |
| Active/ghost settlement status | Economic simulation | Lighting, activity, population density rendering |
| `collection_efficiency` | Economic simulation | Shadow market visual density |
The runtime signals do not change tile positions. They change which tile variant renders
at each position. The implementation is: the rendering system reads `prosperity_index`
per district from the sim's current state and applies a decay curve to tile selection.
---
## 5. Data Quality Issues That Must Be Resolved Before Layer 3
I flagged two data quality issues in Round 3. They become blockers at Layer 3:
**Issue 1: `behavioral_archetype` is mostly NULL**
Of the 275 corporation rows I surveyed, `behavioral_archetype` is NULL on most records.
The fallback chain I proposed in Round 3 still applies:
`behavioral_archetype``supply_chain_role` → commodity tier derivation from
`primary_operation`.
But `supply_chain_role` is also sparse. The realistic near-term fallback is
commodity tier: a corp whose `primary_operation = 'metallic_ore'` is a Producer;
`'financial_services'` is an Intermediary; `'gate_components'` is a Specialist.
This derivation should be added to `import_economics.py` as a backfill step — it can
fill NULL `behavioral_archetype` rows from commodity tier with a low confidence flag.
This is not required for the minimum viable slice but it enriches Layer 3 zoning
quality significantly.
**Issue 2: `scope` field is mixed-type text**
Corporation `scope` values are a mix of enum-ish text ("reach-wide", "local") and
prose descriptions ("GJ 338B local; north corridor secondary"). The generator cannot
reliably parse prose scope descriptions.
For Layer 3 corporate district intensity, I use `corp_count` as the proxy (not scope).
But for landmark building placement, I need to identify reach-wide HQ corps. A cleanup
pass on `scope` to normalize it to an enum (reach_wide | sector | system | local) would
be the right fix. Until then, the generator should treat any `scope` value that doesn't
exactly match a known enum string as `system`-tier.
---
## 6. Open Questions This Round Raises
The planet-down model introduces new questions that Round 3 didn't address:
**OQ-R4-B1: Who owns the latent settlement active/ghost flag?**
I've proposed that settlement active/ghost status is driven by the economic sim at
runtime. But the sim needs to know which settlements are latent and what conditions
activate them. Does this flag live:
- (a) In the generated world state as a sim-readable component on each settlement entity?
- (b) In a systems.db Phase 3 output table (settlement positions + trigger conditions)?
- (c) Computed entirely at runtime from corp presence + health?
Option (b) is cleanest — Phase 3 stores latent settlement positions and trigger
conditions, and the sim reads them alongside `corp_financial_state`. This integrates
naturally with the `generate_regional.py` pipeline.
**OQ-R4-B2: Does the "felled forests → farmland" change generate ChunkMutations?**
I argued above that this is a Layer 2 regional-scale change (large cells, biome-level),
not a Layer 4 tile mutation. But practically: when an agricultural corp expands and new
farmland appears, does this register as ChunkMutations in the save file, or as a biome
grid update to systems.db (requiring `make regen-db`)?
I lean toward: regional land use changes are coarse enough that they update a
`regional_land_use` column in the biome grid at runtime (not a full regen-db), and
the Layer 4 renderer reads the coarse grid to determine what large-scale tile type
fills undeveloped hinterland cells. This is not ChunkMutations — it's a different
resolution.
This needs an answer before Phase 3 and Phase 5 teams start building their respective
land-use systems with incompatible resolution assumptions.
**OQ-R4-B3: Does `prosperity_index` apply at district-level or block-level?**
In Round 3 I defined it at district level (one float per district). Under the
planet-down model, with topography-constrained zoning, a single district might span
dramatically different terrain (the industrial hillside vs. the commercial valley
floor). Should prosperity be a per-district scalar, or a per-block modifier?
Per-district is simpler and consistent with the Phase 5 architecture (DistrictSkeleton
has one `prosperity_index`). Per-block would enable richer decay gradients (a district
where prosperity collapses from the periphery inward). I recommend per-district for
now with a note that the field can be moved to BlockSkeleton in a later pass if the
single-value model proves too coarse.
**OQ-R4-B4: How does `distribution_index = stratified` interact with topography?**
In a stratified system, the wealth gradient is steep — executive district at 0.9,
worker housing at 0.15. In the planet-down model, topography often already encodes
wealth geography (hilltops are historically desirable; industrial lowlands are not).
Do we:
- (a) Let the topographic constraint drive the gradient independently of distribution_index?
- (b) Use `distribution_index` to set gradient magnitude and topography to set direction?
- (c) Only apply distribution_index when topography is flat (no natural gradient)?
Option (b) is my recommendation: topography tells us WHICH end is high-prosperity;
distribution_index tells us HOW STEEP the gradient is. A stratified city on a
hill has elite zones at the top; a moderate city on a hill has mixed zoning across
all elevations.
---
## Summary: Requirements for the Brief
The brief should specify:
1. **Economics enters weakly at Layer 1** (biome probability prior from economic_role —
optional but recommended) and **fully at Layer 2** (hinterland character, settlement
placement, infrastructure density).
2. **The determinism split is:** Layer 4 street/building layout is seed-locked.
Economic sim updates `prosperity_index` per district (render parameter, not tile
mutation). Regional land use (Layer 2) CAN update at biome-cell resolution.
3. **Dynamic settlements are latent at generation:** All economically plausible
settlement positions are placed seed-derived at Layer 2. Active/ghost status is
driven by economic sim. Ghost settlements exist spatially — they just look
abandoned.
4. **The 10×9 matrix applies at Layer 3** (City-Level Planning), modified by
topographic constraints from Layer 1 and infrastructure access from Layer 2.
The matrix provides the zoning prior; terrain provides the spatial constraints.
5. **The minimum economics read set for Layer 3** is the same 6-field set from
Round 3, now delivered via the `CityGenerationContext` struct rather than direct
DB queries at generation time. This is already captured in D-C12.
6. **`prosperity_index` is a Layer 3 output** (derived from economics data, stored
in CityGenerationContext / atlas_cities) and a **Layer 4 runtime parameter** (the
sim updates it; the renderer reads it for tile variant selection).
7. **Data quality pre-work:** `import_economics.py` should add a `behavioral_archetype`
backfill pass from commodity tier before `generate_regional.py` runs. `scope` field
normalization is recommended but not blocking.