feat(db): corp-HQ settlement model + per-settlement population/class bake (T-1074/T-1075, D-242)

Remove the reserved=1 corp-HQ city-pool cross-reference (duplicate co-named cities); corp_specialization keyed on the D-237 vocabulary extended 27->30; authored corp_hq_placement.toml {CityTenant,Standalone} map; headquarters_body backfill via lifted most-populated-body heuristic + body-type tiebreak; standalone HQs emitted as ordinary settlement rows; Zipf rank-size population spread from bodies.population at import; settlement_class defaults PopulationBudget + settlement_name_locked.toml hero pins; retire orphaned populate-corporations.sh; regen systems.db.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 10:29:00 +02:00
co-authored by Claude Fable 5
parent 43a6d55180
commit 62bbf57009
12 changed files with 903 additions and 145 deletions
+31 -4
View File
@@ -238,8 +238,11 @@ CREATE TABLE IF NOT EXISTS corporations (
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
headquarters_body TEXT, -- body_id or station_id (D-242: backfilled at import for
-- every corp via the most-populated-body-in-system heuristic
-- when not authored; see populate_corp_specialization)
specialization TEXT, -- free-text primary product/service (legacy prose; read by
-- generate_corporations for fuzzy brand relevance — left as-is)
parent_corp TEXT REFERENCES corporations(corp_id),
notes TEXT,
@@ -248,6 +251,19 @@ CREATE TABLE IF NOT EXISTS corporations (
supply_chain_role TEXT,
shadow_economy_access INTEGER DEFAULT 0,
-- D-242: controlled-vocabulary corp specialization (HQ-placement key) + its
-- baked placement outcome. corp_specialization is the corp_specialization
-- controlled vocabulary (reuses the D-237 27-value specialization_vocabulary
-- id-space — see wiki/economics/corp_hq_placement.toml header). hq_placement
-- is 'CityTenant' | 'Standalone', derived once at import from corp_specialization
-- via the authored placement map; NULL until populate_corp_specialization runs.
-- headquarters_city_id is the D-242 "corp -> city link" for CityTenant HQs
-- (many corps may tenant one city); NULL for Standalone HQs (which instead
-- get their own atlas_city_names row, cross-referenced by headquarters_body).
corp_specialization TEXT,
hq_placement TEXT,
headquarters_city_id INTEGER REFERENCES atlas_city_names(id),
updated_at TEXT DEFAULT (datetime('now'))
);
@@ -493,6 +509,10 @@ CREATE INDEX IF NOT EXISTS idx_atlas_mountain_ranges_body ON atlas_mountain_rang
-- 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.
-- D-242: a Standalone corp HQ IS a settlement, so it gets an ordinary row here
-- (populate_standalone_hq_settlements) — same shape as a wiki-pooled city, no
-- special kind/flag. A CityTenant HQ is NOT a row here; its corp->city link
-- lives on corporations.headquarters_city_id (the inverse FK) instead.
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,
@@ -500,8 +520,15 @@ CREATE TABLE IF NOT EXISTS atlas_city_names (
kind TEXT NOT NULL DEFAULT 'city', -- 'capital' | 'city'
economic_role TEXT NOT NULL,
population INTEGER NOT NULL,
settlement_class TEXT, -- D-196 SettlementClass variant; NULL until placement
corp_id TEXT REFERENCES corporations(corp_id), -- nullable, corp HQ if applicable
settlement_class TEXT, -- D-196 SettlementClass variant; baked at import (D-242/T-1075)
corp_id TEXT REFERENCES corporations(corp_id), -- SUPERSEDED (D-242): the D-207/D-223 corp-HQ
-- cross-reference insert that populated this is
-- removed (it produced duplicate co-named "cities" —
-- one row per corp HQ, no UNIQUE(body_id, name)).
-- Always NULL going forward; the corp<->settlement
-- relationship now lives on corporations
-- (headquarters_body / headquarters_city_id). Column
-- kept (not dropped) — additive-safe, no live reader.
reserved INTEGER NOT NULL DEFAULT 0, -- 1 = reserved for authored scenario use
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);