40 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Fable 5 8da9670e0f feat(simulation): feature-name pipeline wired + legacy window_granularity u32 retired (T-1169, T-1159)
One commit for two tickets whose changes share the bridge/plugin
plumbing files. T-1169 connects the three dormant feature-name pieces:
atlas_feature_names populated at regen (17,891 rows — 15,190 mountain,
2,701 river — via populate_atlas_feature_names mirroring the city-names
importer; systems.db regenerated, stamp fresh), attach_feature_names
wired into the cascade's Topography block with name pools threaded
DB-free through AnalyzeBody (D-225 pattern) and assignments stored on
Layer1Output/BodyWorldState for future consumers, and a
FeatureNamesRequest/Response read proxy as the bridge's 7th tagged
envelope (D-236 pattern, both SimBridge impls). Client label DRAW is
deliberately NOT here — implementation proved both river and mountain
labels need a wire-carried position (the pool is position-free; course
polylines aren't correlated with the named attractors by construction) —
deferred to T-1195's single design pass. cascade_layer1 golden re-pinned
(additive feature_names field).

T-1159 retires the legacy u32 granularity field fully shadowed by
window_granularity_v2: AtlasLayerRequest.window_granularity,
DistrictWindowLayer.granularity echo, the u32::MAX sentinel, and
resolve_window_granularity are gone server-side; client encode paths and
the caller-less atlas_window_cache legacy key component dropped;
msgpack fixtures regenerated; the T-1150 aliasing regression test now
drives through the surviving enum field. The district_window carrier
itself survives byte-compatible per D-255(c).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 16:10:27 +02:00
jpmschweitzerandClaude Fable 5 e63caeb620 fix(db): PR #177 review round — H1/H2/T1/T2/M1
H1+H2: headquarters_body is reset+derived every run (DB is never source); optional authored frontmatter override, hard-validated; tiebreak now population DESC -> city-bearing body -> type rank -> body_id, preserving belt tenancies and fixing GJ702B to GJ702Bb. T2: NULL-reset pass for corp_specialization/hq_placement before authored re-apply (poison-tested). T1: wiki/corporations/*.md globbed into IMPORT_ECONOMICS_SOURCES. M1: licensed_clinical_services vocabulary value (31st, NonPhysical->CityTenant) + somatic-futures retag. Fixpoint verified stable across 4 consecutive regens (0 diffs); regen systems.db.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:00:03 +02:00
jpmschweitzerandClaude Fable 5 62bbf57009 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>
2026-07-16 10:29:00 +02:00
jpmschweitzerandClaude Fable 5 ba781f9324 feat(db): architecture zone-bias + color-register-band tables + V-TT-06/07 (T-988)
Ratified content baked into systems.db as two new tables:
- architecture_zone_bias (57 rows): sparse per-template, per-zone_type
  token-weight overrides (integer bps), Miri-authored — the D-235 step-2
  zone bias. V-TT-06: every token must exist in that template's own
  visual_bundle axis.
- color_register_bands (28 rows): per color_register integer HSV bands
  (hue centidegrees, sat/val bps), Araminta-authored. V-TT-07: full
  catalog coverage + valid integer bounds (min<max, in range).

Both TOMLs registered in generator_sources (stamp coverage); DDL in
systems-schema.sql + migration.py; validation wired into import_economics
steps 18/19 and test_traits.py failure-branch units (make test-tooling).
systems.db regenerated + re-stamped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:03:19 +02:00
jpmschweitzerandClaude Opus 4.8 cc7c5ada04 feat(db): biosphere_class authoring pipeline + values (T-1085 workstream 3, D-247)
The D-247 chirality/edibility register as authored data on every body, via the
stamped regen-db path (import_economics) — not a manual bake.

- systems-schema.sql + migration.py: nullable bodies.biosphere_class column.
- economy_import/bodies.py: populate_biosphere_class — reads the authored
  environment.biosphere_class frontmatter override, else the D-247 two-gate default
  (Gate 0 habitability from atmosphere/hydrosphere/inhabited; Gate 1 Compatible/Mirror
  from economic_base_primary; neutral remainder -> stable ~50/50 split). Recomputes
  every body each regen (frontmatter + default are its complete source).
- import_economics.py: wired as import step 17.
- frontmatter overrides for the bodies whose default deviates from canon:
  Solandar=NativeMirror, GJ-524=NativeMirror (uninhabited-but-alive), Ha Long=
  NativeCompatible (its transit economy hides its aquaculture identity). Linnaeus,
  Arbour, Oshima, Freyburg, Puerto Ultimo, Edict all default correctly.

systems.db regenerated + stamped. All 9 canon exemplars verified; distribution is
111 Compatible : 108 Mirror among inhabited alive worlds (~50/50 target), Airless for
uninhabited bodies, TerraformedSterile for thin-atmosphere worlds.

Follow-up (rename completion): propagating Linnaeus to bodies.proper_name in the DB
(still "Cadwal" — proper_name is atlas-CLI-owned, and a wipe+reimport would drop
non-proposal columns like founding_age_years; needs a targeted approach).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 11:56:52 +02:00
jpmschweitzerandClaude Fable 5 4f73624eff refactor(db): split import_economics.py; single generator-source registry (T-1067)
import_economics.py 2,620 → 309 lines — a thin orchestrator keeping the
exact CLI, single-transaction/rollback contract, and exit codes. The 16
import steps, MIGRATION_SQL, brands shell-out, validators, and stamp
write now live in tooling/economy-db/economy_import/ (db, migration,
economy, corporations, brands, bodies, atlas, specialization, traits,
validators, stamp, paths, errors). Full type hints throughout.

tooling/generator_sources.py replaces the triplicated source registry
(importer / stamp checker / pr-process watch list — the skill now derives
its list via --list). The registry stamps itself, and economy_import/
modules are globbed fail-closed, so a future module is stamped the moment
it exists — closing the silently-weakened-stamp failure mode.

Rider: connector config helpers centralized in tooling/db/common.py.

Byte-identical behavior proven: full-import table dump diff EMPTY over
107,843 lines / 37 tables (volatile timestamp fields excluded); dry-run
output parity; generated_brands.toml sha unchanged. make test-tooling
PASS; ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 20:13:28 +02:00
jpmschweitzerandClaude Fable 5 0bd895fcac chore(engine): server hygiene batch — workers tests, surname dedup, save pin (T-1063, T-1064)
- workers/pool.rs: 5 new tests; catch_unwind keeps worker threads alive on
  handler panic (in-flight request loss unchanged, pinned by test + #843
  docs); stubs.rs no longer falsely claims the pool is tested
- save/load: execute_save_load pinned .after(Storyteller) so the scheduler
  cannot legally save pre-Input state; exclusive-system exception recorded
  in tick_phases.rs rules
- surname corpus extracted to bin/shared/surname_corpus.rs (both economy
  generators import it; byte-identical output verified on 23.6MB+1.45MB
  TOMLs); all three stamp/watch registries updated
- generator_spike gated behind non-default 'generator-spike' feature
- economy.rs: 11 new D-181 signal-derivation tests on the new
  econ_sim Simulation::from_economy in-memory constructor
- perception exemption comments now state the consumer sort contract;
  unused bytemuck removed; rayon comment corrected; the 22 allow(dead_code)
  documented as serde schema enforcement

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:22:28 +02:00
jpmschweitzerandClaude Opus 4.8 def70eaf37 feat(simulation): D-239 three-carrier foundation (T-1023/1024/1026)
First foundation slice of the Atlas-to-tile derivation model (epic T-974),
building the carrier layer ahead of its T-1027+ consumers.

T-1026 — Anti-squaring domain warp (D-239 §4): stateless pure
fn(seed,body_id,pos)->(f64,f64), ±8m, f64 to the final voxel then as-i32
truncation for IEEE-754 cross-target determinism. New domain_warp.rs,
SeedDomain::DomainWarp; golden-vector + cross-thread tests. Position math
only — D-010 integer discipline preserved downstream. Marked dead_code
until the T-1028 VoxelColumn pipeline consumes it.

T-1023 — RegionProfile carrier (D-239 §1,§10): new RegionProfile +
TectonicClass/GlaciationGrade/PrecipitationClass enums + BodyParams; derived
per-region river_threshold replacing the global 200 for tile consumers.
regions: BTreeMap on BodyWorldState, populated via the cascade's new
RegionProfile layer (runs when body_params is Some, else falls back to
Settlement). D-010 integer discipline, BTree ordering.

T-1024 — District climate primitives (D-239 §2): nullable temperature_c +
moisture on RegionProfile, mean-annual scalar (no clock dep; dynamic branch
deferred to Q-105). Hybrid inputs — new bodies.axial_tilt_deg column imported
from planet-gen body-defs (populate_axial_tilt_deg, 2611 bodies), luminosity
and orbital distance derived at runtime; greenhouse + diurnal-swing tables in
source-canonical climate_constants.toml. D-239 implementation note added.

cargo test: 1498 passed, 0 failed. clippy clean (pre-existing
large_enum_variant only). make check-systems-db: stamp fresh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:28:31 +02:00
jpmschweitzerandClaude Opus 4.8 a4727bf73e fix(economics): address PR #148 review — dry-run guard + pin validation
Review fixes (Hoshe/Tyre, both APPROVE):

- populate_trait_templates / populate_atlas_body_trait_bias: the table
  DELETEs ran unconditionally (safe only via transaction rollback on
  dry-run, and divergent from every other populate_* function). Restructured
  so validation + guardrails always run (dry-run now actually surfaces the
  would-bake counts and catches errors) but mutations happen only under
  `if not dry_run:`. Verified: --dry-run reports 28 templates, writes nothing.
- atlas_body_trait_bias: reject `pin` entries that carry a
  weight_multiplier_bps (pin is mandatory, no multiplier) — closes a silent-
  accept gap before #1017 authors ~30-40 real pins.

Deferred (noted on tickets): visual_bundle fallback-map completeness
(Phase-5/Araminta), pin-count-vs-K bake check (#1017 acceptance),
geographic_sector pool-narrowing semantics (#977).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:36:37 +02:00
jpmschweitzerandClaude Opus 4.8 375d04a29d data(content): core architecture-flavor trait-template catalog (#1005)
The D-232 core hand-curated catalog (28 templates) the architecture-flavor
draw reads from, baked into trait_templates (#993):

- Cross-corridor pool (10): economic-function templates gated by BulkClass
  (extraction/industrial/cold-chain/precision/information/civic) + universal
  baselines + the foreign-import swerve. Guarantees the CI floor.
- Per-corridor baseline (8): each corridor's default cohesive look — core
  cosmopolitan, north anglo-frontier, south lusophone, west germanic +
  compact-cooperative, east dense-utilitarian, deep-frontier surname +
  hardscrabble.
- Heritage sub-pools (10): deep-history callbacks tied to the D-237 heritage
  taxonomy (scottish highland, west-african compound, iberian hacienda,
  atlantic creole, nordic timber, central-european blok, east-asian temple,
  vietnamese water village, afrikaans kraal, arab oasis).

Each template = holistic bundle: two-tier eligibility (hard gates
bulk_class/ubiquity/prosperity_bps + soft weight mods), zone_affinity over
the real DistrictType enum, allow/block tags from a shared ObjectTag
palette, and a D-235 visual_bundle with generic fallback parents. All
numerics integer basis-points (D-010).

Adds the D-232 CI guardrails to the baker (V-TT-01: >=5 templates eligible
per BulkClass; V-TT-02: no template >60% of pool weight) — both pass with
margin (23-24 eligible/class, 5% max share). Catalog can grow via the
bounded Gemma pass (#992); hero pins are #1017.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:04:43 +02:00
jpmschweitzerandClaude Opus 4.8 a133b6416e feat(schema): trait_templates + atlas_body_trait_bias tables + bake (#993)
D-232 architecture-flavor storage mechanism (content is #1005/#1017):

- trait_templates: shared catalog of holistic template bundles — two-tier
  eligibility (hard gates bulk_class/prosperity_bps/ubiquity + soft weight
  mods), zone_affinity, allow/block tags, visual_bundle. List/map fields
  are JSON; all numerics integer basis-points (D-010).
- atlas_body_trait_bias: sparse per-body hero pins — pin/boost/suppress
  with basis-point multiplier ranges (boost ≤3×, suppress ≥0.33× never 0).
- Baked at import (steps 14/15) from wiki/economics/architecture_trait_
  catalog.toml + architecture_trait_bias.toml. FK-validated (body_id →
  bodies, template_tag → trait_templates), enum + multiplier-range checks,
  graceful on absent source. Source location provisional pending Q-107;
  the baked tables are invariant per D-232. Retires the round-2
  atlas_body_culture tables.
- Catalog ships a 3-template bootstrap seed to prove the bake end-to-end;
  the full ~35-template catalog is #1005, hero pins #1017.
- Stamp sources updated (IMPORT_ECONOMICS_SOURCES + GENERATOR_SOURCES).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:47:35 +02:00
jpmschweitzerandClaude Opus 4.8 21793c1a94 data(economics): #1016 cultural-write pass + canonical 47-value heritage migration
Completes the deferred cultural half of #1016 and migrates the heritage
vocabulary to the canonical 47-value taxonomy (D-237).

Migration (mechanical):
- _CULTURAL_HERITAGE replaced with the canonical 47-value set
  (docs/.../heritage-taxonomy-draft.md). Renamed the 4 shipped hero
  values to canonical: afrikaans_cape->afrikaans, french_provencal->
  french, italian_northern->italian, norse_compact->nordic.

Content pass (authored from GTTR/name heritage triage, 6-sector fan-out):
- +139 new cultural_specialization pins and 1 change (Altmark
  cosmopolitan->financial_technocratic), taking coverage 32 -> 171
  across 54 distinct values. Each pin is grounded in an explicit GTTR
  founding-community statement or system/feature name etymology, applied
  only where founding heritage DIVERGES from the corridor baseline
  (D-167/D-232); corridor-typical systems left NULL.

Distribution is now balanced and diverse — portuguese 15, german 12,
vietnamese 10, afrikaans 10 (no longer dominant post-#1019), then a long
tail filling the previously-missing slots (welsh, herero, czech, akan,
hungarian, konkan, afro_brazilian, cape_verdean, shona, arab/persian/
turkic, etc.).

Judgment calls (flagged for review):
- Kruger 60 + Pedra Seca -> xhosa (mixed SA founding; adds diversity).
  Pedra Seca has a Portuguese name but Afrikaans/Xhosa GTTR founding —
  name/heritage mismatch noted for a future content fix.
- Kampala Gate / Moyale / Mwangaza -> swahili as the nearest token for
  East-African-interior heritage; taxonomy may want a dedicated value.
- Kept hero pins Kensho=scholarly, Keid=scholarly, Nyrheim=nordic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:06:23 +02:00
jpmschweitzerandClaude Opus 4.8 8844bef139 data(content): rebalance 5 thin afrikaans systems to MENA heritage (#1019)
The corridor over-represented southern-African heritage (afrikaans on 15
systems; arab=2, persian/turkic=0) as an artifact of the earlier
name-balancing overcorrecting. Convert five genuinely-thin afrikaans
systems — whose distinctive threads are heritage-agnostic — into
Arab/Persian/Turkic founders so the cultural cascade is built on a
balanced set. Load-bearing mystery systems (Eerste Wacht / Helderoog /
Brandpunt etc.) are left untouched.

  Droëland  (GJ 914A)  -> Marib    (arab)
  Stilwater (GJ 508A)  -> Sawad    (arab)
  Koeberg   (GJ 1245B) -> Akhgar   (persian)
  Skuilplek (GJ 722)   -> Siginak  (turkic)
  Carnarvon (GJ 680)   -> Golestan (persian; Mostert dynasty -> Farahani)

Result: afrikaans 15->10; arab 0->2, persian 0->2, turkic 0->1.

Full narrative retouch (not just names): gttr.md + index.md prose,
body/station proper_names, cultural_specialization, gttr_hook,
atlas_city_names, and the four brand-corps named after the converted
systems (corp IDs kept stable; only display names + products renamed).
Cross-references in neighbour systems, the catalog, the aggregate
drifter guides, and the atlas proposals are updated to match.

arab/persian/turkic added to _CULTURAL_HERITAGE so the values validate;
the full canonical 47-value migration remains #1016.

Also fixes a pre-existing canon bug: Sawad (GJ 508A) no longer claims
GJ 914A is "unsettled" — it is Marib, a three-century settlement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 07:11:10 +02:00
jpmschweitzerandClaude Opus 4.8 9fe4ddfb04 fix(economics): restore dropped vocab_rows.append in import_system_specialization
The #1015 V-SES-03 edit replaced the vocabulary validation loop body and
dropped the trailing vocab_rows.append(), so specialization_vocabulary was
inserted with 0 rows even though per-system UPSERTs (which read the dict
directly via valid_spec_ids) still succeeded. regen-db reported
'vocab 0 | economic 27 | ...'. Restored the append at the end of the loop.

Verified: regen-db now writes 27 vocab rows + 27/27 economic/cultural;
spot checks financial_hub=NonPhysical/Specialist, Ran=breadbasket.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:09:49 +02:00
jpmschweitzerandClaude Opus 4.8 2f994c1795 feat(economics): D-237 specialization CI guardrails + coverage report (#1015)
Adds the V-SES / V-FAC guardrail suite to import_system_specialization():

Always-on hard errors (abort the import transaction): V-SES-01 economic
value in vocabulary; V-SES-03 production_ubiquity_override >= commodity
catalog concentration (HUB values with empty override exempt — they
intentionally project lower); V-SES-04 cultural value in the combined
activity+heritage vocabulary; V-SES-05 faction in 8-value vocab; V-SES-06
vocab commodity FK; plus unknown system_id.

Completeness gates — V-SES-02 (every inhabited system resolves a non-null
economic value) and V-FAC-01 (every inhabited named system has authored
faction) — are HARD only under --strict-specialization (default off). Their
preconditions are the #1014 heuristic fallback (blocked by #982) and the
#1016 content pass; until those land they print as warnings so regen-db
stays green.

Soft warnings (always print): W-SES-01 MonopolySource D-177 review list,
W-SES-02 single-value concentration, W-SES-08 estate_farming + large pop,
W-FAC-01 compact + TRACTUS currency, W-FAC-02 compact + MonopolySource
(terroir/marble exempt), W-FAC-03 syndic_dominant w/o corp HQ, W-FAC-04
compact_sympathetic + MARK currency. Coverage report always prints
economic/cultural/faction counts + BulkClass/ProductionUbiquity dists.

Cultural vocabulary is _CULTURAL_ACTIVITY | _CULTURAL_HERITAGE module
constants — #1016 adds heritage values there in one place.

Validated on a copy of the live DB: default exits 0 (gates as warnings, real
W-SES-08 hit on Arbour); strict exits 1 on V-SES-02 (Sol, Struve) and rolls
back cleanly; dry-run exits 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 12:44:03 +02:00
jpmschweitzerandClaude Opus 4.8 883c1ab0b1 feat(economics): import_system_specialization() — D-237 authored layer (#1013)
Wires the D-237 authored specialization layer into import_economics.py:

- import_system_specialization(): reads specialization_vocabulary.toml
  (FK-validated against commodities; repopulates specialization_vocabulary)
  and system_specialization.toml (UPSERTs economic_specialization +
  cultural_specialization onto system_economy, dominant_faction onto
  system_factions for authored systems only). Hard errors on bad commodity
  FK / projected enum / unknown system_id abort the transaction; prints a
  coverage report + missing-row warnings.
- Called as step 4b in main() (after commodities so the FK resolves, before
  currency zones). economic/cultural columns are importer-owned and cleared
  to NULL first for idempotency; dominant_faction only overwritten for
  authored systems (never globally cleared — shared with other derivation).
- specialization_vocabulary added to MIGRATION_SQL (after commodities for FK)
  so the migration path on existing DBs creates the table, not just fresh
  systems-schema.sql builds — this is what #1011 missed. Also cleared before
  commodities in the FK-safe clear block.
- Both source TOMLs added to IMPORT_ECONOMICS_SOURCES and mirrored in
  check-systems-db-stamp GENERATOR_SOURCES so editing them flips the stamp.

Validated: full non-dry-run import on a copy of the live systems.db exits 0;
27 vocab rows, 27/27/27 economic/cultural/faction set, no missing rows;
spot checks Ran=breadbasket/agrarian, Vuurkloof=independent,
financial_hub=NonPhysical/Specialist. Dry-run also exits 0.

V-SES CI guardrail suite remains #1015; code note flags that V-SES-03
(equal-or-higher) must not hard-fail HUB specializations.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:09:06 +02:00
jpmschweitzerandClaude Opus 4.8 4f53793755 feat(schema): D-237 specialization columns + specialization_vocabulary table
Ticket #1011. system_economy gains economic_specialization +
cultural_specialization (TEXT, NULL = heuristic fallback); new
specialization_vocabulary table (specialization_id PK, commodity_id
FK->commodities, production_ubiquity_override, projected BulkClass +
ProductionUbiquity for D-233, description) + commodity index. Columns
mirrored into COLUMN_MIGRATIONS for idempotent migration; table rides
systems-schema.sql. dominant_faction already exists on system_factions
(populate-only). Validated: importer parses; schema applies to scratch DB;
idempotent migration ran 2x on a copy of live systems.db, 301 rows preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:04:00 +02:00
jpmschweitzerandClaude Opus 4.8 9f4b1bf02c fix(db): address PR #144 review (#1000)
- Update D-216 amendment text to match the implemented decision:
  events-first uses event_type='colonial_charter' (9 systems, 11 bodies,
  5 diverging from wave) — not the stale founding/first_settlement/
  colonization scaffolding vocabulary.
- Add ORDER BY sort_order ASC to the colonial_charter subquery for a
  deterministic pick if a system ever has two charter rows.
- Add explicit ELSE NULL to the wave-fallback CASE so an unexpected
  settlement_wave leaves a trace instead of silently nulling.

Data unchanged: 269/273 set, 4 Sol NULL; stamp fresh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 22:07:06 +02:00
jpmschweitzerandClaude Opus 4.8 4319e40a63 chore(db): backfill founding_age_years for inhabited bodies (#1000)
Add idempotent MIGRATION_SQL UPDATE to import_economics.py deriving
bodies.founding_age_years via COALESCE(colonial_charter event age,
settlement_wave fallback) per the D-216 amendment.

- Events-first: colonial_charter age_years (authored; 11 bodies).
- Wave fallback: wave_1=600/wave_2=500/wave_3=300/wave_4=100/wave_5=40
  (258 bodies).
- Exclude settlement_wave in (origin, unsettled) -> stays NULL: 4 Sol
  bodies remain NULL (out of player scope, D-236).

Source-canonical via regen-db; 269/273 inhabited bodies now populated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 16:07:47 +02:00
jpmschweitzerandClaude Opus 4.7 b71f339996 feat: complete file-based heightmap migration — tEXt sea_level, client relief, drop BLOB (#963)
- heightmap.rs: read sea_level from the PNG tEXt chunk (bake writes it),
  default-fallback param; new test reads_sea_level_from_text_chunk.
- client atlas_viewer.gd: load reliefmap.png (color display) instead of
  heightmap.png (now 16-bit grayscale elevation, cascade-only).
- drop atlas_body_heightmaps: removed from systems-schema.sql; DROP TABLE in
  import_economics MIGRATION_SQL (the PNG is the store now).
- D-202 amendment: implementation-status note (consumer + producer done),
  resolving the review's 'reads done but producer pending' point.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:23:23 +02:00
jpmschweitzerandClaude Opus 4.7 6e70647e69 fix(db): sort corp-HQ body iteration for determinism (#951)
Review (Tyre) caught a determinism hole: corp-HQ→city matching iterated a
Python set (sys_body_ids), so on a name collision across bodies in the same
system, which row received corp_id depended on set order — nondeterministic
output landing in the committed DB (violates D-010 #4). Iterate sorted().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:09:14 +02:00
jpmschweitzerandClaude Opus 4.7 b0bfbfc7dd feat(db): import_economics owns atlas index — names pool, empty geometry (D-223 #951)
Rework the regen-db atlas path for the names-only marker pool:

- populate_atlas_city_names now reads the names.cities pool instead of
  the retired geometry-bearing cities[] records; population/kind are
  deferred to placement (#955). Adds a deterministic clear-then-insert
  (no UNIQUE on (body_id, name)) that fixes a latent duplicate-
  accumulation bug — atlas_city_names dropped from an inflated 3276 to a
  clean 329 pooled names + 134 corp-HQ rows.
- ensure_atlas_index_schema applies the canonical ATLAS INDEX block from
  systems-schema.sql and empties the 8 geometry tables every regen; the
  Phase 4 server cascade fills them (they start empty — the revealed gap).
- Sol (system 'GJ 0') is permanently exempt from the normal generators:
  skipped in both the name-pool importer and the corp-HQ cross-ref.
- MIGRATION_SQL drops the retired generate_atlas meta stamp row so the
  fail-closed stamp checker accepts older committed DBs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:28:53 +02:00
jpmschweitzerandClaude Opus 4.6 29f9945291 feat(db): body_radius_km scatter + gas giant/moon scale classes
Deterministic ±scatter on body radii seeded by body_id hash — no two
bodies share the same radius. Gas giants 40k-60k km, moons 200-2600 km,
rocky planets ±15% from class base. Oort/asteroid skip radius (NULL).
Sol system gets real planetary radii. body_radius_km exported to
star_map_data.json for client orbital diagram sizing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 20:11:31 +02:00
jpmschweitzerandClaude Opus 4.6 332b9404be fix(simulation): address PR #142 review — 11 issues resolved
Rust fixes: D-218 Backwater complexity (moved to Full arm), priority
queue dispatch gated on thread saturation, panic→soft-fail in
name_index, duplicated LCG unified into atlas::rng module, misleading
Safety comment removed, D-210 SubBiomeVariant deferred to #948.

Schema/data fixes: atlas_city_positions table (D-211 persistence),
settlement_class column on atlas_city_names, per-body transaction
boundaries in import_province_boundaries, import_city_names.py added
to GENERATOR_SOURCES, Python perf documented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 15:30:50 +02:00
jpmschweitzer 64a873fc1a Merge remote-tracking branch 'origin/main' into sprint-38/server
# Conflicts:
#	CHANGELOG.md
#	server/data/systems.db
#	tooling/check-systems-db-stamp
#	tooling/economy-db/import_economics.py
#	tooling/planet-gen/generate_atlas.py
2026-05-03 09:30:48 +02:00
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
jpmschweitzerandClaude Opus 4.6 873746ff8b fix(ci): address PR #139 review — 5 issues + source list sync
1. decisions_sync.py: fix refs_created inflation (check rowcount),
   remove dead IntegrityError except block
2. Extract SCHEMA_VERSION to shared tooling/schema_version.py —
   both generators import from single source of truth
3. generate_atlas.py: narrow bare except to OperationalError +
   "duplicate column" check
4. check-systems-db-stamp: add cross-generator schema_version
   agreement assertion (defense-in-depth)
5. decision wrapper: add show + orphan-tickets to usage text
6. Add schema_version.py to all three source watch lists
   (GENERATOR_SOURCES, IMPORT_ECONOMICS_SOURCES, generate_atlas
   _write_stamp) — prevents silent staleness on version bump

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 10:29:24 +02:00
jpmschweitzerandClaude Opus 4.6 5aa998cb86 chore(db): switch meta.schema_version to monotonic semver (#888)
Replace SHA-1 hash in meta.schema_version with an orderable semver
string ("1.0.0"). SHA preserved in new schema_sha column for tamper
detection. Enables savegame migration lineage in Phase 5+ — saves can
record their schema version and determine which migrations to apply.

Updated both generators, check-systems-db-stamp validation (rejects
old SHA-hex values), schema DDL, and asset-pipeline docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 10:09:13 +02:00
jpmschweitzerandClaude Opus 4.6 3d9dd7d909 chore(ci): merge brand pipeline into import_economics + harden review findings
Addresses all blocking + minor items from PR #136 review.

Architectural change (T2/H3 — the review's main complaint):

  generate_brands was previously a separate Rust binary that produced a TOML
  artifact, with its stamp written "on behalf" by import_economics.py at the
  end of its own run.  Reviewers flagged the invisible coupling: two sources
  of truth in a system designed to have one, and no way to tell from the
  stamp that one "generator" was really a subroutine of the other.

  import_economics now invokes tooling/generate-brands as the first step of
  its main() flow, before opening its own DB connection.  The TOML artefact
  is still produced and still committed (useful for diff-review of brand
  changes), but there's now one pipeline owner.  The meta table carries two
  rows (import_economics, generate_atlas) not three; the Rust binary's
  source SHA folds into import_economics' stamp via IMPORT_ECONOMICS_SOURCES.
  A MIGRATION_SQL DELETE cleans up pre-merge DBs that still have the
  orphan generate_brands row.

Other review items addressed in-line:

  H1  generate_atlas._write_stamp no longer commits — transaction ownership
      stays with the caller (matches import_economics pattern).  Stamp +
      atlas data now commit atomically; a failed stamp rolls back the
      atlas data rather than leaving a stamp-missing-data intermediate.

  H2  _file_sha1 (in both import_economics, generate_atlas,
      check-systems-db-stamp) raises FileNotFoundError on missing sources
      instead of silently contributing an empty-bytes hash.  A ghost-SHA
      convergence could otherwise produce vacuous "fresh" passes.

  H4  pre-push no-meta-table warning rephrased — was "run after next
      regeneration", now "run now if this DB was generated by you".

  T1  asset-pipeline.md determinism claim softened: the stamp is
      deterministic (same source → same recorded SHA), the DB binary is
      not (generated_at + SQLite rowids/freelist churn).

  T3  asset-pipeline.md gains a "migration escape hatch" section naming
      MIGRATION_SQL in import_economics.py as the only sanctioned path
      for direct writes, and forbidding hand-run sqlite-exec / one-off
      patch scripts / SQLite-GUI edits.

  T4  Makefile regen-db now runs as a single shell with `set -e`.  A
      failure in one generator halts the pipeline immediately, preventing
      the "stale data, fresh stamp" state where a later step stamped a
      DB whose earlier step had failed.  import_economics' exit code 2
      (coverage gate warning) remains explicitly tolerated.

  T5  pre-push stamp check now runs on a branch's first push too —
      compares against origin/main instead of origin/$BRANCH, closing
      the gap where a new branch could ship a stale DB via the first push.

  T6  check-systems-db-stamp fails closed on unknown generator_names in
      meta — a future branch adding a new generator without registering
      it in GENERATOR_SOURCES will now be rejected, not silently skipped.

  T7  /pr-push watch list gains a mutual cross-reference comment with
      GENERATOR_SOURCES in check-systems-db-stamp, plus the missing
      names.rs source file, so the two lists cannot silently drift.

Follow-up tickets created:

  #887 T8  decisions-orphan-tickets CLI — surfaces tickets whose
           decision_ref points at a non-existent D-record.
  #888 T9  meta.schema_version monotonic semver — for savegame migration
           lineage in Phase 5+ (SHA comparison can't be ordered).

Verified:

  make regen-db end-to-end — OK
  make check-systems-db    — OK, 2 generator(s) up to date
  STALE detection          — OK, verified by touching generate_atlas.py
  /pr-push watch list      — OK, flags this branch's changed sources
  decision show D-159      — OK, structured output with tickets + refs

Refs: #855 #856 #857 #858 #859 PR #136

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-22 08:38:41 +02:00
jpmschweitzerandClaude Opus 4.6 8371e05e52 chore(ci): generator-driven asset pipeline — meta stamp + hook + regen target
Adds systems.db regeneration discipline (#855) via a `meta` table (#856)
stamped by every generator, a pre-push hook that rejects stale DBs (#857),
and the top-level `make regen-db` / `make check-systems-db` targets that
drive the whole pipeline.

The stamp stores SHA-1 of generator source + schema, so the pre-push hook
can cheaply detect "you changed a generator but forgot to regen the DB"
before a binary merge conflict lands. Sprint 36 hit that class of conflict
on two branches touching systems.db simultaneously — this is the systemic
fix.

Regenerated systems.db is stamped; `make check-systems-db` passes.

Refs: #855 #856 #857

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 17:34:52 +02:00
jpmschweitzer c3ff7040ae Merge remote-tracking branch 'origin/sprint-36/server'
# Conflicts:
#	server/data/systems.db
2026-04-19 18:45:06 +02:00
jpmschweitzerandClaude Sonnet 4.6 add2507eac fix(content): add price_tier to brand layer schema, import, and V-B06 (#828 blocker)
price_tier was documented in D-189 §5 and present in brands.toml but
silently discarded on import — absent from schema, INSERT, and V-B06.

- Add price_tier TEXT column to brand_products CREATE TABLE
- Add COLUMN_MIGRATIONS entry for ALTER TABLE on existing DBs
- Add VALID_PRICE_TIERS constant (mass/premium/luxury/flagship/institutional)
- Include price_tier in product_rows tuple and INSERT OR REPLACE
- Add price_tier to V-B06 enum checks; skip NULL (nullable column)
- Backfill 4 pre-amendment anchor brands (8 entries): Calloway flagship/premium,
  VGV luxury/premium, thrds luxury/premium, Bífröst flagship/luxury

V-B01..V-B06 all pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 15:37:33 +02:00
jpmschweitzerandClaude Sonnet 4.6 e11a9c308a feat(engine): generate_brands pipeline — 10K minor brands from templates (#829)
New generate_brands binary reads 126 brand archetype templates
(wiki/economics/archetypes/brand_templates.toml), assigns halo+volume pairs
to all 48 hand-authored corps, outputs wiki/economics/corporations/generated_brands.toml.

Result: 10,000 brand_product rows, 26,750 brand_inputs, all 48 corps covered.
Brand structural validation V-B01..V-B06 passes. Generated file is gitignored
(regenerated on each `make economy-db` run).

make economy-db now runs generate-brands before import_economics.py.
import_economics.py merges generated_brands.toml alongside hand-authored brands.toml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 13:15:03 +02:00
jpmschweitzer 64bb83f749 fix(simulation): address PR #129 review — brand importer hardening
- V-B06 enum validation: the five VALID_* sets
  (VALID_BRAND_CATEGORIES, VALID_VALUE_TRAJECTORIES, VALID_SCARCITY_CLASSES,
  VALID_BRAND_TIERS, VALID_CURRENCY_DENOMINATIONS) were defined but never
  referenced. brand_products.brand_category etc. are plain TEXT with no
  CHECK constraints, so a typo like `brand_category = "terrior"` silently
  imported. `validate_brands` now runs a V-B06 pass that asserts every
  enum column is a member of its VALID_* set. V-B01..V-B05 + V-B06 all
  reported together on import failure.

- Explicit transaction wrapper: the clear-then-reimport cycle (10 DELETEs
  followed by 9 imports and structural validation) used to depend on
  Python's implicit-deferred-transaction semantics and sys.exit() on
  validation failure. A crash mid-import could leave the DB with some
  tables empty and others intact. The body now runs inside
  `conn.execute("BEGIN")` + try/except with an explicit `_ImportAborted`
  for validation failures and a `BaseException` catch-all for
  KeyboardInterrupt / programmer errors. All failure paths rollback
  before exit; the commit only fires after structural validation
  passes. Dry-run leaves the transaction open so the coverage check
  below can still SELECT against in-memory state.

- system_fiscal docstring: previously cited the D-189 §6 derived
  formula (`collection_efficiency = 1.0 - shadow_economy_intensity × 0.6`)
  while the implementation hardcodes `collection_efficiency = 0.85` for
  every system. The docstring now explicitly states these are Phase 2
  placeholder values (with named constants PHASE2_CORP_TAX_RATE and
  PHASE2_COLLECTION_EFFICIENCY) and calls out the shadow_economy.toml
  pipeline as the Phase 3 follow-up.
2026-04-15 09:15:16 +02:00
jpmschweitzer 03e0d1c022 feat(simulation): brand layer schema and import pipeline (#827)
Adds the brand layer per D-189 §5:
- Schema: brand_products, brand_inputs, system_fiscal, corp_financial_state,
  corp_lifecycle_events (+ 5 indexes).
- Importer: reads wiki/economics/corporations/brands.toml, populates the
  new tables, validates V-B01–V-B05 structural rules, and derives
  system_fiscal for inhabited systems.
- Data: 8 brand_products, 16 brand_inputs, 301 system_fiscal rows.

Brand products are demand nodes — they consume commodities; they are not
commodities themselves (D-185). Depends on copy PR #127 for the corp
records referenced by brands.toml.
2026-04-14 17:24:51 +02:00
jpmschweitzerandClaude Opus 4.6 dcd1c1cf4e feat(simulation): add EconEvent stub and import currency zones from TOML
Add D-180 EconEvent struct (target, effect, duration, visibility enums)
with no-op handler to satisfy #809 spec. Import MARK_PRIMARY and MIXED
currency zone assignments from wiki/economics/currency_zones.toml (D-172).
All four D-179 stability tests now pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:04:59 +02:00
jpmschweitzerandClaude Sonnet 4.6 f79de07bb3 fix(simulation): resolve corp_presence location granularity bug (#805)
import_corp_presence() was inserting system IDs with location_type='system',
violating the schema which expects body/station IDs (location_type='body'|'station').

Fix:
- import_economics.py: add _resolve_hq_location() that picks the most-populated
  body in the HQ system (falling back to any body, then any station)
- econ-sim/db.rs: load_corp_presences() now JOINs bodies/stations to recover
  system_id from body/station location_ids, dropping the 'system' filter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 10:59:58 +02:00
jpmschweitzerandClaude Sonnet 4.6 3447b38bcb feat(simulation): extend economy-db pipeline with corporation sync and validation (#805)
Extends import_economics.py from 6-step to 8-step pipeline:
- Loads wiki corporation markdown frontmatter as authoritative source
- Syncs corporations table (hard error on proper_name divergence per D-182)
- Populates corp_presence table (one row per corp × headquarters system)
- Splits validation: structural checks block commit; coverage checks post-commit
- D-175 Phase 2 gate: 3+ corps per commodity, 1+ corp per system with pop > 100K

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 13:55:40 +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 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