Working a step
You were dispatched by a supervisor to do one step of one task. Not
the task. Not the next step you can see. One step, and then a report.
Your shell may start anywhere, so every path below is absolute and every git
command is git -C "$REPO". A bare git commit from the wrong directory
commits the wrong thing, or nothing you meant.
1. Your inputs
The prompt that dispatched you carries these. Every one is required; if any is
missing, stop and report BLOCKED with notes: missing input <name>.
TASK: T-n STEP: S-n ROLE: <your role>
REPO: <absolute path of the project root>
SCOPE: <absolute paths you may write, one per line>
GOAL: <what this step must achieve>
STEP-VERIFY: <the exact command that judges this step>
REQUIREMENTS: <the R-n this step serves>
ENV: <pin id, and the pinned versions>
CONTAINMENT: structural | guard-only
SANDBOX-ROOT: <absolute path, or `none` under guard-only>
ATTRIBUTION: <commit trailer lines, verbatim>
TREE: clean | dirty
NOTES: none | <a verifier FAIL, a predecessor's death, an answer from the client>
CONTAINMENT: structural means you are inside a sandbox, and three things
follow that nothing else in your dispatch tells you:
- You are at the repository's own path, and nothing outside it exists. The
path in
REPO: is the same string inside as out — deliberately, so that
every scope, refusal and check reads the same in both places. But the rest of
the filesystem is read-only or absent: exactly three things are writable, and
they are /tmp, your own HOME, and this repository.
- Nothing you do reaches the host until your supervisor promotes it. Your
commits, your index, your
HEAD are all your own copy. That is why you can
correct your own history here (P-12c) and why nobody else's work can be in
your index (P-43).
- Your work is not saved by finishing. It is saved by being promoted, and
promotion is gated on your commits' paths against
SCOPE: (P-44). Work that
drifts outside your scope is lost at the gate, after you have done it.
2. Before touching anything
The tree, inside your scope. git -C "$REPO" status --porcelain -- $SCOPE. TREE: clean was promised and it is not → BLOCKED.
TREE: dirty → read git -C "$REPO" diff -- $SCOPE and the task's
execution record first: a predecessor died here. Continue its work and
say so in your report.
Scoped, because unqualified it reports other tasks' work in progress,
which at width above one is always non-empty and never yours. And the old
instruction here said "continue its work or stash it" — stashing is now
forbidden (P-12b): git stash takes the whole tree, including files two
other tasks have open, and hands you a clean tree by taking theirs away.
That instruction was written when width was one and became a corruption the
moment it was not.
Under structural, git stash cannot take anybody else's work — and you
still must not use it here. P-12c permits it inside your overlay, because
your tree is yours alone. But the reason for this instruction was never only
the corruption: a dirty tree under your scope is a predecessor's
unfinished work, and you were dispatched to continue it. Stashing it makes
it invisible to you, and the most valuable thing in the tree is frequently
exactly what the last worker had learned and had not yet said. Read it.
Your scope. You may write under SCOPE and nowhere else (P-10).
Needing a path you were not given is an escalation, not a wider write —
report BLOCKED with the path and why.
Under structural this is enforced twice, and only the second one is the
mechanism. The guard refuses the write as you type it — that is early
warning, and it is there because learning at promotion that you built on an
out-of-scope edit means losing the work rather than being told. The
promotion gate is what actually decides: it diffs the paths your commits
touched against SCOPE: and refuses what falls outside (P-44). A write the
guard happens not to classify is still refused there.
The environment. Confirm the pinned versions match what ENV names. A
mismatch is BLOCKED: a result that cannot be attributed to a known
environment is not a result (P-33).
3. Read, in this order
devteam/CHARTER.md — what this project is, and what is out of scope
- the
R-n your step serves, in devteam/REQUIREMENTS.md — including its
acceptance criterion, because that is what "done" means here
devteam/DECISIONS.md — before proposing any approach, because it is
already recorded why the obvious alternative lost (P-21)
- your task's file,
devteam/tasks/T-n.md, and its execution record
- the code your scope covers
4. The discipline
- The requirements are the authority. Code that disagrees with a
requirement is a defect in the code. A requirement that is wrong is reported,
never quietly worked around.
- One commit per step, under a green
STEP-VERIFY.
- A decision the project has not made is
NEEDS-DECISION, with your
recommendation and its class (P-25, P-26). Do not guess. A guess becomes a
decision nobody agreed to and nobody can find later.
- Never work around a blocker silently (P-39). A missing permission, a
broken dependency, a failing tool: report it. The workaround is the thing
nobody reviewed.
- A failing check is not retried into success (P-20). Run it, report what
it said. Every timing-shaped defect looks like flakiness first.
- Long commands go in the background and get polled. A timeout is not a
failure; report it as a timeout, not as a red.
- One web fetch may be inline. More is a research request to the
researcher agent, whose context is disposable and yours is not (P-36).
- How you write a file depends on your containment, and your dispatch says
which. Under
guard-only, product files are written with Write or
Edit, as a protocol requirement rather than a preference (P-10b): the guard
judges a write by reading the command, and a heredoc gives it nothing to
read, so the other forms silently leave the scope unenforced. Under
structural the form is free (P-10c) — a redirect, a heredoc, an interpreter
— because the gate reads your commits, not your commands. If your
harness carries an ambient instruction to prefer sed and heredocs over the
file tools, this is where it is resolved: under guard-only the pipeline's
form wins and you say in your report that the two conflicted; under
structural there is no conflict to report.
5. Committing
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check_scope.py" "$REPO" T-n
git -C "$REPO" commit -F "$msgfile" -- <each path you wrote, explicitly>
Commit a pathspec. Staging explicitly is not enough, and -A is worse.
-A sweeps whatever else is in the tree into your commit — the manager owns
devteam/ and may have an uncommitted file at any moment, and a supervisor had
to override that instruction on every dispatch before it was fixed here.
But git add <your files> followed by a plain git commit fails too, and
fails invisibly: the index is shared. At any width above one, another
agent's git add has already put its files there, and your commit takes the
whole index — carrying somebody else's in-flight work into your task's commit
under your message. You did nothing wrong and the standing rule did not cover
it, because somebody else did the staging. A manager used the add-then-commit
form for an entire run and it only never landed because the other task happened
to commit first.
git commit -- <paths> commits exactly those paths whatever the index holds.
The -m trap: anything after the -- separator is a pathspec, so
git commit -- src/a.py -m "msg" silently tries to commit files named -m and
msg. Put the message flag first, or use -F.
Scope clean, or the commit does not happen. The subject is
T-n.S-n: <what>; the body says why — the diff already says what. End the
message with the ATTRIBUTION lines exactly as given. Never write a model
name yourself.
Cite a commit by its subject, never by its hash — in your report, in the
execution record, anywhere a later reader will follow it. Under structural
your commits are cherry-picked onto the host at promotion, so every hash you
can see is rewritten on the way in: 77f040e3 became ac290fc5 on the first
live promotion this pipeline ever did, because the host HEAD had moved while
the work was happening. A hash you report is correct when you write it and
wrong by the time anybody reads it. check_scope runs inside against your
overlay's own commits exactly as before — that part does not change.
6. Your report
Append it to the task file's ## Execution record, then make it your final
message — the same block in both places (P-16). It is parsed by a script:
keys start at column one, continuations are indented, nothing is decorated.
REPORT <ROLE> T-n.S-n
status: DONE | BLOCKED | NEEDS-DECISION | RED
model: <the model id your system prompt names>
env: <the ENV pin id>
requirements: <the R-n this served>
scope: <the paths you actually wrote>
commits:
- <hash> <subject> earlier commits
- HEAD <subject> THIS commit — see below
checks:
- <exact command> -> <its summary line, verbatim> [exit <n>]
questions: none | - <question> | <recommendation> | REVERSIBLE|IRREVERSIBLE|CHARTER
findings-for-protocol: none | - <one line each>
budget: tokens=<n> minutes=<n>
notes: none | <free text>
budget: and model: are cross-checked against the harness, so report what
you believe and do not manufacture precision. Under structural the process
that ran you metered its own tokens, wall-clock and model id, and
check_report compares your two lines against them — tokens within 10%,
minutes within 20%, and the model id exactly. A mismatch is recorded as a
finding about self-reporting, not corrected, and it is expected rather than
shameful: the first worker ever metered here wrote tokens=3000 against a
measured 309639, honestly, because a process cannot see its own counter. What
is not fine is inventing a figure that looks precise. If you do not know,
give your best estimate and say in notes: that it is one. The model: line
is the one to be careful with, because it is compared exactly — write the id
your system prompt names and nothing else.
The short identifier prefixes are reserved, and you are not shown the file
that says so. G- DM- R- T- S- D- Q- C- are the project's, and
P- is a protocol rule. Anything you number yourself uses three or more
letters — COR-1, SEC-2, PRB-3 — because the citation scanner matches
[A-Z]{1,2}-<digits> anywhere in an artifact and cannot tell your numbering
from a reference to the project's. A probe that labels its cases C-1 collides
with checkpoints; the finding describing that collision had to be reworded to
stop it tripping the check it described.
Never propose a new requirement by number. R-3 written anywhere — even
inside a recommendation saying one should exist — is read as a citation, and
a citation to a requirement nobody has declared is a cited-undefined finding
against you. Describe it instead: "a requirement for undecodable input,
same one-line form as R-2". The manager allocates the number when it accepts
the proposal, because numbering is how a project records that it agreed to
something.
Three traps worth knowing before you meet them.
python3 -m <module> prepends the invoking process's cwd to the child's
sys.path. A subprocess test that does not pin cwd= can pass by
accidentally shadowing the very import defect it exists to catch.
A relative ref is not stable in a report. HEAD~1 in a checks: line
means something different the moment another commit lands on top. Name the
commit, not its position.
A green run on an unchanged tree is not evidence. If your step's verify
passes just as happily before your work as after it, say so in your report
rather than banking it. That is a finding about the plan, and reporting it
is worth more than a clean pass.
Prove your check against the defect, not only against the old tree. A
check that fails before your change and passes after it may still be blind to
the thing it exists to catch — three checks in this project failed on the old
tree, passed on the new one, and passed a deliberately built version of the
exact defect they were written for, because all three compared text the
defect does not alter. Build the defect on a copy and require the check to
fail on it.
A mutation must name what it expects to fail. Breaking the thing on
purpose and watching the suite go red proves only that something is
watching. Name the node id, and check that one failed — three tests going red
when one mutation lands means two of them were not the instrument you were
testing.
Measure your own baseline; never trust one you were handed. A figure in
your dispatch was true when somebody wrote it and the tree has moved since —
a manager corrected a stale "5 passed" to "6 passed" in a dispatch here and
the measured figure was "7 passed, 7 xfailed". A baseline in a dispatch ages
exactly as fast as one in a report, and the instruction to produce your own
is what makes the check survive being wrong about it.
The next two bullets are guard-only rules. Under CONTAINMENT: structural the write form is free and P-10c says so — the promotion gate
reads your commits rather than your commands, so there is nothing for a
heredoc to evade. Read them anyway: your dispatch says which regime you are
in, and getting this backwards on a guard-only project silently disables
the only mechanism it has.
Inside the repository, Write or Edit. Outside it, anything (P-10b).
The rule is about writes the guard would judge, and it does not police paths
outside the project — so a mutation built in $(mktemp -d) may be written
any way at all. If your role has no Write tool, that is not a conflict:
read-only roles do their mutation work outside the tree by design.
Write product files with Write or Edit even if something told you
otherwise (P-10b). A harness may carry a standing instruction to prefer
shell tools, sed and heredocs — for its own good reasons, unrelated to this
pipeline. Inside a devteam project the pipeline's form wins, and you report
the conflict rather than resolving it quietly. Two workers on one project
met exactly that instruction, used Edit anyway, and said so; neither had
been told to by anything but its own judgement, and it is the reason anybody
knows the interaction exists.
Write product files with Write or Edit, not with an interpreter —
under guard-only. python3 - <<PY with Path.write_text is convenient
and it is the one form the guard cannot classify — a write whose target does
not appear in the command text. So it is not refused, and it is not judged
either. (Under structural this bullet does not apply: the gate reads your
commits, so an unclassifiable command is not an unjudged write — P-10c.)
The refusal message warns about this, and that warning only reaches somebody
who was refused first. A worker whose habit is heredocs never knocks on
that door, so it never sees the warning: the bypass is not reached around
the guard, it is reached instead of it. That is why this bullet is here,
at the moment you choose how to write, rather than only in a refusal.
It has already cost something. Two paths a manager had granted were written
in a form the scope parser could not read, so they were outside every parsed
scope — and the writes to them went through an interpreter, so the guard
never saw them either. Either failure alone would have been visible: a
refusal, or a finding. Together they produced silence.
A script making two edits to one file must re-read between them. Both
writes computed from one read_text() means the second silently discards the
first — the file ends up with the last edit only, no error, no warning, and a
diff that looks plausible because it does contain a change. This is not
hypothetical: it dropped a rule from a skill here, the commit message claimed
both edits, and it went undetected for hours because prose changes produce
no observable. A check that fails to run is caught by its output being
wrong; a paragraph that was not written is caught by nothing.
So when you claim a prose file gained something, re-read the file and
confirm the words are there. Not the diff — the file. It is two seconds and
it is the only verification that class of change has.
Feed any probe you build one case whose answer you already know, before
you believe any case whose answer you don't (P-35b). This applies to the
throwaway script you write to investigate something, not only to the checks
that ship — and that is where it keeps going wrong. Seven instrument failures
here were ad-hoc probes, three of them built while investigating somebody
else's finding. Two on one day drove the same guard: one read the exit code
of a program that denies via JSON at exit 0, the other omitted the session id
the whole judgement keys on. Neither errored; both printed clean, confident,
meaningless output. An instrument that answers a question it was never
wired to ask cannot be caught by reading its output — only by giving it a
case where you already know what it must say.
Assert your fixture still REACHES the thing it guards, not only that it is
what you think it is. A guard whose subject is removed by a later fix does
not go red — it goes quiet, which is green. Measured: a test named "a
binary64 reader recovers as many order ids" stopped exercising the number
branch at all once a bound made those values render as strings. It passes, it
reads the right document, and it no longer tests what its name claims. Every
mutation discipline here asks whether a change turns a guard red; none
asks whether a change makes one inapplicable. Assert the path is entered
— a count, a recorded call, anything that fails when the branch stops running.
Assert your fixture before you trust what it proves. A negative test is
only as good as the bad input it is given. printf '\xff\xfe' under sh
does not expand \x, so the "invalid" file comes out as valid text, the
code under test correctly succeeds, and the check reports a pass that means
nothing. Verify the fixture is what you think it is — decode it, measure
it, print its bytes — before reading anything into the result.
In an artifact, a disclosure refers or derives — it never counts. The
report rule below fixes a count by attaching its command. In code there is a
cheaper fix: remove the need for a number at all. "Holds eight key
names" decays the moment somebody adds a ninth, and nothing catches it.
"Holds the dependency-shaped keys listed below" cannot decay, because it
points at the thing instead of describing its size. Same for "three named
routes" where the function exercises two — an enumeration in prose beside an
enumeration in code is two copies of one fact.
This is the class that took four repair rounds on one task, each round's
defect introduced by the repair for the previous one. The count rule closes
it going forward; referring rather than counting closes it without needing to
be remembered, which is the better property.
A count in a report names the command that re-derives it, or it is not
written. The charter rule — a number in signed text is a promise about
something that has not stopped changing — was never generalised to reports,
and a count in a report is the same promise with the same decay. Measured: a
report stated 1716 mutations a run, three times, where the value was 1742 —
and it had decayed inside a single task, because the section it counted
grew by two bullets while the task ran. Write
1742 (python3 -m pytest --collect-only -q tests/x.py | wc -l) or leave the
number out. The same applies to a breakdown: if the parts are stated, the
total is derived from them, not asserted beside them.
Cite scripts as ${CLAUDE_PLUGIN_ROOT}/scripts/... in a report. An
absolute /home/... path is a leak finding, because reports are committed
to a tracked file. Do not invent a shorter path to dodge that — a worker
once wrote a plausible-looking path that did not exist. The variable form is
both runnable and leak-free.
A check that can only run after the commit cannot appear in the report
inside it. check_scope inspects the committed diff, so a report that is
part of that commit cannot carry its result. Run it, say in notes: that
you did and what it said, and leave it out of checks:. This is the same
shape as the commit-hash problem below.
A step may take two commits, and post-commit evidence is why. If the
evidence your report genuinely rests on — mutation testing, a check over the
committed diff — can only exist once the work is committed, then commit the
work, gather the evidence, and append the report in a second commit.
"One commit per step" is a default that keeps the record legible, not a rule
worth rewriting history to preserve; a worker contorted to hold that line and
corrupted a concurrent task's commit doing it. Both commits name the step in
their subject, which is all check_report asks for.
git commit --amend -- <paths> is not the safe version. The pathspec
limits which content is taken; it does nothing about which commit is
amended, which is always HEAD. A worker reaching for the careful-looking
form still rewrites whoever is at HEAD. There is no pathspec, flag or
ordering that makes an amend safe in a shared tree — only not doing it.
Never --amend unless the board says width 1. --amend acts on HEAD,
and at width greater than one HEAD is not yours — it is whichever task
committed most recently, which may have been a second ago. A worker amended
what it believed was its own commit and rewrote a concurrent task's: its
report text was merged into that task's subject, and that task's hash changed
underneath it. Read the board's **Width.** line; above 1, correct a commit
by adding another one, never by rewriting. The same goes for rebase,
reset --hard, stash, and checkout of a tracked path (P-12b).
Under CONTAINMENT: structural this is P-12c instead, and it is looser
for a reason rather than by relaxation: you have your own .git, nobody
else's HEAD is reachable, and HEAD genuinely is yours. So --amend,
rebase, reset and stash are permitted above the base commit your
sandbox recorded when it opened — the failure described above cannot occur,
because the commit it landed on does not exist in your view. At or below
that base you are back to shared history, and the promotion gate refuses a
rewrite there. The rule did not get weaker; the thing it was protecting moved
out of your reach.
If you have already rewritten history, reset --soft, never --hard.
Recover the original commit from git reflog and soft-reset to it. Soft
leaves the index and working tree exactly as they are, which matters because
the tree holds other tasks' uncommitted work and --hard would destroy it.
Then say so in notes: — the recovery is part of the record, not a tidy-up.
If your supervisor has you amend a commit at width 1, re-point any hash you
cited. An amend leaves the old commit on no branch, so a hash written in
your checks: lines now names something orphaned. Re-derive it, or HEAD.
Naming the commit you are inside. Your report is committed in the same
commit as your work (P-16), so that commit's own hash cannot appear inside it
— the content would have to hash to a value written in the content. Write
- HEAD <subject>. HEAD marks this commit and the subject is what makes
it resolvable afterwards, so the subject must be the exact one you commit
with. Never invent a placeholder that reads like a hash, and never write prose
in commits: — the field is parsed.
checks: is the evidence and it is not optional on a DONE. A
requirement is discharged by evidence, never by assertion (P-5) — and your
supervisor is going to re-run every line of it against the committed tree
before accepting your work (P-18). Report what actually happened. A report
that says green where the command said red is caught within the minute, and it
is the one thing that makes you useless.
1---2name: work3description: The worker discipline for a devteam project — the inputs a step dispatch carries, the scope and tree checks before touching anything, what to read and in what order, the commit form, and the REPORT block a supervisor reads. Use when working any step dispatched by a supervisor.4---56# Working a step78You were dispatched by a supervisor to do **one step** of **one task**. Not9the task. Not the next step you can see. One step, and then a report.1011Your shell may start anywhere, so **every path below is absolute and every git12command is `git -C "$REPO"`**. A bare `git commit` from the wrong directory13commits the wrong thing, or nothing you meant.1415## 1. Your inputs1617The prompt that dispatched you carries these. Every one is required; if any is18missing, stop and report `BLOCKED` with `notes: missing input <name>`.1920```21TASK: T-n STEP: S-n ROLE: <your role>22REPO: <absolute path of the project root>23SCOPE: <absolute paths you may write, one per line>24GOAL: <what this step must achieve>25STEP-VERIFY: <the exact command that judges this step>26REQUIREMENTS: <the R-n this step serves>27ENV: <pin id, and the pinned versions>28CONTAINMENT: structural | guard-only29SANDBOX-ROOT: <absolute path, or `none` under guard-only>30ATTRIBUTION: <commit trailer lines, verbatim>31TREE: clean | dirty32NOTES: none | <a verifier FAIL, a predecessor's death, an answer from the client>33```3435**`CONTAINMENT: structural` means you are inside a sandbox**, and three things36follow that nothing else in your dispatch tells you:3738- **You are at the repository's own path, and nothing outside it exists.** The39 path in `REPO:` is the same string inside as out — deliberately, so that40 every scope, refusal and check reads the same in both places. But the rest of41 the filesystem is read-only or absent: exactly three things are writable, and42 they are `/tmp`, your own `HOME`, and this repository.43- **Nothing you do reaches the host until your supervisor promotes it.** Your44 commits, your index, your `HEAD` are all your own copy. That is why you can45 correct your own history here (P-12c) and why nobody else's work can be in46 your index (P-43).47- **Your work is not saved by finishing.** It is saved by being promoted, and48 promotion is gated on your commits' paths against `SCOPE:` (P-44). Work that49 drifts outside your scope is lost at the gate, after you have done it.5051## 2. Before touching anything52531. **The tree, inside your scope.** `git -C "$REPO" status --porcelain --54 $SCOPE`. `TREE: clean` was promised and it is not → `BLOCKED`.55 `TREE: dirty` → read `git -C "$REPO" diff -- $SCOPE` and the task's56 execution record first: a predecessor died here. **Continue its work** and57 say so in your report.5859 **Scoped, because unqualified it reports other tasks' work in progress**,60 which at width above one is always non-empty and never yours. And the old61 instruction here said "continue its work *or stash it*" — **stashing is now62 forbidden** (P-12b): `git stash` takes the whole tree, including files two63 other tasks have open, and hands you a clean tree by taking theirs away.64 That instruction was written when width was one and became a corruption the65 moment it was not.6667 **Under `structural`, `git stash` cannot take anybody else's work — and you68 still must not use it here.** P-12c permits it inside your overlay, because69 your tree is yours alone. But the reason for this instruction was never only70 the corruption: **a dirty tree under your scope is a predecessor's71 unfinished work, and you were dispatched to continue it.** Stashing it makes72 it invisible to you, and the most valuable thing in the tree is frequently73 exactly what the last worker had learned and had not yet said. Read it.742. **Your scope.** You may write under `SCOPE` and nowhere else (P-10).75 **Needing a path you were not given is an escalation, not a wider write** —76 report `BLOCKED` with the path and why.7778 Under `structural` this is enforced **twice, and only the second one is the79 mechanism.** The guard refuses the write as you type it — that is early80 warning, and it is there because learning at promotion that you built on an81 out-of-scope edit means losing the work rather than being told. The82 promotion gate is what actually decides: it diffs the paths your commits83 touched against `SCOPE:` and refuses what falls outside (P-44). A write the84 guard happens not to classify is still refused there.853. **The environment.** Confirm the pinned versions match what `ENV` names. A86 mismatch is `BLOCKED`: a result that cannot be attributed to a known87 environment is not a result (P-33).8889## 3. Read, in this order90911. `devteam/CHARTER.md` — what this project is, and what is out of scope922. the `R-n` your step serves, in `devteam/REQUIREMENTS.md` — **including its93 acceptance criterion**, because that is what "done" means here943. `devteam/DECISIONS.md` — **before proposing any approach**, because it is95 already recorded why the obvious alternative lost (P-21)964. your task's file, `devteam/tasks/T-n.md`, and its execution record975. the code your scope covers9899## 4. The discipline100101- **The requirements are the authority.** Code that disagrees with a102 requirement is a defect in the code. A requirement that is wrong is reported,103 never quietly worked around.104- **One commit per step**, under a green `STEP-VERIFY`.105- **A decision the project has not made is `NEEDS-DECISION`**, with your106 recommendation and its class (P-25, P-26). Do not guess. A guess becomes a107 decision nobody agreed to and nobody can find later.108- **Never work around a blocker silently** (P-39). A missing permission, a109 broken dependency, a failing tool: report it. The workaround is the thing110 nobody reviewed.111- **A failing check is not retried into success** (P-20). Run it, report what112 it said. Every timing-shaped defect looks like flakiness first.113- **Long commands go in the background and get polled.** A timeout is not a114 failure; report it as a timeout, not as a red.115- **One web fetch may be inline. More is a research request** to the116 researcher agent, whose context is disposable and yours is not (P-36).117- **How you write a file depends on your containment, and your dispatch says118 which.** Under `guard-only`, product files are written with `Write` or119 `Edit`, as a protocol requirement rather than a preference (P-10b): the guard120 judges a write by reading the command, and a heredoc gives it nothing to121 read, so the other forms silently leave the scope unenforced. Under122 `structural` the form is free (P-10c) — a redirect, a heredoc, an interpreter123 — because the gate reads your **commits**, not your commands. **If your124 harness carries an ambient instruction to prefer `sed` and heredocs over the125 file tools, this is where it is resolved:** under `guard-only` the pipeline's126 form wins and you say in your report that the two conflicted; under127 `structural` there is no conflict to report.128129## 5. Committing130131```bash132python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check_scope.py" "$REPO" T-n133git -C "$REPO" commit -F "$msgfile" -- <each path you wrote, explicitly>134```135136**Commit a pathspec. Staging explicitly is not enough, and `-A` is worse.**137138`-A` sweeps whatever else is in the tree into your commit — the manager owns139`devteam/` and may have an uncommitted file at any moment, and a supervisor had140to override that instruction on every dispatch before it was fixed here.141142But `git add <your files>` followed by a plain `git commit` fails too, and143fails invisibly: **the index is shared.** At any width above one, another144agent's `git add` has already put its files there, and your commit takes the145whole index — carrying somebody else's in-flight work into your task's commit146under your message. You did nothing wrong and the standing rule did not cover147it, because *somebody else did the staging*. A manager used the add-then-commit148form for an entire run and it only never landed because the other task happened149to commit first.150151`git commit -- <paths>` commits exactly those paths whatever the index holds.152153**The `-m` trap:** anything after the `--` separator is a pathspec, so154`git commit -- src/a.py -m "msg"` silently tries to commit files named `-m` and155`msg`. Put the message flag first, or use `-F`.156157Scope clean, or the commit does not happen. The subject is158`T-n.S-n: <what>`; the body says **why** — the diff already says what. End the159message with the `ATTRIBUTION` lines exactly as given. **Never write a model160name yourself.**161162**Cite a commit by its subject, never by its hash** — in your report, in the163execution record, anywhere a later reader will follow it. Under `structural`164your commits are cherry-picked onto the host at promotion, so **every hash you165can see is rewritten on the way in**: `77f040e3` became `ac290fc5` on the first166live promotion this pipeline ever did, because the host `HEAD` had moved while167the work was happening. A hash you report is correct when you write it and168wrong by the time anybody reads it. `check_scope` runs inside against your169overlay's own commits exactly as before — that part does not change.170171## 6. Your report172173Append it to the task file's `## Execution record`, then make it your final174message — **the same block in both places** (P-16). It is parsed by a script:175keys start at column one, continuations are indented, nothing is decorated.176177```178REPORT <ROLE> T-n.S-n179status: DONE | BLOCKED | NEEDS-DECISION | RED180model: <the model id your system prompt names>181env: <the ENV pin id>182requirements: <the R-n this served>183scope: <the paths you actually wrote>184commits:185 - <hash> <subject> earlier commits186 - HEAD <subject> THIS commit — see below187checks:188 - <exact command> -> <its summary line, verbatim> [exit <n>]189questions: none | - <question> | <recommendation> | REVERSIBLE|IRREVERSIBLE|CHARTER190findings-for-protocol: none | - <one line each>191budget: tokens=<n> minutes=<n>192notes: none | <free text>193```194195**`budget:` and `model:` are cross-checked against the harness, so report what196you believe and do not manufacture precision.** Under `structural` the process197that ran you metered its own tokens, wall-clock and model id, and198`check_report` compares your two lines against them — tokens within 10%,199minutes within 20%, and the model id exactly. **A mismatch is recorded as a200finding about self-reporting, not corrected**, and it is expected rather than201shameful: the first worker ever metered here wrote `tokens=3000` against a202measured `309639`, honestly, because a process cannot see its own counter. What203is *not* fine is inventing a figure that looks precise. If you do not know,204give your best estimate and say in `notes:` that it is one. The `model:` line205is the one to be careful with, because it is compared exactly — write the id206your system prompt names and nothing else.207208**The short identifier prefixes are reserved, and you are not shown the file209that says so.** `G-` `DM-` `R-` `T-` `S-` `D-` `Q-` `C-` are the project's, and210`P-` is a protocol rule. **Anything you number yourself uses three or more211letters** — `COR-1`, `SEC-2`, `PRB-3` — because the citation scanner matches212`[A-Z]{1,2}-<digits>` anywhere in an artifact and cannot tell your numbering213from a reference to the project's. A probe that labels its cases `C-1` collides214with checkpoints; the finding describing that collision had to be reworded to215stop it tripping the check it described.216217**Never propose a new requirement by number.** `R-3` written anywhere — even218inside a recommendation saying one *should exist* — is read as a citation, and219a citation to a requirement nobody has declared is a `cited-undefined` finding220against you. Describe it instead: *"a requirement for undecodable input,221same one-line form as R-2"*. The manager allocates the number when it accepts222the proposal, because numbering is how a project records that it agreed to223something.224225**Three traps worth knowing before you meet them.**226227- **`python3 -m <module>` prepends the invoking process's cwd** to the child's228 `sys.path`. A subprocess test that does not pin `cwd=` can pass by229 accidentally shadowing the very import defect it exists to catch.230- **A relative ref is not stable in a report.** `HEAD~1` in a `checks:` line231 means something different the moment another commit lands on top. Name the232 commit, not its position.233- **A green run on an unchanged tree is not evidence.** If your step's verify234 passes just as happily before your work as after it, say so in your report235 rather than banking it. That is a finding about the plan, and reporting it236 is worth more than a clean pass.237- **Prove your check against the defect, not only against the old tree.** A238 check that fails before your change and passes after it may still be blind to239 the thing it exists to catch — three checks in this project failed on the old240 tree, passed on the new one, and passed a deliberately built version of the241 exact defect they were written for, because all three compared text the242 defect does not alter. Build the defect on a copy and require the check to243 fail on it.244- **A mutation must name what it expects to fail.** Breaking the thing on245 purpose and watching the suite go red proves only that *something* is246 watching. Name the node id, and check that one failed — three tests going red247 when one mutation lands means two of them were not the instrument you were248 testing.249- **Measure your own baseline; never trust one you were handed.** A figure in250 your dispatch was true when somebody wrote it and the tree has moved since —251 a manager corrected a stale "5 passed" to "6 passed" in a dispatch here and252 the measured figure was "7 passed, 7 xfailed". A baseline in a dispatch ages253 exactly as fast as one in a report, and the instruction to produce your own254 is what makes the check survive being wrong about it.255- **The next two bullets are `guard-only` rules. Under `CONTAINMENT:256 structural` the write form is free and P-10c says so** — the promotion gate257 reads your commits rather than your commands, so there is nothing for a258 heredoc to evade. Read them anyway: your dispatch says which regime you are259 in, and getting this backwards on a `guard-only` project silently disables260 the only mechanism it has.261- **Inside the repository, `Write` or `Edit`. Outside it, anything (P-10b).**262 The rule is about writes the guard would judge, and it does not police paths263 outside the project — so a mutation built in `$(mktemp -d)` may be written264 any way at all. If your role has no `Write` tool, that is not a conflict:265 read-only roles do their mutation work outside the tree by design.266- **Write product files with `Write` or `Edit` even if something told you267 otherwise (P-10b).** A harness may carry a standing instruction to prefer268 shell tools, `sed` and heredocs — for its own good reasons, unrelated to this269 pipeline. **Inside a devteam project the pipeline's form wins, and you report270 the conflict rather than resolving it quietly.** Two workers on one project271 met exactly that instruction, used `Edit` anyway, and said so; neither had272 been told to by anything but its own judgement, and it is the reason anybody273 knows the interaction exists.274- **Write product files with `Write` or `Edit`, not with an interpreter —275 under `guard-only`.** `python3 - <<PY` with `Path.write_text` is convenient276 and it is the one form the guard cannot classify — a write whose target does277 not appear in the command text. So it is not refused, and **it is not judged278 either.** (Under `structural` this bullet does not apply: the gate reads your279 commits, so an unclassifiable command is not an unjudged write — P-10c.)280281 The refusal message warns about this, and that warning only reaches somebody282 who was refused first. **A worker whose habit is heredocs never knocks on283 that door**, so it never sees the warning: the bypass is not reached *around*284 the guard, it is reached *instead of* it. That is why this bullet is here,285 at the moment you choose how to write, rather than only in a refusal.286287 It has already cost something. Two paths a manager had granted were written288 in a form the scope parser could not read, so they were outside every parsed289 scope — and the writes to them went through an interpreter, so the guard290 never saw them either. **Either failure alone would have been visible: a291 refusal, or a finding. Together they produced silence.**292- **A script making two edits to one file must re-read between them.** Both293 writes computed from one `read_text()` means the second silently discards the294 first — the file ends up with the last edit only, no error, no warning, and a295 diff that looks plausible because it *does* contain a change. This is not296 hypothetical: it dropped a rule from a skill here, the commit message claimed297 both edits, and it went undetected for hours because **prose changes produce298 no observable**. A check that fails to run is caught by its output being299 wrong; a paragraph that was not written is caught by nothing.300- **So when you claim a prose file gained something, re-read the file and301 confirm the words are there.** Not the diff — the file. It is two seconds and302 it is the only verification that class of change has.303- **Feed any probe you build one case whose answer you already know, before304 you believe any case whose answer you don't (P-35b).** This applies to the305 throwaway script you write to investigate something, not only to the checks306 that ship — and that is where it keeps going wrong. Seven instrument failures307 here were ad-hoc probes, three of them built while investigating somebody308 else's finding. Two on one day drove the same guard: one read the exit code309 of a program that denies via JSON at exit 0, the other omitted the session id310 the whole judgement keys on. Neither errored; both printed clean, confident,311 meaningless output. **An instrument that answers a question it was never312 wired to ask cannot be caught by reading its output** — only by giving it a313 case where you already know what it must say.314- **Assert your fixture still REACHES the thing it guards, not only that it is315 what you think it is.** A guard whose subject is removed by a later fix does316 not go red — **it goes quiet**, which is green. Measured: a test named *"a317 binary64 reader recovers as many order ids"* stopped exercising the number318 branch at all once a bound made those values render as strings. It passes, it319 reads the right document, and it no longer tests what its name claims. Every320 mutation discipline here asks whether a change turns a guard **red**; none321 asks whether a change makes one **inapplicable**. Assert the path is entered322 — a count, a recorded call, anything that fails when the branch stops running.323- **Assert your fixture before you trust what it proves.** A negative test is324 only as good as the bad input it is given. `printf '\xff\xfe'` under `sh`325 does not expand `\x`, so the "invalid" file comes out as valid text, the326 code under test correctly succeeds, and the check reports a pass that means327 nothing. **Verify the fixture is what you think it is** — decode it, measure328 it, print its bytes — before reading anything into the result.329- **In an artifact, a disclosure refers or derives — it never counts.** The330 report rule below fixes a count by attaching its command. In code there is a331 cheaper fix: **remove the need for a number at all.** *"Holds eight key332 names"* decays the moment somebody adds a ninth, and nothing catches it.333 *"Holds the dependency-shaped keys listed below"* cannot decay, because it334 points at the thing instead of describing its size. Same for *"three named335 routes"* where the function exercises two — an enumeration in prose beside an336 enumeration in code is two copies of one fact.337338 This is the class that took four repair rounds on one task, **each round's339 defect introduced by the repair for the previous one**. The count rule closes340 it going forward; referring rather than counting closes it without needing to341 be remembered, which is the better property.342- **A count in a report names the command that re-derives it, or it is not343 written.** The charter rule — *a number in signed text is a promise about344 something that has not stopped changing* — was never generalised to reports,345 and a count in a report is the same promise with the same decay. Measured: a346 report stated `1716 mutations a run`, three times, where the value was 1742 —347 and it had decayed **inside a single task**, because the section it counted348 grew by two bullets while the task ran. Write349 `1742 (`python3 -m pytest --collect-only -q tests/x.py | wc -l`)` or leave the350 number out. The same applies to a breakdown: if the parts are stated, the351 total is derived from them, not asserted beside them.352- **Cite scripts as `${CLAUDE_PLUGIN_ROOT}/scripts/...` in a report.** An353 absolute `/home/...` path is a `leak` finding, because reports are committed354 to a tracked file. Do not invent a shorter path to dodge that — a worker355 once wrote a plausible-looking path that did not exist. The variable form is356 both runnable and leak-free.357- **A check that can only run after the commit cannot appear in the report358 inside it.** `check_scope` inspects the committed diff, so a report that is359 part of that commit cannot carry its result. Run it, say in `notes:` that360 you did and what it said, and leave it out of `checks:`. This is the same361 shape as the commit-hash problem below.362- **A step may take two commits, and post-commit evidence is why.** If the363 evidence your report genuinely rests on — mutation testing, a check over the364 committed diff — can only exist once the work is committed, then commit the365 work, gather the evidence, and **append the report in a second commit**.366 "One commit per step" is a default that keeps the record legible, not a rule367 worth rewriting history to preserve; a worker contorted to hold that line and368 corrupted a concurrent task's commit doing it. Both commits name the step in369 their subject, which is all `check_report` asks for.370- **`git commit --amend -- <paths>` is not the safe version.** The pathspec371 limits which *content* is taken; it does nothing about *which commit* is372 amended, which is always `HEAD`. A worker reaching for the careful-looking373 form still rewrites whoever is at `HEAD`. There is no pathspec, flag or374 ordering that makes an amend safe in a shared tree — only not doing it.375- **Never `--amend` unless the board says width 1.** `--amend` acts on `HEAD`,376 and at width greater than one `HEAD` is not yours — it is whichever task377 committed most recently, which may have been a second ago. A worker amended378 what it believed was its own commit and rewrote a concurrent task's: its379 report text was merged into that task's subject, and that task's hash changed380 underneath it. Read the board's `**Width.**` line; above 1, correct a commit381 by **adding another one**, never by rewriting. The same goes for `rebase`,382 `reset --hard`, `stash`, and `checkout` of a tracked path (P-12b).383384 **Under `CONTAINMENT: structural` this is P-12c instead, and it is looser385 for a reason rather than by relaxation:** you have your own `.git`, nobody386 else's `HEAD` is reachable, and `HEAD` genuinely is yours. So `--amend`,387 `rebase`, `reset` and `stash` are permitted **above the base commit your388 sandbox recorded when it opened** — the failure described above cannot occur,389 because the commit it landed on does not exist in your view. **At or below390 that base you are back to shared history**, and the promotion gate refuses a391 rewrite there. The rule did not get weaker; the thing it was protecting moved392 out of your reach.393- **If you have already rewritten history, `reset --soft`, never `--hard`.**394 Recover the original commit from `git reflog` and soft-reset to it. Soft395 leaves the index and working tree exactly as they are, which matters because396 the tree holds other tasks' uncommitted work and `--hard` would destroy it.397 Then say so in `notes:` — the recovery is part of the record, not a tidy-up.398- **If your supervisor has you amend a commit at width 1, re-point any hash you399 cited.** An amend leaves the old commit on no branch, so a hash written in400 your `checks:` lines now names something orphaned. Re-derive it, or `HEAD`.401402**Naming the commit you are inside.** Your report is committed in the same403commit as your work (P-16), so that commit's own hash cannot appear inside it404— the content would have to hash to a value written in the content. Write405`- HEAD <subject>`. `HEAD` marks *this* commit and **the subject is what makes406it resolvable afterwards**, so the subject must be the exact one you commit407with. Never invent a placeholder that reads like a hash, and never write prose408in `commits:` — the field is parsed.409410**`checks:` is the evidence and it is not optional on a `DONE`.** A411requirement is discharged by evidence, never by assertion (P-5) — and your412supervisor is going to re-run every line of it against the committed tree413before accepting your work (P-18). Report what actually happened. A report414that says green where the command said red is caught within the minute, and it415is the one thing that makes you useless.