Compare commits

...
6 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.6 4028f71f2f chore(meta): release v0.1.32
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:37:48 +02:00
jpmschweitzerandClaude Opus 4.6 a6c286735e fix(schema): remove unused os import in import_economics.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:33:59 +02:00
jpmschweitzerandClaude Opus 4.6 f80cbe621e chore(meta): update changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:33:05 +02:00
jpmschweitzerandClaude Opus 4.6 571a708d4a feat(schema): extend systems.db for economics (#804)
Add 5 new tables: gate_links (668 bidirectional edges from star-map.json),
commodities (36 types from commodities.toml), production_chains (21
Leontief recipes), chain_inputs (52 input requirements), corp_presence
(empty, populated by future pipeline). Add currency_zone column to
star_systems (D-172), archetype columns to corporations (D-175).

New import pipeline: tooling/economy-db/import_economics.py reads
TOML/JSON source files and populates the DB. Idempotent — safe to
rerun via `make economy-db`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:32:32 +02:00
jpmschweitzerandClaude Opus 4.6 4a3febbe29 refactor(schema): rename commission_certified to commission_certifiable
The flag is a susceptibility marker, not an absolute state — Commission
certification only applies when trading in TRACTUS_PRIMARY zones.
Compact-internal trade ignores it entirely. Renamed across all TOML
source files, schema docs, workshop outputs, and D-184.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:32:18 +02:00
jpmschweitzerandClaude Opus 4.6 bcf15354d2 chore(skills): improve pr-review fresh-start instructions
Clarify that every /pr-review invocation must start fresh — fetch,
check PRs, and re-read the diff even if the branch was reviewed
earlier in the same conversation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 23:32:06 +02:00
16 changed files with 580 additions and 104 deletions
+12 -7
View File
@@ -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
+5
View File
@@ -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
+5 -1
View File
@@ -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:
+1 -1
View File
@@ -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
View File
@@ -1,5 +1,5 @@
name: The Settled Reach
version: 0.1.31
version: 0.1.32
repository: settled-reach
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "settled-reach-server"
version = "0.1.31"
version = "0.1.32"
edition = "2021"
[dependencies]
+75
View File
@@ -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: 512% per hop (gate edge), 13% (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.
+387
View File
@@ -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()
+37 -37
View File
@@ -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
+4 -4
View File
@@ -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)