Files
settled-reach/tooling/DOMAINS.md
T
jpmschweitzerandClaude Opus 5 338644b409 refactor(tooling): T-1286 — generate, pr and dev become reach domains
Twelve scripts retired, three domains registered. `reach` now covers nine.

generate: `generate-brands` and `generate-corporations` were the second and
third copies of the same 24-line build-if-missing-then-exec bash `tooling/atlas`
carried, so they collapsed into `core.process.cargo_binary` rather than being
ported. `import_economics` shelled out to the first of those, so it now calls
that helper — `generated_brands.toml` comes back byte-identical, and the stamp
registry swaps the retired wrapper for `core/process.py`.

pr: `watchlist-diff` derives its watched set from `generator_sources.py` instead
of restating it, so it cannot drift from the stamp check.

dev: the environment scripts split decision from performing, per D-263's
guarded-exec rule. `godot_plan()` and `worktree_plan()` decide what would
happen; `install_godot()`, `install_rust()` and `setup_worktree()` do it.
`tooling/test_environment.py` pins the version pin, both override precedences,
the already-current skip, the platform refusal and both worktree refusals —
none of them performed. `make setup` now installs reach first, since the
targets that install rust and godot are reach verbs.

Two live bugs found while porting:

- The clerk read its decision index from `decisions/README.md`, a path that
  stopped existing when the DQR tree moved to `governance/`. Every clerk agent
  has been grepping blind; its prompt pointed at the same dead directory.
- The conformance exec-check matched any `x.system()` regardless of receiver,
  so `platform.system()` read as `os.system()`. Narrowed and re-proved against
  a real mutant.

`process.run` gains `input=`, `timeout=` and a `ProcessTimeout` subclass so a
killed run stays distinguishable from a verdict. The pre-push hook no longer
merges the clerk's stderr into its stdout — under streaming the last merged
line is a JSONL event, which would read as an unrecognised verdict and block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:00:46 +02:00

11 KiB

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.pynote 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 ported (T-1286). generate-brands + generate-corporations collapsed into core.process.cargo_binary — they were the same 24 lines of bash a third time
dev developer environment and workflow ported (T-1286). The three environment scripts split decision from performing — godot_plan/worktree_plan are pure and pinned by test_environment.py
pr the PR/review loop ported (T-1286). watchlist-diff now reads the watched set from generator_sources.py instead of restating it
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.pydataflow_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.