Files
settled-reach/tooling/DOMAINS.md
T
jpmschweitzerandClaude Opus 5 6daaa2235f docs(governance): D-263 — domains mirror the implant apps, and atlas is one ladder
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) <noreply@anthropic.com>
2026-09-02 14:15:03 +02:00

174 lines
11 KiB
Markdown

# The domain map (T-1271, D-263)
Every Python file and every executable in `tooling/`, assigned to a domain
before anything moves. This is the artefact the per-domain port tickets are
written from — without it their boundaries would be guesses, renegotiated
halfway through the move.
**Status:** map only. Nothing here has moved yet except `check client-version`,
which shipped in T-1262 as the reference implementation.
## What the survey changed
Two corrections to the T-1250 description, both found by counting rather than
reading:
- **Three Rust crates, not one.** `econ-sim`, `line-previewer` and `test-client`
are all Cargo projects with zero `.py` files. None is renamed and none moves;
a hyphen only matters for something Python must import.
- **The "28 singleton prefixes" were an artefact of my own measurement.**
Splitting filenames on the first token scattered coherent families:
`sculpt-star-map`, `tune-star-map-topology` and `generate-star-map*` are one
group, not three orphans. Counting families instead of prefixes, the genuinely
ambiguous set is small and is listed under *Judgment calls* below.
And two found while reading the tree:
- **`garment-fit/` is mostly a Blender payload directory.** 22 of its 23 files
are `blender_author_*.py`, leaving one real module. So the Blender carve-out
is **35 files**, not the 13 visible at top level — the T-1250 description was
right about 35 and an intermediate survey of mine was wrong, because it
counted only the top level. The consequence is not cosmetic: `character`
is a much smaller domain than the directory sizes imply, and the carve-out is
much larger.
- **`tooling/db/` is misnamed.** It holds the audio/image/Trellis connectors and
`wiki_sync.py` — the actual database work is in `economy-db/`. Naming a domain
`db` after that directory would carry the misnomer forward, so its contents
split between `assets` and `wiki` instead.
## Half the executables are bash
Found 2026-08-31, after this map was first written and while starting the first
port. **17 of the 33 extensionless executables are bash**, ~900 lines. This map
originally assigned them as though they were Python files to be moved; they are
rewrites.
Decided: **rewrite all of them in Python** (D-263). Wrapping would achieve one
door while leaving half the surface outside the contract, so `reach --help`
would list verbs that behave differently from the ones beside them — worse than
two doors, because the inconsistency only shows up at a failure.
| shape | scripts | cost |
|---|---|---|
| thin wrappers | `atlas-systems-done` 9, `atlas-names` 17, `generate-brands` 23, `atlas` 24, `generate-corporations` 23, `blender` 28 | near-free; the router calls what the script called |
| logic | `check-fact-ids` 89, `validate-ron` 137, `godot-parse-sweep` 64, `godot-cold-parse` 96, `pr-watchlist-diff` 37, `atlas-update-field` 59, `atlas-commit-and-sync` 60, `tea-comment` 41 | real rewrite; gains the most — testable, failures that teach |
| environment | `install-godot` 108, `install-rust` 33, `worktree-setup` 49 | gains least, riskiest — port the *decision* logic, keep the external calls behind `core/process` |
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 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** |
| `visual` | screenshot and render comparison | `visual-diff`, `visual-thumbnail`, `visual-blank-check` |
| `godot` | Godot parse and cold-start checks | `godot-parse-sweep`, `godot-cold-parse` |
| `generate` | content generators not owned elsewhere | `generate-brands`, `generate-corporations`, `generate-character-manifest`, `generate_corp_stubs.py` |
| `dev` | developer environment and workflow | `install-rust`, `install-godot`, `worktree-setup`, `perf-baseline`, `clerk-review` |
| `pr` | the PR/review loop | `tea-comment`, `pr-watchlist-diff`, `pql-board-html` |
| `blender` | **carve-out** — payloads run by Blender | **35** `blender_*.py` (13 top-level + 22 in `garment-fit/`), `blender` wrapper → `tooling/scripts/blender/` |
## Judgment calls, with reasons
Each of these sets a precedent, so the reasoning matters more than the answer.
**Registries stay module-level data, not commands.** `canvas_sources.py` and
`generator_sources.py` are lists of paths consumed by gates — nothing types them.
They move beside the domain that reads them (`check` and `db` respectively) as
plain modules, not verbs. Making them commands would put something in
`reach --help` that answers no question a person has.
**`schema_version.py` belongs to `db`, and `check` imports it.** It is consumed
by the economics importer *and* by the stamp gate. Shared, but not equally
owned: the importer defines the version, the gate reads it. It goes where it is
defined, and the cross-domain import is legitimate — that is what a service
layer is for.
**`test_*.py` files do NOT become a domain.** They are gate tests run by
`make test-tooling`, not commands anyone types. `reach test …` would imply a
test runner that does not exist. They stay standalone scripts.
**`pql-migrate/` is provenance, not tooling.** One-shot scripts from a completed
migration, already documented as such in `.claude/rules/project-structure.md`.
They are not live tools and must not become verbs. Move to `tooling/archive/`,
excluded from package discovery — deleting them would destroy migration
provenance, and keeping them importable would imply they still run.
**`pr` is a domain the epic did not list.** `tea-comment` and `pr-watchlist-diff`
are the PR/review loop, which is neither `dev` (environment setup) nor anything
else on the original list. Folding them into `dev` would make `dev` the drawer
everything ambiguous goes into — which is how `core/` rots, and the same
argument applies here.
**`atlas` is overloaded, and the port must not merge the three uses.** The word
appears in three unrelated places: the top-level `atlas-*` executables (the map
data surface), `planet-gen/atlas_*.py` (`atlas_cohesion_audit`,
`atlas_common`, `atlas_quality_analysis` — quality analysis of generated
terrain), and `economy-db/atlas.py` (the atlas index tables in `systems.db`).
These are three concerns sharing a noun, not one domain in three places. Each
stays with its owner — `atlas`, `planet` and `db` respectively — and the port
should resist the pull to collect them, which would produce a domain whose only
common thread is a word.
**`economy-db/errors.py` predates `core/errors.py` and is not the same thing.**
Domain-local error types are fine; what must not happen is a silent merge, or a
second `ReachError` with different semantics. Reconcile explicitly when `db` is
ported.
**`character` rather than `garment`.** D-263's sketch says `garment`, but the
files cover bodies, hair, GLB utilities and Quaternius imports as well as
clothing. Naming it for one of its five concerns would leave the other four
looking misfiled.
## Not moving
| what | why |
|---|---|
| `econ-sim/`, `line-previewer/`, `test-client/` | Rust crates. Excluded from package discovery; hyphens are harmless. |
| `scripts/blender/` (after T-1273) | Run under Blender's bundled Python; cannot import `tooling.core`. Excluded from the conformance scope for the same reason. |
| `archive/` (was `pql-migrate/`) | Completed-migration provenance. |
| `test_*.py` | Gate tests, not commands. |
## Naming, applied on the way in
- **Modules** `snake_case`: `check-dataflow-graph.py``dataflow_graph.py`.
- **Verbs** `kebab-case`: `reach check dataflow-graph`.
- **No prefix-as-namespace.** The domain directory supplies what the `blender_`,
`atlas-` and `check-` prefixes were doing by hand. `atlas-verify` becomes
`reach atlas verify`, not `reach atlas atlas-verify`.
- **Layering is a vocabulary, not a quota.** A domain with only a router and a
service gets exactly those two files. Empty `schemas.py` and `dependencies.py`
are worse than absent ones — they suggest a shape the domain does not have.