Files
settled-reach/server/data/systems-schema.sql
T
jpmschweitzerandClaude Opus 4.6 b6ca785f74 feat(db): atlas data pipeline — schema, importers, and normalization (#901-#911)
New tables: atlas_body_heightmaps (D-202), atlas_city_names (D-207),
atlas_feature_names, atlas_province_boundaries (D-205), body_radius_km
column (D-204). Three new importers: heightmap BLOBs, city names from
wiki markers.json, province boundaries via D8 watershed analysis.
economic_role normalized to 7 canonical values (D-194). Stamp fix in
generate_atlas.py to hash all tracked source files. systems.db regenerated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 18:40:33 +02:00

569 lines
26 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Star Systems Database Schema — server/data/systems.db
-- Source of truth for all structured star system data.
-- Authored via tooling pipeline; wiki pages are generated views.
-- Shipped with the game — read-only from the server's perspective.
--
-- Rebuild: tooling/process-wiki-system-changes --rebuild-db
PRAGMA journal_mode=WAL;
PRAGMA foreign_keys=ON;
-- Core identity, physical character, political character
CREATE TABLE IF NOT EXISTS star_systems (
system_id TEXT PRIMARY KEY, -- GJ catalog number (e.g., "GJ 244A")
proper_name TEXT,
system_name TEXT,
star_type TEXT,
spectral_class TEXT,
dist_ly REAL,
geographic_sector TEXT,
geographic_band TEXT,
political_zone TEXT,
-- Physical character
habitable_planet_count INTEGER,
inhabited_planet_count INTEGER,
asteroid_belt INTEGER, -- boolean 0/1
gas_giant INTEGER, -- boolean 0/1
habitability_profile TEXT,
-- Political character
earth_alignment TEXT,
earth_proximity TEXT,
earth_tension TEXT,
-- Content notes
stability_index INTEGER,
system_volatility TEXT,
cultural_corridor TEXT,
generation_priority TEXT,
-- Short narrative hook extracted from the wiki's gttr.md file — the
-- first characterisation paragraph ("where the rules live", "forty
-- years old and still in the draft", etc). Populated by
-- tooling/db/populate_gttr_hook.py from wiki/star-systems/<slug>/gttr.md.
-- Used as compact cultural context in the Gemma 2 naming pipeline
-- (#833) so per-system feel is grounded in the canonical identity
-- rather than generic corridor labels.
gttr_hook TEXT,
-- Economics (D-172)
currency_zone TEXT DEFAULT 'TRACTUS_PRIMARY', -- TRACTUS_PRIMARY | MARK_PRIMARY | MIXED
-- Energy-over-gate (D-186)
-- Gate Corp energy service: on-grid nodes get ~0.3× fusion_fuel utility demand.
-- MARK_PRIMARY zones default false (Compact refused Gate Corp dependency).
gate_energy_connected INTEGER DEFAULT 1, -- boolean 0/1
updated_at TEXT DEFAULT (datetime('now'))
);
-- Gate infrastructure — queried independently for pathfinding/topology
CREATE TABLE IF NOT EXISTS system_gates (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
horizon_station INTEGER, -- boolean 0/1
aperture_count INTEGER,
gate_connections INTEGER,
gate_topology TEXT,
hop_distance_from_gateway INTEGER,
span_gate_network TEXT
);
-- Settlement history
CREATE TABLE IF NOT EXISTS system_history (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
settlement_wave TEXT,
founding_motivation TEXT,
founding_culture_primary TEXT,
founding_culture_secondary TEXT,
cultural_persistence TEXT,
religious_status TEXT,
religious_generation_count INTEGER
);
-- Historical events — proper 1:N child table
CREATE TABLE IF NOT EXISTS historical_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
system_id TEXT NOT NULL REFERENCES star_systems(system_id),
event_type TEXT NOT NULL,
age_years INTEGER,
sort_order INTEGER NOT NULL DEFAULT 0 -- 0 = most recent
);
-- Economic life
CREATE TABLE IF NOT EXISTS system_economy (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
economic_tier INTEGER,
population INTEGER,
economic_base_primary TEXT,
economic_base_secondary TEXT,
distribution_index TEXT,
imprint_access TEXT
);
-- Faction presence
CREATE TABLE IF NOT EXISTS system_factions (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
governance_type TEXT,
dominant_faction TEXT,
primary_fault_line TEXT,
secondary_fault_line TEXT,
assembly_presence TEXT,
commission_presence TEXT,
syndic_presence TEXT,
syndic_type TEXT,
separatist_presence TEXT,
separatist_type TEXT,
institute_presence TEXT,
guardians_presence TEXT
);
-- Cultural voice
CREATE TABLE IF NOT EXISTS system_culture (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
cultural_register TEXT,
cultural_register_secondary TEXT,
ambient_anxiety TEXT,
local_pride TEXT,
atmospheric_tone TEXT,
atmospheric_tone_secondary TEXT,
active_situation TEXT,
silence_threshold TEXT,
primary_archetype TEXT,
secondary_archetype TEXT,
narrative_notable INTEGER -- boolean 0/1
);
-- Authored prose (supply_dependency, faction_notes, silence_topic,
-- narrative_hook, calibration_note) lives in wiki markdown files only.
-- No DB mirror — read the file if you need the text.
-- Celestial bodies — planets, moons, gas giants, asteroid belts, oort clouds
-- Naming convention:
-- GJ-{n}b/c/d... — planets (innermost first)
-- GJ-{n}Ab/c/d... — planets orbiting primary (binary systems)
-- GJ-{n}d-1 — first moon of third planet
-- GJ-{n}-oort — oort cloud region
-- GJ-{n}-belt — asteroid belt
CREATE TABLE IF NOT EXISTS bodies (
body_id TEXT PRIMARY KEY,
system_id TEXT NOT NULL REFERENCES star_systems(system_id),
parent_body_id TEXT REFERENCES bodies(body_id), -- NULL for planets; parent planet for moons
-- Classification
body_type TEXT NOT NULL, -- planet, moon, gas_giant, asteroid_belt, oort_cloud
orbit_index INTEGER, -- innermost = 1 (planets around star, moons around planet)
proper_name TEXT, -- "Xin Chengdu", NULL for unnamed
-- Physical
mass_class TEXT, -- terrestrial, super_earth, ice_giant, gas_giant, dwarf
atmosphere TEXT, -- breathable, thin, toxic, none, dense
surface_gravity REAL, -- in g (Earth = 1.0), NULL for gas giants
orbital_period_days REAL, -- orbital period in Earth days, NULL for oort/belt
rotation_period_hours REAL, -- rotation period in Earth hours (Earth = 24), NULL for tidally locked/gas giants
planet_class TEXT, -- temperate, arid, frozen, oceanic, volcanic, barren, etc.
hydrosphere TEXT, -- ocean, ice, rivers, none, subsurface
-- Settlement
inhabited INTEGER NOT NULL DEFAULT 0,
population INTEGER DEFAULT 0,
economic_role TEXT, -- manufacturing, agricultural, extraction, research, transit
founding_age_years INTEGER,
settlement_pattern TEXT, -- urban_concentrated, dispersed_rural, orbital_only, domed, cave
-- Cultural (inherited from system, can override)
cultural_corridor TEXT, -- override system corridor if different
industrial_corridor TEXT, -- MVG, Gate_Corp, DSMC, Prometheus, Agricultural_Syndic
-- Physical dimensions (D-204, #905)
-- Mean radius in km. NULL until authoritative data is available; fallback
-- derivation from planet_class is applied at query time by the generator.
body_radius_km REAL,
-- Rendering
-- terrain_reference: repo-root-relative path to the body's heightmap PNG.
-- Convention (enforced by populate_terrain_reference.py and assumed by
-- generate_atlas.py and the Godot client's atlas scene loader):
-- wiki/star-systems/<system_slug>/bodies/<body_id>/heightmap.png
-- where <system_slug> = system_id with spaces replaced by hyphens
-- (e.g. "GJ 244A" → "GJ-244A"). NULL means no heightmap has been
-- generated for this body yet. The three downstream pipelines
-- (populate, atlas generator, client loader) all assume this format —
-- changing it requires updating all three sites together.
terrain_reference TEXT,
screenshot_path TEXT, -- planetary shader screenshot path
updated_at TEXT DEFAULT (datetime('now'))
);
-- Stations and orbital facilities
-- Naming: GJ-{n}b-S1 (first station orbiting planet b), GJ-{n}-oort-S1 (horizon station)
CREATE TABLE IF NOT EXISTS stations (
station_id TEXT PRIMARY KEY,
system_id TEXT NOT NULL REFERENCES star_systems(system_id),
orbits_body_id TEXT REFERENCES bodies(body_id),
-- Classification
station_type TEXT NOT NULL, -- horizon, commercial, military, research, industrial, agricultural, transit
proper_name TEXT, -- "Chengdu Orbital", "Horizon Station"
-- Settlement
population INTEGER DEFAULT 0,
economic_role TEXT,
governance_type TEXT, -- can differ from system governance
-- Infrastructure
docking_class TEXT, -- major, standard, restricted, none
has_gate_infrastructure INTEGER DEFAULT 0,
district_count INTEGER DEFAULT 1,
updated_at TEXT DEFAULT (datetime('now'))
);
-- Corporations, combines, and major economic entities
-- Source: wiki/corporations/ pages (canonical)
CREATE TABLE IF NOT EXISTS corporations (
corp_id TEXT PRIMARY KEY, -- slug from wiki (e.g., "gate-corporation", "vethara")
proper_name TEXT NOT NULL, -- display name
corp_type TEXT NOT NULL, -- corporation, combine, syndic, institution, independent
scope TEXT, -- reach-wide, sector, system, local
headquarters_system TEXT REFERENCES star_systems(system_id),
headquarters_body TEXT, -- body_id or station_id
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)
);
-- Brand layer (D-189) — administered-price layer above commodity tâtonnement.
-- Brands consume commodities as demand nodes; they are NOT commodities (D-185).
CREATE TABLE IF NOT EXISTS brand_products (
brand_product_id TEXT PRIMARY KEY,
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
product_name TEXT NOT NULL,
brand_category TEXT NOT NULL, -- terroir|heritage_craft|tech_premium|cultural|service_premium|commodity_branded|design_heritage|platform_catalogue
value_trajectory TEXT NOT NULL, -- appreciating|depreciating|timeless
scarcity_class TEXT NOT NULL, -- capped|constrained|scalable|unlimited
product_subcategory TEXT,
base_premium_multiplier REAL NOT NULL DEFAULT 1.0,
premium_floor REAL NOT NULL DEFAULT 0.0,
origin_system TEXT REFERENCES star_systems(system_id),
terroir_locked INTEGER NOT NULL DEFAULT 0, -- boolean: production bound to origin_system
currency_denomination TEXT NOT NULL DEFAULT 'tractus', -- tractus|mark|mixed|sol_adjacent
shadow_viable INTEGER NOT NULL DEFAULT 0, -- boolean: circulates in shadow economy
brand_tier TEXT NOT NULL, -- halo|volume
halo_brand_id TEXT REFERENCES brand_products(brand_product_id), -- NULL for halo tier
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS brand_inputs (
brand_product_id TEXT NOT NULL REFERENCES brand_products(brand_product_id),
commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
quantity REAL NOT NULL,
PRIMARY KEY (brand_product_id, commodity_id)
);
-- Fiscal parameters per star system (D-189 section 5 + section 6)
CREATE TABLE IF NOT EXISTS system_fiscal (
system_id TEXT PRIMARY KEY REFERENCES star_systems(system_id),
corp_tax_rate REAL NOT NULL DEFAULT 0.22, -- 0.01.0
collection_efficiency REAL NOT NULL DEFAULT 1.0, -- derived: 1.0 - shadow_intensity × 0.6
updated_at TEXT DEFAULT (datetime('now'))
);
-- Phase 2: passive corp health tracking (D-189 section 8)
CREATE TABLE IF NOT EXISTS corp_financial_state (
corp_id TEXT PRIMARY KEY REFERENCES corporations(corp_id),
health_metric REAL NOT NULL DEFAULT 1.0, -- 0.0 (distressed) to 1.0 (healthy)
updated_at TEXT DEFAULT (datetime('now'))
);
-- Phase 3 lifecycle state machine stub (D-189 section 8) — schema correct, not driven yet
CREATE TABLE IF NOT EXISTS corp_lifecycle_events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
corp_id TEXT NOT NULL REFERENCES corporations(corp_id),
event_type TEXT NOT NULL, -- Founded|Growing|Active|Distressed|Acquired|Dissolved
event_tick INTEGER NOT NULL DEFAULT 0,
event_data TEXT, -- JSON blob for future lifecycle detail
created_at TEXT DEFAULT (datetime('now'))
);
-- BEGIN ATLAS INDEX (D-191 §8, #832) -- DO NOT EDIT THE MARKER COMMENTS
-- Atlas index — scalar metadata mirror of markers.json files.
-- Source of truth is wiki/star-systems/.../markers.json (next to the heightmap);
-- these tables exist so the atlas implant app and development queries don't
-- have to scan hundreds of JSON files. Polyline geometry stays in the files —
-- the DB only stores scalar/filterable fields + `point_count` as a rough length
-- proxy. Populated and refreshed by tooling/planet-gen/generate_atlas.py, which
-- extracts this entire block (between BEGIN/END ATLAS INDEX markers) from this
-- file at runtime so the DDL lives in exactly one place.
-- Per-body grid dimensions (one row per body that has a markers.json).
-- Lets any query interpret the pixel coordinates below without touching disk.
CREATE TABLE IF NOT EXISTS atlas_body_grids (
body_id TEXT PRIMARY KEY REFERENCES bodies(body_id) ON DELETE CASCADE,
grid_w INTEGER NOT NULL,
grid_h INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_cities (
city_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>" e.g. "GJ380c/city_0"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL, -- "city_0", "city_1" — matches markers.json id
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- capital|city
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
population INTEGER NOT NULL DEFAULT 0,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_roads (
road_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- commercial|highway|rural|...
point_count INTEGER NOT NULL, -- path length proxy (full geometry in markers.json)
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_railroads (
railroad_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- passenger_freight|freight|maglev|...
point_count INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_pois (
poi_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- transit|institutional|cultural|...
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_rivers (
river_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '', -- empty for untitled procedural rivers
point_count INTEGER NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_oceans (
water_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
kind TEXT NOT NULL, -- lake|sea|ocean
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
area_fraction REAL NOT NULL DEFAULT 0.0, -- fraction of heightmap area covered
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS atlas_mountain_ranges (
range_id TEXT PRIMARY KEY, -- "<body_id>/<local_id>"
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
local_id TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
center_row INTEGER NOT NULL,
center_col INTEGER NOT NULL,
peak_row INTEGER NOT NULL,
peak_col INTEGER NOT NULL,
area_cells INTEGER NOT NULL DEFAULT 0,
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_atlas_cities_body ON atlas_cities(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_cities_kind ON atlas_cities(kind);
CREATE INDEX IF NOT EXISTS idx_atlas_cities_population ON atlas_cities(population);
CREATE INDEX IF NOT EXISTS idx_atlas_cities_name ON atlas_cities(name);
CREATE INDEX IF NOT EXISTS idx_atlas_roads_body ON atlas_roads(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_railroads_body ON atlas_railroads(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_pois_body ON atlas_pois(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_pois_kind ON atlas_pois(kind);
CREATE INDEX IF NOT EXISTS idx_atlas_rivers_body ON atlas_rivers(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_oceans_body ON atlas_oceans(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_mountain_ranges_body ON atlas_mountain_ranges(body_id);
-- Heightmap BLOB storage — float32 LE, row-major (D-202, #901)
-- Only inhabited bodies receive rows at build time; uninhabited bodies are
-- generated on-demand by the runtime-background tier.
CREATE TABLE IF NOT EXISTS atlas_body_heightmaps (
body_id TEXT PRIMARY KEY REFERENCES bodies(body_id) ON DELETE CASCADE,
width INTEGER NOT NULL DEFAULT 512,
height INTEGER NOT NULL DEFAULT 256,
data BLOB NOT NULL, -- float32 LE, row-major, width×height values
sea_level REAL NOT NULL DEFAULT 0.0,
imported_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- City name reservations — replaces authored city positions in markers.json (D-207, #902)
-- Position is generated by the city placement algorithm; name is authored or LLM-generated.
CREATE TABLE IF NOT EXISTS atlas_city_names (
id INTEGER PRIMARY KEY AUTOINCREMENT,
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
name TEXT NOT NULL,
kind TEXT NOT NULL DEFAULT 'city', -- 'capital' | 'city'
economic_role TEXT NOT NULL,
population INTEGER NOT NULL,
corp_id TEXT REFERENCES corporations(corp_id), -- nullable, corp HQ if applicable
reserved INTEGER NOT NULL DEFAULT 0, -- 1 = reserved for authored scenario use
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Geographic feature name reservations — rivers, mountains, passes (D-207 adjacent, #903)
CREATE TABLE IF NOT EXISTS atlas_feature_names (
id INTEGER PRIMARY KEY AUTOINCREMENT,
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
name TEXT NOT NULL,
feature_type TEXT NOT NULL, -- 'river' | 'mountain' | 'pass' | 'ocean' | 'region'
priority INTEGER NOT NULL DEFAULT 0, -- higher = applied first during naming
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Province boundaries — watershed drainage basin polylines (D-205, #904)
-- Pre-computed at build time from D8 drainage analysis.
CREATE TABLE IF NOT EXISTS atlas_province_boundaries (
body_id TEXT NOT NULL REFERENCES bodies(body_id) ON DELETE CASCADE,
basin_id INTEGER NOT NULL,
path TEXT NOT NULL, -- JSON array [[row, col], ...] pixel-space polyline
area_pct REAL NOT NULL, -- fraction of body surface area in this basin
PRIMARY KEY (body_id, basin_id)
);
CREATE INDEX IF NOT EXISTS idx_atlas_body_heightmaps_body ON atlas_body_heightmaps(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_city_names_body ON atlas_city_names(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_city_names_kind ON atlas_city_names(kind);
CREATE INDEX IF NOT EXISTS idx_atlas_city_names_corp ON atlas_city_names(corp_id);
CREATE INDEX IF NOT EXISTS idx_atlas_feature_names_body ON atlas_feature_names(body_id);
CREATE INDEX IF NOT EXISTS idx_atlas_province_boundaries_body ON atlas_province_boundaries(body_id);
-- END ATLAS INDEX (D-191 §8, #832)
-- 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);
CREATE INDEX IF NOT EXISTS idx_star_systems_wave ON system_history(settlement_wave);
CREATE INDEX IF NOT EXISTS idx_star_systems_topology ON system_gates(gate_topology);
CREATE INDEX IF NOT EXISTS idx_star_systems_star_type ON star_systems(star_type);
CREATE INDEX IF NOT EXISTS idx_star_systems_volatility ON star_systems(system_volatility);
CREATE INDEX IF NOT EXISTS idx_historical_events_system ON historical_events(system_id);
CREATE INDEX IF NOT EXISTS idx_system_factions_dominant ON system_factions(dominant_faction);
CREATE INDEX IF NOT EXISTS idx_system_economy_tier ON system_economy(economic_tier);
CREATE INDEX IF NOT EXISTS idx_system_culture_archetype ON system_culture(primary_archetype);
CREATE INDEX IF NOT EXISTS idx_bodies_system ON bodies(system_id);
CREATE INDEX IF NOT EXISTS idx_bodies_parent ON bodies(parent_body_id);
CREATE INDEX IF NOT EXISTS idx_bodies_type ON bodies(body_type);
CREATE INDEX IF NOT EXISTS idx_bodies_inhabited ON bodies(inhabited);
CREATE INDEX IF NOT EXISTS idx_stations_system ON stations(system_id);
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);
-- Generator metadata stamp (#855, #856)
-- One row per generator, updated on each successful non-dry-run.
-- schema_version: SHA-1 of server/data/systems-schema.sql content at generation time
-- generator_sha: SHA-1 of the generator source file(s) content
-- generated_at: ISO-8601 UTC timestamp of the run
--
-- Used by:
-- tooling/check-systems-db-stamp — verifies freshness before push (#857)
-- .config/hooks/pre-push — rejects pushes with stale DB (#857)
-- /pr-push skill — triggers make regen-db if stale (#858)
CREATE TABLE IF NOT EXISTS meta (
generator_name TEXT PRIMARY KEY, -- 'import_economics' | 'generate_atlas' | 'generate_brands'
schema_version TEXT NOT NULL, -- SHA-1 hex of systems-schema.sql content
generator_sha TEXT NOT NULL, -- SHA-1 hex of generator source file(s) content
generated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
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);
-- Brand layer indexes (D-189 section 5 — composite for UI queries)
CREATE INDEX IF NOT EXISTS idx_brand_products_corp_category ON brand_products(corp_id, brand_category);
CREATE INDEX IF NOT EXISTS idx_brand_products_origin ON brand_products(origin_system);
CREATE INDEX IF NOT EXISTS idx_brand_products_tier ON brand_products(brand_tier);
CREATE INDEX IF NOT EXISTS idx_brand_inputs_commodity ON brand_inputs(commodity_id);
CREATE INDEX IF NOT EXISTS idx_corp_lifecycle_events_corp ON corp_lifecycle_events(corp_id);
-- Atlas index (D-191 §8, #832) — see BEGIN/END ATLAS INDEX block above.