Three-round design workshop (Miri, Burnelli-Sheldon, Paula; documented by Qatux, ticketed by SI) that produced D-237. Includes per-agent round outputs, round notes, the independent scout critique, and the final workshop-outcomes.md with the consolidated schema, 27-value economic vocabulary, cultural + faction vocabularies, deterministic-varied fallback spec, must-pin table, and CI guardrails. Clerk-Skip: workshop discussion docs, no D-record surface (D-237 filed separately) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
394 lines
24 KiB
Markdown
394 lines
24 KiB
Markdown
# Round 1 — Burnelli-Sheldon
|
||
## System Economic-Specialization Workshop
|
||
|
||
*Lens: economic credibility + field→commodity mapping + weighting model + integration pipeline + CI guardrails.*
|
||
|
||
---
|
||
|
||
## Q1 — The Field
|
||
|
||
**Proposed field: `dominant_commodity_id TEXT REFERENCES commodities(commodity_id)`**
|
||
|
||
A single foreign-key column on `system_economy` pointing directly into the `commodities` table. One per system. Authored lore-first; the heuristic is the fallback for unspecified systems.
|
||
|
||
### Why a commodity ID and not a category?
|
||
|
||
The field needs to resolve to `dominant_bulk_class × dominant_production_ubiquity` for D-233. The only clean source of that mapping is the commodity catalog itself — `bulk_class` and `production_ubiquity` are already on every commodity record. A separate "specialization category" field would require a second mapping table and would drift from the catalog over time. The FK is the mapping.
|
||
|
||
### The BulkClass × ProductionUbiquity projection
|
||
|
||
D-233 (refined 2026-05-26) already defines the 8→5 `BulkClass` projection from commodities.toml:
|
||
|
||
| `commodities.bulk_class` | → `BulkClass` (D-233) |
|
||
|---|---|
|
||
| `bulk` | `BulkSolid` |
|
||
| `standard` | `BulkSolid` |
|
||
| `compact` | `BulkSolid` |
|
||
| `oversized` | `BulkSolid` |
|
||
| `precision` | `PrecisionDense` |
|
||
| `liquid` | `BulkLiquid` |
|
||
| `perishable` | `Perishable` |
|
||
| `non_physical` | `NonPhysical` |
|
||
|
||
And the 5→4 `ProductionUbiquity` projection from commodities.toml:
|
||
|
||
| `commodities.production_ubiquity` | → `ProductionUbiquity` (D-233) |
|
||
|---|---|
|
||
| `ubiquitous` | `Ubiquitous` |
|
||
| `common` | `Common` |
|
||
| `regional` | `Common` (treated as common for built-form purposes) |
|
||
| `concentrated` | `Specialist` |
|
||
| `monopolistic` | `MonopolySource` |
|
||
|
||
`regional` collapses to `Common` because the built-form distinction is between broadly distributed infrastructure (Ubiquitous/Common) and concentrated infrastructure (Specialist/MonopolySource). Regional production produces the same physical plant pattern as common — dispersed operations, no single dominant facility.
|
||
|
||
### What this field is *not*
|
||
|
||
Services (`non_physical`) ARE a valid `dominant_commodity_id` choice. A financial center like Groombridge should pin to `financial_services`. This gives `BulkClass = NonPhysical`, which D-233 correctly resolves to dense office towers and civic halls (high coverage, 0.85–0.95 roofed fraction) with no operations surface. A service economy *looks* different from an extraction economy, as it should.
|
||
|
||
### Relationship to `economic_base_primary` / `economic_base_secondary`
|
||
|
||
**Do not replace. Coexist.** The existing `economic_base_primary/secondary` fields are free-text narrative prose — "grain agriculture and aquaculture" (Ran), "Commission clearing house and financial arbitration" (Groombridge). They belong to the GTTR voice and are not machine-readable. Keep them.
|
||
|
||
`dominant_commodity_id` is the machine-readable counterpart — one FK, vocabulary-constrained, CI-validated. The two fields serve different consumers (humans vs. the generator) and should stay separate. A mismatch between the prose and the FK is detectable by CI (W-SES-02, below) and flags inconsistency for human review.
|
||
|
||
### The vocabulary: all 36 commodities are valid
|
||
|
||
The complete vocabulary is the `commodity_id` column of `commodities`. Any of the 36 entries is valid:
|
||
|
||
**Raw materials** — meaningful for extraction-dominated systems that operate at raw scale (rare in practice; most systems export intermediates, but a frontier mining outpost can legitimately pin to `metallic_ore` or `lattice_grade_material`).
|
||
|
||
**Intermediates and finals** — the primary choice for most named systems. Ran → `agricultural_produce` or `processed_food` (both defensible; the question is whether Ran is mostly a raw exporter or a processed food exporter; the GTTR implies raw-to-intermediate, so `agricultural_produce`).
|
||
|
||
**Services** — valid for financial, legal, medical, and mixed-service systems. Groombridge → `financial_services`. The Sirius capital system → `commission_certification` or `legal_services`.
|
||
|
||
**The no-pin option:** `dominant_commodity_id = NULL` is permitted for uninhabited systems. Inhabited systems that are `NULL` trigger a CI hard error (V-SES-01).
|
||
|
||
---
|
||
|
||
## Q2 — Within-System Weighted Variation
|
||
|
||
The system `dominant_commodity_id` sets the prior. Bodies deviate from it through a weighted deterministic draw. The algorithm is static: `seed + static data only`. No PressureState, no tâtonnement, no runtime query.
|
||
|
||
### The candidate set (not a 36-commodity draw)
|
||
|
||
The draw operates over a small candidate set — usually 3–6 commodities — not all 36. Drawing over all 36 would produce economically incoherent results (why does a high-tech research moon in an agricultural system randomly get commission_certification?). The candidate set is:
|
||
|
||
1. **System anchor** — always included: `system.dominant_commodity_id`
|
||
2. **Role preferred** — 1–2 commodities per `economic_role` (table below)
|
||
3. **Planet-class preferred** — 0–2 commodities based on `bodies.planet_class`
|
||
4. **Corp HQ commodity** — 0–1 commodity if a corporation is HQ'd here, via `corp_presence.primary_operation` → commodity lookup
|
||
|
||
### Weight assignment (integer, D-010 compliant)
|
||
|
||
All weights are integer basis points. No floats in the derivation path.
|
||
|
||
| Signal | Weight added | To which commodity |
|
||
|---|---|---|
|
||
| System anchor | `W_SYS = 10_000` | `system.dominant_commodity_id` |
|
||
| Body `economic_role` preferred | `W_ROLE = 5_000` | Role's preferred commodity (table below) |
|
||
| Body `economic_role` secondary | `W_ROLE_SEC = 2_000` | Role's secondary commodity if any |
|
||
| Body `planet_class` preferred | `W_PLANET = 3_000` | Planet-class preferred commodity |
|
||
| Body `planet_class` secondary | `W_PLANET_SEC = 1_000` | Planet-class secondary |
|
||
| Corp HQ present | `W_CORP = 4_000` | Corp's `primary_operation` commodity |
|
||
|
||
**Role → preferred commodity mapping (fallback heuristic):**
|
||
|
||
| `economic_role` | Primary preferred | Secondary preferred |
|
||
|---|---|---|
|
||
| `agricultural` | `agricultural_produce` | `processed_food` |
|
||
| `extraction` | `metallic_ore` | `rare_minerals` |
|
||
| `manufacturing` | `refined_metals` | `advanced_alloys` |
|
||
| `financial` | `financial_services` | `insurance` |
|
||
| `service_mixed` | `legal_services` | `medical_services` |
|
||
| `research` | `electronics` | `lattice_substrate` |
|
||
| `transit_hub` | `fusion_fuel` | `refined_metals` |
|
||
| `institutional` | `commission_certification` | `legal_services` |
|
||
| `military` | `heavy_equipment` | `advanced_alloys` |
|
||
| `residential` | `processed_food` | `agricultural_produce` |
|
||
|
||
**Planet class → preferred commodity mapping (fallback heuristic):**
|
||
|
||
| `planet_class` | Primary preferred | Secondary preferred |
|
||
|---|---|---|
|
||
| `terrestrial` | `agricultural_produce` | `metallic_ore` |
|
||
| `rocky` / `barren` | `metallic_ore` | `stone` |
|
||
| `icy` / `ice_giant` | `water` | `fusion_fuel` |
|
||
| `gas_giant` | `fusion_fuel` | — |
|
||
| `ocean` | `agricultural_produce` | `organic_compounds` |
|
||
| `volcanic` | `metallic_ore` | `rare_minerals` |
|
||
| `desert` | `stone` | `metallic_ore` |
|
||
| (station/orbital) | *(no planet bonus)* | — |
|
||
|
||
These are heuristics — they apply only when `dominant_commodity_id` is not pinned to override.
|
||
|
||
### Weighted draw algorithm (deterministic)
|
||
|
||
```
|
||
candidate_weights: HashMap<commodity_id, u64> = {}
|
||
|
||
// Add system anchor
|
||
candidate_weights[system.dominant_commodity_id] += W_SYS
|
||
|
||
// Add role-preferred
|
||
role_preferred = ROLE_MAP[body.economic_role]
|
||
candidate_weights[role_preferred.primary] += W_ROLE
|
||
if role_preferred.secondary:
|
||
candidate_weights[role_preferred.secondary] += W_ROLE_SEC
|
||
|
||
// Add planet class
|
||
planet_preferred = PLANET_MAP[body.planet_class]
|
||
if planet_preferred.primary:
|
||
candidate_weights[planet_preferred.primary] += W_PLANET
|
||
if planet_preferred.secondary:
|
||
candidate_weights[planet_preferred.secondary] += W_PLANET_SEC
|
||
|
||
// Add corp HQ if present
|
||
if corp_hq_commodity != NULL:
|
||
candidate_weights[corp_hq_commodity] += W_CORP
|
||
|
||
// Weighted draw (integer arithmetic only — D-010)
|
||
total_weight = sum(candidate_weights.values())
|
||
seed = SeedChain(world_seed)
|
||
.derive(SeedDomain::Layer3Settlement, fnv1a_64(body_id))
|
||
.seed()
|
||
rng = AtlasRng::new(splitmix64(seed))
|
||
draw = rng.next_u64() % total_weight
|
||
// Walk the candidate list (sorted by commodity_id for determinism)
|
||
// to find the winner
|
||
```
|
||
|
||
The resulting commodity is looked up in the commodity catalog for `bulk_class` → `BulkClass` and `production_ubiquity` → `ProductionUbiquity`.
|
||
|
||
### The farming moon in a foundry system (worked example)
|
||
|
||
System: `dominant_commodity_id = advanced_alloys` (BulkSolid, Specialist — an alloying hub).
|
||
|
||
Moon: `economic_role = agricultural`, `planet_class = terrestrial`, no corp HQ.
|
||
|
||
Weight allocation:
|
||
- `advanced_alloys` ← 10_000 (system anchor)
|
||
- `agricultural_produce` ← 5_000 (role primary) + 3_000 (planet class) = 8_000
|
||
- `processed_food` ← 2_000 (role secondary)
|
||
- `metallic_ore` ← 1_000 (planet class secondary)
|
||
|
||
Total weight: 21_000.
|
||
|
||
Draw probabilities: `advanced_alloys` 47.6%, `agricultural_produce` 38.1%, `processed_food` 9.5%, `metallic_ore` 4.8%.
|
||
|
||
The moon **is** a farming moon about 38% of seeds. The system still reads as a foundry system because most bodies (those without strongly competing role signals) resolve to `advanced_alloys`. The farming moon exists; it is not the dominant read.
|
||
|
||
### Why W_SYS = 10_000 specifically
|
||
|
||
This ratio is calibrated so that a body with *two strong competing signals* (both role AND planet class pointing away from the system anchor) still resolves to the system anchor ~33% of the time. That threshold keeps system identity legible: a 5-body system will usually have 3–4 bodies expressing the system character. If W_SYS were lower (e.g., 5_000), too many bodies would deviate, destroying the authored read. If higher (e.g., 15_000), the minority body (the farming moon) becomes nearly impossible, which is also wrong.
|
||
|
||
These ratios can be tuned in a single named-constant block without touching algorithm logic. They are NOT authored per-system.
|
||
|
||
---
|
||
|
||
## Q3 — `economy_size`?
|
||
|
||
**Out. Don't add it.**
|
||
|
||
This is the first Q where I'll be direct: `economy_size` doesn't earn its slot because it is substantially covered by fields that already exist.
|
||
|
||
### What economy_size would modify vs. what already exists
|
||
|
||
The proposed modifications for `economy_size` are:
|
||
1. **Coverage density** — already modulated by `density_class` (D-220), which is derived from `population` and `economic_role`.
|
||
2. **Count of specialized blocks** — already modulated by settlement footprint size, which is derived from `population` and `prosperity_baseline` (D-197).
|
||
3. **Labor draw for residential follow-on** — already set by `BulkClass` extraction multiplier (D-233).
|
||
|
||
Every function that `economy_size` would provide is already covered. Adding it introduces a third authoritative source for the same signal, which creates inconsistency problems:
|
||
|
||
- If `economy_size = major` but `population = 50_000`, which wins?
|
||
- If `economy_size = minor` but `prosperity_baseline = 0.85` (high prosperity), do we compress the settlement?
|
||
|
||
The answers require tiebreaker rules, which are complexity with no payoff.
|
||
|
||
### The genuine case it might address
|
||
|
||
The one scenario where `economy_size` might seem necessary: a low-population monopoly extraction site that should have massive industrial infrastructure. A lattice_grade_material mine with 10,000 workers that ships to the entire galaxy.
|
||
|
||
This is already handled:
|
||
- `production_ubiquity = monopolistic` → `MonopolySource` → D-233 concentration rule: "MonopolySource concentrates contiguous block groups (the mine *is* the settlement, everything else is support)." The infrastructure is there.
|
||
- `prosperity_baseline` for an extraction site with corp HQ presence will be elevated.
|
||
- `density_class` for an extraction chokepoint with economic pull is Dense (D-220: "extraction chokepoints... pull people in faster").
|
||
|
||
A 10,000-person monopoly mine reads correctly under the existing system: low population, high density (terrain-constrained + economic pull), MonopolySource concentration, elevated prosperity (corp-funded infrastructure). No `economy_size` field required.
|
||
|
||
### Verdict
|
||
|
||
`economy_size` is redundant with `population × prosperity_baseline`. If those two values are incorrectly authored for a system (e.g., the system wiki says it's a major economic hub but the population is set to 50K), that's an authoring error to fix — not a signal to add a third field that masks it.
|
||
|
||
---
|
||
|
||
## Q4 — The Content Pass: Field→Commodity Mapping + CI Guardrails + Integration
|
||
|
||
### Source location
|
||
|
||
New TOML file: **`wiki/economics/system_specialization.toml`**
|
||
|
||
One entry per system that has been authored. Uninhabited and unspecified systems have no entry (CI will hard-error if an inhabited system has no entry after the pass is complete).
|
||
|
||
```toml
|
||
# wiki/economics/system_specialization.toml
|
||
# Source of truth for authored dominant commodity per star system.
|
||
# Compiled to system_economy.dominant_commodity_id via import_economics.py.
|
||
# FK: must exist in wiki/economics/commodities.toml
|
||
#
|
||
# Decisions: D-233, D-184, this workshop (re-amends D-233)
|
||
|
||
[GJ-144] # Ran — core agricultural breadbasket
|
||
dominant_commodity_id = "agricultural_produce"
|
||
|
||
[GJ-273] # Groombridge — Assembly clearing house
|
||
dominant_commodity_id = "financial_services"
|
||
|
||
[GJ-15A] # Sirius — Commission/Assembly capital
|
||
dominant_commodity_id = "commission_certification"
|
||
|
||
# ... ~300 systems total
|
||
```
|
||
|
||
One line per system. The comment is optional documentation for human reviewers. The TOML key is `system_id` (matching the `star_systems.system_id` column).
|
||
|
||
### Database integration
|
||
|
||
Add one column to `system_economy` in `systems-schema.sql`:
|
||
|
||
```sql
|
||
ALTER TABLE system_economy ADD COLUMN
|
||
dominant_commodity_id TEXT REFERENCES commodities(commodity_id);
|
||
```
|
||
|
||
Add to `COLUMN_MIGRATIONS` in `import_economics.py` (idempotent ALTER TABLE).
|
||
|
||
New import step in `import_economics.py`:
|
||
|
||
```python
|
||
def import_system_specialization(conn, dry_run=False):
|
||
"""Read wiki/economics/system_specialization.toml and upsert
|
||
dominant_commodity_id into system_economy for authored systems."""
|
||
# Read TOML
|
||
# Validate FK against commodity catalog (V-SES-01, V-SES-02)
|
||
# UPSERT into system_economy
|
||
# Log coverage report
|
||
```
|
||
|
||
Source file added to `IMPORT_ECONOMICS_SOURCES` for meta stamp tracking.
|
||
|
||
### CI guardrails
|
||
|
||
**Hard errors (build aborts):**
|
||
|
||
| ID | Rule | Rationale |
|
||
|---|---|---|
|
||
| V-SES-01 | Every system with `bodies.inhabited > 0` AND `system_economy.population > 0` MUST have `dominant_commodity_id` after the content pass is marked complete. Before the pass is complete: warn only. | Guarantees generator has authored data for every city it generates. |
|
||
| V-SES-02 | `dominant_commodity_id` must exist in `commodities(commodity_id)` | FK integrity — typos produce silent fallbacks otherwise. |
|
||
| V-SES-03 | Systems where `dominant_commodity_id` maps to a service commodity (`tier IN ('service_professional', 'service_luxury')`) but whose `bodies` are all non-inhabited or uninhabited MUST be flagged | Services require population to be plausible. A services-dominant system with no bodies makes no sense. |
|
||
|
||
**Soft warnings (build succeeds, prints):**
|
||
|
||
| ID | Rule | Rationale |
|
||
|---|---|---|
|
||
| W-SES-01 | `production_ubiquity = 'monopolistic'` systems: print list for human review | Monopoly pins are load-bearing and easy to over-assign. Lore must support the monopoly claim (D-177). |
|
||
| W-SES-02 | Systems where `economic_base_primary` (prose) contains words that don't match the commodity name/description of `dominant_commodity_id` | Contradiction detection between narrative prose and the machine field. Not a hard error — authors may intentionally be more specific in prose. |
|
||
| W-SES-03 | `dominant_commodity_id = water OR agricultural_produce` for named, non-frontier systems | Water and agricultural produce are ubiquitous. A named core system claiming water as its identity is probably wrong (every system produces water). Exception: a system whose entire identity is regional food export may legitimately pin to `agricultural_produce`. Flag for review, don't abort. |
|
||
| W-SES-04 | More than 20% of inhabited systems pinned to the same `dominant_commodity_id` | A balanced economy needs diversity. If 60 systems all claim `refined_metals`, the catalog entry is being used as a generic default, not an authored identity. |
|
||
|
||
### Coverage report (new output from import_economics.py)
|
||
|
||
After the import, print:
|
||
```
|
||
system_specialization coverage:
|
||
authored: NNN / MMM inhabited systems (NN%)
|
||
by BulkClass: BulkSolid=NN, BulkLiquid=NN, Perishable=NN, PrecisionDense=NN, NonPhysical=NN
|
||
by ProductionUbiquity: Ubiquitous=NN, Common=NN, Specialist=NN, MonopolySource=NN
|
||
MonopolySource systems: [list]
|
||
```
|
||
|
||
This gives the content team a live view of market structure as they author. A healthy distribution avoids over-concentration in any one BulkClass or ubiquity tier.
|
||
|
||
### Who authors what (the 300-system content pass)
|
||
|
||
**Miri**: Canonical commodity for every named/hero system from GTTR lore. The economic_base_primary prose is the input signal; Miri determines which of the 36 commodities best represents the system's authored identity. Source of truth on Ran (agricultural_produce), Groombridge (financial_services), the east reach lattice systems (lattice_grade_material), etc.
|
||
|
||
**Paula**: Faction homeworlds, Compact systems, and political capitals. A Compact homeworld's dominant commodity is not just economic — it's a political statement. The Mark-primary zone currency choice and the shadow economy intensity are correlated with the commodity choice (Compact: tends toward energy independence → fusion_fuel, or organic-based industries independent of Commission certification routes).
|
||
|
||
**Burnelli-Sheldon (me)**: Economic credibility review of all ~300 entries. Specifically:
|
||
1. **Monopoly-class check**: Does the lore actually support a `monopolistic` production_ubiquity pin? (D-177 constraints are the test — Kvitfjell marble, brach fiber, Calloway terroir, VGV varietals are the canon examples of genuine monopoly-class production. Assign monopolistic sparingly.)
|
||
2. **Distribution balance**: Check that the final distribution of commodity pins across 300 systems is plausible. An economy where 80% of systems are NonPhysical services is not credible.
|
||
3. **Field→BulkClass sanity**: Does the built form make sense? A financial-services dominant system should not look like a mine. If a pin produces an unexpected BulkClass (e.g., `water` → BulkLiquid for a city), flag for reconsideration.
|
||
4. **Heuristic fallback validation**: For uninhabited/unnamed systems that get the fallback heuristic (not authored), spot-check that the role+planet_class heuristic produces plausible results for representative cases.
|
||
|
||
### Authoring tool expectation
|
||
|
||
The content pass is a text editor + TOML file workflow. No specialized tool needed. The `make regen-db` build validates every entry against the commodity catalog. A companion query helps authors check consistency:
|
||
|
||
```bash
|
||
tooling/db/sqlite-query "
|
||
SELECT se.system_id, se.dominant_commodity_id,
|
||
c.bulk_class, c.production_ubiquity,
|
||
se.economic_base_primary
|
||
FROM system_economy se
|
||
JOIN commodities c ON c.commodity_id = se.dominant_commodity_id
|
||
WHERE c.production_ubiquity = 'monopolistic'
|
||
ORDER BY se.system_id"
|
||
```
|
||
|
||
---
|
||
|
||
## Q5 — Additional Identity Fields
|
||
|
||
The question is: while touching all ~300 systems by hand once, what other 1–2 fields meaningfully flesh out identity at marginal additional authoring cost? The guard is explicit: every field is authoring burden ×300 and a maintenance surface.
|
||
|
||
### My recommendation: ONE additional field
|
||
|
||
**`atlas_body_trait_bias` hero pins** (D-232) — this is the architecture-flavor anchor for hero bodies (~30–40). It is technically a per-body field, not a per-system field, but it is authored during the same pass since the hero body is the system's primary reading body.
|
||
|
||
This earns its slot on one condition: **it is authored ONLY for hero bodies, not for all 300 systems.** The ~270 non-hero bodies run the algorithm with no bias (D-232 is explicit: "Non-hero bodies run the identical algorithm with no bias"). So the authoring burden is ~30–40 entries (one per hero body), not 300.
|
||
|
||
From my economic-credibility lens, the trait pin is justified because:
|
||
- D-232's hard-gate economics (bulk_class, prosperity, production_ubiquity) already constrain the template pool. The pin selects within the economically-valid set, not outside it.
|
||
- The wrong architectural read on a canonical system is expensive — it cannot be corrected by a post-launch patch without breaking determinism (D-010).
|
||
- The trait catalog is designed with pin support (`pin` counts toward K, K=1 for Minimal complexity tier).
|
||
|
||
**However**: the actual `atlas_body_trait_bias` pins are Miri + Araminta territory (cultural meaning + visual bundle). I'll defer to them on which ~30–40 bodies get a pin. My role is to confirm that the economic eligibility rules don't need to be overridden to support the intended templates.
|
||
|
||
### Fields I recommend against
|
||
|
||
**`economy_size`** — eliminated in Q3.
|
||
|
||
**`trade_orientation` (export/import/self-sufficient)** — Derivable from `dominant_commodity_id` × `production_ubiquity`. A `MonopolySource` system is obviously export-dominant. A `NonPhysical` services system is self-contained (services don't traverse gates). An `Ubiquitous` raw material system may import more than it exports (since ubiquitous goods have thin margins and don't drive trade specialization). Authoring this separately introduces a second signal that can contradict the FK. Don't add it.
|
||
|
||
**`political_register`** — Paula's domain. Plausibly valuable for faction-political characterization, but I should not advocate for fields outside my lane. I will say only that from an economic integration perspective, the political register would not touch the commodity-building pipeline at all — it would be consumed by NPC generation and dialogue, not by the generator. That makes it zero authoring burden overlap with the commodity pass.
|
||
|
||
**`shadow_economy_intensity`** — D-174 explicitly defines this as derived from inputs (Commission presence, Compact membership, hop distance, gate topology), not authored per-system. The derivation model is correct. Don't replace it with an authored field; the emergent calculation is the point.
|
||
|
||
### Summary of recommended field set
|
||
|
||
| Field | Scope | Authoring burden | Earns slot? |
|
||
|---|---|---|---|
|
||
| `dominant_commodity_id` | Per system (system_economy) | ~300 entries | **YES — primary deliverable** |
|
||
| `atlas_body_trait_bias` pins | Per hero body (~30–40) | ~30–40 entries | **YES — but limited to hero bodies only** |
|
||
| `economy_size` | Per system | ~300 entries | **NO — redundant** |
|
||
| `trade_orientation` | Per system | ~300 entries | **NO — derivable** |
|
||
| `political_register` | Per system | ~300 entries | **OUT OF LANE — Paula opines** |
|
||
|
||
---
|
||
|
||
## Integration summary
|
||
|
||
The proposed change to the existing pipeline is minimal:
|
||
|
||
1. **`wiki/economics/system_specialization.toml`** — new authored source file. One `dominant_commodity_id` per inhabited system.
|
||
2. **`systems-schema.sql`** — add `dominant_commodity_id TEXT REFERENCES commodities(commodity_id)` to `system_economy`.
|
||
3. **`import_economics.py`** — add `import_system_specialization()` step, five new CI validation rules, coverage report output.
|
||
4. **`city_context_reader.rs`** — replace the two `#982 design-blocked` stub fields with a DB query that reads the commodity catalog join, runs the weighted draw algorithm, and populates `dominant_bulk_class` + `dominant_production_ubiquity`.
|
||
5. **`MIGRATION_SQL`** in `import_economics.py` — `ALTER TABLE system_economy ADD COLUMN dominant_commodity_id TEXT`.
|
||
|
||
The existing `economic_base_primary/secondary` fields, the `economic_role` field on `bodies`, and the `system_factions` data are **all preserved and unchanged**. The new field plugs into existing signals; it does not replace them.
|
||
|
||
Everything is static: read at build time into `systems.db`, resolved at generation-dispatch time by `CityContextReader`, no PressureState, no tâtonnement, no save-state. The per-body weighted draw uses `SeedChain(world_seed, body_id)` — deterministic, D-010 compliant, re-derivable from seed + static inputs alone.
|