diff --git a/.pql/changelog/ticket_idmap/2026-09.sql b/.pql/changelog/ticket_idmap/2026-09.sql index 7b55711fb..e161d8b32 100644 --- a/.pql/changelog/ticket_idmap/2026-09.sql +++ b/.pql/changelog/ticket_idmap/2026-09.sql @@ -1 +1,2 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06G63FEA51J4JKE3A7473D3QZ0', 'T-1284', '2026-09-02 10:50:23.145', '2026-09-02 10:50:23.145', NULL, '74113524e3a280bd06875305a547349a', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= ticket_idmap.updated_at; +INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06G642RKW8PNW7AP9BY9203SPG', 'T-1285', '2026-09-02 12:14:48.290', '2026-09-02 12:14:48.290', NULL, '020f491d1ae8fbde83d2aae0f8325011', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= ticket_idmap.updated_at; diff --git a/.pql/changelog/tickets/2026-09.sql b/.pql/changelog/tickets/2026-09.sql index 928e3cb9b..5a1ff5136 100644 --- a/.pql/changelog/tickets/2026-09.sql +++ b/.pql/changelog/tickets/2026-09.sql @@ -74,3 +74,4 @@ Neither was visible by inspection of the diff; both surfaced on the first real r sys.exit REMOVED from diff.py and thumbnail.py — four sites. A service must not end the process; they raise ReachError with a remedy now. CALLERS REWIRED, and this is the wider blast radius than the make targets: tests/run-visual invoked visual-blank-check, visual-thumbnail and visual-diff by path at four sites, and the pre-push hook invoked godot-parse-sweep. All now call reach --no-input. .claude/settings.json loses the godot-cold-parse entry; Bash(reach *) covers it.', 'done', 'medium', NULL, NULL, 'D-263', '2026-08-31 15:30:48.013', '2026-09-02 11:59:14.536', NULL, 'a3a0b27a427faca6f2c47ae6a9a6f9a5', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at; +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06G642RKW8PNW7AP9BY9203SPG', 'task', '06G1S3D0M1TQW0GMFBBPQZG3ZM', 'Port the atlas domain — the authoring and inspection verbs', 'The eight top-level atlas-* scripts become FLAT verbs on reach atlas: the Rust binary wrapper (stats, show-system, list-bodies, populate), atlas-check, atlas-verify, atlas-names, atlas-systems-done, atlas-update-field, atlas-commit-and-sync, atlas-flatness. Flat rather than nested because these act on the atlas as a whole; the nested groups are for GENERATION per rung (reach atlas map, reach atlas planet), which are separate tickets. Five of the eight are BASH, so those are rewrites: atlas 24, atlas-names 17, atlas-systems-done 9, atlas-update-field 59, atlas-commit-and-sync 60. Standard port acceptance as on T-1281. THREE THINGS NEEDING A DECISION OR CARE, each recorded rather than assumed: (1) the Rust binary — tooling/atlas builds server/target/debug/atlas if missing then execs it with all args, so its verbs live in Rust not in the shell; declare the four known verbs in the router so reach --help stays a complete index, and keep a passthrough for anything the binary gains later. (2) atlas-commit-and-sync MAKES GIT COMMITS, which is a risk class nothing else in reach has; port it to stage and report by default with committing behind an explicit flag, and say so in the verb help rather than changing behaviour silently. (3) atlas-update-field MUTATES systems.db directly, which .claude/rules/asset-pipeline.md forbids outside a migration — establish whether it predates that rule or is a sanctioned exception BEFORE porting, because the port must not launder a violation into a first-class verb.', 'backlog', 'high', NULL, NULL, 'D-263', '2026-09-02 12:14:48.290', '2026-09-02 12:14:48.290', NULL, 'cf4a2315117474f165f1e689d6271300', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at; diff --git a/governance/decisions/architecture.md b/governance/decisions/architecture.md index 63b59481d..c56e0eba9 100644 --- a/governance/decisions/architecture.md +++ b/governance/decisions/architecture.md @@ -2619,6 +2619,14 @@ tooling/ - **Failures that name the fix are the highest-value requirement in this record.** The reader of an error message is usually an agent deciding what to run next. A message that says only "no" costs a whole exploratory turn; one that names the command costs none. - **Machine-readable output is the default path, not the exception.** stderr is a pipe far more often than a terminal, so JSONL-when-not-a-TTY matches reality rather than accommodating an edge case. +**Player-facing domains mirror the implant apps; tooling domains mirror nothing** *(settled 2026-09-02)*. `reach`'s domain names are not a fresh taxonomy — where the game already presents something to the player, the CLI uses **that** name and **that** shape. What you browse in-game is what you generate and inspect from the terminal. + +- **The two kinds.** `atlas`, `ledger` and `wiki` mirror implant apps and follow their structure. `check`, `validate`, `godot`, `visual`, `jobs`, `dev` mirror nothing — no app exists, none is expected, and inventing a player-facing framing for a lint gate would be worse than having none. +- **`atlas` is one ladder, not three domains.** D-191 states it: *"Atlas is the star map extended downward, not a separate app — `implant/map` at different zoom levels"*, four rungs Reach map → system → planetary → regional. An earlier domain map (T-1271) had `atlas`, `starmap` and `planet` as peers, which contradicted that record and would have presented as three unrelated things what the game presents as one descent. **Generation nests by rung** — `reach atlas map …`, `reach atlas planet …` — so `reach atlas --help` shows the ladder rather than thirty flat verbs. Authoring and inspection verbs stay flat on `atlas`, because they act on the whole thing rather than on a rung. +- **`ledger` aggregates economics**, and is the name because it is the UI component the player will use — markets, wealth, transactions — the economic counterpart to what the Atlas offers for topography. Not `db`, which named a storage layer nobody looks at, and not `economics`, which names a subject rather than the thing on screen. +- **Where the mirror does not apply, do not force it.** The distinction that matters is player-facing versus tooling. A domain in the first group inherits its name and its shape; one in the second is free to be organised however the work is organised. +- **One caution, since the words collide.** D-191's MVP criterion 7 says *"Atlas is read-only (no verbs execute from map)"*. That governs the **app**. The atlas *tooling* writes — it commits proposals, mutates fields, syncs the wiki — and a later reader must not take the app's constraint as licence to delete the authoring verbs. + **The bash scripts are rewritten in Python, not wrapped** *(settled 2026-08-31, after a survey found the tree is half shell)*. **17 of the 33 tooling executables are bash, ~900 lines** — the record and the domain map had both assumed a Python tree, so porting them is a rewrite rather than a move. They are rewritten anyway. - **Why not wrap them.** Wrapping achieves one door while leaving half the surface outside the contract: no `@command`, no remedy on failure, no streaming, no testable service. `reach --help` would then list verbs that behave differently from the ones beside them, which is worse than two doors because the inconsistency is invisible until a failure. diff --git a/tooling/DOMAINS.md b/tooling/DOMAINS.md index 27d1d4eee..420dd847e 100644 --- a/tooling/DOMAINS.md +++ b/tooling/DOMAINS.md @@ -57,16 +57,39 @@ two doors, because the inconsistency only shows up at a failure. A **bash** source turns a port from mechanical into a rewrite that needs its own parity evidence. Each per-domain ticket must say which of its sources are bash. +## Revised 2026-09-02: domains mirror the implant apps + +The first version of this map invented a taxonomy. It should have read one off +the game, and D-263 now says so: **where the game presents something to the +player, the CLI uses that name and that shape.** + +Two corrections follow, and the first was a contradiction of an existing record +rather than a matter of taste: + +- **`atlas` absorbs `starmap` and `planet`.** D-191: *"Atlas is the star map + extended downward, not a separate app — `implant/map` at different zoom + levels"*, four rungs Reach map → system → planetary → regional. This map had + them as three peer domains, presenting as unrelated what the game presents as + one descent. Generation now **nests by rung**; authoring and inspection verbs + stay flat on `atlas`, because they act on the whole thing. +- **`db` becomes `ledger`.** Named for the UI component that aggregates + economics — markets, wealth, transactions — the economic counterpart to what + the Atlas offers for topography. `db` named a storage layer nobody looks at. + +Domains that mirror nothing — `check`, `validate`, `godot`, `visual`, `jobs`, +`dev` — are unaffected. No app exists for a lint gate and none should be +invented. + ## Domains | domain | what it is | sources | |---|---|---| | `check` | repo consistency gates the push hook runs | `check-client-version` ✅, `check-canvas-version`, `check-systems-db-stamp`, `check-fact-ids`, `check-dataflow-graph.py` | | `validate` | content and schema validation | `validate-content`, `validate-checklist`, `validate-ron` | -| `atlas` | the Atlas map data surface | `atlas`, `atlas-check`, `atlas-names`, `atlas-commit-and-sync`, `atlas-systems-done`, `atlas-update-field`, `atlas-verify`, `atlas-flatness` | -| `starmap` | the star map and its renders | `generate-star-map.py`, `generate-star-map-data.py`, `generate-star-map-svg.py`, `sculpt-star-map.py`, `tune-star-map-topology.py`, `star-map-seed.json` | -| `planet` | planetary generation and its guards | `planet-gen/` (30 files) | -| `db` | the economics DB pipeline | `economy-db/` (17 files), `schema_version.py` | +| `atlas` | **the whole spatial ladder** (D-191). Flat verbs for authoring and inspection; nested groups per rung for generation | flat: `atlas` (Rust binary), `atlas-check`, `atlas-names`, `atlas-commit-and-sync`, `atlas-systems-done`, `atlas-update-field`, `atlas-verify`, `atlas-flatness` · `atlas map`: the 5 star-map files · `atlas planet`: `planet-gen/` (30) | +| ~~`starmap`~~ | **folded into `atlas map`** — the top rung of the same ladder | — | +| ~~`planet`~~ | **folded into `atlas planet`** — the third rung of the same ladder | — | +| `ledger` | the economics pipeline, named for the UI component that will aggregate it | `economy-db/` (17 files), `schema_version.py` | | `wiki` | wiki sync and content maintenance | `wiki/`, `db/wiki_sync.py`, `db/populate_gttr_hook.py`, `db/backfill_cultural_corridor.py`, `assign-astro-ids.py`, `fill-missing-globes.py`, `migrate-s-to-gj.py`, `patch-core-sector.py`, `process-wiki-system-changes` | | `assets` | connectors to the tower-of-joy generators | `db/audio_*.py`, `db/audio-*`, `db/image_connector.py`, `db/trellis_connector.py`, `db/common.py`, `trellis-batch.sh`, `synth_ui_sounds.py` | | `character` | bodies, garments, GLB handling | `garment-fit/make_logo.py`, `garment-qa/analyze_captures.py`, `convert_outfit.py`, `glb_strip_utility_nodes.py`, `inspect_glb.py`, `check_hair_symmetry.py`, `check_icosphere.py`, `render_quaternius_test.py`, `setup_clothing_metadata.py` — **note this is far smaller than `garment-fit/`'s file count suggests; 22 of its 23 files are Blender payloads and belong to the carve-out** |