From 6daaa2235f7b0530871501c8dbf014af8d2f2dd1 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 2 Sep 2026 14:15:03 +0200 Subject: [PATCH] =?UTF-8?q?docs(governance):=20D-263=20=E2=80=94=20domains?= =?UTF-8?q?=20mirror=20the=20implant=20apps,=20and=20atlas=20is=20one=20la?= =?UTF-8?q?dder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reach's domain names should not be a fresh taxonomy. Where the game already presents something to the player, the CLI takes that name and that shape: what you browse in-game is what you generate and inspect from the terminal. That splits domains in two. atlas, ledger and wiki mirror implant apps and follow their structure. check, validate, godot, visual, jobs and dev mirror nothing — no app exists for a lint gate, and inventing a player-facing framing for one would be worse than having none. The first consequence corrects a contradiction rather than a preference. D-191 already says "Atlas is the star map extended downward, not a separate app — implant/map at different zoom levels", four rungs from Reach map to regional. The domain map had atlas, starmap and planet as peers, which would have presented as three unrelated things 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 rather than a rung. The second is a rename with the same reasoning: db becomes ledger, after the UI component that will aggregate economics — markets, wealth, transactions, the economic counterpart to what the Atlas offers for topography. db named a storage layer nobody looks at. One caution recorded because 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. Co-Authored-By: Claude Opus 5 (1M context) --- .pql/changelog/ticket_idmap/2026-09.sql | 1 + .pql/changelog/tickets/2026-09.sql | 1 + governance/decisions/architecture.md | 8 +++++++ tooling/DOMAINS.md | 31 +++++++++++++++++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) 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** |