docs(tooling): T-1256 — adoption: the reach skill, the standing rule, the diagram
The consolidation has to be the only way to add tooling, or it re-fragments the way the tree did — ~160 files and four naming conventions, because nothing ruled out adding one more. - Skill(reach) (T-1254), for both audiences. Using it: discover through --help, the domain index, the global flags an agent needs (--no-input, --detach with `jobs log --follow`), the stdout/stderr/verdict contract, and "read the exit status, never a field". Extending it: add a verb, add a domain, the seven invariants the conformance test enforces, a worked ReachError-with-fix example, the parity-first porting method, and the traps this repo has already paid for (the stamp registry, the canvas registry, archived generators that destroy data if run). - CLAUDE.md (T-1255): no tooling is developed outside the CLI. The two exceptions are named so they aren't relitigated: Blender payloads and the Rust crates. Wording as confirmed by Jeroen, 2026-08-20. - docs/diagrams/architecture/reach-cli.d2: the door, core, the domains, the carve-out, the one guarded exec, and the enforced invariants as a legend. D-263 now links to it; the amendment that had removed the dangling forward reference asked for exactly this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# reach — one CLI for all repo tooling (D-263)
|
||||
#
|
||||
# WHAT THIS ANSWERS: when something invokes a repo tool, what does the call
|
||||
# pass through, and where is each kind of code allowed to live?
|
||||
#
|
||||
# Verified 2026-09-23 against the source (tooling/main.py, core/, domains/,
|
||||
# test_conformance.py) — module names are real; the arrows are the import and
|
||||
# call paths, not inferred. The conformance test enforces the rules in the
|
||||
# legend; this picture does not.
|
||||
#
|
||||
# View: clide draw --file docs/diagrams/architecture/reach-cli.d2
|
||||
|
||||
direction: down
|
||||
|
||||
classes: {
|
||||
caller: {style: {fill: "#1f6f43"; stroke: "#8fd9ae"; font-color: "#ffffff"; bold: true; border-radius: 4}}
|
||||
door: {shape: hexagon; style: {fill: "#2b4c7e"; stroke: "#9dc0f0"; font-color: "#ffffff"; bold: true}}
|
||||
core: {style: {fill: "#5c2d6e"; stroke: "#d3a8e6"; font-color: "#ffffff"; border-radius: 4}}
|
||||
domain: {style: {fill: "#7a4b12"; stroke: "#e0b070"; font-color: "#ffffff"; border-radius: 4}}
|
||||
outside: {style: {fill: "#3a3a42"; stroke: "#9a9aa4"; font-color: "#ffffff"; border-radius: 4; stroke-dash: 4}}
|
||||
ext: {shape: cylinder; style: {fill: "#2a2a30"; stroke: "#9a9aa4"; font-color: "#ffffff"}}
|
||||
}
|
||||
|
||||
# ---- callers ----
|
||||
agent: "agents + humans\nreach <domain> <verb>" {class: caller}
|
||||
hooks: "git hooks\nreach --no-input check …" {class: caller}
|
||||
make: "make (orchestration only)\none-line delegates, e.g. regen-db" {class: caller}
|
||||
|
||||
# ---- the door ----
|
||||
main: "tooling/main.py\nrouting only · DOMAINS registry\nlazy: --help imports no domain" {class: door}
|
||||
|
||||
# ---- core ----
|
||||
command: "core/command.py @command\njob identity ⊃ handle_errors ⊃ logged" {class: core}
|
||||
console: "core/console.py\nstdout = data · stderr = JSONL events + one verdict" {class: core}
|
||||
errors: "core/errors.py ReachError\nevery raise carries fix=" {class: core}
|
||||
process: "core/process.py — the ONE guarded exec\nargv lists · missing-binary remedy · cargo_binary" {class: core}
|
||||
jobs: "core/jobs.py + domains/jobs\n--detach · log --follow · wait" {class: core}
|
||||
|
||||
# ---- domains ----
|
||||
router: "domains/<d>/router.py\ntransport: args in, format out\nthe only place typer may be imported" {class: domain}
|
||||
service: "domains/<d>/*.py (services)\nplain args in, data out\ncallable from tests without a CLI" {class: domain}
|
||||
list: "14 domains\ncheck · validate · atlas {map, planet} · ledger · wiki\nassets · character · godot · visual · generate\nblender · pr · jobs · dev" {class: domain}
|
||||
|
||||
# ---- outside the package ----
|
||||
payloads: "tooling/scripts/blender/ (41 payloads)\nrun under Blender's Python — never imported" {class: outside}
|
||||
archive: "tooling/archive/\nprovenance — never run, not linted" {class: outside}
|
||||
|
||||
# ---- external programs ----
|
||||
extbin: "cargo · godot · blender · ffmpeg · git · xvfb-run" {class: ext}
|
||||
|
||||
agent -> main
|
||||
hooks -> main
|
||||
make -> main
|
||||
main -> router: "loads ONE domain, on use"
|
||||
router -> command: "wraps every verb"
|
||||
router -> service: "delegates"
|
||||
router -> list {style.stroke-dash: 3}
|
||||
command -> errors
|
||||
command -> jobs
|
||||
service -> console: "the only output path"
|
||||
errors -> console: "verdict + fix"
|
||||
service -> process: "any external program"
|
||||
process -> extbin
|
||||
process -> payloads: "reach blender run"
|
||||
|
||||
legend: "ENFORCED BY tooling/test_conformance.py\n\n1 typer only in router.py / main.py / core/cli.py\n2 no print(): everything through console\n3 every verb is @command, and has help\n4 every ReachError names a fix\n5 no subprocess outside core/process.py\n6 the Blender carve-out stays outside the package\n\nColour: green caller · blue the door · purple core\namber domains · grey dashed outside the package" {
|
||||
style: {fill: "#1b1b22"; stroke: "#9a9aa4"; font-color: "#c8d0e0"}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 37 KiB |
Reference in New Issue
Block a user