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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
- 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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>