Root-Cause And Fix
Overview
Dev-only loop for the flowai repo. A red test is a claim about the world, not a
fact about the product: the harness may have lost the evidence, the checklist may
test yesterday's framework, or the scenario may never have supplied what it
demands. Measured splits of instrument to product: 7:1 on 2026-08-20, 1:3 on
2026-08-21, 5:5 on 2026-08-22. Decide the layer first, fix second.
Autonomy contract: invoking this skill IS authorization for the whole loop —
reading sandboxes, running single scenarios, editing the layer the diagnosis
points at, re-measuring, and committing on a non-main branch. Do NOT pause to
ask which hypothesis to test or whether a run may be started; the audit trail is
the Hypothesis Board plus the evidence quoted in each finding. Hand back only on
a hard blocker — a guard fires (system_health, process_watchdog, a
pre-commit hook), the environment is missing (auth, Docker, disk), or the second
fix attempt for one failure fails — then emit a STOP-ANALYSIS REPORT.
Rules
- Raw session over any rendering.
judge-evidence.md and report.html are
the judge's rendering; ground truth is the transcript the CLI wrote for itself
plus the sandbox on disk. Renderings also reorder: a report.html verdict
block is NOT the run directory of the same index. Absence in a rendering is
not absence in the world — every "the agent never X" claim comes from a
tool-call count or a session line, never from a summary.
- Guards are signals. A guard that blocks the run means conditions are
wrong. Never reach for
--force, --no-verify or an override env var; report
the blocker instead.
- Fix the layer the cause is in. Product defect → the primitive. Instrument
defect →
scripts/acceptance-tests/lib/. Stale contract → the scenario. Never
quiet a red test by editing whichever of the three is easiest to reach.
- Never flip a checklist into its opposite. Rewriting "must have X" as "must
NOT have X" trades one red for another and hides that the requirement was
conditional. Scope the item to what it tests and record every dead version.
- No test-fitting. Do not hint the answer in
userQuery, do not script the
persona, do not pre-create what the skill should produce. Change a query only
because it was malformed, and say so in the file.
- Correct the record where the wrong claim lives. A comment that encoded a
wrong diagnosis costs the next reader a whole investigation — rewrite it in
place with what the evidence shows, and keep the retracted version visible.
- This file is an output of the loop. Rules 4 and 6 apply to it too: a
signature that proves wrong gets corrected in place, and what the loop learns
gets written back (Phase 6). A lesson left in chat is lost.
Phase 0 — Instrument or product
Run this BEFORE anything else. Each signature below was paid for once.
- Exit 1, empty trace, ~10-20 s.
OAuth session expired, Invalid API key,
Usage credits required: source .env before the sweep. detectAuthFailure
throws only at zero tool calls — a scenario driving another IDE's CLI surfaces
THAT child's error, a true observation.
- Exit 124 with tool calls present. A global timeout, a warning rather than a
blocker when the trace is non-empty. Ask whether the work legitimately outlives
the cap —
deep-research does.
- The judge says something is missing or wrong and the disk says otherwise.
It only ever saw the evidence blob, so ask what that blob contained. Five known
holes, all fixed, all worth re-checking: a file over 30 KB elided mid-file
(
renderFileForEvidence); evidence carrying only git diff <init>..HEAD, which
hid the product of a workflow that stops before committing (adapt-skills-basic);
the TRACE cut head+tail, deleting the middle of the conversation (2026-08-28: 5 of 10 turns survived);
a deliverable whose extension is outside TEXT_EXTENSIONS in evidence.ts, which
never reaches the judge at all (.html on 2026-09-06 — "HTML contents are not
provided", five items red, file on disk); and a [codex-agents] NEW_TASK with an
empty Payload:, which is codex ≥ 0.145 ENCRYPTING the prompt in the rollout, not
an empty dispatch. Count turns (grep -c '^\[turn' judge-evidence.md) before believing "the agent never did X".
- The cache file is not the run.
acceptance-tests/cache/<pack>/<id>/<ide>.json
holds the last RECORDED verdict, often an older green one, and contradicts the
run without saying so. Take per-item verdicts from the run, never the cache.
- Adjacent-negative fails, correct neighbour lives in another pack. The runner
mounts
core plus the scenario's pack, so there was nothing to defer to. Set
extraPacks (FR-ACCEPT.TRIGGER).
- Checklist demands an artefact the primitive's own text forbids. Read the
SKILL.md first:
init forbids wrapper scripts when the project's runner suffices,
while its checklist demanded scripts/check.ts.
- The red scenario is untracked.
git ls-files does not list it — another
session's deliberate RED, not your regression. Never fix it.
- A verdict that changed with no tree change. Load noise — a real defect fails
the same way three runs running. Then ask what the load EXPOSED: re-measuring
hides a deterministic cause as readily as it clears a false one. The watchdog's
rss test was dismissed as noise for months, while every green re-run was a poll
landing inside the second before the OS compressed its buffer away.
- The turn ends while a dispatched subagent is still running. The ACP loop
runs only while the emulator answers, so the result is never collected
(
maintenance-basic died at 228 s where siblings ran 950 s). Cure it in the
primitive: "wait for all subagents" must say waiting means COLLECTING, and that
nothing is presented while one is outstanding.
- The fixture is not what the scenario thinks it is.
git show <init-sha>:<file>
in the sandbox is the exact input. setup() runs AFTER that commit, so what it
plants is an UNCOMMITTED change in the agent's own diff — the judge read one as
a line the agent had WRITTEN and failed the item asking whether it had READ it;
planted pre-existing state must be committed. The fixture must also pass every
deterministic checker or the agent stops on THAT and never reaches the planted
defect (three of ten scenarios on 2026-08-22).
- Exit 144, no verdict, a
[fork-loop guard] line. The guard aimed at the
BENCH (fixed by adoptablePgid), or at a legitimate shell pipeline (fixed by
raising maxDescendants to 16 on 2026-08-25).
- Zero tool calls. At 0.0 s
system_health refused the spawn and quoted its
reason INSIDE the checklist ERROR (load avg N/CPU > 4/CPU), reading as a
finding about the product; families dispatching subagents (plan-*,
ship-task-*) trip it at -p 2 and above, so re-measure at -p 1. At full
duration and exit 0 the agent stopped instead: review_ready means it produced
the artefact unaided, blocked means the query never supplied what it asked
for. With a userPersona, check interactive — UserEmulator exists only when
that flag is true, so otherwise the persona is dead text.
- A
mocks entry does not stand in for the whole pipeline. writeMockBin
shadows the binary on PATH, so the canned text is the next pipe stage's INPUT.
A mock holding already-parsed output passes only while the agent skips the pipe.
- Every judge call fails, or a probe hangs into
aborted_streaming. Read the
agent's own text before blaming the CLI: API Error: 529 Overloaded sits there
in plain words. grep -l "API Error: 5" over a sweep bounds it.
- A scenario at 2/3 passes the threshold and can still hold a real defect.
Read the failing run. A fix that MOVES a red to 2/3 has the same shape: it cured
one real cause and uncovered the next. Attempts on two different causes are not
two attempts at one, so STOP-ANALYSIS does not fire — but say which cause each
attempt addressed, or the count is unauditable.
- The rule was in the file and still did not fire. First prove the text
reached the agent — a green sibling sharing the artefact does it in one line.
Then read the session for the shape. Never mentions it → never bound; bind it
AHEAD of the decision, since an agent holding a solution reads a rule for
exemptions. Quotes and overrides it → say what compliance PRODUCES, and grep for
a neighbouring rule claiming the case, since the weaker of two wins. Reclassifies
the case → your own carve-out is the escape; scope it to what the user NAMED.
Forbid the act, not the harm: a rule stated as a consequence dies by denying the
consequence.
Print the verdict — INSTRUMENT or PRODUCT — with the evidence line that
decided it, then continue.
Phase 1 — Evidence
Read in this order and stop as soon as the cause is unambiguous.
- The raw session.
<run>/<scenario>/run-N/bench-home is a SYMLINK into
$TMPDIR/flowai-bench/run-N-<hash>/; readlink it, then find the
.claude/projects/<slug>/<uuid>.jsonl inside. Tool histogram (add
and .name=="Skill" piped to .input for skill calls; subagent transcripts
sit under <uuid>/subagents/):
jq -r 'select(.message.content|type=="array") | .message.content[] | select(.type=="tool_use") | .name' <file> | sort | uniq -c | sort -rn
- The sandbox on disk (
readlink <run>/<scenario>/run-N/sandbox) — what the agent
wrote settles claims no transcript reading can.
- The failed agent itself, when the diagnosis is about wording. Resume in place
(
cd <sandbox> && HOME=<bench-home> claude -p --resume <uuid> "<question>")
and ask neutrally which phrase left room — three runs named the same defective
sentence in one round where three rounds of guessing had not. The transcript
shows what it did, the interview the words it justified it by.
- If the run dir was pruned, the transcript and per-item JSON survive in
report.html — parse with python, never grep raw HTML, and never Read a
.jsonl (104 000 tokens for 44 lines).
- The scenario file: what it asserts, and whether the query supplies what it
demands. Then the primitive's own text — the atom under
framework/atoms/,
never the generated SKILL.md.
Phase 2 — Hypotheses
Propose 3–7 candidate causes with probabilities summing to ~100, one line of
reasoning each, and print the Hypothesis Board. For the highest-probability
untested one, design an experiment with a discrete outcome — state before running
it what success and failure each prove. Execute, record, re-weight, reprint. With
several reds in hand, check each isolates a different hypothesis. Terminate at
~80 %, after three experiments that move nothing, or 5 iterations.
Diagnostic edits are reverted from cp backups — never with git checkout --
or git restore, which return the index rather than what you wrote.
Phase 3 — Fix
- Product: edit
framework/atoms/<name>.md (or the skill/agent file), then
deno run -A scripts/generate-skill-composites.ts --write; the nine generated
SKILL.md files are gitignored build artefacts (--list-targets names them).
- Instrument: edit
scripts/acceptance-tests/lib/. Extract the decision into
a pure exported function and unit-test it in a file deno task check runs, NOT
runner_test.ts, which task-check.ts ignores.
- Contract: edit the scenario or fixture, saying in the file what the old
version held and why it was wrong.
When the layer is a RULE that did not fire, two shapes decide whether the repair
holds. Prefer precondition grammar to prohibition — "X is a precondition on Y,
not a step you may or may not enter" binds where "you are NOT done until X" does
not. And elaborate a shared paragraph on ALL its triggers or none: two sentences
about a missing VALUE made the same paragraph stop binding for a missing SCRIPT,
and a green scenario fell to 1/3 for a case I never touched.
Every fix carries, in the file it touches, the measurement that justified it:
date, runs, what the sessions showed. Then grep the file for the NEW text —
the script's exit code is not evidence the edit landed. One replace was
computed and never applied, and a whole measurement round paid for it. A
framework rule may not name a project's doc paths either
(FR-UNIVERSAL.DOC-SCHEMA) — that fails check-skills.
Phase 4 — Verify
- Single scenario, cache bypassed, in the background,
.env sourced in the same
shell (they do not persist): deno task acceptance-tests -f <id> -n 3. -f
takes ONE substring, last wins; -p sets concurrency; the lock forbids
concurrent runs. A foreground run past the tool's cap is killed mid-flight.
- Host preflight: load, free swap, orphaned runners (
ps -Ao pid,etime,command | grep -E "runtests.py|benchmark.ts run"). Under pressure system_health aborts
sessions and every result is noise; ten of eleven such failures retried green.
- The verdict is the run log's
--- PASS RATES --- block, not report.html.
The threshold is 2/3, so [PASS] covers a run that failed a critical item,
and a section whose verdict JSON you cannot scrape is not a red — read the log.
- An instrument fix is verified by its unit test AND by the scenario it misread;
a product fix, against the raw sessions and the sandbox. The number has to move
for the reason claimed. Re-measure one GREEN sibling of every primitive you
edited — that guard caught a scenario dropping 3/3 to 1/3.
- Name what the green number covers, and build the task to cover it. A rule
phrased "if you see X" binds at every stage while the suite holds one, so "3/3"
reads general when it is not. An A/B asking whether a primitive still earns its
place must exercise EVERY hazard it addresses: a one-hazard task the bare model
already handles returned 3/3 against 3/3 on 2026-08-30 and read as "the
primitive changes nothing"; a four-hazard task split the arms 0/3 to 3/3.
deno task check before every commit. Its verdict is the final
N passed | M failed line; the three === FAIL deno eval Deno.exit(...) lines
are intentional fixtures.
Phase 5 — Record and commit
- Update the docs the change maps to (AGENTS.md Documentation Map): an instrument
change in SDS §3.4, a trigger lesson in FR-ACCEPT.TRIGGER. When a doc calls the
behaviour you are changing deliberate, say so and ask.
- Check
git diff --cached --stat in a SEPARATE tool call, then commit by
explicit paths. The tree is shared with other sessions.
- The commit message states what was wrong, what the evidence was, and what
remains red. A fix whose measurement did not move says so.
Phase 6 — Amend this skill
This file is the only artefact that carries a lesson into the next session.
Decide at the end of every run whether it should change and say which way — a
silent skip is indistinguishable from a forgotten step.
Earns an edit: a cause Phase 0 would NOT have caught, that can recur (one
bullet, dated); a rule you broke that cost real work; a signature here that
proved wrong — correct it in place, keeping the retracted claim visible; a
command, path or flag that changed under you.
Does not earn an edit: a one-off in one environment; a restatement of a rule
already here; a finding about the PRODUCT rather than about diagnosing it; a run
that went well.
Budget: 270 lines, raised five times (240 → 270 across 2026-08-24/25) and
never again. The ratchet is the failure the budget exists to prevent, so the next
addition DISPLACES a bullet. Compress first, never as a fallback: two bullets on
one failure shape are one bullet, and on 2026-08-30 that merge paid for three new
signatures at no net cost. Commit the amendment with the work that produced it
and say what was learned, not that the skill was updated.
Verification
1---2name: root-cause-and-fix-23description: Autonomous investigate → root-cause → fix → verify loop for this repo's own defects. Decides first whether the product or the measuring instrument is broken, fixes that layer, proves it with the test that would have caught it, and writes what it learned back into itself. Use when a scenario, check or benchmark fails and the cause is not known.4---56# Root-Cause And Fix78## Overview910Dev-only loop for the flowai repo. A red test is a claim about the world, not a11fact about the product: the harness may have lost the evidence, the checklist may12test yesterday's framework, or the scenario may never have supplied what it13demands. Measured splits of instrument to product: 7:1 on 2026-08-20, 1:3 on142026-08-21, 5:5 on 2026-08-22. Decide the layer first, fix second.1516**Autonomy contract**: invoking this skill IS authorization for the whole loop —17reading sandboxes, running single scenarios, editing the layer the diagnosis18points at, re-measuring, and committing on a non-`main` branch. Do NOT pause to19ask which hypothesis to test or whether a run may be started; the audit trail is20the Hypothesis Board plus the evidence quoted in each finding. Hand back only on21a hard blocker — a guard fires (`system_health`, `process_watchdog`, a22pre-commit hook), the environment is missing (auth, Docker, disk), or the second23fix attempt for one failure fails — then emit a STOP-ANALYSIS REPORT.2425## Rules26271. **Raw session over any rendering.** `judge-evidence.md` and `report.html` are28 the judge's rendering; ground truth is the transcript the CLI wrote for itself29 plus the sandbox on disk. Renderings also reorder: a `report.html` verdict30 block is NOT the run directory of the same index. Absence in a rendering is31 not absence in the world — every "the agent never X" claim comes from a32 tool-call count or a session line, never from a summary.332. **Guards are signals.** A guard that blocks the run means conditions are34 wrong. Never reach for `--force`, `--no-verify` or an override env var; report35 the blocker instead.363. **Fix the layer the cause is in.** Product defect → the primitive. Instrument37 defect → `scripts/acceptance-tests/lib/`. Stale contract → the scenario. Never38 quiet a red test by editing whichever of the three is easiest to reach.394. **Never flip a checklist into its opposite.** Rewriting "must have X" as "must40 NOT have X" trades one red for another and hides that the requirement was41 conditional. Scope the item to what it tests and record every dead version.425. **No test-fitting.** Do not hint the answer in `userQuery`, do not script the43 persona, do not pre-create what the skill should produce. Change a query only44 because it was malformed, and say so in the file.456. **Correct the record where the wrong claim lives.** A comment that encoded a46 wrong diagnosis costs the next reader a whole investigation — rewrite it in47 place with what the evidence shows, and keep the retracted version visible.487. **This file is an output of the loop.** Rules 4 and 6 apply to it too: a49 signature that proves wrong gets corrected in place, and what the loop learns50 gets written back (Phase 6). A lesson left in chat is lost.5152## Phase 0 — Instrument or product5354Run this BEFORE anything else. Each signature below was paid for once.5556- **Exit 1, empty trace, ~10-20 s.** `OAuth session expired`, `Invalid API key`,57 `Usage credits required`: source `.env` before the sweep. `detectAuthFailure`58 throws only at zero tool calls — a scenario driving another IDE's CLI surfaces59 THAT child's error, a true observation.60- **Exit 124 with tool calls present.** A global timeout, a warning rather than a61 blocker when the trace is non-empty. Ask whether the work legitimately outlives62 the cap — `deep-research` does.63- **The judge says something is missing or wrong and the disk says otherwise.**64 It only ever saw the evidence blob, so ask what that blob contained. Five known65 holes, all fixed, all worth re-checking: a file over 30 KB elided mid-file66 (`renderFileForEvidence`); evidence carrying only `git diff <init>..HEAD`, which67 hid the product of a workflow that stops before committing (`adapt-skills-basic`);68 the TRACE cut head+tail, deleting the middle of the conversation (2026-08-28: 5 of 10 turns survived);69 a deliverable whose extension is outside `TEXT_EXTENSIONS` in `evidence.ts`, which70 never reaches the judge at all (`.html` on 2026-09-06 — "HTML contents are not71 provided", five items red, file on disk); and a `[codex-agents]` `NEW_TASK` with an72 empty `Payload:`, which is codex ≥ 0.145 ENCRYPTING the prompt in the rollout, not73 an empty dispatch. Count turns (`grep -c '^\[turn' judge-evidence.md`) before believing "the agent never did X".74- **The cache file is not the run.** `acceptance-tests/cache/<pack>/<id>/<ide>.json`75 holds the last RECORDED verdict, often an older green one, and contradicts the76 run without saying so. Take per-item verdicts from the run, never the cache.77- **Adjacent-negative fails, correct neighbour lives in another pack.** The runner78 mounts `core` plus the scenario's pack, so there was nothing to defer to. Set79 `extraPacks` (FR-ACCEPT.TRIGGER).80- **Checklist demands an artefact the primitive's own text forbids.** Read the81 SKILL.md first: `init` forbids wrapper scripts when the project's runner suffices,82 while its checklist demanded `scripts/check.ts`.83- **The red scenario is untracked.** `git ls-files` does not list it — another84 session's deliberate RED, not your regression. Never fix it.85- **A verdict that changed with no tree change.** Load noise — a real defect fails86 the same way three runs running. Then ask what the load EXPOSED: re-measuring87 hides a deterministic cause as readily as it clears a false one. The watchdog's88 rss test was dismissed as noise for months, while every green re-run was a poll89 landing inside the second before the OS compressed its buffer away.90- **The turn ends while a dispatched subagent is still running.** The ACP loop91 runs only while the emulator answers, so the result is never collected92 (`maintenance-basic` died at 228 s where siblings ran 950 s). Cure it in the93 primitive: "wait for all subagents" must say waiting means COLLECTING, and that94 nothing is presented while one is outstanding.95- **The fixture is not what the scenario thinks it is.** `git show <init-sha>:<file>`96 in the sandbox is the exact input. `setup()` runs AFTER that commit, so what it97 plants is an UNCOMMITTED change in the agent's own diff — the judge read one as98 a line the agent had WRITTEN and failed the item asking whether it had READ it;99 planted pre-existing state must be committed. The fixture must also pass every100 deterministic checker or the agent stops on THAT and never reaches the planted101 defect (three of ten scenarios on 2026-08-22).102- **Exit 144, no verdict, a `[fork-loop guard]` line.** The guard aimed at the103 BENCH (fixed by `adoptablePgid`), or at a legitimate shell pipeline (fixed by104 raising `maxDescendants` to 16 on 2026-08-25).105- **Zero tool calls.** At 0.0 s `system_health` refused the spawn and quoted its106 reason INSIDE the checklist ERROR (`load avg N/CPU > 4/CPU`), reading as a107 finding about the product; families dispatching subagents (`plan-*`,108 `ship-task-*`) trip it at `-p 2` and above, so re-measure at `-p 1`. At full109 duration and exit 0 the agent stopped instead: `review_ready` means it produced110 the artefact unaided, `blocked` means the query never supplied what it asked111 for. With a `userPersona`, check `interactive` — `UserEmulator` exists only when112 that flag is true, so otherwise the persona is dead text.113- **A `mocks` entry does not stand in for the whole pipeline.** `writeMockBin`114 shadows the binary on `PATH`, so the canned text is the next pipe stage's INPUT.115 A mock holding already-parsed output passes only while the agent skips the pipe.116- **Every judge call fails, or a probe hangs into `aborted_streaming`.** Read the117 agent's own text before blaming the CLI: `API Error: 529 Overloaded` sits there118 in plain words. `grep -l "API Error: 5"` over a sweep bounds it.119- **A scenario at 2/3 passes the threshold and can still hold a real defect.**120 Read the failing run. A fix that MOVES a red to 2/3 has the same shape: it cured121 one real cause and uncovered the next. Attempts on two different causes are not122 two attempts at one, so STOP-ANALYSIS does not fire — but say which cause each123 attempt addressed, or the count is unauditable.124- **The rule was in the file and still did not fire.** First prove the text125 reached the agent — a green sibling sharing the artefact does it in one line.126 Then read the session for the shape. Never mentions it → never bound; bind it127 AHEAD of the decision, since an agent holding a solution reads a rule for128 exemptions. Quotes and overrides it → say what compliance PRODUCES, and grep for129 a neighbouring rule claiming the case, since the weaker of two wins. Reclassifies130 the case → your own carve-out is the escape; scope it to what the user NAMED.131 Forbid the act, not the harm: a rule stated as a consequence dies by denying the132 consequence.133134Print the verdict — `INSTRUMENT` or `PRODUCT` — with the evidence line that135decided it, then continue.136137## Phase 1 — Evidence138139Read in this order and stop as soon as the cause is unambiguous.1401411. The raw session. `<run>/<scenario>/run-N/bench-home` is a SYMLINK into142 `$TMPDIR/flowai-bench/run-N-<hash>/`; `readlink` it, then `find` the143 `.claude/projects/<slug>/<uuid>.jsonl` inside. Tool histogram (add144 `and .name=="Skill"` piped to `.input` for skill calls; subagent transcripts145 sit under `<uuid>/subagents/`):146 `jq -r 'select(.message.content|type=="array") | .message.content[] | select(.type=="tool_use") | .name' <file> | sort | uniq -c | sort -rn`1472. The sandbox on disk (`readlink <run>/<scenario>/run-N/sandbox`) — what the agent148 wrote settles claims no transcript reading can.1493. The failed agent itself, when the diagnosis is about wording. Resume in place150 (`cd <sandbox> && HOME=<bench-home> claude -p --resume <uuid> "<question>"`)151 and ask neutrally which phrase left room — three runs named the same defective152 sentence in one round where three rounds of guessing had not. The transcript153 shows what it did, the interview the words it justified it by.1544. If the run dir was pruned, the transcript and per-item JSON survive in155 `report.html` — parse with python, never grep raw HTML, and never `Read` a156 `.jsonl` (104 000 tokens for 44 lines).1575. The scenario file: what it asserts, and whether the query supplies what it158 demands. Then the primitive's own text — the atom under `framework/atoms/`,159 never the generated `SKILL.md`.160161## Phase 2 — Hypotheses162163Propose 3–7 candidate causes with probabilities summing to ~100, one line of164reasoning each, and print the Hypothesis Board. For the highest-probability165untested one, design an experiment with a discrete outcome — state before running166it what success and failure each prove. Execute, record, re-weight, reprint. With167several reds in hand, check each isolates a different hypothesis. Terminate at168~80 %, after three experiments that move nothing, or 5 iterations.169170Diagnostic edits are reverted from `cp` backups — never with `git checkout --`171or `git restore`, which return the index rather than what you wrote.172173## Phase 3 — Fix174175- **Product**: edit `framework/atoms/<name>.md` (or the skill/agent file), then176 `deno run -A scripts/generate-skill-composites.ts --write`; the nine generated177 `SKILL.md` files are gitignored build artefacts (`--list-targets` names them).178- **Instrument**: edit `scripts/acceptance-tests/lib/`. Extract the decision into179 a pure exported function and unit-test it in a file `deno task check` runs, NOT180 `runner_test.ts`, which `task-check.ts` ignores.181- **Contract**: edit the scenario or fixture, saying in the file what the old182 version held and why it was wrong.183184When the layer is a RULE that did not fire, two shapes decide whether the repair185holds. Prefer precondition grammar to prohibition — "X is a precondition on Y,186not a step you may or may not enter" binds where "you are NOT done until X" does187not. And elaborate a shared paragraph on ALL its triggers or none: two sentences188about a missing VALUE made the same paragraph stop binding for a missing SCRIPT,189and a green scenario fell to 1/3 for a case I never touched.190191Every fix carries, in the file it touches, the measurement that justified it:192date, runs, what the sessions showed. **Then grep the file for the NEW text —193the script's exit code is not evidence the edit landed.** One `replace` was194computed and never applied, and a whole measurement round paid for it. A195framework rule may not name a project's doc paths either196(`FR-UNIVERSAL.DOC-SCHEMA`) — that fails `check-skills`.197198## Phase 4 — Verify199200- Single scenario, cache bypassed, in the background, `.env` sourced in the same201 shell (they do not persist): `deno task acceptance-tests -f <id> -n 3`. `-f`202 takes ONE substring, last wins; `-p` sets concurrency; the lock forbids203 concurrent runs. A foreground run past the tool's cap is killed mid-flight.204- Host preflight: load, free swap, orphaned runners (`ps -Ao pid,etime,command |205 grep -E "runtests.py|benchmark.ts run"`). Under pressure `system_health` aborts206 sessions and every result is noise; ten of eleven such failures retried green.207- The verdict is the run log's `--- PASS RATES ---` block, not `report.html`.208 The threshold is 2/3, so `[PASS]` covers a run that failed a critical item,209 and a section whose verdict JSON you cannot scrape is not a red — read the log.210- An instrument fix is verified by its unit test AND by the scenario it misread;211 a product fix, against the raw sessions and the sandbox. The number has to move212 for the reason claimed. **Re-measure one GREEN sibling of every primitive you213 edited** — that guard caught a scenario dropping 3/3 to 1/3.214- Name what the green number covers, and build the task to cover it. A rule215 phrased "if you see X" binds at every stage while the suite holds one, so "3/3"216 reads general when it is not. An A/B asking whether a primitive still earns its217 place must exercise EVERY hazard it addresses: a one-hazard task the bare model218 already handles returned 3/3 against 3/3 on 2026-08-30 and read as "the219 primitive changes nothing"; a four-hazard task split the arms 0/3 to 3/3.220- `deno task check` before every commit. Its verdict is the final221 `N passed | M failed` line; the three `=== FAIL deno eval Deno.exit(...)` lines222 are intentional fixtures.223224## Phase 5 — Record and commit225226- Update the docs the change maps to (AGENTS.md Documentation Map): an instrument227 change in SDS §3.4, a trigger lesson in FR-ACCEPT.TRIGGER. When a doc calls the228 behaviour you are changing deliberate, say so and ask.229- Check `git diff --cached --stat` in a SEPARATE tool call, then commit by230 explicit paths. The tree is shared with other sessions.231- The commit message states what was wrong, what the evidence was, and what232 remains red. A fix whose measurement did not move says so.233234## Phase 6 — Amend this skill235236This file is the only artefact that carries a lesson into the next session.237Decide at the end of every run whether it should change and say which way — a238silent skip is indistinguishable from a forgotten step.239240**Earns an edit:** a cause Phase 0 would NOT have caught, that can recur (one241bullet, dated); a rule you broke that cost real work; a signature here that242proved wrong — correct it in place, keeping the retracted claim visible; a243command, path or flag that changed under you.244245**Does not earn an edit:** a one-off in one environment; a restatement of a rule246already here; a finding about the PRODUCT rather than about diagnosing it; a run247that went well.248249**Budget**: 270 lines, raised five times (240 → 270 across 2026-08-24/25) and250never again. The ratchet is the failure the budget exists to prevent, so the next251addition DISPLACES a bullet. Compress first, never as a fallback: two bullets on252one failure shape are one bullet, and on 2026-08-30 that merge paid for three new253signatures at no net cost. Commit the amendment with the work that produced it254and say what was learned, not that the skill was updated.255256## Verification257258<verification>259[ ] Phase 0 verdict printed — INSTRUMENT or PRODUCT — with the deciding evidence.260[ ] Raw session read before any claim about what the agent did.261[ ] Hypothesis Board printed before and after each experiment.262[ ] Diagnostic edits reverted from copies; worktree clean between experiments.263[ ] Fix landed in the layer the cause is in, with its measurement in the file, and264 a test in a file `deno task check` runs.265[ ] Scenario re-measured with `-n 3`; variance separated from regression.266[ ] `deno task check` green; commit by explicit paths after a separate index check.267[ ] Report names what is still red and why; a documented decision you had to268 overturn was raised, not resolved quietly.269[ ] Phase 6 decision stated aloud: amended, or left alone with the reason.270</verification>