agent-bios 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/claude/guides/cli-multi-model-workflow.md +46 -0
- package/claude/guides/coding-staged-workflow.md +1 -0
- package/claude/guides/review-defect-criteria.md +205 -0
- package/claude/guides/review-request.md +15 -1
- package/claude/hooks/tooling-gotchas-hook.py +37 -2
- package/claude/skills/repo-charter/SKILL.md +149 -0
- package/codex/guides/cli-multi-model-workflow.md +46 -0
- package/codex/guides/coding-staged-workflow.md +1 -0
- package/codex/guides/review-defect-criteria.md +205 -0
- package/codex/guides/review-request.md +15 -1
- package/compose/assemble.py +191 -21
- package/compose/canary.sh +13 -0
- package/compose/check-domains.py +144 -20
- package/compose/domains.json +4 -0
- package/compose/prune-backups.py +57 -7
- package/install.sh +396 -27
- package/launch/agent-launch.py +3976 -595
- package/launch/agent-launch.toml +10 -4
- package/launch/agent-launch.zsh +7 -2
- package/launch/i18n/en.toml +127 -7
- package/launch/i18n/ja.toml +126 -7
- package/launch/i18n/ko.toml +126 -7
- package/learn/check-learning.py +19 -1
- package/learn/collect-learning.py +57 -2
- package/learn/migrate-learnings.py +140 -16
- package/learn/redact.py +14 -5
- package/package.json +3 -2
- package/provenance.json +1 -1
- package/session-cost.py +402 -33
- package/wrappers/claude-run.sh +49 -4
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Two layers:
|
|
|
24
24
|
- **Every span we write into a file we do not own has a remover** — the Codex config block, the settings hook registrations, the `AGENTS.md` central region and the zsh hook line each sit in a span identifiable as ours (a marker pair, a tagged line, or a manifest name), and `uninstall` removes exactly those and nothing around them. The entry `CLAUDE.md`/`AGENTS.md` are seeded once and then yours: the corpus is assembled under `central/` and the entry file only imports it, so your own additions are never mixed with ours and never removed with them. `uninstall` is a security operation — everything of ours goes, and what it took leaves as one archive you can hand off or delete.
|
|
25
25
|
- **Metadata selects, observation authorizes** — a record of what was promoted can propose an irreversible act but never authorize one; it has never seen the machine it will run on. Clearing a user's personal copy of a promoted learning asks the deployed corpus whether the replacement is really there, excludes the copy being deleted from that evidence, and keeps the copy on any uncertainty: a kept duplicate is redundant, a wrong removal is data loss.
|
|
26
26
|
- **The deployment outlives the act that made it** — a command's success says nothing about which version landed; a cached package can serve the previous one at exit 0. So the deployed state carries its own version marker and `status` reads that marker rather than the source it was launched from. With no marker the answer is `unknown`, never a guess.
|
|
27
|
-
- **`Evidence Base` (per guide) is the single owner of numbers.** Measure with `session-cost.py
|
|
27
|
+
- **`Evidence Base` (per guide) is the single owner of numbers.** Measure with `session-cost.py` (`agent-bios cost` on an installed package).
|
|
28
28
|
- **English is canonical and installed; Korean (`ko/`) is reference only.** The harness loads only fixed-name English files; the installer deploys English only.
|
|
29
29
|
|
|
30
30
|
## Layout
|
|
@@ -42,7 +42,7 @@ Two layers:
|
|
|
42
42
|
| `wrappers/` | internal Codex wrappers, deployed to `$CODEX_HOME/bin/` |
|
|
43
43
|
| `gates/` | author-side verification (mirror generation, parity, lexicon, payload, assembler scenarios) — reachable only from a repo checkout, and `check-package.sh` fails if any of it enters the npm payload |
|
|
44
44
|
| `ontology/` | what a change obliges elsewhere — entities, obligation edges, and the service's routes, held against real source by `check-ontology.py`. `instances/graph.json` is canonical; `LEXICON.md`, the RDF views, the HTML map, and the competency/extension docs are generated from it |
|
|
45
|
-
| `install.sh`, `session-cost.py` | the CLI and the cost meter — the two things you run directly |
|
|
45
|
+
| `install.sh`, `session-cost.py` | the CLI and the cost meter — the two things you run directly (`agent-bios` and `agent-bios cost` once installed) |
|
|
46
46
|
| `decisions/` | the decision record for developing this repo — what was decided and which alternative it closed; author-side, never shipped |
|
|
47
47
|
| `packages/` | authored corpus packages, organized by package identity rather than by concept home |
|
|
48
48
|
| `.githooks/` | the pre-commit hook that runs the gates against the index, enabled per clone with `core.hooksPath` |
|
|
@@ -95,11 +95,11 @@ agent-bios uninstall # remove deployed files and the zsh hook
|
|
|
95
95
|
|
|
96
96
|
From a git clone, run `./install.sh install` directly (the same CLI). `install` respects `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, `AGENT_LAUNCH_VENV`, and `ZDOTDIR`; `--dry-run` prints actions without changing anything. Deploy to **every active environment in one sitting** — a partial deploy leaves a shared global pointing at a guide some environment lacks; globals are English only. Replaced files are backed up under `~/.local/share/agent-bios/backups/<timestamp>/`, and the installed set is recorded in a manifest that `uninstall` consumes. Those copies are **a manual escape hatch, not a restore mechanism**: nothing reads them back, so recovering from one means copying files yourself. The supported paths are the ones `agent-bios help` prints — a clone rolls corpus content back to a registered version, an npm install rolls the whole package back by version — and `uninstall` emits one archive of everything it removed. The backups exist for the case those three do not cover: the exact bytes that were on disk before a particular install. The published npm package ships only the deploy set (never `settings.json`, `config.toml`, `ko/`, or `benchmarks/`).
|
|
97
97
|
|
|
98
|
-
In a TTY, zero-argument `codex` or `claude` opens the launch preflight. Every arrow-key TUI selection screen keeps the complete current setup in a fixed top panel, followed by the highlighted option's description and then the option list. Move with Up/Down, select with Enter, use Esc to return to the previous menu, and use `q` to cancel; Esc at the mode root also cancels. Each tier's model is chosen from the host's configured catalog, or via **Other** to type any model id the backend accepts; that text input preserves values that start with `q`, so Esc or Ctrl-C cancels immediately there, while submitting `q` cancels after Enter. The Textual preflight reflows to the terminal size, so there is no fixed minimum geometry. The root menu picks a mode — **Software Engineer** (repo-scoped work that defers to the project's own AGENTS.md/CLAUDE.md: **Vanilla**, the bare CLI with no launch contract, tier bindings, or applied permissions, plus **Custom**; SE-specific review defaults arrive later), **Builder** (the tier presets: Balanced, Deep review, Fast batch, Solo with delegation off, plus Custom), **Session distill** — then a preset within it. Select **Custom** (in Software Engineer or Builder) to open a persistent settings hub for the main tier, review setup, host policy, and each tier binding. Every edit returns to that hub; **Start with these settings** is the final launch confirmation, **Save these settings globally and start** additionally persists the setup as a named preset (with host-scoped tier overrides) in your user config for reuse elsewhere, and **Exit without launching** cancels the launch. The rich preflight runs from a managed virtualenv (`launch/provision-venv.sh`, at `~/.local/share/agent-launch/venv`) that the launcher re-execs into on the interactive path; when that venv is unavailable, or the call is non-interactive, or `TERM` is `dumb`/unset, the launcher falls back to numbered prompts, where `b` is the back command. Backend command names are resolved from the calling environment's `PATH`; shell functions are not re-entered. Codex child bindings are materialized as session-selected agent configs under the user cache, while Claude receives model and effort in `--agents` JSON when delegation is enabled. Review runs cross-family by default (`review_family`, default `cross`; `same` restores today's same-family projection): because the main's tiers are one model family, every dispatchable review route — native and the deep route — runs on the opposite family. The exception is `slash-review`, the host's own built-in review command (`/code-review` on Claude, with `ultra` for its deep multi-agent pass; `/review` on Codex): it needs no dependency and always resolves, but being the main's own command it cannot be dispatched cross-family, so under `cross` it runs as the same-family floor and its verdicts are labeled PROPOSED. A Claude main dispatches gpt/codex review (native via the `codex-run` reviewer wrapper resolved under `$CODEX_HOME/bin`, deep via plain `codex exec -m <frontier model> -c model_reasoning_effort="ultra"` with a self-contained packet on stdin — `-c service_tier="fast"` is the explicit faster, shallower opt-in); a Codex main dispatches Anthropic/Claude review (native via `claude -p --permission-mode plan`, deep via the `claude` CLI headless with the keyword `ultracode` in the prompt, which is what opens Claude Code's dynamic workflow for that turn). The concrete reviewer command, resolved absolute path, and opposite-family tier bindings are named in the injected session-start contract; cross-family reviewers are dispatched as read-only subprocesses, not CLI-native subagents, since neither CLI hosts the other family as a native subagent. When a cross-family route is unavailable at launch or unauthenticated at use time it degrades to same-family native subagent review labeled PROPOSED (family collapse) rather than blocking; a requested non-none review with no cross-family route and no same-family fallback (delegation off) stays fail-closed. Review setup means configured/requested; this launcher does not claim that review completed, and unavailable runtimes such as Ultrawork are not offered until integrated.
|
|
98
|
+
In a TTY, zero-argument `codex` or `claude` opens the launch preflight. Every arrow-key TUI selection screen keeps the complete current setup in a fixed top panel, followed by the highlighted option's description and then the option list. Move with Up/Down, select with Enter, use Esc to return to the previous menu, and use `q` to cancel; Esc at the mode root also cancels. Each tier's model is chosen from the host's configured catalog, or via **Other** to type any model id the backend accepts; that text input preserves values that start with `q`, so Esc or Ctrl-C cancels immediately there, while submitting `q` cancels after Enter. The Textual preflight reflows to the terminal size, so there is no fixed minimum geometry. The root menu picks a mode — **Software Engineer** (repo-scoped work that defers to the project's own AGENTS.md/CLAUDE.md: **Vanilla**, the bare CLI with no launch contract, tier bindings, or applied permissions, plus **Custom**; SE-specific review defaults arrive later), **Builder** (the tier presets: Balanced, Deep review, Fast batch, Solo with delegation off, plus Custom), **Session distill** — then a preset within it. Two entries below the modes act on the installation rather than the launch: **Corpus packages** opens a checklist of the optional domain packages (core + infra always install) and, on Apply, hands the exact checked set to `agent-bios onboard --domains` — the installer alone deploys, runs the activation canary, and records the outcome, which the status panel then reports, loudly when an apply failed. **Language** switches the interface text between English, 한국어, and 日本語 and persists the choice in your own `launcher.local.toml`; it changes the launcher's own screens only, since the corpus the models consume is English-unified. Select **Custom** (in Software Engineer or Builder) to open a persistent settings hub for the main tier, review setup, host policy, and each tier binding. Every edit returns to that hub; **Start with these settings** is the final launch confirmation, **Save these settings globally and start** additionally persists the setup as a named preset (with host-scoped tier overrides) in your user config for reuse elsewhere, and **Exit without launching** cancels the launch. The rich preflight runs from a managed virtualenv (`launch/provision-venv.sh`, at `~/.local/share/agent-launch/venv`) that the launcher re-execs into on the interactive path; when that venv is unavailable, or the call is non-interactive, or `TERM` is `dumb`/unset, the launcher falls back to numbered prompts, where `b` is the back command. Backend command names are resolved from the calling environment's `PATH`; shell functions are not re-entered. Codex child bindings are materialized as session-selected agent configs under the user cache, while Claude receives model and effort in `--agents` JSON when delegation is enabled. Review runs cross-family by default (`review_family`, default `cross`; `same` restores today's same-family projection): because the main's tiers are one model family, every dispatchable review route — native and the deep route — runs on the opposite family. The exception is `slash-review`, the host's own built-in review command (`/code-review` on Claude, with `ultra` for its deep multi-agent pass; `/review` on Codex): it needs no dependency and always resolves, but being the main's own command it cannot be dispatched cross-family, so under `cross` it runs as the same-family floor and its verdicts are labeled PROPOSED. A Claude main dispatches gpt/codex review (native via the `codex-run` reviewer wrapper resolved under `$CODEX_HOME/bin`, deep via plain `codex exec -m <frontier model> -c model_reasoning_effort="ultra"` with a self-contained packet on stdin — `-c service_tier="fast"` is the explicit faster, shallower opt-in); a Codex main dispatches Anthropic/Claude review (native via `claude -p --permission-mode plan`, deep via the `claude` CLI headless with the keyword `ultracode` in the prompt, which is what opens Claude Code's dynamic workflow for that turn). The concrete reviewer command, resolved absolute path, and opposite-family tier bindings are named in the injected session-start contract; cross-family reviewers are dispatched as read-only subprocesses, not CLI-native subagents, since neither CLI hosts the other family as a native subagent. When a cross-family route is unavailable at launch or unauthenticated at use time it degrades to same-family native subagent review labeled PROPOSED (family collapse) rather than blocking; a requested non-none review with no cross-family route and no same-family fallback (delegation off) stays fail-closed. A reviewer this launcher has never seen is yours to add: **Register another reviewer…** in the review editor asks for the descriptor a method needs, proves the candidate by running it through the real config reader before a byte is written, and appends it to `review-methods.local.toml` beside your config — a file the installer never deploys, verifies, or overwrites, whose entries face exactly the validation a shipped one does and whose name may not shadow a shipped method. A refusal shows the reader's own message and leaves that file byte-identical. Review setup means configured/requested; this launcher does not claim that review completed, and unavailable runtimes such as Ultrawork are not offered until integrated.
|
|
99
99
|
|
|
100
100
|
At the shell-wrapper boundary, every argument-bearing command (`codex exec ...`, `claude -p ...`) and every non-TTY invocation skips launch-profile projection and preserves caller arguments. The Claude direct path intentionally retains its wrapper default, `--dangerously-skip-permissions`. `codex --no-tui ...` / `claude --no-tui ...` explicitly take that direct path, and `AGENT_LAUNCH_TUI=0` disables zero-argument TUI interception for a process tree.
|
|
101
101
|
|
|
102
|
-
Direct `agent-launch` calls still require a valid profile to resolve the backend command and its default arguments. `--preset`, `--custom`, or `--dry-run` select the configured-launch path even when non-TTY or combined with `--no-tui`; a non-TTY bare `--dry-run` deterministically uses Balanced, and a custom profile without that preset must pass `--preset NAME`. Forwarded backend arguments are
|
|
102
|
+
Direct `agent-launch` calls still require a valid profile to resolve the backend command and its default arguments. `--preset`, `--custom`, or `--dry-run` select the configured-launch path even when non-TTY or combined with `--no-tui`; a non-TTY bare `--dry-run` deterministically uses Balanced, and a custom profile without that preset must pass `--preset NAME`. Forwarded backend arguments are appended verbatim after the projected defaults; one that would override a projected option (the seat, the contract, delegation, policy) is refused at launch so the contract keeps describing the run, and the summary discloses forwarded arguments when present. For scripted configured launches, call `$HOME/.local/bin/agent-launch --preset NAME --yes HOST -- ...` or add `$HOME/.local/bin` to `PATH`. The summary goes to stderr so backend stdout stays machine-consumable.
|
|
103
103
|
|
|
104
104
|
Add `$CODEX_DIR/bin` to `PATH` or invoke the wrappers by absolute path. `codex-helm` follows the local CLI default and launches the HELM main with `--dangerously-bypass-approvals-and-sandbox`; an explicit `--sandbox MODE` disables bypass for that run regardless of flag order. `AGENTS.md` gives root/main local Codex sessions standing ordinary-subagent authorization when the delegation gates fire. A non-Ultra HELM main sets native multi-agent off by default and instructs HELM to send tiered dispatch through the internal `codex-run` adapter, where the selected model, effort, and sandbox are pinned; native multi-agent defaults on only when the HELM main itself is explicitly Ultra. FRONTIER is instructed to run as a separate `gpt-5.6-sol` root that is always read-only, at max by default, Ultra for genuinely divisible complex work, or a lower supported effort when cost or latency dominates. Because the HELM main has bypass authority and arbitrary expert `-c` by design, this dispatch route is an instruction-backed, live-E2E-verified default rather than a security boundary. Keep `codex-run` as the low-level internal adapter, not as a user-facing policy boundary. Both wrappers accept `-c key=value` as an expert override, and that override may intentionally change wrapper defaults for a single run.
|
|
105
105
|
|
|
@@ -154,6 +154,50 @@ How much independence a review actually bought, as an ordinal grade per reviewer
|
|
|
154
154
|
- After resume/clear/relocation, verify pwd, branch, and HEAD against the pinned handoff before acting.
|
|
155
155
|
- Attribute a parallel session's action (commit, branch, resource) by execution evidence in that session's own transcript, never by token mentions — shared handoff/memory files inject the same tokens into every session's context.
|
|
156
156
|
|
|
157
|
+
## Context Budget And Reset
|
|
158
|
+
|
|
159
|
+
Context growth is a property of the work, not of the host: over 1,075 sessions of 50+
|
|
160
|
+
requests across both CLIs it runs ~2,400 tokens per request (IQR 1,850-2,950), the two
|
|
161
|
+
hosts within 7% of each other (2,280 Claude, 2,450 Codex); the longest sessions (400+
|
|
162
|
+
requests) run lower, ~1,800. One budget therefore serves both, and what differs per host
|
|
163
|
+
is the price of ignoring it. The figure is a prior; the live session is measured below.
|
|
164
|
+
|
|
165
|
+
- **Automatic compaction fires only when the window is nearly full** — Claude at 84-87%
|
|
166
|
+
(windows cluster at 200K and 1M), Codex at ~95% of the window its transcript records as
|
|
167
|
+
`model_context_window` (258,400 on the sessions measured; a later Codex/model pair
|
|
168
|
+
records 353,400 — read the value, never assume it). Cache read is charged per
|
|
169
|
+
request against the whole loaded context, so leaving the reset to the host pays the
|
|
170
|
+
maximum on every request before it. Measured: input is 92-94% of session cost and
|
|
171
|
+
output 6-8%, at a 95-97% cache hit rate — the context is the bill, and uncached input
|
|
172
|
+
is 0.0% of it.
|
|
173
|
+
- Reset deliberately instead. Cost per request falls ~4x from an 867K auto-compact point
|
|
174
|
+
to 200K. The cost-theoretic optimum is ~65K, but it buys a compaction every ~32
|
|
175
|
+
requests at 2-3 minutes each, so 150-250K is the working range and the tail below it is
|
|
176
|
+
not worth chasing.
|
|
177
|
+
- **What bounds the budget is what survives the reset, not the token count.** A compaction
|
|
178
|
+
keeps a ~14K summary plus 3-4 recent messages and discards the rest — unguided when it
|
|
179
|
+
fires on its own. Anything already written to a file survives every reset, so the
|
|
180
|
+
earliest safe threshold is the one where durable state is already on disk. That is what
|
|
181
|
+
decouples cost from loss: without it, resetting more often loses proportionally more.
|
|
182
|
+
- Choose the mechanism by what is known, not by how large the context grew:
|
|
183
|
+
|
|
184
|
+
| Situation | Mechanism |
|
|
185
|
+
|---|---|
|
|
186
|
+
| Stage finished, what to keep is known | clear + dated handoff file — cheapest, and the loss is not a loss |
|
|
187
|
+
| Mid-stage, what to keep is known | write the handoff first, then compact with explicit instructions |
|
|
188
|
+
| Mid-stage, the needed detail is not yet identifiable | compact with instructions; a summary spans what a file cannot yet name |
|
|
189
|
+
| Original detail likely wanted later | clear, and record the transcript path in the handoff — transcripts persist on disk |
|
|
190
|
+
| The next question is unknown | new session plus messaging; only this keeps a round trip available |
|
|
191
|
+
| Growth is tool output | offload to subagents instead — measured ~20x cheaper (tier ~5x, context isolation ~4x) |
|
|
192
|
+
| The judgement trail itself is load-bearing | keep the session and pay the 2-5x resume |
|
|
193
|
+
|
|
194
|
+
- Measure rather than estimate: `agent-bios cost --context <transcript>` (the installed
|
|
195
|
+
entry to `session-cost.py`) reads either
|
|
196
|
+
host's transcript and reports current context, growth rate, compactions, and requests
|
|
197
|
+
remaining against a budget. Codex records `model_context_window` directly; Claude does
|
|
198
|
+
not, so the tool reports its observed auto-compaction point instead of assuming a
|
|
199
|
+
window.
|
|
200
|
+
|
|
157
201
|
## Handoff Contract
|
|
158
202
|
|
|
159
203
|
Write for the next agent and re-verification, not narrative. Required content:
|
|
@@ -223,3 +267,5 @@ Single owner of numeric defaults; one production environment, 2026-07. Recalibra
|
|
|
223
267
|
| two live delegation sessions | tiering saved ~3.3×; discarded prefixes made fresh respawn cheaper; unpinned reviewers inherited FRONTIER |
|
|
224
268
|
| Codex reach contrast | inherit ~16.5K vs hermetic ~8.7K tokens; schema and stdout/stderr contract verified |
|
|
225
269
|
| Codex native-spawn probe + HELM E2E | requested max/Ultra native children recorded xhigh/role null; separate read-only roots recorded max and Ultra successfully |
|
|
270
|
+
| 1,075 sessions of 50+ requests, both hosts (2026-08-16; the earlier 62-session top-by-size sample gave ~1,800-2,000) | context grows ~2,400 tok/request (IQR 1,850-2,950), hosts within 7%, ~1,800 in 400+-request sessions; auto-compaction fires at 84-95% of window, never earlier |
|
|
271
|
+
| 2 sessions decomposed by cost component (2026-08) | input 92-94% of cost, output 6-8%, cache hit 95-97%, uncached input 0.0%; an 867K→200K budget cuts cost per request ~4x |
|
|
@@ -80,6 +80,7 @@ patching them one at a time is a queue that refills.
|
|
|
80
80
|
|
|
81
81
|
- At each stage, run review loops as appropriate: self review, subagent review when available, and structured multi-lens review when the repository or domain supports one (concrete tool: Environment Binding below).
|
|
82
82
|
- Iterate until material issues reach zero: review, identify material issues, fix them, and review again.
|
|
83
|
+
- "Material issues reach zero" is counted over the declared defect criterion's stop-relevant class; choosing and declaring that criterion is owned by `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/review-defect-criteria.md`.
|
|
83
84
|
- Use the severity contract for materiality — the canonical definition is the ladder below; external review tools map their levels onto it: blocker, high, and medium are material; low and info are non-material.
|
|
84
85
|
- Treat blocker as primary happy-path or core-contract failure.
|
|
85
86
|
- Treat high as supported user, environment, data, or execution path failure.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
guide_id: review-defect-criteria
|
|
3
|
+
language: en
|
|
4
|
+
status: active
|
|
5
|
+
use_when:
|
|
6
|
+
- declaring what counts as a defect before dispatching any review
|
|
7
|
+
- choosing or writing the defect criterion for a system type × work goal
|
|
8
|
+
- a review loop plateaus, diverges, or its "material 0" stop never arrives
|
|
9
|
+
- a finding's class is contested at the boundary, or two lenses class it differently
|
|
10
|
+
core_rules:
|
|
11
|
+
- choose and declare the criterion before the review starts — undeclared, every reviewer substitutes its own
|
|
12
|
+
- the severity ladder answers how bad; the criterion answers whether it is a defect, of which class, and what "zero" is counted over
|
|
13
|
+
- a criterion with an empty cell is a hunch, not a criterion — do not start the review
|
|
14
|
+
- paste the goldens into the packet verbatim; a definition alone does not classify consistently at the boundary
|
|
15
|
+
- put the class enum on the accepting channel where one exists; on prose routes, run the fold procedure
|
|
16
|
+
- a mixed packet is split — never run two observers in one trajectory
|
|
17
|
+
verification_focus:
|
|
18
|
+
- every dispatched packet names its criterion and carries its goldens
|
|
19
|
+
- each round audits reported class against post-measurement class; repeated disagreement mints the next golden
|
|
20
|
+
- the stop condition is evaluated over the stop-relevant class only, never the whole finding count
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Review Defect Criteria
|
|
24
|
+
|
|
25
|
+
A defect criterion is chosen, not assumed. This guide is a scoped extension of
|
|
26
|
+
`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/review-request.md`: before that guide's
|
|
27
|
+
request composition starts, this one decides what the review is hunting. The
|
|
28
|
+
evidence: one codebase, two stop criteria, opposite trajectories.
|
|
29
|
+
|
|
30
|
+
| Rounds | Criterion | Trajectory |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| early | material = contract violation **or** unprotected contract sentence, small frozen surface | 10 → 4 → 1 → 0 |
|
|
33
|
+
| late | same, scoped to client-observable behavior | 17 → 19 → 21 → 23 → 19 — **plateau** |
|
|
34
|
+
| final | split classes: behavioral_defect / coverage_gap / doc_gap; stop = behavioral 0 | behavioral 10 → 5 — falling again |
|
|
35
|
+
|
|
36
|
+
The plateau was the criterion, not the code: counting "a contract sentence no test
|
|
37
|
+
protects" as a defect means every fix adds contract rows, each a potential defect
|
|
38
|
+
next round — a self-refilling criterion cannot reach zero while its surface grows.
|
|
39
|
+
Yet that same criterion is exactly right for a library, where an unprotected promise
|
|
40
|
+
is a first-class defect. Neither is wrong; starting without choosing is.
|
|
41
|
+
|
|
42
|
+
The **severity ladder** (in `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/coding-staged-workflow.md`)
|
|
43
|
+
answers *how bad* a finding is. The **criterion** answers *whether* it is a defect at
|
|
44
|
+
all, of which class, and what "zero" is counted over. The plateau happened entirely
|
|
45
|
+
inside "material" — no severity adjustment could have ended it. Ordering: system type
|
|
46
|
+
+ goal → defect criterion → classification → defect? → severity → materiality → stop.
|
|
47
|
+
|
|
48
|
+
## The criterion schema
|
|
49
|
+
|
|
50
|
+
One entry carries all of these. An empty cell makes it a hunch, not a criterion — do
|
|
51
|
+
not start the review on it.
|
|
52
|
+
|
|
53
|
+
| Field | Meaning |
|
|
54
|
+
|---|---|
|
|
55
|
+
| observer | whose eyes judge — client, caller, operator, a model reading the output |
|
|
56
|
+
| defect | what the observer must experience |
|
|
57
|
+
| classes | the enum, exactly ONE class stop-relevant; the others are relief valves that keep the stop class honest |
|
|
58
|
+
| evidence | what one finding must present |
|
|
59
|
+
| non-defects | defect-lookalikes excluded by this criterion — named explicitly |
|
|
60
|
+
| stop condition | what "0" means, counted over the stop class, and WHY it is reachable — how a growing surface is pinned |
|
|
61
|
+
| misclassification cost | which error is expensive — the direction to tell the reviewer to lean |
|
|
62
|
+
| goldens | ≥2 positive, ≥2 negative, ≥1 boundary; each with why, date, and provenance: `measured` (dated incident) or `constructed` (authored to pin a boundary) |
|
|
63
|
+
|
|
64
|
+
## The catalog
|
|
65
|
+
|
|
66
|
+
Four system types. A starting set, not a census.
|
|
67
|
+
|
|
68
|
+
### Web service / API surface (contract compliance)
|
|
69
|
+
|
|
70
|
+
- **Observer**: the HTTP client.
|
|
71
|
+
- **Defect**: a client today receives a response that differs from the contract — status code, envelope, stream terminal frames, the model actually run, isolation, resource caps.
|
|
72
|
+
- **Classes**: `behavioral_defect` (stop-relevant) · `coverage_gap` · `doc_gap`.
|
|
73
|
+
- **Evidence**: the concrete request and observed vs promised response.
|
|
74
|
+
- **Non-defects**: internal lifecycle (unless a request sequence shows two different responses), absence of a test, documentation wording.
|
|
75
|
+
- **Stop**: behavioral 0 — reachable: the code surface is finite and shrinks monotonically unless a fix creates a new behavioral defect.
|
|
76
|
+
- **Misclassification cost**: false negatives (a client actually breaks); but inflating coverage gaps into defects destroys the stop condition — audit category creep separately.
|
|
77
|
+
- **Goldens** (all `measured`, 2026-08-13–15):
|
|
78
|
+
- **+** `GET` on a nonexistent path returned 405, not 404 — an existing-method complaint attached to a path that does not exist.
|
|
79
|
+
- **+** a streaming endpoint's terminal delta carried only `output_tokens`, so cache/input token counts never reach a streaming client — non-streaming was correct, which is why nobody saw it.
|
|
80
|
+
- **−** "no test catches a one-line flip of this default": today's behavior is correct — `coverage_gap` here, a defect only under the library criterion.
|
|
81
|
+
- **−** child-process kill grace period — out of scope until a request sequence shows a client-visible difference; pins the observer to the client.
|
|
82
|
+
- **±** rejecting a documented-as-valid boundary value (compression 100, docs say "below 100 requires jpeg/webp") — the documentation is what makes it a defect; without that sentence it is taste.
|
|
83
|
+
|
|
84
|
+
### Library / SDK / contract-first system
|
|
85
|
+
|
|
86
|
+
- **Observer**: the caller **and** the future maintainer.
|
|
87
|
+
- **Defect**: a promise violated today, or a promise unprotected — a one-line change breaks it and nothing catches it. The API criterion's `coverage_gap` is first-class here.
|
|
88
|
+
- **Classes**: `contract_defect` (stop-relevant) · `style_note` · `internal_change`.
|
|
89
|
+
- **Evidence**: for a violation, as above; for an unprotected promise, the promise sentence + the one-line change that breaks it + the absence of any catching test.
|
|
90
|
+
- **Non-defects**: style, internal structure.
|
|
91
|
+
- **Stop**: caution — this criterion self-refills while the contract grows. Declare "0" only over a surface whose growth has stopped; on a growing surface, narrow the stop to "every promise this change added is protected."
|
|
92
|
+
- **Misclassification cost**: balanced — miss an unprotected promise and the next refactor breaks it silently; over-report and the loop never ends.
|
|
93
|
+
- **Goldens**:
|
|
94
|
+
- **+** a streaming-image event test matched only `.completed`, so an edit stream misnamed `image_generation.completed` still passed — mutation survival showed the promise unprotected. (`measured`, 2026-08-13–15)
|
|
95
|
+
- **+** the contract promised "storage serves a large image at least once"; the code evicted it on the next request — resolved by fixing the **contract**: the disagreement is the defect, whichever side moves. (`measured`, 2026-08-13–15)
|
|
96
|
+
- **−** renaming an internal helper no document promises — the observer holds the contract; undocumented internals carry no promise. (`measured`, 2026-08-13–15)
|
|
97
|
+
- **−** a caller breaking on undocumented iteration order — no promise existed; the rename boundary pinned from the caller's side. (`constructed`, 2026-08-19)
|
|
98
|
+
- **±** an inventory-listed test turns out vacuous — asserts nothing about its promise. "A test exists" is a claim about names until the assertion is read; hand the reviewer "check the assertion, not the name." (`measured`, 2026-08-13–15)
|
|
99
|
+
|
|
100
|
+
### AI workbench / harness (agent orchestration, review loops, verification pipelines)
|
|
101
|
+
|
|
102
|
+
- **Observer**: the operator, and any model consuming the harness's output.
|
|
103
|
+
- **Defect**: **a false signal that looks true** — a wrong PASS, a vacuous test, a silent fallback, a packet missing the call site, a wrong denominator. Being wrong is not the defect; being wrong while *looking right* is. Second form: a run claimed without provable dispatch — no receipt.
|
|
104
|
+
- **Classes**: `false_signal` (stop-relevant) · `detected_miss`. An unproven dispatch — a run or PASS claimed with no receipt — is classed `false_signal`, not given its own relief valve: a signal that cannot be shown true is counted false, or a round could declare completion while every dispatch stayed unproven.
|
|
105
|
+
- **Evidence**: the input on which the instrument gave a false verdict, plus the known correct answer. The standard probe: run the instrument against an input whose answer is known to be the opposite.
|
|
106
|
+
- **Non-defects**: one output's style; a false positive the harness itself **detected** — a caught error is the harness working.
|
|
107
|
+
- **Stop**: every PASS emitted this run survives a known-opposite check and evidences its own dispatch. The unit is "this run's signals are trustworthy," not a standing zero.
|
|
108
|
+
- **Misclassification cost**: false passes dominate. An instrument bug reporting failure dies in minutes because someone looks; one reporting success survives — a selection effect.
|
|
109
|
+
- **Goldens**:
|
|
110
|
+
- **+** a shell test runner received one nonexistent path (zsh does not word-split an unquoted variable), so every run exited 1 and every mutation reported KILLED — a green instrument that never ran its subject. (`measured`, 2026-08-13–15)
|
|
111
|
+
- **+** a test-inventory grep matched only `^test('...'` and missed every parameterized name; three reviewers judged a populated file empty — nothing tied the inventory's denominator to the source's own count. (`measured`, 2026-08-13–15)
|
|
112
|
+
- **+** a negative control kept passing after a faithful revert of the fix it was written against — a guard satisfied by an absence, a false PASS about the gate itself; found only by re-running reverts. (`measured`, 2026-08)
|
|
113
|
+
- **+** a review round returned "clean" with no receipt evidencing that the declared packet was dispatched on the exact seat — `false_signal` although nothing observed was wrong: what the stop refuses is absence of proof of dispatch, not proof of falsity. (`constructed`, 2026-08-19, pinned after a classification dispute at exactly this boundary)
|
|
114
|
+
- **−** one reviewer over-classed an item as material; another lens plus measurement filtered it — the harness caught it, so it worked: `detected_miss`, not a defect. (`measured`, 2026-08-13–15)
|
|
115
|
+
- **−** five review rounds returned 8 → 9 → 10 → 5 → 12 findings, refusing to converge — and every count was true. A truthful unpleasant signal is not a false signal; the defect lived in the undeclared criterion. (`measured`, 2026-08-16–17)
|
|
116
|
+
- **±** a surviving mutation proved equivalent — the platform already normalized what the mutated guard checked. Neither a harness defect nor a test gap; but a harness that auto-reads "survived = gap" has a defect in that rule. (`measured`, 2026-08-13–15)
|
|
117
|
+
|
|
118
|
+
### Decision ontology (an ontology a model decides from)
|
|
119
|
+
|
|
120
|
+
- **Observer**: a model or agent deciding from the ontology alone.
|
|
121
|
+
- **Defect**: a representation that produces a wrong decision or blocks a right one: overlapping concept boundaries, a missing distinction the decision needs, an instance contradicting reality, a wrong relation direction or cardinality, a name implying what the definition does not say.
|
|
122
|
+
- **Classes**: `decision_defect` (stop-relevant) · `representation_note` · `out_of_scope_gap`.
|
|
123
|
+
- **Evidence**: a **decision scenario** — "answered from the ontology alone, this question yields X; reality is Y" — with the question, the path taken, and the ground truth.
|
|
124
|
+
- **Non-defects**: representation format, completeness as such, a question the model would get wrong without the ontology too.
|
|
125
|
+
- **Stop**: zero wrong decisions over the agreed scenario set. The set is the scope — fix it first, or this criterion self-refills like the library one.
|
|
126
|
+
- **Misclassification cost**: situational — feeding a hard gate makes false negatives expensive; exploratory aid makes false positives expensive. Filling this cell is mandatory at declaration.
|
|
127
|
+
- **Goldens** (all `constructed`, 2026-08-19, authored to pin boundaries the source loop's decision-scenario framing left open):
|
|
128
|
+
- **+** `Customer` and `Account` both define "the paying party"; a refund-routing question resolves through both paths — overlap is a decision defect even when each definition is individually correct.
|
|
129
|
+
- **+** `Order —hasOne→ Payment` while split payments exist — cardinality is a claim, and a false claim misleads the deciding model.
|
|
130
|
+
- **−** a verbose concept description — no decision changes; form is outside this observer's sight.
|
|
131
|
+
- **−** a domain absent that no scenario in the agreed set needs — completeness is scoped by the set, not the world.
|
|
132
|
+
- **±** the distinction exists but the model cannot find it (name or link missing) — with the observer fixed as "a model seeing only the ontology," unreachable is a representation defect, not a search defect. The observer clause decides the class.
|
|
133
|
+
|
|
134
|
+
## Before any review
|
|
135
|
+
|
|
136
|
+
1. Write one sentence: the system type and this work's goal. ("API surface — make the responses clients receive today match the contract." / "Harness — make this run's PASS signals trustworthy.")
|
|
137
|
+
2. Pick a criterion from the catalog, or fill the schema fresh. A cell you cannot fill means the review does not start.
|
|
138
|
+
3. Paste the goldens into the packet verbatim. Never the definition alone.
|
|
139
|
+
4. Declare the stop condition and why it is reachable — or how the scope was pinned to make it so.
|
|
140
|
+
5. Put the classification on the accepting channel (next section).
|
|
141
|
+
6. Audit every round: reported class vs the class confirmed after measurement. A repeated disagreement is the next golden.
|
|
142
|
+
|
|
143
|
+
## Enforcing the enum, by channel
|
|
144
|
+
|
|
145
|
+
Ranked by how much the channel refuses for you:
|
|
146
|
+
|
|
147
|
+
1. **A route with a submit schema**: classification is a required enum field. The
|
|
148
|
+
measured precedent is anchors — 2,466 of 2,466 findings carried one, because the
|
|
149
|
+
schema refuses output without it. Where this channel exists, use it.
|
|
150
|
+
2. **Prose-packet routes** (the shipped deep-review methods): the packet header
|
|
151
|
+
declares `Criterion: <name>` with the goldens pasted verbatim, and the dispatching
|
|
152
|
+
agent runs this fold procedure on what comes back:
|
|
153
|
+
1. For each returned finding row, look up its class against the declared enum.
|
|
154
|
+
2. A row carrying a class from the enum enters the findings ledger under that class.
|
|
155
|
+
3. A row with no class, or a class outside the enum, is **not admitted**: send it
|
|
156
|
+
back once for classification, or record it as refused with the reason. Never
|
|
157
|
+
admit it unclassified, and never guess its class for it.
|
|
158
|
+
4. Count the stop condition over the stop-relevant class only.
|
|
159
|
+
5. Record the audit pair (reported class, confirmed class) for procedure step 6.
|
|
160
|
+
|
|
161
|
+
Stated honestly: on a prose route this is steering, not control — the fold is
|
|
162
|
+
performed by an agent following this guide, and nothing structural refuses a
|
|
163
|
+
class-less row for it. That is the known weaker result of a request-only rule.
|
|
164
|
+
3. A structural criterion slot in the review dispatch machinery is deliberately not
|
|
165
|
+
part of this guide: a criterion is per-review while dispatch config is per-launch,
|
|
166
|
+
and a slot with no schema-backed consumer would be an inert value. Until such a
|
|
167
|
+
channel exists, ranks 1–2 are the real ones.
|
|
168
|
+
|
|
169
|
+
## Golden lifecycle
|
|
170
|
+
|
|
171
|
+
- **Admission**: only a golden that decides a boundary the definition leaves open,
|
|
172
|
+
evidenced by a recorded classification disagreement or an audited misclassification;
|
|
173
|
+
provenance labeled `measured` or `constructed`, each with its why and date.
|
|
174
|
+
- **Overturn**: only by a measured counterexample. Correct the golden in place — this
|
|
175
|
+
guide describes the present — plus a dated decision record, in whatever channel the
|
|
176
|
+
repo keeps decisions, naming the closed golden and the counterexample. Silent
|
|
177
|
+
deletion is forbidden: a wrong golden makes reviewers systematically wrong, and an
|
|
178
|
+
untracked fix hides that it ever did.
|
|
179
|
+
|
|
180
|
+
## Declaring, switching, and mixed packets
|
|
181
|
+
|
|
182
|
+
- Every dispatched packet names its criterion. A harness round declares `AI harness`;
|
|
183
|
+
a round whose system type has no catalog entry declares a **task-local criterion**,
|
|
184
|
+
written in the packet itself, conforming to the schema above and labeled task-local
|
|
185
|
+
— repeated use is what earns a catalog entry.
|
|
186
|
+
- A mixed packet is split into one packet per criterion. Two observers in one
|
|
187
|
+
trajectory produce findings no single stop condition can count.
|
|
188
|
+
- When a criterion changes mid-loop: re-classify only the findings still open; rounds
|
|
189
|
+
already recorded are dated history. Never splice pre- and post-switch counts into
|
|
190
|
+
one series — they count different things.
|
|
191
|
+
|
|
192
|
+
## Evidence base
|
|
193
|
+
|
|
194
|
+
Derived from a 39-round adversarial review loop (3 lenses × a frontier reviewer) on a
|
|
195
|
+
peer OAuth CLI-API adapter, 2026-08-13–15: the same codebase plateaued at
|
|
196
|
+
17→19→21→23→19 findings under a self-refilling criterion and resumed falling
|
|
197
|
+
(behavioral 10→5) the round the classes were split and the stop counted over
|
|
198
|
+
`behavioral_defect` alone. The boundary evidence is from the same loop: a packet
|
|
199
|
+
instruction saying "do not inflate coverage gaps" still left two lenses classing one
|
|
200
|
+
finding `doc_gap` and `behavioral_defect`, both defensibly — one golden would have
|
|
201
|
+
decided it. The non-convergence golden (8→9→10→5→12, every count true) is this
|
|
202
|
+
environment's own launcher-review loop, 2026-08-16–17, run under an undeclared
|
|
203
|
+
"material" criterion; the two series are two populations under two criteria and are
|
|
204
|
+
cited separately for exactly the reason the migration section gives. Re-derive when
|
|
205
|
+
the review routes or the bound models change.
|
|
@@ -26,7 +26,11 @@ verification_focus:
|
|
|
26
26
|
This guide is a scoped extension of the global Coding Guidelines. Use it when
|
|
27
27
|
composing what you ask a reviewer for — the request, the evidence bar, the
|
|
28
28
|
verdict shape. It does not cover when to review, how deep, or what counts as
|
|
29
|
-
material
|
|
29
|
+
material — that last question splits in two: the severity ladder and review loop in
|
|
30
|
+
`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/coding-staged-workflow.md` own *how bad*,
|
|
31
|
+
while whether something is a defect at all, of which class, and what "zero" is
|
|
32
|
+
counted over is the defect criterion, owned by
|
|
33
|
+
`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/review-defect-criteria.md`. Nor
|
|
30
34
|
which reviewer kind to route to — the convergence heuristic in
|
|
31
35
|
`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/verification-discipline.md` owns that. Phrasing a prompt for a specific model
|
|
32
36
|
family is out of scope here; where that guidance ships, the rule that needs it
|
|
@@ -38,6 +42,16 @@ is a per-model claim; these are the failures that persist regardless of who
|
|
|
38
42
|
reviews. Every rule names the evidence behind it, because a review guide that
|
|
39
43
|
asserts without evidence would fail its own bar.
|
|
40
44
|
|
|
45
|
+
## Declare the criterion
|
|
46
|
+
|
|
47
|
+
Before composing anything else, name the defect criterion the review runs under —
|
|
48
|
+
the per-system-type definition of what a defect is, its class enum, and what "zero"
|
|
49
|
+
is counted over. `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/guides/review-defect-criteria.md`
|
|
50
|
+
owns choosing it; this guide assumes one is declared. Put `Criterion: <name>` at the
|
|
51
|
+
top of the packet with that criterion's goldens pasted verbatim — a definition
|
|
52
|
+
without goldens does not classify consistently at the boundary, and an undeclared
|
|
53
|
+
criterion means every reviewer substitutes its own.
|
|
54
|
+
|
|
41
55
|
## Demand a failure path, not a gap
|
|
42
56
|
|
|
43
57
|
The dominant reviewer failure is not hallucination. Across 372 rejected
|
|
@@ -172,9 +172,19 @@ def main() -> int:
|
|
|
172
172
|
payload = json.load(sys.stdin)
|
|
173
173
|
except Exception:
|
|
174
174
|
return 0 # never block on malformed input
|
|
175
|
-
|
|
175
|
+
# Malformed is a SHAPE, not just a parse failure. The guard above only covered bytes
|
|
176
|
+
# that are not JSON; a well-formed payload of the wrong shape — a top-level array, a
|
|
177
|
+
# list-valued tool_input, a non-string command — walked straight into `.get` or into
|
|
178
|
+
# `re.search` and left this hook exiting 1 with a traceback. That is the one thing
|
|
179
|
+
# the charter says it never does, and it would do it before EVERY Bash call in every
|
|
180
|
+
# deployed session, on a payload shape decided by a host this repo does not own.
|
|
181
|
+
# Advisory means silent on anything it cannot read, so each check returns 0.
|
|
182
|
+
if not isinstance(payload, dict) or payload.get("tool_name") != "Bash":
|
|
183
|
+
return 0
|
|
184
|
+
tool_input = payload.get("tool_input")
|
|
185
|
+
command = tool_input.get("command") if isinstance(tool_input, dict) else None
|
|
186
|
+
if not isinstance(command, str) or not command:
|
|
176
187
|
return 0
|
|
177
|
-
command = (payload.get("tool_input") or {}).get("command") or ""
|
|
178
188
|
hits = matches(command)
|
|
179
189
|
if not hits:
|
|
180
190
|
return 0
|
|
@@ -219,6 +229,31 @@ def self_test() -> int:
|
|
|
219
229
|
if r2.stdout.strip():
|
|
220
230
|
problems.append(f"hook injected on a command that matches nothing: {r2.stdout.strip()[:80]!r}")
|
|
221
231
|
|
|
232
|
+
# Never-blocks is a claim about EVERY payload, so the shapes that are valid JSON and
|
|
233
|
+
# not the expected object are the ones worth naming. Each of these exited 1 with a
|
|
234
|
+
# traceback before the shape guard existed, and each stands for a different way the
|
|
235
|
+
# host's payload could move: a different top-level type, a different tool_input type,
|
|
236
|
+
# a command that is not a string. The two controls above are the other half — a guard
|
|
237
|
+
# that returns 0 on everything would satisfy this block and nothing else.
|
|
238
|
+
for label, raw in (
|
|
239
|
+
("top-level array", "[1, 2, 3]"),
|
|
240
|
+
("top-level null", "null"),
|
|
241
|
+
("top-level string", '"hello"'),
|
|
242
|
+
("tool_input as a list", '{"tool_name": "Bash", "tool_input": [1, 2]}'),
|
|
243
|
+
("tool_input as null", '{"tool_name": "Bash", "tool_input": null}'),
|
|
244
|
+
("command as a number", '{"tool_name": "Bash", "tool_input": {"command": 123}}'),
|
|
245
|
+
("command as a list", '{"tool_name": "Bash", "tool_input": {"command": ["git", "pull"]}}'),
|
|
246
|
+
("command absent", '{"tool_name": "Bash", "tool_input": {}}'),
|
|
247
|
+
("empty stdin", ""),
|
|
248
|
+
):
|
|
249
|
+
r3 = subprocess.run([sys.executable, str(here)], input=raw, capture_output=True, text=True)
|
|
250
|
+
if r3.returncode != 0:
|
|
251
|
+
problems.append(
|
|
252
|
+
f"hook exited {r3.returncode} on {label} instead of staying silent: "
|
|
253
|
+
f"{r3.stderr.strip()[-120:]}")
|
|
254
|
+
elif r3.stdout.strip():
|
|
255
|
+
problems.append(f"hook injected context for {label}: {r3.stdout.strip()[:80]!r}")
|
|
256
|
+
|
|
222
257
|
# Every rule needs a command that must reach it. A nonempty pattern is not evidence: a
|
|
223
258
|
# trigger of `(?!)` can never match, and a rule suppressed by the special-case logic in
|
|
224
259
|
# matches() never reaches the caller either — both leave the rule inert while this file
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: repo-charter
|
|
3
|
+
description: Write or overhaul a repository's AGENTS.md (with CLAUDE.md as a one-line shim) for the agents that will work in it. Use when a repo has no agent instructions, when its AGENTS.md is a preamble that names nothing, or when the user asks to set a repo up for coding agents. Reads invariants and traps out of the code rather than pasting a template.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Repo charter
|
|
7
|
+
|
|
8
|
+
A repository's AGENTS.md is the repo's own layer of agent instruction — what a global
|
|
9
|
+
corpus cannot know because it is true only here. This skill produces that layer for a real
|
|
10
|
+
repository, and it produces it *from the repository*: the substantive half is reading the
|
|
11
|
+
invariants, the gates and the traps out of the code, and no template can do that part.
|
|
12
|
+
|
|
13
|
+
The output belongs to the repository — its team, its contributors, its git history. Nothing
|
|
14
|
+
this skill writes carries a marker, a version stamp, or copyable boilerplate, because nothing
|
|
15
|
+
outside that repo will ever update it.
|
|
16
|
+
|
|
17
|
+
## 0. First decide whether the file is warranted
|
|
18
|
+
|
|
19
|
+
Many repositories correctly have no instruction file. Content and documentation repos need
|
|
20
|
+
at most a few lines of orientation; a personal repo rarely needs governance; and a one-line
|
|
21
|
+
fix in someone else's project is not the moment to charter it — the file is theirs to create.
|
|
22
|
+
Write one when agents will do repeated, non-trivial work here and the repo carries rules a
|
|
23
|
+
newcomer would otherwise learn by breaking something. If the answer is no, say so and stop;
|
|
24
|
+
an empty file with a heading is worse than none.
|
|
25
|
+
|
|
26
|
+
Read enough to answer honestly before deciding: the top-level tree, the package or build
|
|
27
|
+
manifest, hook and CI configuration, and the last fifty commit subjects. Steps 0 and 1 are
|
|
28
|
+
provisional; step 3 may revise them. **Draft from the code first** — if the repo already has
|
|
29
|
+
an instruction file, open it only at step 8, because reading it earlier anchors your
|
|
30
|
+
structure to it and the comparison stops being evidence.
|
|
31
|
+
|
|
32
|
+
## 1. Purpose type, then category mix
|
|
33
|
+
|
|
34
|
+
Decide what the repo is *for* before writing a line. The type fixes which categories must be
|
|
35
|
+
thick; everything else stays thin or absent.
|
|
36
|
+
|
|
37
|
+
| Purpose type | Categories that must be thick |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| Library / framework that accepts outside contribution | contribution protocol, completion gates, hard boundaries |
|
|
40
|
+
| Application / product | project invariants, pitfall warnings, co-change duties |
|
|
41
|
+
| CLI / single-author tool | repo orientation, project invariants, task procedure |
|
|
42
|
+
| Monorepo / platform | context routing, task procedure |
|
|
43
|
+
| Corpus / payload the repo publishes elsewhere (the code is delivery, the content is the product) | hard boundaries, co-change duties, completion gates |
|
|
44
|
+
| Content / docs | repo orientation only, minimal |
|
|
45
|
+
|
|
46
|
+
A repo may take two rows; take the union and note which row explains each thick category.
|
|
47
|
+
|
|
48
|
+
The category vocabulary, in one line each: **repo orientation** (what lives where, so nobody
|
|
49
|
+
has to search), **command recipes** (the exact commands), **house form** (the shape of
|
|
50
|
+
outputs), **project invariants** (semantic contracts the code must keep), **context routing**
|
|
51
|
+
(when to open which document, skill or tool), **task procedure** (the order for a kind of
|
|
52
|
+
work), **pitfall warnings** (traps and environment quirks that cause misdiagnosis),
|
|
53
|
+
**contribution protocol** (how a change gets accepted), **completion gates** (what must pass
|
|
54
|
+
before "done"), **co-change duties** (change X, then also Y), **hard boundaries** (never),
|
|
55
|
+
**human authority** (where to stop and hand over), **agent conduct** (how the agent itself
|
|
56
|
+
operates). Across thousands of real files, human authority is the rarest of these — people
|
|
57
|
+
write prohibitions but not escalation paths — so it is asked for explicitly in step 5.
|
|
58
|
+
|
|
59
|
+
## 2. AGENTS.md is the body; CLAUDE.md is a shim
|
|
60
|
+
|
|
61
|
+
Write one file: `AGENTS.md`, which Codex reads natively. Where Claude Code is used, add a
|
|
62
|
+
`CLAUDE.md` whose only content is an import of `AGENTS.md` (`@AGENTS.md`), plus at most a
|
|
63
|
+
sentence saying why the shim exists — and if that sentence claims something about a tool's
|
|
64
|
+
behaviour, date it or drop it, because such claims rot into lies. Two bodies drift; one body
|
|
65
|
+
and one pointer cannot. A file's name does not tell an agent which tool it is for — content
|
|
66
|
+
that opens "guidance for tool X" is copied between tools verbatim, so leave the tool's name
|
|
67
|
+
out of the body; the shim sentence is the one exception.
|
|
68
|
+
|
|
69
|
+
## 3. Read the invariants and traps out of the code
|
|
70
|
+
|
|
71
|
+
This is the work, and it is done by reading, not by asking the repo's owner to dictate.
|
|
72
|
+
For an existing repo, collect at least these before drafting:
|
|
73
|
+
|
|
74
|
+
- **What runs before a commit and what it enforces.** Hooks, CI configuration, gate scripts,
|
|
75
|
+
lint and test entrypoints. For each rule you state, name the file that enforces it, and
|
|
76
|
+
mark the rules nothing enforces as convention — a reader must be able to tell a gate from
|
|
77
|
+
a wish.
|
|
78
|
+
- **What a green check does and does not prove.** A test suite that skips a leg, a check that
|
|
79
|
+
runs against a fixture rather than the real path, a scan whose subject set can be empty.
|
|
80
|
+
These belong in the file because they are exactly what a newcomer misreads.
|
|
81
|
+
- **Generated paths.** Anything produced from another file must be named as such, with the
|
|
82
|
+
generator and the command that regenerates it; hand-editing a projection is the most
|
|
83
|
+
common silent defect.
|
|
84
|
+
- **Where authority lives.** For each value that appears in more than one place, which copy
|
|
85
|
+
is the source and how the others follow it.
|
|
86
|
+
- **What the history paid for.** Read the recent log and any postmortems or dated design
|
|
87
|
+
notes for mistakes that cost a real attempt — the deploy that took the wrong tree, the
|
|
88
|
+
command whose exit status lied, the flag that was rejected rather than implemented. Each
|
|
89
|
+
becomes a present-tense warning that names the symptom.
|
|
90
|
+
- **Runtime shape that a reader cannot infer.** Which entrypoint is real, which directory
|
|
91
|
+
is payload versus tooling, which environment the tests assume.
|
|
92
|
+
|
|
93
|
+
State each finding as a rule about the present, in one or two sentences, and cite the code
|
|
94
|
+
by **file and identifier** — function, constant, leg name — never by line number: a symbol
|
|
95
|
+
survives an edit and is greppable, a line number is wrong within a day. A rule that cannot
|
|
96
|
+
cite its enforcer or its evidence is a candidate for deletion. Stop collecting when a new
|
|
97
|
+
source stops producing rules you would keep — usually after the hooks, the gate entrypoints,
|
|
98
|
+
the packaging manifest, the installer, and the recent commit subjects.
|
|
99
|
+
|
|
100
|
+
## 4. Choose the sections from the findings, not from a template
|
|
101
|
+
|
|
102
|
+
Group what step 3 produced under headings that match the repo's thick categories from
|
|
103
|
+
step 1. Order by what an agent hits first: orientation and the pre-commit contract before
|
|
104
|
+
style; traps near the commands that trigger them. The file is re-sent to every session, so it
|
|
105
|
+
is a token budget: if the draft exceeds roughly 2,000 words, the excess is a guide — move the
|
|
106
|
+
longest section behind a pointer and keep the one rule that says when to read it.
|
|
107
|
+
|
|
108
|
+
## 5. Ask for human authority explicitly
|
|
109
|
+
|
|
110
|
+
Ask the user, in outcome terms, which decisions an agent must bring to a person rather than
|
|
111
|
+
settle: releases and publishes, schema or data migrations, anything touching credentials or
|
|
112
|
+
production, deleting or rewriting history, spending money, changing a public contract.
|
|
113
|
+
Write the answer as a short list of *stop points* with what to bring (evidence, options),
|
|
114
|
+
not as a prohibition. If the user has no answer yet, record the question as open in the
|
|
115
|
+
file rather than inventing a policy; if the run is unattended, draft the stop points as a
|
|
116
|
+
proposal marked as requiring confirmation, and never infer one the code does not evidence
|
|
117
|
+
as irreversible or outward-facing.
|
|
118
|
+
|
|
119
|
+
## 6. Refuse the zero-content preamble
|
|
120
|
+
|
|
121
|
+
Delete any sentence that could open any repository's file: "this file provides guidance to
|
|
122
|
+
agents working in this repository", "follow best practices", "write clean code", restated
|
|
123
|
+
general engineering rules the agent already carries. Read the draft once more asking of each
|
|
124
|
+
line, "what would an agent do differently after reading this?" — a line with no answer goes.
|
|
125
|
+
|
|
126
|
+
## 7. Directory-scoped files only for the monorepo or platform type
|
|
127
|
+
|
|
128
|
+
A `CLAUDE.md` or `AGENTS.md` placed inside a subdirectory is loaded when a file beside it is
|
|
129
|
+
touched, which makes it survive the loss of early session context. That is valuable when
|
|
130
|
+
subtrees have genuinely different rules — the monorepo case — and noise everywhere else,
|
|
131
|
+
where it splits one file's authority in two. Default to one root file.
|
|
132
|
+
|
|
133
|
+
## 8. Before handing it over
|
|
134
|
+
|
|
135
|
+
Self-review against three constraints, and fix rather than annotate:
|
|
136
|
+
|
|
137
|
+
- Nothing in the output is boilerplate meant to be pasted elsewhere, and nothing points back
|
|
138
|
+
to this skill, to a version, or to any tool that would "update it later" — no such
|
|
139
|
+
mechanism exists, and a stamp that promises one is a lie the repo will carry.
|
|
140
|
+
- Every rule names the code that enforces it, or is marked as convention.
|
|
141
|
+
- The file states current behaviour only. Change narratives, rejected alternatives and
|
|
142
|
+
handoff logs are pointed to where they live (design notes, the log), not repeated here. A
|
|
143
|
+
live process that is followed but not enforced is current, not history — it belongs in the
|
|
144
|
+
file or behind a pointer, and length decides which.
|
|
145
|
+
|
|
146
|
+
Now read the repo's existing instruction file, if any, and verify each of its claims against
|
|
147
|
+
the code — a stale sentence beside current code reads as fact. Show the user your skeleton
|
|
148
|
+
beside it and name the divergences: a large gap means either the draft or the existing file
|
|
149
|
+
is wrong, and finding out which is the point of the exercise.
|