Found on starting the first port: 17 of the 33 tooling executables are bash, about 900 lines. Both this record and the domain map had assumed a Python tree, so those are rewrites rather than moves — a materially larger epic than T-1250 was written for. Decided: rewrite them, do not wrap them. Wrapping would achieve one door while leaving half the CLI 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 something fails. The cost lands unevenly and the record says where. The grep-pipeline scripts compute verdicts and gain most from becoming services. The environment scripts — install-godot, install-rust, worktree-setup — gain least and carry the most regression risk, because downloading a specific Godot build or driving rustup is awkward to exercise in a gate. For those, port the decision logic into a testable service and keep the irreducible external calls behind core/process: a rewrite that cannot be tested has to be trusted instead, and trusting an installer is how a working environment becomes an unreproducible one. The domain map gains the inventory by shape, and a rule that every per-domain ticket states which of its sources are bash — since that is what turns a port from mechanical into a rewrite needing its own parity evidence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
151 lines
9.6 KiB
Markdown
151 lines
9.6 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.
|
|
|
|
## 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` |
|
|
| `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.
|