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