Commit Graph
191 Commits
Author SHA1 Message Date
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 0251c65b96 feat(wiki): surface D-237 specialization fields in the system infobox
generate_infobox() now renders economic_specialization (Specialization) and
cultural_specialization (Cultural Register) as read-only infobox rows when
authored (NULL = omitted, falls to heuristic). dominant_faction already
showed via Governance. Makes the authored generator-input values visible on
the wiki face so the #1016 content pass output renders. Verified: Ran shows
'breadbasket'/'agrarian'; unauthored systems omit the rows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 14:29:37 +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 a4541bde18 chore(db): raise ticket WIP limit 3 → 5
Per maintainer call — 3 was too tight for the parallel Phase 4 work in
flight; 5 better matches how many tickets are genuinely active at once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 19:33:42 +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 9a10c6ffd6 chore(deps): rand 0.9.4 + compatible sweep + serde_yaml→serde_norway (#966)
Focused Rust dependency-maintenance pass from the 2026-05-23 security/freshness
review. No CVEs; one advisory cleared and one deprecated crate replaced.

- rand 0.9.2 → 0.9.4 (lockfile): clears RUSTSEC-2026-0097 (unsound with a
  custom logger using rand::rng()). Semver-compatible; rand 0.10 is a separate
  major.
- Compatible-update sweep: ~90 lockfile-only patch/minor bumps (bevy 0.18.0→
  0.18.1, clap 4.5→4.6, rayon 1.11→1.12, pathfinding 4.14→4.15, uuid 1.20→1.23,
  zerocopy, serde_json, tracing-subscriber, etc.). cargo test green.
- serde_yaml 0.9 (deprecated/archived upstream) → serde_norway 0.9, an actively
  maintained drop-in fork. In the server it is test-only (poi.rs round-trip,
  trait_modifiers.rs fixture, tests/news_ticker.rs) so it moves to
  dev-dependencies; line-previewer parses dialogue/monologue pool YAML at
  runtime, so it keeps it as a normal dependency. API is identical (from_str/
  to_string).

news_ticker.rs also picks up its share of the #967 clippy sweep (HashSet/HashMap
→ BTree, doc-list indent) since it is the same file as the serde rename.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 11:02:57 +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 3b2cd6914e feat(tooling): rewrite import_heightmaps as the 16-bit heightmap + relief bake (#963)
Replaces the dead atlas_body_heightmaps DB-BLOB importer (Tyre review B2 —
it asserted 256×512 and wrote a dropped table) with the canonical asset bake:
- one-time rename of the legacy color heightmap.png → reliefmap.png for ALL
  bodies (incl. Sol — display-file rename only, no re-sim);
- for each non-Sol inhabited body: simulate() at 1024×512 → clean reliefmap.png
  (render_heightmap, no painted features) + 16-bit grayscale heightmap.png with
  sea_level in a tEXt chunk;
- no systems.db writes; uses parse_system for proper body defs; run via uv.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:22:50 +02:00
jpmschweitzerandClaude Opus 4.7 dd296b1ae5 test: review-driven determinism + coverage (#953 #963)
From the Hoshe/Tyre review:
- drainage: assert flow_accumulation/max_accumulation determinism + clamp ≥ 1
  (the D-209 strength denominator); isolated-basin merge path (no panic).
- subbiome: each derivable variant reachable + Volcanic never emitted.
- planet_simulation: new test_sim_determinism.py — same body simulates to a
  bit-identical elevation array at 1024×512 (the 271-body bake can't be cheaply
  re-run, so a silent drift = full re-bake). Verified PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 08:09:26 +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 cabdd7c097 refactor(tooling): bump planet sim to native 1024×512, drop compute_rivers (#963)
- planet_simulation: GRID 512×256 → 1024×512. The elevation noise is
  resolution-independent (normalized coords + absolute freqs), so the
  finer grid samples the SAME terrain — features keep physical size,
  generation stays deterministic. Pixel-unit constants (gaussian sigma,
  crater radii, peak-filter window, erosion slope) scale by GRID_W/512.
  Validated: non-Sol bodies render same-world-crisper at 1024.
- Remove compute_rivers + _rivers_to_grid + the rivers/river_grid terrain
  keys: rivers are the Rust cascade's job (D8 drainage, D-208), the single
  source of river truth. The old heuristic didn't even reach the sea.
- render_heightmap: stop painting rivers onto the relief (cascade/Atlas
  overlay computed rivers instead).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 01:31:52 +02:00
jpmschweitzerandClaude Opus 4.7 b11e847337 chore(tooling): retire atlas geometry generator + LLM naming cluster (D-223 #951)
The procedural server cascade (Phase 4) and the frozen names-only pool
supersede the Python atlas geometry generator and the LLM namer. Retire:

- generate_atlas.py (geometry production — cities/roads/rivers placement)
- gemma_naming.py, naming_core.py + tests (test_batch_naming,
  test_register_selection, qa_naming) and run-atlas-naming.sh (the LLM
  place-namer; its output is now the frozen pool)
- apply_name_fixes.py (name-field patches), fix_fewshot_bleed.py /
  prune_atlas_features.py (geometry tools)
- import_city_names.py (redundant with import_economics name-pool path)

Pipeline updates: drop the generate_atlas step + atlas-generate /
test-atlas-determinism targets from the Makefile; remove generate_atlas
from the stamp registry (import_economics is the sole regen-db generator);
drop run-atlas-determinism from tests/run-all; refresh stale references in
schema_version, backfill_cultural_corridor, earth_blocklist (kept as
reference data), populate_terrain_reference, and heightmap.rs.

The Gemma prompting methodology is preserved in
docs/gemma-naming-methodology.md (separate commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:29:08 +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.7 642be3ac41 refactor(tooling): extract atlas_common from generate_atlas (D-223 #951)
Move the shared atlas-DB utilities (schema application, inhabited-body
query, body-def loader, grid constants) out of the soon-to-be-retired
generate_atlas.py into a dedicated atlas_common.py with no dependency on
geometry-production code. Repoint the surviving build-time importers
(import_heightmaps, import_province_boundaries) at the new module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:28:38 +02:00
jpmschweitzerandClaude Opus 4.7 d87e700039 fix(tooling): remove unused import in fill-missing-globes.py
ruff F401 — unused 'import os'. Pre-existing in the globe-coverage script; ruff
runs at pre-push (not pre-commit), so it surfaced only now and blocked the push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 18:43:00 +02:00
jpmschweitzerandClaude Opus 4.7 bf3659d1a5 fix(meta): clerk-review — incomplete reviews no longer false-reject
The per-commit clerk had three flaws, exposed by a 20-commit push where 6 of 7
rejections were false (incl. a CHANGELOG-only commit):

1. No-verdict / max-turns / timeout defaulted to REJECTED — an unfinished review
   read as 'hard contradiction found'. Now a third outcome, INCOMPLETE, which is
   non-blocking (the push proceeds with a warning); only a real REJECTED blocks.
2. Turn/time budget too tight (6 turns / 150s) for decision-heavy commits. Raised
   defaults to 15 turns / 300s, and the prompt now biases to APPROVED when no
   concrete contradiction is found ('unsure' means APPROVED, never REJECTED).
3. The skip valve matched its own feature commit because it scanned for the token
   anywhere in the message. Moved to a trailer-line match so prose/subject mentions
   no longer trip it.

Pre-push hook updated to treat INCOMPLETE as a non-blocking warning. The git-commit
skill documents the trailer form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 16:59:44 +02:00
jpmschweitzerandClaude Opus 4.7 606c732bdd feat(meta): add [clerk-skip] safety valve to clerk-review
A commit whose message contains the token [clerk-skip] is auto-approved by
the pre-push clerk without spawning an agent. Intended for bulk content/data
commits — e.g. shipping thousands of generated planetary description files —
where D-record review is moot and would only burn agents on noise.

Documented in the /git-commit skill with the caveat: never use it on commits
that touch decisions/, code, or ticket-bearing work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:41:28 +02:00
jpmschweitzerandClaude Opus 4.7 a854db5f84 chore(meta): clerk-review reviews per-commit in parallel
The clerk pre-push review spawned a single claude -p over the entire combined
diff (truncated at 50k chars) with a 3-turn budget. On a large push (e.g. 754
files / 2.8M chars) it ran out of turns before emitting a verdict, which the
wrapper defaulted to REJECTED.

Rewrite to review one commit at a time — commits are the logical units, so each
clerk agent sees a self-contained change plus its commit message (enabling the
'does this match ticket #NNN?' check). Commits are reviewed by a bounded pool of
parallel clerk agents and the verdicts aggregated (REJECTED if any commit
contradicts an active D-record). Oversized commit diffs truncate to a budget.

Adds --plan (print the per-commit plan, no agents spawned) and env knobs:
SR_CLERK_COMMIT_BUDGET / SR_CLERK_WORKERS / SR_CLERK_MAX_TURNS / SR_CLERK_TIMEOUT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:29:06 +02:00
jpmschweitzerandClaude Opus 4.6 903efda191 feat(assets): 100% globe coverage — generic images + donor fill
Generated oort cloud and asteroid belt generic globe images via Gemini.
Background-masked with flood-fill (shadow-preserving). Donor fill script
copies matching globe.png from same planet_class+body_type pool with
per-body hash selection, no repeats within a system. 629 bodies filled,
3240/3240 now have globe.png.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 20:12:00 +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 e6a557e8e7 feat(meta): replace sprint workflow with kanban + milestones (D-221)
Sprint-based workflow (38 sprints) replaced by kanban + milestones.
Milestones are many-to-many with tickets and can block each other.

New: /whats-next skill (dependency-driven batch selection with Si
refinement review), /pr-process skill (renamed from pr-push, adds
review comment pickup), clerk agent + pre-push hook for D-record
consistency checks.

Deleted: sprint CLI, sprint-start/sprint-plan/sprint-status skills,
team-scoped file restrictions. Si rewritten as refinement manager.
All 19 agent briefings updated from stale PROJECT_STATE.md reference
to live ticket milestone queries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 20:11:00 +02:00
jpmschweitzerandClaude Opus 4.6 13259dbfd5 feat(client): render province boundaries on planetary map (#927, D-205)
Export pre-computed drainage basin boundaries from atlas_province_boundaries
into markers.json during generate_atlas. AtlasMarkerOverlay draws province
polylines with semi-transparent fill under the political_zones overlay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 16:12:59 +02:00
jpmschweitzerandClaude Opus 4.6 0d09d905e4 fix(simulation): address PR #142 review round 2
gen_queue: add in_flight_count tracking for all work item types (not
just AnalyzeBody). Rewrite saturation test with AnalyzeBody items.
Fix priority_ordering test thread count to match new gate.

rng: collapse to single AtlasRng::new(seed) constructor — callers
own their seed transform.

import_province_boundaries: fix "savepoint" comment to "transaction".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 15:51:10 +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
jpmschweitzerandClaude Opus 4.6 5ad8754b85 fix(atlas): remove unused imports in import_province_boundaries.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 09:46:53 +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 d57f0566d2 fix(atlas): suffix monotony auto-fix + cultural-history prompting (#886)
gemma_naming.py now re-queries affected bodies when >40% suffix
clustering is detected. naming_core.py build_batch_prompt accepts
cultural_history param threading secondary corridor substyles into
the few-shot prompt for richer cross-cultural name blending.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 18:40:21 +02:00
jpmschweitzerandClaude Opus 4.6 91066e4be6 chore(db): expand stamp GENERATOR_SOURCES for naming helpers (#892)
Add gemma_naming.py and naming_core.py to generate_atlas source
tracking in check-systems-db-stamp. Mirror in pr-push watch list.
Prevents naming helper changes from bypassing stale-stamp detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-02 18:40:12 +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 ffa638a46c chore(db): add decisions-orphan-tickets CLI (#887)
New `tooling/db/decision orphan-tickets` subcommand scans tickets with
a decision_ref that doesn't match any row in the decisions table.
Surfaces silently orphaned tickets from typo'd or renumbered D-IDs.
Makefile target: `make decisions-orphan-tickets`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 10:09:03 +02:00
jpmschweitzer 94a687fc08 Merge remote-tracking branch 'origin/main' into sprint-37/server
# Conflicts:
#	CHANGELOG.md
#	Makefile
#	server/data/systems.db
2026-04-22 09:01:43 +02:00
jpmschweitzerandClaude Opus 4.6 531bb1df5c refactor(assets): atlas naming — dedup, compass filter, river vocab, infra names (#853)
Addresses five of seven subtasks from atlas-generator-refinement-notes:

- Cross-body dedup: corpus keyed by (corridor, feature_type) instead of
  (system_id, feature_type), seeded from existing atlas_* rows so re-runs
  don't collide with already-committed names. §1, §2.
- Empty-name fallback for mountain ranges when Gemma returns fewer names
  than needed ({body_proper} Range {i+1}). §2.
- Suffix monotony detection: flags bodies where >40% of mountain names
  share a trailing word (warning only — batch pipeline has no voice
  access for auto-fix). §3. Follow-up in #886.
- Compass-direction ban: build_batch_prompt explicitly forbids
  "Eastern/Northern/Western X" in few-shot instructions. §4.
- River vocabulary filter: is_valid_name rejects "X Flow" / "X Current"
  when feature_type="river" — these are ocean terms bleeding through. §6.
- Infrastructure naming: deterministic post-pass assigns "{CityA}–{CityB}
  {corridor_suffix}" to unnamed roads and railroads (Corridor/Road/Estrada/
  Strasse/Track by corridor). §7.

Cultural-history prompt threading (§5) remains as existing corridor_substyles
refill mechanism; explicit cultural-history blurb deferred to #886.

naming_core bumped to v0.3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-22 08:54:37 +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 a41f7aa06c feat(tooling): add decision show <D-NNN> with implementing tickets + refs
Closes the decision-to-ticket coverage gap (#723). The `decisions-coverage`
Makefile target already reported per-decision counts; this adds the
single-decision drill-down via `tooling/db/decision show D-159`, returning
linked tickets, outbound refs, and inbound refs in one shot.

Schema unchanged — reverse link is a SELECT on tickets.decision_ref.

Refs: #723

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 17:35:23 +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
jpmschweitzer 9c69f0bd9d Merge remote-tracking branch 'origin/sprint-36/copy' 2026-04-19 16:42:57 +02:00
jpmschweitzerandClaude Opus 4.7 6d9bb6eadd fix(content): PR #133 round 2 review — 3 remaining Miri items
- tooling/planet-gen/earth_blocklist.txt: document GJ0d (Earth body) as
  blocklist-exempt so Brussels (and other Earth-canonical city names) are
  not flagged on next sol_import.py run (R2 issue 1)
- wiki/star-systems/GJ-380/bodies/GJ380c/markers.json: rename Selet Basin
  → Subin Basin (Kumasi river namesake). Brings Akan register on GJ380c
  to 3/29 features distributed across river, mountain, lake — credible
  multi-generational trade corridor read instead of minimum-viable patch
  (R2 issue 2)
- server/data/systems.db: atlas_oceans resynced for GJ380c
- docs/atlas/hand-refine-log.md:119: corrected stale log entry — Aldren
  Pass was subsequently renamed Randalfoss to eliminate the cross-system
  Aldren stem collision with GJ380c (R2 nit 3)
- tooling/planet-gen/refine_log_849.md: Groombridge cross-corridor
  addendum updated to reflect 3/29 Akan register distribution

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:34:14 +02:00
jpmschweitzerandClaude Opus 4.7 98b2a9ed94 docs(content): refine log — Paula's Earth rebalance header entry
Parallel addition to the rebalance addendum already in the GJ 0 Sol
section — Paula's header-style entry at the summary level complements
the in-context detail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:52:00 +02:00
jpmschweitzerandClaude Opus 4.7 2c3e3ff5eb fix(content): PR #133 review — 9 remaining items resolved
Closes 9 of 10 review items (blocker 1 handled in add2507e + d78d3b59):

- decisions/economics.md: reformat D-189 §5 amendment to standard
  Amendment (YYYY-MM-DD) block pattern (review #4)
- tooling/planet-gen/sol_name_fixes.py: dedup guard + argparse
  --dry-run (reviews #5, #10a)
- tooling/planet-gen/sol_markers/earth_features.json: trim to 11
  cities with selection rationale (review #6A); user-approved
  rebalance Sydney → Lagos and London → Brussels (review #6B)
- wiki/star-systems/GJ-380/bodies/GJ380c/markers.json: 2 secondary
  features renamed to Akan/Asante register — Kesset → Nkwanta Beck,
  Holt Spur → Bosomtwe Spur (review #7)
- docs/atlas/hand-refine-log.md: Aethelred lore-migration
  documentation + see-also cross-link to refine_log_849.md
  (reviews #8, #13)
- tooling/planet-gen/refine_log_849.md: rebalance addendum
- wiki/star-systems/GJ-0/bodies/GJ0d/markers.json, server/data/
  systems.db: re-synced after rebalance

Stub depth (review blocker #2) handled via split — tracked as
follow-up ticket #861 (three-layer narrative authoring).

Final Earth cities (11): Beijing, Brussels, Cairo, Delhi, Istanbul,
Lagos, Moscow, New York, São Paulo, Singapore, Tokyo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:51:17 +02:00
jpmschweitzerandClaude Sonnet 4.6 d78d3b599c fix(content): argparse --dry-run for generate_corp_stubs.py; populate cross_refs (#828 nits)
- generate_corp_stubs.py: add argparse with --dry-run (prints planned stub
  creations without writing files) and --help
- calloway-distillery.md: cross_refs → [thrds, mercado-travessia]
- thrds.md: cross_refs → [calloway-distillery, mercado-travessia]
- vins-de-grand-vide.md: cross_refs → [nordmark-skog]
- thalassa-resort-group.md: cross_refs → [the-registry, prometheus-labs]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 15:39:41 +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 Opus 4.7 cf3e14cc5e chore(tooling): remove unused imports in generate_corp_stubs.py
Fixes F401 errors caught by pre-push hook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:50:12 +02:00
jpmschweitzerandClaude Opus 4.6 48c8fb7dea fix(ui): PR #131 review round 2 — manifest cleanup, system index helper, stderr help
Addresses 6 mechanical issues from PR review:

- ImplantAppManifest: drop dead display_name and icon_path fields.
  default_key carries a TODO noting its future migration to a keybinds
  manifest (input concern in app manifest is a layering violation,
  tracked explicitly).
- default_mode wire format is now a String ("fullscreen" / "insert") for
  mod-author discovery. ImplantRegistry parses via _MODE_MAP, caches the
  resolved int in _resolved_modes, and exposes get_resolved_mode(app_path).
  main.gd reads the resolved int directly instead of re-parsing.
- Extract client/ui/implant/widgets/system_index.gd (class_name SystemIndex,
  static get_sorted_systems). Removes duplicated star_map_data.json loader +
  sort lambda from atlas_app and economics overview_screen.
- ImplantApp.on_insert_deactivated() default auto-closes only when the app
  is active in INSERT mode. FULLSCREEN apps no longer spuriously close on
  insert state changes.
- tooling/db/sqlite-query and sqlite-exec: --help output goes to stderr
  (exit 0). Keeps stdout reserved for JSON payloads so JSON-parsing
  callers can't get silently corrupted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 14:21:03 +02:00