Commit Graph
204 Commits
Author SHA1 Message Date
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 ccc194d5f9 fix(meta): address PR #155 review — DEVOPS layout + common.py cleanup
Hoshe (QA): docs/DEVOPS.md Repository Layout still listed `decisions/` — corrected to
`governance/` (the DQR tree) and added a `.pql/` entry for the planning store.

Tyre (architecture, non-blocking): tooling/db/common.py docstring named deleted scripts
as consumers and `resolve_db_path`/`load_config`/`get_connection` were dead settledreach.db
code. Trimmed common.py to just `ensure_venv` (the only symbol any kept connector imports)
and rewrote the docstring to name the real consumers.

ruff clean; common.py parses; ensure_venv intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 23:06:39 +02:00
jpmschweitzerandClaude Opus 4.8 5a399c7924 chore(meta): retire legacy SQLite ticket/decision tooling (pql migration phase 6)
The pql cutover is stable, so remove the superseded SQLite planning tooling. Surgical
— only the ticket/decision/raw-SQL scripts (all settledreach.db-bound and replaced by
pql) are deleted; the asset/audio/wiki connectors and shared common.py stay.

Removed:
- tooling/db/{ticket,decision,decisions-sync,decisions_sync.py,sqlite-query,sqlite-exec,
  sqlite-init,sqlite-seed,sqlite_connector.py}
- tooling/{db-backup,db-install} + docs/backups/settledreach.db.backup (the binary-DB
  backup ritual; tickets now live in the git-tracked .pql/changelog/)
- tooling/check-decision-ids (dead stub, superseded by `pql decisions validate`)
- Makefile db-backup/db-install targets; SR_DB_PATH + tooling/db/{ticket,sqlite-*,
  decision*} entries from .claude/settings.json (audio entries kept)

Updated docs to pql: DEVOPS.md (SQLite Access + Decisions System → pql), project
structure, ticket-cli closing note, asset-pipeline raw-SQL warning.

Kept (verified still imported by the asset connectors via common.ensure_venv): common.py,
config.json, audio/image/trellis/wiki connectors. The live settledreach.db file
(gitignored, repo-parent) is left on disk as a cold rollback only.

ruff clean; pql decisions validate ok (357 decisions / 1013 tickets).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 22:13:29 +02:00
jpmschweitzerandClaude Opus 4.8 f8d9b777b0 fix(config): address PR #154 review — gitattributes glob + script roots
Tyre (architecture review):
- .gitattributes: `.pql/changelog/*.sql` matched nothing (files are one level
  deeper at .pql/changelog/<table>/<YYYY-MM>.sql), so the union-merge driver never
  applied — `git check-attr merge` returned `unspecified`. Fixed to
  `.pql/changelog/**/*.sql`; now resolves to `merge: union` for monthly + schema
  files. Restores the changelog's conflict-free merge guarantee.
- Migration scripts: the re-runnable ones (seed_tickets.py, add_workshop_provenance.py)
  now derive the repo root from `git rev-parse --show-toplevel` instead of a hardcoded
  /main path, so re-running from a worktree/clone targets the right checkout. The three
  one-shot transforms (restructure_decisions, repath_references, retag_ticket_refs)
  get a comment noting they're already-applied and unsafe to re-run (git mv on moved
  sources) — keeping the path honest rather than implying re-runnability.

Hoshe approved (all QA checks passed). ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:05:31 +02:00
jpmschweitzerandClaude Opus 4.8 943f2e7749 docs(workshops): add decision_refs provenance frontmatter (pql migration phase 5)
Workshop -> decision provenance was prose-only. Adds a `decision_refs:` YAML
frontmatter list (the confirmed D-records each workshop-outcomes.md touches,
filtered against the governance decision set) to all 17 workshop outcomes; the two
that lacked frontmatter (commodity-catalog, system-economic-specialization) get a
minimal block. pql indexes the list and `SELECT fm.decision_refs` round-trips it, so
"which workshops touch D-NNN" is answerable via SELECT + filter or `pql search`.

decision_refs is a relevance signal (decisions a workshop discusses/produces), not a
strict authorship claim — historical bare refs aren't disambiguated. Generated wiki
read-only sections are left untouched.

Noted in pql-requirements #5: 1.6.2 has no working DSL operator for frontmatter
list-membership (`~`/`contains` error, `in` matches nothing), so membership queries
need a client-side filter for now.

Reproducible via tooling/pql-migrate/add_workshop_provenance.py (idempotent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 13:06:33 +02:00
jpmschweitzerandClaude Opus 4.8 aefbb4bd88 docs(meta): switch ticket-reference convention #N -> T-N (pql migration phase 3)
Adopts the T-NNN convention (T-N == old #N == pql ticket id) across the active
operational layer: governance/ decision records, .claude/{rules,agents,skills},
CLAUDE.md, DECISIONS.md. 283 references rewritten.

Guarded against false positives (17 correctly skipped, each logged):
  - PR references kept (PR #136/#138/... — PRs are a separate #-namespace)
  - non-ticket numbers kept (#4122; the "#1 process failure" idiom; "task #3")
  - only #N where N is an actual ticket id is rewritten; the 1-4 digit word-bounded
    match also excludes 6-digit hex colours in the visual decision records

Git history is NOT rewritten (a commit's #N already equals T-N numerically), and
historical archives (docs/sprints, docs/discussions, docs/workshops) keep their
point-in-time #N. The /pr-process ticket-ID extraction logic moves to T-NNN in the
Phase 4 consumer cutover.

Verified: pql decisions validate ok; sync 357 records / 1057 refs / broken 0 (the
prose edits don't affect decision parsing or the tickets.decision_ref linkage).
Transform committed at tooling/pql-migrate/retag_ticket_refs.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 12:41:52 +02:00
jpmschweitzerandClaude Opus 4.8 88d9be070e feat(meta): seed 1013 tickets into pql changelog (pql migration phase 2)
One-way ticket data migration from the legacy settledreach.db into pql's
git-tracked changelog. Establishes the T-N == #N id bijection (old #440 -> T-440)
so thousands of #NNN git-commit references stay a trivial mapping.

Migrated (verified by full `pql plan rebuild` from changelog):
  - 1013 tickets  (status + type distributions match source exactly)
  - 481  dependencies
  - 16   history rows (deterministic content hash so ON CONFLICT(hash) dedups)
  - 90   labels = 48 source + 42 milestone-derived
           (active "Phase 4" milestone -> phase:4; "Process Rewire" ->
            milestone:process-rewire; milestone_deps was empty/vestigial)

Transforms: ids T-prefixed, sprint_id dropped (legacy/archival), decision_ref
and the one 'server,client' comma-team (#575) kept verbatim, deleted_at NULL,
canonical_version 1. Seeded rows carry hash=NULL on tickets/deps/labels — proven
safe: PK-based ON CONFLICT, updated_at drives LWW, and replay is idempotent.

Key finding that dissolves pql-requirements item #1 ("Critical"): seeding via
direct-INSERT does NOT require explicit ticket ids from `ticket new`. The id
counter derives from max(id), so after seeding T-1..T-1021 the next native
`pql ticket new` mints T-1022 — no recycling, no collision. The blocker only
applied to the `ticket new --id` path we never use for bulk seeding.

Scaffolding: .pql/changelog/<table>/0000-schema.sql (canonical pql 1.6.2 schema,
byte-identical across the 4 table dirs) + .gitattributes union-merge driver so
changelog SQL never produces binary-style merge conflicts. Seed is reproducible
via tooling/pql-migrate/seed_tickets.py (read-only on the source, idempotent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 12:32:50 +02:00
jpmschweitzerandClaude Opus 4.8 05d7bffd6f docs(meta): repoint decisions/ paths to governance/ tree (pql migration)
Phase 1 follow-up: update the active instruction layer (CLAUDE.md, project
structure rule, DECISIONS.md redirect, agent personalities, skill docs) to
reference governance/{decisions,questions,rejected}/<domain>.md instead of the
retired flat decisions/*.md layout.

Path references only — command-surface references (tooling/db/decision*,
decisions-sync, Makefile targets, clerk) are repointed to the pql CLI in the
Phase 4 consumer cutover. Historical archives (docs/sprints, docs/discussions,
docs/workshops) keep their point-in-time decisions/ paths; the separate
whatsinagame/ template distribution is untouched. Agent-memory is gitignored
and out of scope.

The agent/skill repath was applied by tooling/pql-migrate/repath_references.py
(ordered, meaning-preserving replacements; bare-dir rule uses a negative
lookbehind so it can't corrupt a freshly-created governance/decisions/ path),
committed for provenance. CLAUDE.md, project-structure.md, and DECISIONS.md
were hand-edited (structural tree/table changes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 12:18:49 +02:00
jpmschweitzerandClaude Opus 4.8 f936e503da docs(decisions): restructure into pql governance DQR tree
Phase 1 of the pql migration. Moves the flat decisions/*.md layout into
governance/{decisions,questions,rejected}/<domain>.md — the tree pql's
`decisions sync` parses natively (record type from subdir, domain from
filename stem). Proven against pql 1.6.2: sync reports 357 records
(237 D / 108 Q / 12 R), 1057 refs, broken: 0; validate ok.

- 6 D-domain files -> governance/decisions/ (git renames)
- 5 questions-<domain>.md -> governance/questions/<domain>.md (prefix dropped)
- rejected.md split by domain -> governance/rejected/{architecture(R-001..010),
  economics(R-011),perception(R-012)}.md
- decisions/README.md + questions.md index folded into governance/README.md;
  pql's `decisions sync` now auto-maintains the record index appended below
  the hand-written domain guidance (no more manual ID-list table upkeep).
- .pql/config.yaml: canonical vault config (tracked, not ignored).

Link rewrites are token-preserving: only the relative `foo.md` path portion
changes (e.g. `rejected.md#r-011` -> `../rejected/economics.md#r-011`); every
`[D-NNN]` bracket text and `#anchor` stays byte-identical, so pql's reference
extraction is unaffected. The one-shot transform is committed at
tooling/pql-migrate/restructure_decisions.py for provenance.

Codebase path references to decisions/ (CLAUDE.md, rules, skills, docs) are
updated in a follow-up commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 12:12:45 +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 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