Compare commits
6
Commits
8eb33551cf
...
v0.1.32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4028f71f2f | ||
|
|
a6c286735e | ||
|
|
f80cbe621e | ||
|
|
571a708d4a | ||
|
|
4a3febbe29 | ||
|
|
bcf15354d2 |
@@ -62,18 +62,23 @@ Pre-existing warnings are not PR blockers but should be tracked for cleanup.
|
||||
|
||||
### 1. Determine the branch to review
|
||||
|
||||
If the user provided a branch name as argument, use it. Otherwise list open
|
||||
PRs and ask the user which branch to review.
|
||||
**Always start fresh.** Even if you reviewed this branch before in this
|
||||
conversation, the branch may have new commits, a new PR, or main may have
|
||||
moved. Do NOT skip steps or reuse earlier results. Every `/pr-review`
|
||||
invocation is a full review cycle.
|
||||
|
||||
**Always fetch and check for PRs first:**
|
||||
|
||||
To list open PRs on Gitea:
|
||||
```bash
|
||||
git fetch --all
|
||||
tea pr list --login schweitz --repo jpmschweitzer/settled-reach --state open --output simple
|
||||
```
|
||||
|
||||
Fetch remote branches first:
|
||||
```bash
|
||||
git fetch --all
|
||||
```
|
||||
Then determine the branch:
|
||||
- If the user provided a branch name as argument, match it to an open PR.
|
||||
If a PR exists for that branch, note the PR number. If no PR exists,
|
||||
proceed with the branch diff but note "no PR found" in the output.
|
||||
- If no argument was given, list open PRs and ask which to review.
|
||||
|
||||
### 2. Determine reviewer team
|
||||
|
||||
|
||||
@@ -6,7 +6,11 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.1.32] — 2026-04-06
|
||||
|
||||
### Added
|
||||
- Economics schema extension (#804): 5 new tables in systems.db (gate_links, commodities, production_chains, chain_inputs, corp_presence) + currency_zone and corporation archetype columns
|
||||
- Economics import pipeline (`tooling/economy-db/import_economics.py`): reads TOML/JSON source data, populates systems.db. Idempotent via `make economy-db`
|
||||
- Planet generator pipeline (`tooling/planet-gen/`) — procedural terrain simulation, Whittaker biome classification, equirectangular heightmap + 512px globe rendering from wiki data
|
||||
- Externalized biome/color configuration (`tooling/planet-gen/biomes.toml`) — single source for all classification tables, palettes, and rendering parameters
|
||||
- Batch runner with error handling, resume support, determinism verification, and 50% error rate circuit breaker
|
||||
@@ -29,6 +33,7 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
- Sprint worktree creation no longer spawns a spurious "None" team tab when tickets have no team assigned
|
||||
|
||||
### Changed
|
||||
- Renamed `commission_certified` → `commission_certifiable` across all TOML, schema, and decision files — flag is a susceptibility marker, not an absolute state
|
||||
- Pre-push hook now validates JSON syntax on changed files (python3 -m json.tool)
|
||||
- HUD status panel: merged TimeDisplay into ImplantPanel — time, health, perception in one themed panel (#786)
|
||||
- HUD moved from UILayer (20) to InsertOverlay (10) for bloom treatment per D-049
|
||||
|
||||
@@ -3,7 +3,7 @@ GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null)
|
||||
.PHONY: help setup build check-protocol client server game stop test lint lint-python setup-venv ci ci-client ci-server clean \
|
||||
decisions-sync decisions-coverage decisions-active decisions-orphan \
|
||||
db-backup db-install validate-content check-fact-ids setup-hooks \
|
||||
audit atlas-verify \
|
||||
audit atlas-verify economy-db \
|
||||
pre-pr pre-pr-lint pre-pr-build pre-pr-test pre-pr-validate pre-pr-fixtures \
|
||||
pre-pr-server pre-pr-client pre-pr-content \
|
||||
fixtures-client fixtures-gauntlet golden-diff golden-update \
|
||||
@@ -53,6 +53,7 @@ help:
|
||||
@echo " make validate-content Validate content YAML against schemas"
|
||||
@echo " make check-fact-ids Check fact_id references against knowledge catalogs"
|
||||
@echo " make atlas-verify Verify atlas proposal JSONs (all in docs/atlas/proposals/)"
|
||||
@echo " make economy-db Import economics data into systems.db (TOML/JSON → SQLite)"
|
||||
@echo " make fixtures-client Generate GDScript->Rust cross-encoder fixtures (#475)"
|
||||
@echo " make golden-diff Show diff if golden file output has changed"
|
||||
@echo " make golden-update Regenerate golden file and stage for commit"
|
||||
@@ -323,6 +324,9 @@ db-backup:
|
||||
db-install:
|
||||
@tooling/db-install
|
||||
|
||||
economy-db: ## Import economics data (commodities, chains, gate links) into systems.db
|
||||
@python3 tooling/economy-db/import_economics.py
|
||||
|
||||
# --- Decisions ---
|
||||
|
||||
decisions-sync:
|
||||
|
||||
@@ -238,7 +238,7 @@ This domain covers: currency system, commodity taxonomy, shadow economy, corpora
|
||||
- **3 luxury services** — tourism, entertainment/holo-content, fine dining/hospitality; location-bound
|
||||
- **Source of truth:** `wiki/economics/commodities.toml` and `wiki/economics/production_chains.toml`
|
||||
- 21 production chains including 2 substitution routes
|
||||
- Schema: 14 fields per commodity including 3 political sub-flags (`commission_certified`, `compact_contested`, `shadow_viable`), `production_ubiquity`, `demand_model`, `panic_threshold_weeks`
|
||||
- Schema: 14 fields per commodity including 3 political sub-flags (`commission_certifiable`, `compact_contested`, `shadow_viable`), `production_ubiquity`, `demand_model`, `panic_threshold_weeks`
|
||||
- **Rationale:** The catalog is the concrete realization of the D-173 taxonomy. 36 types is rich enough to produce meaningful geographic specialization while remaining authorable. The 14-field schema captures economic, political, and behavioral properties needed by the tâtonnement simulation. Two substitution routes prevent hard lock-outs in chains where one raw source is geographically constrained.
|
||||
- **Raised by:** Full planning team, Sprint 32 Workshop #801.
|
||||
- **Dissent:** None.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# unit tonnes | units | contracts
|
||||
# production_ubiquity ubiquitous | common | regional | concentrated | monopolistic
|
||||
# demand_model market | utility | compliance
|
||||
# commission_certified Requires Commission certification for formal sale
|
||||
# commission_certifiable Requires Commission certification for formal sale
|
||||
# compact_contested Compact contests Commission authority over this
|
||||
# shadow_viable Shadow market routinely exists
|
||||
# panic_threshold_weeks Stockpile level (in weeks) triggering panic hoarding; 0 = no panic
|
||||
@@ -35,7 +35,7 @@ bulk_class = "bulk"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -50,7 +50,7 @@ bulk_class = "precision"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -65,7 +65,7 @@ bulk_class = "precision"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "monopolistic"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = false
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -80,7 +80,7 @@ bulk_class = "bulk"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -95,7 +95,7 @@ bulk_class = "perishable"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -110,7 +110,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 1
|
||||
@@ -125,7 +125,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -140,7 +140,7 @@ bulk_class = "bulk"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -155,7 +155,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -176,7 +176,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -191,7 +191,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -206,7 +206,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "utility"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 2
|
||||
@@ -221,7 +221,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 2
|
||||
@@ -236,7 +236,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -251,7 +251,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 3
|
||||
@@ -266,7 +266,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -281,7 +281,7 @@ bulk_class = "standard"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -296,7 +296,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -311,7 +311,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = false
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -332,7 +332,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -347,7 +347,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -362,7 +362,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -377,7 +377,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -392,7 +392,7 @@ bulk_class = "precision"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -407,7 +407,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -422,7 +422,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "monopolistic"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -437,7 +437,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -452,7 +452,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -473,7 +473,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -488,7 +488,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -503,7 +503,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -518,7 +518,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -533,7 +533,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "compliance"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -554,7 +554,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -569,7 +569,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -584,7 +584,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
|
||||
@@ -140,7 +140,7 @@ implant_hardware gate_components
|
||||
(cert+contested+shadow) (cert+contested+shadow, monopolistic)
|
||||
```
|
||||
|
||||
Both terminals are commission_certified + compact_contested + shadow_viable. A lattice_grade_material shortage disrupts both at once. The chemicals dependency adds a second, less geographic chokepoint.
|
||||
Both terminals are commission_certifiable + compact_contested + shadow_viable. A lattice_grade_material shortage disrupts both at once. The chemicals dependency adds a second, less geographic chokepoint.
|
||||
|
||||
Politically, this means: the Compact's shadow market for implant_hardware is structurally linked to the lattice_grade_material supply chain, which is east-reach anchored. Disrupting east-reach exports affects the formal market everywhere and the shadow market in the Compact zone.
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ lattice_substrate → gate_components (with advanced_alloys + drive_cores)
|
||||
Assessment:
|
||||
- **Viable.** `lattice_grade_material` is the only monopolistic raw in the catalog. East_reach holds the only source. Everything downstream of lattice_substrate is gated on east_reach supply.
|
||||
- `implant_hardware` and `gate_components` are both high-value finals requiring lattice_substrate. This gives east_reach structural importance to two critical civilizational systems (neural tech and wormhole infrastructure).
|
||||
- `commission_certified = true` and `shadow_viable = true` on `lattice_grade_material` correctly models the east_reach political tension — state-sanctioned scarcity creates black market pressure.
|
||||
- `commission_certifiable = true` and `shadow_viable = true` on `lattice_grade_material` correctly models the east_reach political tension — state-sanctioned scarcity creates black market pressure.
|
||||
- The double-path through lattice (implants + gates) means east_reach supply disruption has cascading effects in two independent final-good chains simultaneously. This is good design; it makes east_reach conflict economically legible.
|
||||
|
||||
---
|
||||
|
||||
@@ -35,7 +35,7 @@ CREATE TABLE commodities (
|
||||
demand_model TEXT NOT NULL CHECK(demand_model IN (
|
||||
'market', 'utility', 'compliance'
|
||||
)),
|
||||
commission_certified INTEGER NOT NULL DEFAULT 0,
|
||||
commission_certifiable INTEGER NOT NULL DEFAULT 0,
|
||||
compact_contested INTEGER NOT NULL DEFAULT 0,
|
||||
shadow_viable INTEGER NOT NULL DEFAULT 0,
|
||||
panic_threshold_weeks INTEGER NOT NULL DEFAULT 0 CHECK(panic_threshold_weeks >= 0),
|
||||
@@ -97,7 +97,7 @@ CREATE INDEX idx_chain_inputs_commodity ON chain_inputs(commodity_id);
|
||||
| `unit` | enum | yes | `tonnes`, `units`, `contracts` | quantity basis |
|
||||
| `production_ubiquity` | enum | yes | `ubiquitous`, `common`, `regional`, `concentrated`, `monopolistic` | B-S init guidance |
|
||||
| `demand_model` | enum | yes | `market`, `utility`, `compliance` | B-S demand floors |
|
||||
| `commission_certified` | bool | yes | true/false | Gestalt political |
|
||||
| `commission_certifiable` | bool | yes | true/false | Gestalt political |
|
||||
| `compact_contested` | bool | yes | true/false | Gestalt political |
|
||||
| `shadow_viable` | bool | yes | true/false | Gestalt political |
|
||||
| `panic_threshold_weeks` | int | yes | ≥ 0 | B-S panic mechanism |
|
||||
@@ -151,7 +151,7 @@ CREATE INDEX idx_chain_inputs_commodity ON chain_inputs(commodity_id);
|
||||
| V-10 | Services (`tier = service_*`) must NOT appear as chain inputs or outputs | D-173: services outside production chain |
|
||||
| V-11 | `panic_threshold_weeks >= 0` | Schema constraint |
|
||||
| V-12 | `demand_model = "utility"` requires `elasticity` ∈ {perfectly_inelastic, inelastic} | Utility demand can't be highly deferrable |
|
||||
| V-13 | `demand_model = "compliance"` requires `commission_certified = true` | Compliance demand requires a certifying authority |
|
||||
| V-13 | `demand_model = "compliance"` requires `commission_certifiable = true` | Compliance demand requires a certifying authority |
|
||||
| V-14 | Services must have `bulk_class = "non_physical"` | Services can't be transported |
|
||||
| V-15 | Services must have `unit = "contracts"` | Service unit convention |
|
||||
| V-16 | Commodity IDs must match pattern `[a-z][a-z0-9_]*` | Slug format for Rust/SQL |
|
||||
@@ -163,7 +163,7 @@ CREATE INDEX idx_chain_inputs_commodity ON chain_inputs(commodity_id);
|
||||
| W-01 | Every non-service physical commodity SHOULD appear as chain input or chain output | Orphan detection |
|
||||
| W-02 | Raw tier commodities SHOULD NOT appear as chain outputs | Raws are extracted, not produced |
|
||||
| W-03 | Final tier commodities SHOULD NOT appear as chain inputs | Finals are end-of-chain |
|
||||
| W-04 | `shadow_viable = true` SHOULD have `commission_certified = true` OR `compact_contested = true` | Shadow markets arise from regulatory friction |
|
||||
| W-04 | `shadow_viable = true` SHOULD have `commission_certifiable = true` OR `compact_contested = true` | Shadow markets arise from regulatory friction |
|
||||
| W-05 | `production_ubiquity = "monopolistic"` commodities with `elasticity = "inelastic"` SHOULD be reviewed | Monopoly + inelastic = potential runaway pricing |
|
||||
|
||||
### Coverage warnings (future — gated by corp data)
|
||||
|
||||
@@ -69,7 +69,7 @@ I'm merging the best from all four proposals. My Round 1 had 8 fields. The conve
|
||||
- `demand_model` — determines tâtonnement behavior: `market` (standard), `utility` (population-driven floor, never hits zero), `compliance` (regulation-driven, tracks Commission presence). This is what makes water/fuel behave differently from consumer goods under the same supply shock.
|
||||
|
||||
**From Gestalt (replaces my `political_sensitivity` boolean):**
|
||||
- `commission_certified` — requires Commission certification for formal sale. The cost of certification IS the shadow economy incentive.
|
||||
- `commission_certifiable` — requires Commission certification for formal sale. The cost of certification IS the shadow economy incentive.
|
||||
- `compact_contested` — Compact officially contests Commission authority. Creates the political friction.
|
||||
- `shadow_viable` — shadow market routinely exists. Flags where the shadow economy bites.
|
||||
|
||||
@@ -98,7 +98,7 @@ Three booleans instead of one gives the sim three independent political signals
|
||||
# unit — tonnes | units | contracts
|
||||
# production_ubiquity — ubiquitous | common | regional | concentrated | monopolistic
|
||||
# demand_model — market | utility | compliance
|
||||
# commission_certified — bool; requires Commission certification for formal sale
|
||||
# commission_certifiable — bool; requires Commission certification for formal sale
|
||||
# compact_contested — bool; Compact contests Commission authority over this
|
||||
# shadow_viable — bool; shadow market routinely exists for this commodity
|
||||
# description — optional flavor text (for wiki/UI, not consumed by sim)
|
||||
@@ -332,7 +332,7 @@ CREATE TABLE commodities (
|
||||
demand_model TEXT NOT NULL CHECK(demand_model IN (
|
||||
'market', 'utility', 'compliance'
|
||||
)),
|
||||
commission_certified INTEGER NOT NULL DEFAULT 0,
|
||||
commission_certifiable INTEGER NOT NULL DEFAULT 0,
|
||||
compact_contested INTEGER NOT NULL DEFAULT 0,
|
||||
shadow_viable INTEGER NOT NULL DEFAULT 0,
|
||||
description TEXT
|
||||
@@ -376,14 +376,14 @@ All Round 1 rules remain. Additions for new fields:
|
||||
| All `bulk_class` values must match enum (7 values now) | Expanded from 4 |
|
||||
| Services must have `bulk_class = "non_physical"` | Services can't be transported |
|
||||
| `demand_model = "utility"` commodities must have `elasticity` ∈ {perfectly_inelastic, inelastic} | Utility goods can't be highly elastic — people need them |
|
||||
| `demand_model = "compliance"` commodities must have `commission_certified = true` | Compliance demand requires a certifying authority |
|
||||
| `demand_model = "compliance"` commodities must have `commission_certifiable = true` | Compliance demand requires a certifying authority |
|
||||
| `fusion_fuel` must appear as an intermediate, not raw | Directive (c) constraint |
|
||||
|
||||
### New soft warnings
|
||||
|
||||
| Rule | Rationale |
|
||||
|------|-----------|
|
||||
| Commodities with `shadow_viable = true` SHOULD have `commission_certified = true` OR `compact_contested = true` | Shadow markets arise from regulatory friction — if there's no friction, why shadow? |
|
||||
| Commodities with `shadow_viable = true` SHOULD have `commission_certifiable = true` OR `compact_contested = true` | Shadow markets arise from regulatory friction — if there's no friction, why shadow? |
|
||||
| `production_ubiquity = "monopolistic"` commodities SHOULD have `elasticity` ∈ {elastic, highly_elastic} | Monopolistic supply + inelastic demand = runaway prices with no equilibrium |
|
||||
|
||||
---
|
||||
@@ -402,7 +402,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
description = "Water ice, atmospheric gases. Life support critical. Fusion fuel feedstock."
|
||||
@@ -420,7 +420,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "utility"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
description = "Deuterium/tritium refined from water. Low yield. Continuous consumption."
|
||||
@@ -438,7 +438,7 @@ bulk_class = "precision"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
description = "Neural interfaces, lattice components. Commission-certified. THE Compact flashpoint."
|
||||
@@ -478,7 +478,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "compliance"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
description = "Commission regulatory certification. Tractus-denominated. Availability inverse to shadow economy."
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name: The Settled Reach
|
||||
version: 0.1.31
|
||||
version: 0.1.32
|
||||
repository: settled-reach
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "settled-reach-server"
|
||||
version = "0.1.31"
|
||||
version = "0.1.32"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -38,6 +38,9 @@ CREATE TABLE IF NOT EXISTS star_systems (
|
||||
cultural_corridor TEXT,
|
||||
generation_priority TEXT,
|
||||
|
||||
-- Economics (D-172)
|
||||
currency_zone TEXT DEFAULT 'TRACTUS_PRIMARY', -- TRACTUS_PRIMARY | MARK_PRIMARY | MIXED
|
||||
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
@@ -201,9 +204,73 @@ CREATE TABLE IF NOT EXISTS corporations (
|
||||
specialization TEXT, -- primary product/service
|
||||
parent_corp TEXT REFERENCES corporations(corp_id),
|
||||
notes TEXT,
|
||||
|
||||
-- Economics (D-175)
|
||||
behavioral_archetype TEXT, -- Monopolist|Distributor|Producer|Specialist|Cooperative|Intermediary
|
||||
supply_chain_role TEXT,
|
||||
shadow_economy_access INTEGER DEFAULT 0,
|
||||
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Gate adjacency graph — bidirectional edges from star-map.json (D-178)
|
||||
-- Transport cost computation: 5–12% per hop (gate edge), 1–3% (orbital edge)
|
||||
CREATE TABLE IF NOT EXISTS gate_links (
|
||||
from_system_id TEXT NOT NULL REFERENCES star_systems(system_id),
|
||||
to_system_id TEXT NOT NULL REFERENCES star_systems(system_id),
|
||||
PRIMARY KEY (from_system_id, to_system_id)
|
||||
);
|
||||
|
||||
-- Commodity catalog — 36 types (D-184)
|
||||
-- Source: wiki/economics/commodities.toml
|
||||
CREATE TABLE IF NOT EXISTS commodities (
|
||||
commodity_id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
tier TEXT NOT NULL, -- raw|intermediate|final|service_professional|service_luxury
|
||||
elasticity TEXT NOT NULL, -- perfectly_inelastic|inelastic|unit_elastic|elastic
|
||||
base_price REAL NOT NULL,
|
||||
bulk_class TEXT, -- bulk|liquid|perishable|standard|compact|precision|oversized|non_physical
|
||||
unit TEXT, -- tonnes|units|contracts
|
||||
production_ubiquity TEXT, -- ubiquitous|common|regional|concentrated|monopolistic
|
||||
demand_model TEXT, -- market|utility|compliance
|
||||
commission_certifiable INTEGER DEFAULT 0, -- subject to certification in TRACTUS_PRIMARY zones
|
||||
compact_contested INTEGER DEFAULT 0,
|
||||
shadow_viable INTEGER DEFAULT 0,
|
||||
panic_threshold_weeks INTEGER DEFAULT 0,
|
||||
description TEXT,
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Leontief production chain recipes (D-178)
|
||||
-- Source: wiki/economics/production_chains.toml
|
||||
CREATE TABLE IF NOT EXISTS production_chains (
|
||||
chain_id TEXT PRIMARY KEY,
|
||||
output_commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
|
||||
output_quantity REAL NOT NULL DEFAULT 1.0,
|
||||
location_bound INTEGER DEFAULT 0,
|
||||
description TEXT,
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Input requirements per production chain
|
||||
CREATE TABLE IF NOT EXISTS chain_inputs (
|
||||
chain_id TEXT NOT NULL REFERENCES production_chains(chain_id),
|
||||
input_commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
|
||||
quantity REAL NOT NULL,
|
||||
PRIMARY KEY (chain_id, input_commodity_id)
|
||||
);
|
||||
|
||||
-- Corporation operating locations (D-175)
|
||||
-- Populated by corporation assignment pipeline, not by initial import
|
||||
CREATE TABLE IF NOT EXISTS corp_presence (
|
||||
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
|
||||
location_id TEXT NOT NULL, -- body_id or station_id
|
||||
location_type TEXT NOT NULL, -- body|station
|
||||
primary_operation TEXT,
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
PRIMARY KEY (corp_id, location_id)
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
-- astronomical_id removed: system_id IS the GJ catalog number
|
||||
CREATE INDEX IF NOT EXISTS idx_star_systems_sector ON star_systems(geographic_sector);
|
||||
@@ -224,3 +291,11 @@ CREATE INDEX IF NOT EXISTS idx_stations_orbits ON stations(orbits_body_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_stations_type ON stations(station_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_corps_system ON corporations(headquarters_system);
|
||||
CREATE INDEX IF NOT EXISTS idx_corps_type ON corporations(corp_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_star_systems_currency ON star_systems(currency_zone);
|
||||
CREATE INDEX IF NOT EXISTS idx_gate_links_from ON gate_links(from_system_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_gate_links_to ON gate_links(to_system_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_commodities_tier ON commodities(tier);
|
||||
CREATE INDEX IF NOT EXISTS idx_production_chains_output ON production_chains(output_commodity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_chain_inputs_commodity ON chain_inputs(input_commodity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_corp_presence_corp ON corp_presence(corp_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_corp_presence_location ON corp_presence(location_id);
|
||||
|
||||
Binary file not shown.
Executable
+387
@@ -0,0 +1,387 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Import economics data into systems.db.
|
||||
|
||||
Reads TOML/JSON source files and populates the economics tables:
|
||||
- gate_links from docs/design/star-map.json (335 edges, bidirectional)
|
||||
- commodities from wiki/economics/commodities.toml (36 types)
|
||||
- production_chains + chain_inputs from wiki/economics/production_chains.toml
|
||||
- currency_zone on star_systems (default TRACTUS_PRIMARY)
|
||||
|
||||
Does NOT populate corp_presence — that's a future pipeline step.
|
||||
|
||||
Usage:
|
||||
python3 tooling/economy-db/import_economics.py
|
||||
python3 tooling/economy-db/import_economics.py --dry-run
|
||||
python3 tooling/economy-db/import_economics.py --db path/to/systems.db
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sqlite3
|
||||
import sys
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
DB_PATH = REPO_ROOT / "server" / "data" / "systems.db"
|
||||
STAR_MAP = REPO_ROOT / "docs" / "design" / "star-map.json"
|
||||
COMMODITIES_TOML = REPO_ROOT / "wiki" / "economics" / "commodities.toml"
|
||||
CHAINS_TOML = REPO_ROOT / "wiki" / "economics" / "production_chains.toml"
|
||||
SCHEMA_SQL = REPO_ROOT / "server" / "data" / "systems-schema.sql"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Schema migration — add new tables and columns to existing DB
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
MIGRATION_SQL = """
|
||||
-- Economics tables (idempotent — safe to re-run)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS gate_links (
|
||||
from_system_id TEXT NOT NULL REFERENCES star_systems(system_id),
|
||||
to_system_id TEXT NOT NULL REFERENCES star_systems(system_id),
|
||||
PRIMARY KEY (from_system_id, to_system_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS commodities (
|
||||
commodity_id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
tier TEXT NOT NULL,
|
||||
elasticity TEXT NOT NULL,
|
||||
base_price REAL NOT NULL,
|
||||
bulk_class TEXT,
|
||||
unit TEXT,
|
||||
production_ubiquity TEXT,
|
||||
demand_model TEXT,
|
||||
commission_certifiable INTEGER DEFAULT 0,
|
||||
compact_contested INTEGER DEFAULT 0,
|
||||
shadow_viable INTEGER DEFAULT 0,
|
||||
panic_threshold_weeks INTEGER DEFAULT 0,
|
||||
description TEXT,
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS production_chains (
|
||||
chain_id TEXT PRIMARY KEY,
|
||||
output_commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
|
||||
output_quantity REAL NOT NULL DEFAULT 1.0,
|
||||
location_bound INTEGER DEFAULT 0,
|
||||
description TEXT,
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS chain_inputs (
|
||||
chain_id TEXT NOT NULL REFERENCES production_chains(chain_id),
|
||||
input_commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
|
||||
quantity REAL NOT NULL,
|
||||
PRIMARY KEY (chain_id, input_commodity_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS corp_presence (
|
||||
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
|
||||
location_id TEXT NOT NULL,
|
||||
location_type TEXT NOT NULL,
|
||||
primary_operation TEXT,
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
PRIMARY KEY (corp_id, location_id)
|
||||
);
|
||||
|
||||
-- New indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_star_systems_currency ON star_systems(currency_zone);
|
||||
CREATE INDEX IF NOT EXISTS idx_gate_links_from ON gate_links(from_system_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_gate_links_to ON gate_links(to_system_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_commodities_tier ON commodities(tier);
|
||||
CREATE INDEX IF NOT EXISTS idx_production_chains_output ON production_chains(output_commodity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_chain_inputs_commodity ON chain_inputs(input_commodity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_corp_presence_corp ON corp_presence(corp_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_corp_presence_location ON corp_presence(location_id);
|
||||
"""
|
||||
|
||||
# Columns to add to existing tables (ALTER TABLE is idempotent via try/except)
|
||||
COLUMN_MIGRATIONS = [
|
||||
("star_systems", "currency_zone", "TEXT DEFAULT 'TRACTUS_PRIMARY'"),
|
||||
("corporations", "behavioral_archetype", "TEXT"),
|
||||
("corporations", "supply_chain_role", "TEXT"),
|
||||
("corporations", "shadow_economy_access", "INTEGER DEFAULT 0"),
|
||||
]
|
||||
|
||||
|
||||
def _add_column(conn: sqlite3.Connection, table: str, col: str, col_type: str):
|
||||
"""Add a column if it doesn't exist. SQLite has no IF NOT EXISTS for ALTER."""
|
||||
try:
|
||||
conn.execute(f"ALTER TABLE {table} ADD COLUMN {col} {col_type}")
|
||||
except sqlite3.OperationalError as e:
|
||||
if "duplicate column" in str(e).lower():
|
||||
pass # already exists
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gate links
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def import_gate_links(conn: sqlite3.Connection, dry_run: bool) -> int:
|
||||
with open(STAR_MAP) as f:
|
||||
data = json.load(f)
|
||||
|
||||
edges = data["edges"]
|
||||
system_ids = {r[0] for r in conn.execute("SELECT system_id FROM star_systems").fetchall()}
|
||||
|
||||
rows = []
|
||||
skipped = []
|
||||
for a, b in edges:
|
||||
if a not in system_ids:
|
||||
skipped.append(a)
|
||||
continue
|
||||
if b not in system_ids:
|
||||
skipped.append(b)
|
||||
continue
|
||||
rows.append((a, b))
|
||||
rows.append((b, a))
|
||||
|
||||
if skipped:
|
||||
unique_skipped = sorted(set(skipped))
|
||||
print(f" warning: {len(unique_skipped)} system(s) in star-map.json not in DB: {unique_skipped[:5]}...")
|
||||
|
||||
if not dry_run:
|
||||
conn.executemany(
|
||||
"INSERT OR IGNORE INTO gate_links (from_system_id, to_system_id) VALUES (?, ?)",
|
||||
rows,
|
||||
)
|
||||
|
||||
return len(rows)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Commodities
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def import_commodities(conn: sqlite3.Connection, dry_run: bool) -> int:
|
||||
with open(COMMODITIES_TOML, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
|
||||
rows = []
|
||||
for cid, c in data.items():
|
||||
rows.append((
|
||||
cid,
|
||||
c["name"],
|
||||
c["tier"],
|
||||
c["elasticity"],
|
||||
c["base_price"],
|
||||
c.get("bulk_class"),
|
||||
c.get("unit"),
|
||||
c.get("production_ubiquity"),
|
||||
c.get("demand_model"),
|
||||
int(c.get("commission_certifiable", False)),
|
||||
int(c.get("compact_contested", False)),
|
||||
int(c.get("shadow_viable", False)),
|
||||
c.get("panic_threshold_weeks", 0),
|
||||
c.get("description"),
|
||||
))
|
||||
|
||||
if not dry_run:
|
||||
conn.executemany(
|
||||
"""INSERT INTO commodities (
|
||||
commodity_id, name, tier, elasticity, base_price,
|
||||
bulk_class, unit, production_ubiquity, demand_model,
|
||||
commission_certifiable, compact_contested, shadow_viable,
|
||||
panic_threshold_weeks, description
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
rows,
|
||||
)
|
||||
|
||||
return len(rows)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Production chains + inputs
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def import_chains(conn: sqlite3.Connection, dry_run: bool) -> tuple[int, int]:
|
||||
with open(CHAINS_TOML, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
|
||||
chain_rows = []
|
||||
input_rows = []
|
||||
for chain_id, c in data.items():
|
||||
chain_rows.append((
|
||||
chain_id,
|
||||
c["output"],
|
||||
c.get("output_quantity", 1.0),
|
||||
int(c.get("location_bound", False)),
|
||||
c.get("description"),
|
||||
))
|
||||
for inp in c.get("inputs", []):
|
||||
input_rows.append((
|
||||
chain_id,
|
||||
inp["commodity"],
|
||||
inp["quantity"],
|
||||
))
|
||||
|
||||
if not dry_run:
|
||||
conn.executemany(
|
||||
"""INSERT INTO production_chains (
|
||||
chain_id, output_commodity_id, output_quantity,
|
||||
location_bound, description
|
||||
) VALUES (?, ?, ?, ?, ?)""",
|
||||
chain_rows,
|
||||
)
|
||||
conn.executemany(
|
||||
"""INSERT INTO chain_inputs (
|
||||
chain_id, input_commodity_id, quantity
|
||||
) VALUES (?, ?, ?)""",
|
||||
input_rows,
|
||||
)
|
||||
|
||||
return len(chain_rows), len(input_rows)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Currency zones
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def set_currency_zones(conn: sqlite3.Connection, dry_run: bool) -> dict:
|
||||
"""Set currency_zone on star_systems. Default TRACTUS_PRIMARY, Sol = MIXED."""
|
||||
if dry_run:
|
||||
return {"TRACTUS_PRIMARY": "all", "MIXED": "GJ 0"}
|
||||
|
||||
# Default everything to TRACTUS_PRIMARY
|
||||
conn.execute("UPDATE star_systems SET currency_zone = 'TRACTUS_PRIMARY' WHERE currency_zone IS NULL")
|
||||
|
||||
# Sol system is MIXED (Earth legacy currency presence)
|
||||
conn.execute("UPDATE star_systems SET currency_zone = 'MIXED' WHERE system_id = 'GJ 0'")
|
||||
|
||||
# Future: Compact systems → MARK_PRIMARY (requires authored Compact membership data)
|
||||
|
||||
counts = {}
|
||||
for row in conn.execute("SELECT currency_zone, COUNT(*) FROM star_systems GROUP BY currency_zone"):
|
||||
counts[row[0]] = row[1]
|
||||
|
||||
return counts
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def validate(conn: sqlite3.Connection) -> list[str]:
|
||||
errors = []
|
||||
|
||||
# FK integrity
|
||||
fk_issues = conn.execute("PRAGMA foreign_key_check").fetchall()
|
||||
if fk_issues:
|
||||
for issue in fk_issues[:10]:
|
||||
errors.append(f"FK violation: table={issue[0]} rowid={issue[1]} "
|
||||
f"parent={issue[2]} fkid={issue[3]}")
|
||||
|
||||
# Chain inputs reference valid commodities
|
||||
orphan_inputs = conn.execute("""
|
||||
SELECT ci.chain_id, ci.input_commodity_id
|
||||
FROM chain_inputs ci
|
||||
LEFT JOIN commodities c ON ci.input_commodity_id = c.commodity_id
|
||||
WHERE c.commodity_id IS NULL
|
||||
""").fetchall()
|
||||
for chain_id, cid in orphan_inputs:
|
||||
errors.append(f"chain_inputs: chain '{chain_id}' references unknown commodity '{cid}'")
|
||||
|
||||
# Chain outputs reference valid commodities
|
||||
orphan_outputs = conn.execute("""
|
||||
SELECT pc.chain_id, pc.output_commodity_id
|
||||
FROM production_chains pc
|
||||
LEFT JOIN commodities c ON pc.output_commodity_id = c.commodity_id
|
||||
WHERE c.commodity_id IS NULL
|
||||
""").fetchall()
|
||||
for chain_id, cid in orphan_outputs:
|
||||
errors.append(f"production_chains: chain '{chain_id}' outputs unknown commodity '{cid}'")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Import economics data into systems.db")
|
||||
parser.add_argument("--db", default=str(DB_PATH), help="Path to systems.db")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Validate without writing")
|
||||
args = parser.parse_args()
|
||||
|
||||
db_path = Path(args.db)
|
||||
if not db_path.exists():
|
||||
print(f"error: {db_path} not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n Economics Import Pipeline")
|
||||
print(f" DB: {db_path}")
|
||||
if args.dry_run:
|
||||
print(f" Mode: DRY RUN")
|
||||
print()
|
||||
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
conn.execute("PRAGMA foreign_keys=ON")
|
||||
|
||||
# 1. Migrate schema
|
||||
print(" [1/5] Schema migration...")
|
||||
for table, col, col_type in COLUMN_MIGRATIONS:
|
||||
_add_column(conn, table, col, col_type)
|
||||
conn.executescript(MIGRATION_SQL)
|
||||
print(" tables and columns ready")
|
||||
|
||||
# Clear economics tables in FK-safe order (children before parents)
|
||||
if not args.dry_run:
|
||||
conn.execute("DELETE FROM chain_inputs")
|
||||
conn.execute("DELETE FROM production_chains")
|
||||
conn.execute("DELETE FROM commodities")
|
||||
conn.execute("DELETE FROM gate_links")
|
||||
|
||||
# 2. Gate links
|
||||
print(" [2/5] Importing gate links...")
|
||||
n_links = import_gate_links(conn, args.dry_run)
|
||||
print(f" {n_links} rows (bidirectional)")
|
||||
|
||||
# 3. Commodities
|
||||
print(" [3/5] Importing commodities...")
|
||||
n_commodities = import_commodities(conn, args.dry_run)
|
||||
print(f" {n_commodities} commodities")
|
||||
|
||||
# 4. Production chains
|
||||
print(" [4/5] Importing production chains...")
|
||||
n_chains, n_inputs = import_chains(conn, args.dry_run)
|
||||
print(f" {n_chains} chains, {n_inputs} inputs")
|
||||
|
||||
# 5. Currency zones
|
||||
print(" [5/5] Setting currency zones...")
|
||||
zones = set_currency_zones(conn, args.dry_run)
|
||||
for zone, count in sorted(zones.items()):
|
||||
print(f" {zone}: {count}")
|
||||
|
||||
# Validate
|
||||
print("\n Validating...")
|
||||
errors = validate(conn)
|
||||
if errors:
|
||||
print(f" ERRORS ({len(errors)}):")
|
||||
for e in errors:
|
||||
print(f" - {e}")
|
||||
conn.close()
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(" FK integrity OK")
|
||||
|
||||
if not args.dry_run:
|
||||
conn.commit()
|
||||
print("\n Committed.")
|
||||
else:
|
||||
print("\n Dry run — no changes written.")
|
||||
|
||||
conn.close()
|
||||
|
||||
print(f"\n Done: {n_links} gate_links, {n_commodities} commodities, "
|
||||
f"{n_chains} chains, {n_inputs} inputs\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -11,7 +11,7 @@
|
||||
# unit tonnes | units | contracts
|
||||
# production_ubiquity ubiquitous | common | regional | concentrated | monopolistic
|
||||
# demand_model market | utility | compliance
|
||||
# commission_certified Requires Commission certification for formal sale
|
||||
# commission_certifiable Requires Commission certification for formal sale
|
||||
# compact_contested Compact contests Commission authority over this
|
||||
# shadow_viable Shadow market routinely exists
|
||||
# panic_threshold_weeks Stockpile level (in weeks) triggering panic hoarding; 0 = no panic
|
||||
@@ -35,7 +35,7 @@ bulk_class = "bulk"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -50,7 +50,7 @@ bulk_class = "precision"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -65,7 +65,7 @@ bulk_class = "precision"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "monopolistic"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = false
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -80,7 +80,7 @@ bulk_class = "bulk"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -95,7 +95,7 @@ bulk_class = "perishable"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -110,7 +110,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 1
|
||||
@@ -125,7 +125,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -140,7 +140,7 @@ bulk_class = "bulk"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -155,7 +155,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -176,7 +176,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -191,7 +191,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -206,7 +206,7 @@ bulk_class = "liquid"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "utility"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 2
|
||||
@@ -221,7 +221,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "utility"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 2
|
||||
@@ -236,7 +236,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -251,7 +251,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 3
|
||||
@@ -266,7 +266,7 @@ bulk_class = "standard"
|
||||
unit = "tonnes"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -281,7 +281,7 @@ bulk_class = "standard"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -296,7 +296,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -311,7 +311,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = false
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -332,7 +332,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -347,7 +347,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -362,7 +362,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -377,7 +377,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "ubiquitous"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -392,7 +392,7 @@ bulk_class = "precision"
|
||||
unit = "units"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -407,7 +407,7 @@ bulk_class = "compact"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -422,7 +422,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "monopolistic"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -437,7 +437,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -452,7 +452,7 @@ bulk_class = "oversized"
|
||||
unit = "units"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -473,7 +473,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -488,7 +488,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -503,7 +503,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = true
|
||||
panic_threshold_weeks = 0
|
||||
@@ -518,7 +518,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -533,7 +533,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "compliance"
|
||||
commission_certified = true
|
||||
commission_certifiable = true
|
||||
compact_contested = true
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -554,7 +554,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "concentrated"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -569,7 +569,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "common"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
@@ -584,7 +584,7 @@ bulk_class = "non_physical"
|
||||
unit = "contracts"
|
||||
production_ubiquity = "regional"
|
||||
demand_model = "market"
|
||||
commission_certified = false
|
||||
commission_certifiable = false
|
||||
compact_contested = false
|
||||
shadow_viable = false
|
||||
panic_threshold_weeks = 0
|
||||
|
||||
@@ -35,7 +35,7 @@ CREATE TABLE commodities (
|
||||
demand_model TEXT NOT NULL CHECK(demand_model IN (
|
||||
'market', 'utility', 'compliance'
|
||||
)),
|
||||
commission_certified INTEGER NOT NULL DEFAULT 0,
|
||||
commission_certifiable INTEGER NOT NULL DEFAULT 0,
|
||||
compact_contested INTEGER NOT NULL DEFAULT 0,
|
||||
shadow_viable INTEGER NOT NULL DEFAULT 0,
|
||||
panic_threshold_weeks INTEGER NOT NULL DEFAULT 0 CHECK(panic_threshold_weeks >= 0),
|
||||
@@ -97,7 +97,7 @@ CREATE INDEX idx_chain_inputs_commodity ON chain_inputs(commodity_id);
|
||||
| `unit` | enum | yes | `tonnes`, `units`, `contracts` | quantity basis |
|
||||
| `production_ubiquity` | enum | yes | `ubiquitous`, `common`, `regional`, `concentrated`, `monopolistic` | B-S init guidance |
|
||||
| `demand_model` | enum | yes | `market`, `utility`, `compliance` | B-S demand floors |
|
||||
| `commission_certified` | bool | yes | true/false | Gestalt political |
|
||||
| `commission_certifiable` | bool | yes | true/false | Gestalt political |
|
||||
| `compact_contested` | bool | yes | true/false | Gestalt political |
|
||||
| `shadow_viable` | bool | yes | true/false | Gestalt political |
|
||||
| `panic_threshold_weeks` | int | yes | ≥ 0 | B-S panic mechanism |
|
||||
@@ -151,7 +151,7 @@ CREATE INDEX idx_chain_inputs_commodity ON chain_inputs(commodity_id);
|
||||
| V-10 | Services (`tier = service_*`) must NOT appear as chain inputs or outputs | D-173: services outside production chain |
|
||||
| V-11 | `panic_threshold_weeks >= 0` | Schema constraint |
|
||||
| V-12 | `demand_model = "utility"` requires `elasticity` ∈ {perfectly_inelastic, inelastic} | Utility demand can't be highly deferrable |
|
||||
| V-13 | `demand_model = "compliance"` requires `commission_certified = true` | Compliance demand requires a certifying authority |
|
||||
| V-13 | `demand_model = "compliance"` requires `commission_certifiable = true` | Compliance demand requires a certifying authority |
|
||||
| V-14 | Services must have `bulk_class = "non_physical"` | Services can't be transported |
|
||||
| V-15 | Services must have `unit = "contracts"` | Service unit convention |
|
||||
| V-16 | Commodity IDs must match pattern `[a-z][a-z0-9_]*` | Slug format for Rust/SQL |
|
||||
@@ -163,7 +163,7 @@ CREATE INDEX idx_chain_inputs_commodity ON chain_inputs(commodity_id);
|
||||
| W-01 | Every non-service physical commodity SHOULD appear as chain input or chain output | Orphan detection |
|
||||
| W-02 | Raw tier commodities SHOULD NOT appear as chain outputs | Raws are extracted, not produced |
|
||||
| W-03 | Final tier commodities SHOULD NOT appear as chain inputs | Finals are end-of-chain |
|
||||
| W-04 | `shadow_viable = true` SHOULD have `commission_certified = true` OR `compact_contested = true` | Shadow markets arise from regulatory friction |
|
||||
| W-04 | `shadow_viable = true` SHOULD have `commission_certifiable = true` OR `compact_contested = true` | Shadow markets arise from regulatory friction |
|
||||
| W-05 | `production_ubiquity = "monopolistic"` commodities with `elasticity = "inelastic"` SHOULD be reviewed | Monopoly + inelastic = potential runaway pricing |
|
||||
|
||||
### Coverage warnings (future — gated by corp data)
|
||||
|
||||
Reference in New Issue
Block a user