Development record
A verifier works on a copy: no agent mutates a live worktree to test it, and the tree is proved clean before any act that reads it
The rule
Verification that changes the thing being verified is not verification. Two obligations follow, and they sit on different parties.
On the verifier. An agent that mutates code to see whether a test catches the mutation — or that patches, stubs, or instruments a tree to probe it — does that on a copy, never on the live worktree:
git -C <worktree> archive HEAD | tar -x -C <scratch>/mut
The copy goes outside every working tree, in a directory of its own under
~/.abcd.noindex/ keyed on the root-commit SHA (the session's scratchpad only
for a copy the same turn makes and discards), never
.abcd/.work.local/scratch/ (amended 2026-10-16 per iss-2610161501104088: a
copy handed to a verifier agent is read by a later agent, and the scratchpad
does not survive a reboot or a new session). Before reporting, the verifier
proves the original is untouched, and says so:
git -C <worktree> status --porcelain # must be empty
On whoever acts on the tree. A merge, a commit, a push, or a gate run is preceded by that same emptiness check, taken immediately before the act and not inherited from earlier in the sequence. A tree that was clean five minutes ago is not evidence about a tree now.
Why
The hazard is the window, not the intent. An in-place mutation is usually
restored within seconds, and the agent doing it is behaving carefully by its
own lights. But while it is applied, every other reader of that tree is wrong:
a gate reports on code nobody wrote, a merge takes a mutation into a branch, a
commit makes it permanent. Nothing announces the window, and the restore step
is itself fallible — a sibling agent lost a job to an interactive cp -i
prompt while restoring a file this way, leaving the mutation in place until it
was noticed.
It also breaks the peer-work convention from the other end. A concurrent session that sees the modification cannot tell a mutation from real work, so the correct response — leave it alone and report it — costs a round trip and invites the wrong response, which is to revert a peer's genuine change.
The gate
- Given an agent verifying by mutation, probe or instrumentation, when it applies the change, then the change lands in a scratch copy and the reviewed worktree is untouched.
- Given a verifier's report, when it is delivered, then it states that the reviewed worktree is clean, and names any file it could not restore.
- Given a merge, commit, push or gate run, when it begins, then the working tree is proved clean immediately before it, not earlier in the sequence.
Fit
The discipline family is the right home: this fixes a method across every verification, and has no user moment of its own. It is adjacent to the one-writer-per-file principle and does not overlap it — that principle governs committed records across merge windows and puts per-worktree files explicitly out of scope, while this governs the working tree inside a single window.
Staging
Reach is this repository only, and that is a real limit rather than a rounding error. A rule reaches every abcd-managed repo when it is a recall keyword and rule set in a bundled default domain, and those are compiled into the binary. This record is durable and citable here; it is not yet injected anywhere else.
The rungs, smallest first, per script-first-mvp:
- The documented protocol — this record, plus the line in the repository's own conventions. Live at filing. It is what agent briefs cite.
- A recall-injected domain rule, so a session working on review or
verification receives it without being told. In this repository that is one
entry in
.abcd/rules.json; reaching other managed repos means a bundled default domain, which is a code change and its own intent. - A mechanical check. The verifier half is observable — a clean-tree assertion at the end of a review — and the actor half is a precondition on the merge path. Neither is built, and neither should be built before the protocol has been run enough times to say what it costs.
Nothing above rung 1 is claimed. A brief that cites this record is the whole of the enforcement today.
Frontmatter
| id | itd-193 |
|---|---|
| type | intent |
| lifecycle | disciplines |
| kind | discipline |
| severity | minor |
.abcd/development/intents/disciplines/itd-193-a-verifier-works-on-a-copy-no-agent.md
open on GitHub ↗ · commit history ↗
Referenced by2
- itd-196A capability is rehearsed end to end before it ships, over a real tree, in a throwaway snapshotbuilt on by
- itd-2609251624540864A lab runs one procedure from intention to discardbuilt on by
mentions6
- dcn-26083000000000262026-08-30 — The in-place mutation hazard graduates from a session habit to the committed record as itd-193, a discipline: a verifier works on a copy, and whoever acts on a tree proves it clean immediately before the act rather than inheriting an earlier check. Occasioned by a reviewer running its mutation matrix against a live build worktree while a peer review, correctly, reported the modification as untouchable peer work it could not distinguish from real work. Routed by the four-piece table: the stance and its gate to a discipline record; the operational line to the AGENTS.md concurrent-sessions section beside the peer-work rule it complements; no capability, because the documented protocol is the MVP at this rung (script-first-mvp); no ADR, because it governs method rather than architecture. Adjacent to the one-writer-per-file principle and deliberately not folded into it: that principle governs committed records across merge windows and puts per-worktree files out of scope, while this governs the working tree inside a single window. REACH IS THIS REPOSITORY ONLY and the record says so — a rule reaches every abcd-managed repo through a bundled default domain compiled into the binary, which is a code change and its own intent; rung 2 (a recall-injected domain rule) and rung 3 (a mechanical clean-tree assertion) are named in the record and neither is claimed.
- dcn-26091500000000082026-09-15 — itd-200 / spc-70 ship with five implementation rulings the spec left open, taken by the implementing session and reversible by the maintainer (recorded here so none is silent). (1) The status-line offer is its own consent category, `status-line`, advisory and never approved by `--yes`, exactly as the git-identity pin is: it rewrites a harness-wide user setting and takes element choices, so only an answered prompt writes it, and `yes |` answers it. A decline is never persisted; the offer returns on the next install until the user-level setting exists. (2) Harness detection is positive evidence only: the harness's user settings file exists and parses (its directory from `$CLAUDE_CONFIG_DIR`, else the harness home); no file means nothing offered and nothing written. Ownership of a status command is decided by shape (`<absolute entry with leaf abcd> statusline`), which is what makes a dangling entry decidable when the binary is gone. (3) The two record counts on the row are FOLDER counts (files named `iss-*.md` in the open ledger; `itd-*.md` in drafts plus planned), not the board's parsing readers, because the verb runs on every status refresh and a parse over a clone-controlled record measured 0.96 s at twenty thousand records; folder membership is the record's own status signal, so the number differs only where a parser would skip an unreadable record. The spec's Scope text is corrected to say so. (4) Two guards the reviews required and the spec did not name: the previous-command fallback refuses to run when its own marker is already in the environment and marks its child, so a recorded command that reaches `abcd statusline` in any spelling terminates instead of forking without bound, and the install refuses to record such a command at all; and every write to the harness settings re-reads the file immediately before the act and merges into the fresh document, refusing when the status-line key changed under the prompts (itd-193's rule applied to a user file a live harness also writes). (5) `abcd mode <state>` prints the owed-answer line only when the user-level setting is absent or disabled — the setting is the only evidence of a status surface abcd has — and `managed` prints nothing; the bare board renders the presence line even when the line is disabled, because the switch silences the line and the board is the fallback. Rejected: making the offer a config-change gap (one consent for the PATH entry and the status line would let the reason paragraph be skipped); counting through the board's readers with a cache (a second mechanism to keep correct for the cheapest surface in the tree). The fidelity audit rcp-636b39d5541a records five criteria met with concerns, all disclosed on the shipped record; the one the record could close is closed: the GRILL rule domain now tells an agent to set the mode before it stops for a verdict.
- dcn-26101000000000062026-10-10 — A finding of the ruthless reviewer of lane-1 of run-2610101926030564 (iss-2610090642392144) is deferred, not fixed in the change that puts a verifier's copy outside every working tree: the bundled `OPINIONS` rule in `internal/core/rules/defaults/rules.json` ("The user's directory is theirs") still allows a verifier's copy in the checkout's `.abcd/.work.local/` tier, against the repository's own `CONCURRENCY` rule, AGENTS.md, itd-193 and the users-directory principle, which now forbid it. Reason: the bundled rule is injected into every managed repository, so changing it is a user-visible change the issue's remedy did not name, and it waits on a person's ruling, recorded as iss-2610102021042657.
- iss-2608301715040589an interactive cp prompt stalls an agent silently and has now cost three sessions time in one day
- iss-2610102021042657Once the drain lane for iss-2610090642392144 lands, this repository's AGENTS.md and .abcd/rules.json say a verifier's copy goes outside every working tree, never under .abcd/.work.local/scratch/. The bundled default CONCURRENCY rule every managed repository receives and the principle the-users-directory-is-theirs.md still allow it in the checkout's .abcd/.work.local/ tier, and itd-193 and adr-2609091014087993 quote the archive command without saying where the copy goes. Changing the bundled rule changes what every managed repository is taught, so that lane left it alone.
- iss-2610161501104088Nothing tells an agent that the host's scratch folder is wiped on reboot. The host hands each session a scratchpad under /private/tmp and asks it to keep temporary files there. Agents keep everything there, scripts reused across sessions and turns included. On 2026-10-16 a reboot wiped it, taking the preflight queue script, the PR watcher, PR text files and every frozen review copy. The bundled rules send runtime output to .abcd/.work.local/ and verifier copies under ~/.abcd.noindex/, but none says that the host's scratchpad does not survive a reboot or a new session.