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>
31 KiB
title, description, type, status, workshop, agent, round, created
| title | description | type | status | workshop | agent | round | created |
|---|---|---|---|---|---|---|---|
| Round 2 — Burnelli-Sheldon: Economics Algorithm Proposals | Full specification of the three-component district mix model, attractor-matching compatibility matrix, latent settlement generalization under fully generative placement, and TerritorialStatus threshold convergence. | workshop | active | planet-down-cascade | burnelli-sheldon | 2 | 2026-05-01 |
Round 2 — Burnelli-Sheldon: Economics Algorithm Proposals
This document provides concrete algorithm specifications for the four economics assignments from the Round 1 notes. No positions are hedged — these are proposals for convergence.
1. Three-Component District Mix Model — Full Specification
1.1 Component 1: Population Tier Guarantees
The population tier guarantees establish a mandatory district set that is allocated before the economic role weight table distributes remaining slots. These are derived from the sub-settlement hierarchy in the brief, mapped to the D-C4 district count formula.
Guaranteed district set by population tier:
| Sub-settlement tier | Population | Guaranteed types | Notes |
|---|---|---|---|
| Waypoint | < 50 | 1× Mixed | All functions fused into one district |
| Outpost | 50–999 | Residential + Mixed | Two functional zones |
| Town | 1,000–9,999 | Residential + Commercial | Entertainment present inside Commercial character |
| City | 10,000–99,999 | Residential + Commercial + Entertainment | Dedicated entertainment justified by population |
| Large City | 100,000+ | Residential + Commercial + Entertainment + Administrative | Bureaucratic complexity requires dedicated admin |
BS-Q1 resolution (9,999-person Town without dedicated Entertainment): Confirmed plausible. A Town's Commercial district contains the entertainment function — the pub, the social hall, the gambling den — as a character property of that district. When the settlement grows to City tier (10,000+), specialization makes a dedicated Entertainment district economically viable. The function always exists; the dedicated district is the marker of scale.
WorldTier exception — domed/cave settlements: Always 1 district regardless of population. Single district type = Mixed, carrying all functions.
Capital city bonus: +1 district slot (as per D-C4 rule) distributed by the weight table after guarantees are placed.
When D-C4 count is less than the guaranteed set size:
D-C4 can produce fewer total district slots than the guaranteed set requires. When this occurs, the overflow guaranteed types collapse into Mixed:
if D_total < len(guaranteed_set):
collapse_to_mixed():
1 district if D_total == 1 → type = Mixed (all functions)
2 districts if D_total == 2 → Residential + Mixed (remaining functions)
This correctly handles outposts with D-C4 = 1 that the formula would label Passage-tier.
1.2 Component 2: Economic Role Multiplier Table
The weight table distributes the remaining district slots after the guaranteed set is placed. It also applies to the guaranteed set as a character modifier for which variant of each type the settlement gets — but it does not remove guaranteed types.
No weight is ever zero. Minimum value is 3 (not 0.2 as I wrote in Round 1 — I converted to integer weights summing to 100 per row for implementation clarity). This means every district type has some non-zero probability of appearing in any settlement's non-guaranteed slots, while the character differences between economic roles remain pronounced.
Full weight table (integer weights, each row sums to 100):
| 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 |
How to read this table:
The mining row has Log = 28, Ind = 22 as dominant weights. After placing guaranteed Residential + Commercial (at Town) or + Entertainment (at City), remaining slots almost always produce Industrial and Logistics districts. Entertainment weight = 6 means occasionally a second Entertainment appears in a large mining city — a second drinking district for a city that grew big enough to support it. This is correct.
The energy row has Spe = 24, Log = 24 as dominant. Energy installations need Specialized facilities (reactors, control centers, research wings) and Logistics (heavy transport for fuel and output). Entertainment = 3 and Commercial = 3 are minimal but non-zero — large energy cities have a company canteen and a gym, which earns each a district representation at high enough population.
BS-Q3 resolution (energy Entertainment weight): At Outpost/Town scale (1-2 districts), energy settlements don't get a dedicated Entertainment slot from the weight table — Entertainment is present inside the Mixed/Commercial district instead. At City scale (10,000+), the guaranteed Entertainment takes effect, producing a dedicated district with rough-canteen character. The weight = 3 only applies to additional Entertainment districts beyond the guarantee, which are rare. This is the correct behavior.
Political archetype modifiers stacked on top: These remain as described in the brief. They're additive modifiers to the weight table row before normalization:
CompanyTown: Adm -= 10, Ind += 10, Log += 5, Res += 5 (production-heavy, thin governance)
AdminCapital: Adm += 20, Ent += 5, Spe += 5, Ind -= 15 (governance-heavy, civic services)
FreePort: Com += 15, Trn += 10, Mix += 5, Adm -= 15 (commerce-heavy, light governance)
Contested: no modifier (both political forces fight over what to build; neither wins)
OrganicGrowth: Mix += 15, Res += 10, Ind -= 10, Adm -= 5 (unplanned, service-led growth)
1.3 Component 3: Settlement Age Modifier
Where founding_age_years comes from:
For named cities: bodies.founding_age_years (INTEGER, already in the bodies schema).
For sub-settlements without a direct bodies row, derive from parent body age:
| Sub-settlement type | Derived age |
|---|---|
| Mining camp | max(5, parent_body.founding_age_years - 20) |
| Trade waypoint | max(5, parent_body.founding_age_years / 3) |
| Agricultural node | max(5, parent_body.founding_age_years - 10) |
| Shadow node | 10 (always nascent — informal, lacks permanence by definition) |
BS-Q2 resolution: Age modifier applies only at Backwater tier and above. Waypoint and Passage settlements are too structurally simple to differentiate by age; they get nascent character regardless of founding date.
Age bracket classification:
| founding_age_years | Age bracket | Name |
|---|---|---|
| 0–49 | nascent | Raw production focus |
| 50–149 | young | Functional |
| 150–349 | established | Differentiated |
| 350+ | mature | Layered |
What age bracket affects:
Age modifies the character class of each district. This propagates to three
DistrictSkeleton fields:
perimeter_treatment: nascent → Open; young → Fenced; established → Walled; mature → variable (can be any)density_pctof commercial/entertainment blocks: nascent = 0.4–0.6; young = 0.5–0.7; established = 0.6–0.8; mature = 0.6–0.9 (wide range — old cities have both dense cores and thin outskirts)- Content system character selector: nascent entertainment = "bare supply function"; mature entertainment = "institutional legacy"
What age bracket does NOT affect: District count. District type. Prosperity baseline. These are determined by Components 1 and 2 alone.
Age × WorldTier cap interaction:
effective_age_bracket =
if world_tier in (Waypoint, Passage): nascent # always
elif founding_age_years < 50: nascent
elif founding_age_years < 150: young
elif founding_age_years < 350: established
else: mature
A 400-year-old Waypoint is still nascent. A 10-year-old Epicenter city is still nascent. The cap ensures structural simplicity wins over chronological age for small settlements.
1.4 The Combined Algorithm — Full Formula
Algorithm: compute_district_distribution(city, body, system, terrain)
Inputs (all from systems.db or session DB Layer 1-2 output):
city.population, city.world_tier, city.political_archetype
body.economic_role, body.founding_age_years, body.settlement_pattern
system.economic_tier, system.distribution_index
terrain.topographic_gradient_direction # from Layer 1 output
seed = child_seed(world_seed, city_id) # SeedChain
Returns: Vec<DistrictSpec>
where DistrictSpec = { type: DistrictType, prosperity_baseline: f32,
character_class: CharacterClass, density_pct: f32,
perimeter_treatment: PerimeterTreatment }
──────────────────────────────────────────────────────────────
Step 1: Total district count
D_raw = max(1, floor(city.population / 50_000)) # log-scaled for pop > 500k
D_total = min(D_raw, WorldTier_cap[city.world_tier])
if city.is_capital: D_total = min(D_total + 1, WorldTier_cap[city.world_tier])
if body.settlement_pattern in (domed, cave): D_total = 1
Step 2: Mandatory districts from population tier
mandatory_types = guaranteed_set(city.population, city.world_tier)
# Apply D_total cap: if len(mandatory_types) > D_total, collapse to Mixed
mandatory = place_guaranteed_types(mandatory_types, D_total)
remaining_slots = D_total - len(mandatory)
# NOTE: computation is purely local — NO queries about neighboring cities.
# Each settlement is computed independently from its own fields and seed.
Step 3: Fill remaining slots from economic role weight table
if remaining_slots > 0:
weights = ROLE_WEIGHT_TABLE[body.economic_role] # 9-element, all > 0
weights = apply_archetype_modifiers(weights, city.political_archetype)
weights = renormalize_to_100(weights)
# Sample without replacement — can produce duplicate types (second Ind, second Log)
additional_types = seeded_weighted_sample(weights, remaining_slots, seed)
all_types = mandatory + additional_types
else:
all_types = mandatory
Step 4: Assign age character class
age_bracket = effective_age_bracket(body.founding_age_years, city.world_tier)
for d in all_types:
d.character_class = AGE_CHARACTER_TABLE[d.type][age_bracket]
d.density_pct = age_density_range(d.type, d.character_class)[seeded_float(seed)]
d.perimeter_treatment = age_perimeter(d.type, age_bracket)
Step 5: Assign prosperity gradients
base_prosperity = economic_tier / 5.0 + ROLE_PROSPERITY_MODIFIER[body.economic_role]
gradient_direction = terrain.topographic_gradient_direction # high point = high prosperity
gradient_magnitude = DISTRIBUTION_INDEX_SCALE[system.distribution_index]
# Paula's topographic modifier is additive to the base, direction set by terrain:
# hilltop district (top 30% elevation): +0.05
# flood-adjacent district (bottom 20% vs. sea level): -0.05
for (i, d) in enumerate(all_types):
positional_rank = rank_in_gradient_direction(d.grid_position, gradient_direction, len(all_types))
topo_modifier = topo_offset(d, terrain)
d.prosperity_baseline = clamp(
base_prosperity + (positional_rank - 0.5) × gradient_magnitude + topo_modifier,
0.05, 0.95
)
──────────────────────────────────────────────────────────────
ROLE_PROSPERITY_MODIFIER (from L3-Q5 brief formula):
extraction: -0.10
research: +0.15
service_mixed: +0.10
frontier: -0.20
(all others: 0.00)
DISTRIBUTION_INDEX_SCALE:
"stratified": 0.7 (steep gradient — 0.7 × positional rank ± 0.35 spread)
"moderate": 0.2 (shallow gradient — ≈ Gaussian around base, σ = 0.1)
Self-contained confirmation: The algorithm references only local city/body/system fields. There are no lookups into neighboring city data, no distance calculations to adjacent settlements, no regional density queries. Each city generates its district distribution independently from its seed and the fields above. This satisfies the SeedChain isolation property from D-C10.
2. Attractor-Matching: Full Compatibility Matrix
2.1 The Compatibility Matrix
Scale 0–10 where 0 = incompatible/excluded and 10 = preferred/ideal match. Geographic attractor types: the seven tags from the brief (Section 1, Layer 1 minimum viable) plus two additional tags identified by Paula (CoastalLowland, RiverValley) that emerged in Round 1 discussion.
| economic_role | RiverConfl | CoastalHarbor | MtnPass | ArablePlain | ResourceConc | Defensible | NaturalBarrier | CoastalLowland | RiverValley |
|---|---|---|---|---|---|---|---|---|---|
| agricultural | 8 | 5 | 2 | 10 | 1 | 2 | 0 | 7 | 8 |
| extraction | 3 | 3 | 5 | 2 | 10 | 3 | 1 | 2 | 3 |
| mining | 2 | 2 | 6 | 1 | 10 | 4 | 1 | 1 | 2 |
| transit | 9 | 9 | 10 | 3 | 1 | 3 | 0 | 5 | 7 |
| commercial | 8 | 9 | 6 | 4 | 2 | 2 | 0 | 6 | 7 |
| research | 4 | 4 | 4 | 4 | 6 | 8 | 3 | 3 | 4 |
| manufacturing | 7 | 6 | 3 | 5 | 5 | 2 | 0 | 6 | 6 |
| service_mixed | 6 | 6 | 4 | 6 | 2 | 3 | 0 | 6 | 6 |
| frontier | 5 | 5 | 5 | 4 | 5 | 6 | 3 | 4 | 4 |
| energy | 4 | 5 | 3 | 2 | 8 | 4 | 2 | 4 | 3 |
2.2 Forbidden Combinations (score = 0)
These are not just low-probability — they are implausible enough that the generator should flag them for review rather than place silently:
| Economic role | Forbidden attractor | Reason |
|---|---|---|
| agricultural | NaturalBarrier | No farming in impassable terrain |
| transit | NaturalBarrier | Transit hubs do not sit at physical barriers — they use gaps in them (MtnPass) |
| commercial | NaturalBarrier | Commerce requires accessible flow of goods and people |
| manufacturing | NaturalBarrier | Heavy industry requires accessible logistics |
| service_mixed | NaturalBarrier | Services require customers who can reach them |
Note on NaturalBarrier: This attractor type is a settlement exclusion zone for most roles. The only roles with non-zero scores there are extraction (1), mining (1), research (3), frontier (3), energy (2) — all roles whose economic activities specifically exploit inaccessibility or operate in remote terrain.
2.3 Preferred Combinations (score = 9–10)
These are the "inevitable placements" Ozzie flagged — a player seeing a transit hub at a mountain pass should feel the placement was obvious:
| Economic role | Preferred attractor | Score | Narrative |
|---|---|---|---|
| transit | MountainPass | 10 | The pass is the reason the city exists |
| transit | CoastalHarbor | 9 | Port = transit nexus |
| transit | RiverConfluence | 9 | River junction = historic trade/ferry crossing |
| agricultural | ArablePlain | 10 | The land is the reason for the city |
| extraction | ResourceConcentration | 10 | The deposit is the reason for the city |
| mining | ResourceConcentration | 10 | Same |
| commercial | CoastalHarbor | 9 | Ports = commercial centers historically |
2.4 Full Attractor-Matching Algorithm (reconciled from four Round 1 proposals)
The four Round 1 proposals (Gestalt, Tyre, Burnelli-Sheldon, Paula) are structurally equivalent; they differ in ordering and handling edge cases. This is the convergence:
Algorithm: attractor_matching(cities, attractors, body, systems_db, seed)
──────────────────────────────────────────────────────────────
Phase 0: Build score matrix and validate HQ constraints
For each city_i and attractor_j:
S[i,j] = compat_score(city_i.economic_role, attractor_j.type)
× quality_bonus(attractor_j)
× population_weight(city_i.population)
where:
quality_bonus = 1.2 if attractor_j is highest-quality of its type on this body
population_weight = 1.0 + min(log10(city_i.population / 1000) × 0.10, 0.30)
HQ hard constraints (corp cross-references in systems.db):
for each corp with headquarters_body = this body:
matching_city = city with matching name in atlas_city_names
compatible_attractors = attractors where compat_score ≥ 6
if no compatible_attractor exists:
FLAG for lead review: "[city_name] has no compatible attractor for [corp_role]"
assign to best available attractor (highest score, even below 6)
else:
force S[matching_city, incompatible_attractor] = 0 for all incompatible
──────────────────────────────────────────────────────────────
Phase 1: Priority ordering
Sort cities into tiers:
Tier A: cities with HQ corp cross-reference (locked, must-place)
Tier B: remaining named cities, sorted by population descending
Tier C: unnamed cities (population budget, sorted by population descending)
──────────────────────────────────────────────────────────────
Phase 2: Assign Tier A cities (hard constraints first — Paula's ordering)
For each corp in extraction-order (most geographically constrained roles first):
economic role constraint order: extraction → mining → energy → agricultural
→ manufacturing → transit → commercial → research
→ service_mixed → frontier
Assign each Tier A city to its highest-scoring available compatible attractor.
Mark attractor as consumed.
──────────────────────────────────────────────────────────────
Phase 3: Assign Tier B and Tier C cities (Hungarian algorithm)
Build sub-matrix S' for unassigned cities × remaining attractors.
Solve maximum-weight bipartite matching.
O(N³) where N ≤ 30: negligible compute.
Derive FoundingOrientation from assigned attractor_type:
RiverConfluence → RiverCrossing
CoastalHarbor → PortFacing
MountainPass → PassControl
ArablePlain → AgriculturalExpanse
ResourceConc. → ExtractionTether
Defensible → DefenseFacing
NaturalBarrier → (fortress pattern within MountainPass or Defensible)
CoastalLowland → AgriculturalExpanse (coastal variant)
RiverValley → RiverCrossing (valley variant)
──────────────────────────────────────────────────────────────
Phase 4: Overflow handling
If N_cities > M_attractors after Phase 3:
For each unmatched city (sorted by population descending):
Place synthetic attractor:
position = seed_derived offset from nearest NameLocked settlement
distance ∈ [15%, 40%] × body_scale
biased toward nearest road corridor (if road graph available)
type = nearest_attractor.type
(inherits the dominant attractor character of the region)
geographically_triggered = false
FoundingOrientation = AdminFacing (Paula's OrganicGrowth resolution)
──────────────────────────────────────────────────────────────
Phase 5: Produce SettlementRecord per city
settlement_class = NameLocked if Tier A
= PopulationBudget if Tier B/C, geographically_triggered = true
= OrganicGrowth if overflow, geographically_triggered = false
Ozzie's constraint enforced: Any placement that triggers a compat_score = 0 (forbidden combination) is blocked at Phase 0. The generator cannot silently place a fishing port in the mountains — it either finds a compatible attractor or flags for lead review. This satisfies the "feels inevitable, not arbitrary" requirement.
3. Latent Settlements Under Fully Generative Placement
3.1 What Changed
Under the old model, wiki-authored cities were always active. Sub-settlements were latent. Under Amendment 3, ALL settlements are generator-placed. The latent concept must generalize.
The key economic insight: latency tracks economic justification, not authoring method. A settlement is latent if its continued existence depends on economic conditions that can change. A settlement is non-latent if it exists for structural reasons that don't change with economics (a mountain pass town exists because the pass exists; that doesn't change).
3.2 Settlement Classes and Active Status Logic
Introduce a SettlementClass enum on all generated settlement records:
pub enum SettlementClass {
/// Placed by HQ corp cross-reference. The corp's HQ exists as long as the
/// corp exists. Active if corp health_metric > 0.0 (any presence).
NameLocked,
/// Placed by population budget — geographic attractor exists but name is
/// generator-assigned. Active if nearest NameLocked settlement is economically
/// healthy (its sponsoring corp health > 0.4). Becomes ghost if the anchor corp
/// collapses and no other corp moves in.
PopulationBudget,
/// Sub-settlements placed by economic trigger conditions:
/// mining camps, trade waypoints, agricultural nodes.
/// Active if triggering corp health_metric > 0.4.
EconomicTriggered,
/// Overflow settlements placed at Province centroids without geographic attractor.
/// Active if provincial avg corp health > 0.5 (needs regional economic density).
OrganicGrowth,
}
Why NameLocked is not always-active: If the corp dissolves entirely
(corp_lifecycle_events.event_type = Dissolved), the HQ city loses its economic
anchor. The city doesn't vanish spatially (the streets and buildings remain), but
it becomes a ghost: dark, unmaintained, repopulated by scavengers and squatters.
The physical city persists; the economic life drains out. This is the only case where
a NameLocked settlement becomes ghost.
Why this is stronger than the old latent concept: The old model had named cities as unconditionally active and sub-settlements as optionally latent. The new model correctly identifies that a city's activity is always economically contingent — it just has different trigger conditions by class. The full active/ghost spectrum applies to all settlement types.
3.3 Schema Addition
Add settlement_class and geographically_triggered to the generated settlement
records in BodyWorldState:
pub struct GeneratedSettlement {
pub city_id: String,
pub position: (f32, f32), // body-local normalized coords
pub founding_orientation: FoundingOrientation,
pub settlement_class: SettlementClass,
pub geographically_triggered: bool, // Paula's NEW-Q2 disambiguation flag
pub activating_corp: Option<String>, // corp_id for EconomicTriggered
pub activating_condition: Option<String>, // description for OrganicGrowth
}
geographically_triggered = false is the OrganicGrowth disambiguation flag Paula
identified. It answers "was this settlement placed because of a geographic attractor
or because of population pressure alone?" FoundingOrientation = AdminFacing when false.
3.4 The Latent Settlement Table in Systems.db
My Round 1 recommendation was option (b): store latent settlement positions in a
systems.db Phase 3 output table. Under Amendment 1 (three-tier execution), this shifts:
latent settlement positions are runtime background generation output stored in session DB
(as part of BodyWorldState), not systems.db.
The economic SIM still reads from systems.db for its aggregate calculations (corp_presence, corp_financial_state). It does not need the spatial settlement positions to compute economics — it just needs to emit health_metric updates that the renderer reads to determine active/ghost status per settlement.
The handoff: The sim emits corp_health_changed(corp_id, new_metric) events.
The Layer 3-4 generator, when processing a city, queries: is the activating corp's
health_metric above or below the threshold for this settlement class? This query is
a single lookup into the economic sim's current state. No full re-generation is needed.
4. TerritorialStatus Economic Thresholds — Convergence Proposal
The Round 1 notes show three proposals (Paula, Tyre, Burnelli-Sheldon). This section proposes convergence thresholds that satisfy all three agents' requirements.
Note on Province dimensions: Tyre's ARCH-1 through ARCH-4 blockers include confirmation of Province scale. The thresholds below use Province-relative quantities (fraction of Province cells) to remain valid regardless of absolute Province dimensions. When ARCH-1 is resolved, the fractions convert to concrete cell counts.
4.1 Convergence Table
| TerritorialStatus | Threshold | Economic signal |
|---|---|---|
| CoreTerritory | ≥ 2 named settlements (City or Town) within Province boundaries AND road_coverage_fraction ≥ 0.55 across Province cells AND ≥ 1 active corp (health_metric ≥ 0.5) with presence in Province | Economically integrated, actively maintained |
| FrontierTerritory | ≥ 1 settlement (any tier) in Province AND (road_coverage_fraction < 0.55 OR road_quality < 0.5 at Province boundary edges) | Settled but underdeveloped; economic reach doesn't fill the Province |
| ExtractiveZone | ≥ 1 ResourceConcentration geographic feature tag in Province AND ≥ 1 corp_presence with extraction/mining primary_operation commodity AND road corridor from resource site to nearest City (even if narrow) | Productive but not balanced; the corridor is the territory |
| ContestZone | ≥ 2 settlements from different political_archetype types within Province AND overlapping MaintenanceAuthority on shared road segments (or road networks crossing Province internal boundary without single authority) | Competing claims with no resolution |
| WildernessBuffer | 0 settlements placed + 0 roads + no ResourceConcentration tag | Untouched; no economic vector yet |
| AbandonedZone | ≥ 1 settlement with placed_at_generation = true in Province AND 0 active settlements at current sim evaluation (all occupying corps have health_metric < 0.2) |
Was settled; economic basis collapsed |
4.2 What Changed vs. Round 1 (My Position)
-
CoreTerritory: Added "≥ 1 active corp with health_metric ≥ 0.5" as a third condition. My Round 1 threshold was purely structural (settlement count + road maintenance). Paula's version required the economic health signal. She's right — a ghost city Province with intact roads but zero active corps is not CoreTerritory; it's AbandonedZone.
-
FrontierTerritory: Adopted Paula's road quality threshold at Province edges. My Round 1 version was vague about what "road density < 0.5" meant. Road quality < 0.5 at Province boundary is more testable.
-
ContestZone: Adopted Tyre's "two CoreTerritory zones from different political archetypes overlapping" framing as the primary test. My Round 1 threshold (competing road networks) is now a diagnostic signal rather than the definition.
-
AbandonedZone: Added Paula's
placed_at_generation = trueflag as the necessary condition to distinguish "abandoned" from "never settled." My Round 1 version would mis-classify a WildernessBuffer Province where a corp briefly established presence and then left; the flag provides the correct disambiguation.
4.3 Runtime Derivation
TerritorialStatus can be re-derived at runtime from the economic simulation's state without re-running the Layer 1-2 generator. The inputs are:
settlement_active_stateper settlement (from SettlementClass + corp health query)road_coverage_fractionper Province (from Layer 2 session cache — does not change)corp_presence+corp_financial_state.health_metric(from economic sim)
This means TerritorialStatus is a render parameter like prosperity_current — it
reflects the current economic state and updates when the sim changes, but it does not
trigger a layout regeneration. The Province boundaries don't move; only the status label
changes.
5. Open Positions and Requests for Other Agents
5.1 BS-Q1 — Final Resolution (no further input needed)
A 9,999-person Town without a dedicated Entertainment district is economically correct. Entertainment is functional inside the Commercial district at Town scale. Gestalt validated this in Round 1 notes (no objection to the threshold).
5.2 Topographic Modifier to prosperity_baseline (Paula + Burnelli-Sheldon)
Paula proposed a topographic modifier (+0.05 hilltop, -0.05 flood-adjacent). This is included in the formula at Step 5 of §1.4 as an additive offset on the base gradient. No incompatibility with my gradient direction/magnitude split. Confirmed compatible.
5.3 Gestalt: Character table feedback needed
The age character classes in §1.3 (AGE_CHARACTER_TABLE) propagate to DistrictSkeleton
fields (perimeter_treatment, density_pct). I need Gestalt to confirm whether these
fields are sufficient to express the character differences, or whether additional fields
on DistrictSkeleton are needed. Specifically: does a "nascent entertainment district"
vs. a "mature entertainment district" need a named field, or does density_pct + prosperity
alone carry it?
5.4 Tyre: ARCH-3 schema question
The attractor-matching algorithm requires atlas_city_names with fields:
body_id, city_name, economic_role, population, hq_for_corp_id (nullable).
Does this replace or extend atlas_cities? My recommendation: extend with additional
columns. atlas_cities currently stores authored pixel positions (which become obsolete
under Amendment 3) plus name + population + kind (which are still needed). The new
columns are: hq_for_corp_id (nullable FK to corporations) and assigned_attractor_id
(nullable, written by Layer 2 generator to session DB, not systems.db).
5.5 Threshold for "abandoned body" condition on NameLocked settlements
My SettlementClass model has NameLocked cities becoming ghost when their founding corp
dissolves. I need confirmation from Paula: does corp dissolution in the economics sim
produce a corp_lifecycle_events.event_type = Dissolved record? The rendering system
needs a way to read this state. Alternatively: corp_financial_state.health_metric = 0.0
as a proxy. Which is the correct signal to read?