Skip to content

Method 3.6: a resolver that resolves nothing is a mechanism gap - #18

Open
LTSCommerce wants to merge 29 commits into
nextfrom
editor/gap-terms
Open

LTSCommerce wants to merge 29 commits into
nextfrom
editor/gap-terms

Conversation

@LTSCommerce

@LTSCommerce LTSCommerce commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

What this changes

SPEC.md, two paragraphs, both clarity edits.

  • Clause 3.6. A mechanism that cannot resolve identifiers at all is now called a mechanism
    gap under clause 3.2, not a toolchain gap. Because it also breaches the toolchain
    specification's own obligations, it is recorded against the toolchain's claim as well, where
    clause 3.2 says, and it still blocks that claim under section 7 until fixed.
  • Section 2, the "When it can" paragraph. One sentence added after the requirement that the
    defence is attempted before the defect is fixed: the method begins at clause 3.1, working out
    what class the defect belongs to, and section 3 gives the order of the steps that follow. It
    does not change what that requirement covers. It fixes a keyed miss on question 2a in run 88,
    where a reader relying on section 2 left out the attribution step.

The observed case

An independent review of pull request 14 found the two clauses giving one case two names.
Clause 3.2 keeps "toolchain gap" for a language with no extensible detector and no practical
bespoke one, where the class waits, and calls a toolchain lacking a mechanism the toolchain
specification requires, "such as ... an Identifier resolver", a mechanism gap, where the rule is
still built. Clause 3.6 called a resolver that resolves nothing a toolchain gap. A reader
following 3.6 could take the class to be out of reach and stop building the rule. Section 2's
three ways out of the method draw the same line as 3.2.

Kind of change

  • Clarity edit: no MUST, SHOULD or MAY added or removed; a reword that keeps the keyword and most of the words counts as clarity
  • Obligation: the five tests below are answered

The five scope tests (obligations only)

Not applicable: a clarity edit. tools/changes.py finds no obligation sentence changed.

Where it goes

Clause 3.6, the paragraph on an existing rule's identifier that does not resolve; section 2,
the "When it can" paragraph.

Acceptance

The acceptance process proposed in pull request 22 is merged into this branch, and runs 88 and
89 were made under it, at its commit 40f7895, named before the readers started. Merge pull
request 22 first.

Run Rules Verdict
34 earlier process FAIL, on base text other changes rework
88 pull request 22 FAIL: one reader of five missed 2a; fixed in section 2
89 pull request 22 PASS

No reader of runs 88 or 89 named either paragraph the change altered. Their findings lie in
untouched text and are recorded in acceptance/ROUTED.md; one is a new row (clause 3.3 Part B,
"Then the proof, which runs the other way round, and both ways."). Every run's sheets, prompt,
command and marker instruction are posted as comments below and linked from its record.

Run 89 records open points marked by the open-point rule, one of which its pass rests on: one
reader's 2a, "only then fix. Sweep and fix every instance.", which can be read either way on
whether the sweep comes before the fix, was marked for the reader.

spec-qa.py is clean, the unit tests pass, build-agent-surfaces.py --check is current and
tools/changes.py is clean against next.

🤖 Generated with Claude Code

…a toolchain gap

Clause 3.2 keeps "toolchain gap" for a language with no extensible detector
and no practical bespoke one, and names a missing identifier resolver as a
mechanism gap. Clause 3.6 called a mechanism that cannot resolve identifiers
at all a toolchain gap, so one case had two names. It is now a mechanism
gap under clause 3.2; it still blocks the toolchain's claim under section 7
until fixed, as a breach of the toolchain specification's own obligations.
Clarity edit: no MUST, SHOULD or MAY changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LTSCommerce and others added 21 commits October 10, 2026 15:18
No failure traces to the changed 3.6 sentence. The misses fall on base text that other open
changes are clarifying.
…eys that ask what the question asks

Keys list the required elements per part of each question, questions ask for exactly
those, a confusion finding is settled run by run and blocks only in text the change
wrote or altered (others go to acceptance/ROUTED.md), single misses count against the
document only where the key names the reading, and open marking points are settled in
the rules before the next run rather than invented per run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One route for each kind of change to the test (quotation and new item in the specification
pull request, everything else a process change); ROUTED.md and runs/ are records, not process
files; question 1 defined once; open points marked in a stated direction; quoting needs a
six-word run; a finding lies in the change's text when any sentence of its paragraph changed;
wrong readings stated as grade plus reasoning; keys ask only what their questions ask (detector
2a, toolchain 2c); the question 3 tables separate the condition from the explanation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Question 1 defined once (first six bullets, same clause counts once, a bullet on neither list
not counted); the verdict items of question 3 ask for their reason and the keys state each
as an accepted answer with a condition; a finding lies in the change's text also where the
readers set it against that text, read against the merge base; the cohort's model is the
version the editor names; a keyed miss whose fix would change an obligation is routed and
waited for; the question set is the one list of load-bearing questions; ROUTED.md defines its
status column and links its clauses.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two routes for changing the test, with a changed or removed obligation's key item following
the text in the specification pull request; an open point holds up only its own pull request;
a fresh run follows a change to the document, not to the record; detector 2b names both
things; the section 7 and section 9 reasons accept every failing clause; the restatement rule
reads plainly; ROUTED.md defines a finding as ACCEPTANCE.md does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The method key lists fixing the original defect with a reproducing test among the
obligations, says a bullet on the verdict resting on reproduction describes the reviewer, and
says that an answer to 2a naming no step does not state the steps. The toolchain key lists the
governing principle, says a bullet on how conformance is measured is neither correct nor
wrong, and accepts a section 9 reason that points to the grades given. acceptance/ROUTED.md
gains the findings runs 53 to 59 routed, and the runs that raised existing rows again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The toolchain key counts a bullet on not describing partial conformance as conformance, and
says a bullet calling section 7 recommended or a SHOULD asserts no obligation.
acceptance/ROUTED.md gains clause 3.4's "tremendous" sentence from run 61 and notes that run
on the two rows it raised again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
acceptance/ROUTED.md gains the detector's clause 6.1 paragraph on a project's own rule, from
run 63, and notes run 64 on the four method rows it raised again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ows from run 67

acceptance/ROUTED.md gains a Closed table, with the detector's clauses 7.1, 8.1, 5.3 and 6.1
rows closed by editor/detector-route-rule's run 66, notes run 66 on the clause 6.1 row on a
project's own rule, and gains the toolchain's clause 6.2 heading and documented-check
paragraph from run 67.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… run 64

acceptance/ROUTED.md gains the passages runs 19 to 30 on the semantic-detectors change named
in text that change no longer touches (the Narrowing entry, section 4's lead-in to the
owner's decisions, clause 3.1's paragraph on independent techniques and the section 3 table's
3.1 row), notes those runs on the rows they raised again, and closes the method's two-bounds
paragraph and bundled-defence paragraph by editor/defined-terms's run 64.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s 68 to 71

The method key lists the sentence behind a narrowing, and the owner's decision where it cannot
be written, among the obligations, after three readers of run 70 restated it.
acceptance/ROUTED.md gains Appendix A's closing paragraph from run 70 and the detector's
clause 5.3 from run 69, and notes runs 68, 70 and 71 on the rows they raised again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ED 5.3 row adds run 75

Run 72's open point: three readers graded clause 4.3 on the condition that the ignore comment
is Lintomatic's only suppression route, and the key did not say which case the fixture
presents. The 4.3 row now says so, and such a grade is the grade for this case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…parted paragraph is the change's text

Settles the open points of runs 76, 77, 78 and 80. ACCEPTANCE.md says a remark that another
document was not supplied, or that a term is defined only there, is a note however much of the
document it quotes to locate the reference; and that a paragraph the pull request parts from
what it refers to, by inserting a paragraph between them or moving either, is the change's
text. The Lintomatic fixture says `check --only` prints its findings as `check` does, and the
detector key's 4.2 row says so and which case a conditional grade is for. ROUTED gains the
toolchain 6.1 Project record sentence (run 76) and the detector 4.2 sentence on a detector
failing 4.1 (run 78, taken by editor/defined-terms); the 4.3 row links the section, whose
heading the toolchain change rewrites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… 6.2 and 6.3 speak only of exceptions

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, from run 83

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…es 6.1, 6.2 and 6.4

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… 6.4 again

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…inst 6.2 new

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rms: run under the proposed acceptance process
@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 088: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

Command and working directory

ccy-claude -p --model haiku --safe-mode --tools '' --setting-sources '' --strict-mcp-config --no-session-persistence --output-format json < prompt.txt, each reader in a fresh empty directory made by mktemp -d outside the repository

Model version each reader reported

reader 1: claude-haiku-5-5
reader 2: claude-haiku-5-5
reader 3: claude-haiku-5-5
reader 4: claude-haiku-5-5
reader 5: claude-haiku-5-5

The delegated marker's task prompt

MODE: mark

Worktree: <worktree> (branch `editor/gap-terms`). The readers read commit f984911. Read, at that commit and in that worktree: ACCEPTANCE.md in full (it is the process as pull request 22 proposes it, merged into this branch; the run is made under that process change before it merges, at commit 40f7895, named here before the readers started), acceptance/runs/README.md, acceptance/method/QUESTIONS.md, acceptance/method/KEY.md, acceptance/fixtures/remediation-ledger.md and SPEC.md. You may read acceptance/runs/070-semantic-detectors-run-13.md on the branch `editor/semantic-detectors` (`git -C <worktree> show origin/editor/semantic-detectors:acceptance/runs/070-semantic-detectors-run-13.md`) for the shape of a record only; ACCEPTANCE.md says a judgement an earlier run recorded is not a rule. Do not take any judgement from acceptance/runs/034-gap-terms-method-run-1.md, which was made under the earlier rules.

Run: 088. Document: SPEC.md. Change: gap terms, pull request 18 (a clarity edit to clause 3.6: a mechanism that cannot resolve identifiers at all is called a mechanism gap under clause 3.2, not a toolchain gap, and it still blocks the toolchain's claim under section 7 until fixed, as a breach of the toolchain specification's own obligations). No key or question changed for this change: no key quotes the altered sentence and the change adds no obligation. Pull request: 18, https://github.com/Defence-Before-Fix/defence-before-fix.github.io/pull/18. Run within the pull request: 2 (run 34, under the earlier process, came before). Record file to write: <worktree>/acceptance/runs/088-gap-terms-method-run-2.md. Do not commit it.

Sheets: <run-dir>/sheet-1.md to sheet-5.md, each the `result` of reader-N.json. Prompt the readers received: <run-dir>/prompt.txt; its sha256 is 339c36f9a6f4be2d5c860991c0bf4b69667a945d798467a4688ae2354e7a3f2e, and it is byte for byte the question set, SPEC.md and the fixture at f984911, each after one `===== FILE: <name> =====` line, one blank line between files. Model: every reader reported claude-haiku-5-5 (<run-dir>/models.txt); the cohort's model named for the run is claude-haiku-5-5. Every reader's JSON has is_error false and subtype success; no reader is set aside.

Facts the editor supplies for the record: cohort of five readers, none reused from any earlier run; each a separate `claude -p` process started through the `ccy-claude` wrapper (which only reattaches the session's credential) in a fresh empty directory made by `mktemp -d` outside any repository, with the packet on standard input, command `ccy-claude -p --model haiku --safe-mode --tools '' --setting-sources '' --strict-mcp-config --no-session-persistence --output-format json < prompt.txt`; no tools, no settings, no project context, no saved session; the machine applies no managed settings; the editor attests the rest of the isolation. Marked by a delegated marker, a fresh `dbf-spec-editor` agent (Claude Opus) given this instruction and its agent definition, having seen neither the drafting nor the review.

Text the change wrote or altered, for "Where a finding lies": the output of `git -C <worktree> diff 9e914fe f984911 -- SPEC.md` (9e914fe is the merge base with `next`). Run it and read it. A finding in a paragraph that diff adds, rewords, moves, deletes a sentence from, or changes a word, term or link in, a paragraph the change parts from what it refers to, or that a reader sets against such a paragraph, blocks; any other finding is routed to acceptance/ROUTED.md, and the record says so for each.

Mark every sheet, keyed item by keyed item, by ACCEPTANCE.md's Marking section and the key alone. Write the record stating what acceptance/runs/README.md lists, in its order, with the marks table, each miss with the reader's reasoning in brief, every passage named under question 4 with its words or description, clause and readers, each finding and where it lies, any open point and the direction you marked it, the process-change commit 40f7895, the verdict, and a placeholder line for the link to the posted sheets. Reply with the verdict and the findings only, under thirty lines.

The agent definition the marker ran under

---
name: dbf-spec-editor
description: Drafts, reviews, key-updates, marks or records a change to the Defence Before Fix specification, one mode per dispatch, following the specification repository's CONTRIBUTING.md, SCOPE.md and ACCEPTANCE.md exactly. Dispatched by the dbf-spec-change skill; it cannot run a cohort itself.
tools: Read, Write, Edit, Grep, Glob, Bash
model: opus
---

You work on one change to the Defence Before Fix specification, in the worktree path you are
given. The process is defined by that repository's `CONTRIBUTING.md`, `SCOPE.md`,
`ACCEPTANCE.md` and `acceptance/README.md`; read the ones your mode needs, in full, before you
act. You invent no steps. You never merge, push to `next` or `main`, release, tag or publish,
and you never change a question, threshold or fixture. Your prompt names one mode.

Write as the specification is written: British spelling, no em dashes, defined terms linked as
`spec-qa.py` requires, every clause or section number linked to its heading. Commit on the
worktree's branch.

## MODE: draft

1. Read `SCOPE.md`, `DECLINED.md`, and every clause the change touches, with the terminology
   entries they use.
2. Decide the kind. A change to the sentences carrying MUST, SHOULD or MAY in a numbered section
   or appendix is an obligation; anything else is a clarity edit.
3. For an obligation, answer the five SCOPE tests in a sentence or two each, and name the
   section the obligation extends. If a test fails, or an existing clause already covers it, say
   so and stop without editing.
4. Make the smallest edit that does the job, in the clause that owns the subject.
5. Add one line under `Unreleased` in `CHANGELOG.md`, saying what changed and why, with a
   placeholder `run NNN`. Change no version.
6. If Appendix A changed, run `python3 build-agent-surfaces.py` and commit its outputs.
7. Run `python3 spec-qa.py` and `python3 -m unittest discover -s tools -p 'test_*.py'`; both
   clean.
8. Reply with: kind, SCOPE answers, files changed, commit, QA result, and a filled
   `.github/PULL_REQUEST_TEMPLATE.md` body. Under forty lines.

## MODE: review

You have not seen the drafting. Read the whole changed document and the diff against
`origin/next`. Report, numbered, with clause numbers: contradictions with other clauses, terms
used outside their definition, a sentence a reader could take two ways, an obligation an owner
could not verify from the record. Do not edit.

## MODE: keys

Read `acceptance/README.md` and the `KEY.md` for each changed document. Update every quotation of
a changed sentence to the new wording, verbatim, in a new commit; run `python3 spec-qa.py` until
`key-quote-stale` is gone. If the change adds a load-bearing obligation no keyed question
covers, propose the question and its key in your reply; add it only if your prompt says the owner
has decided to.

## MODE: mark

You have not seen the drafting or the review. Read `ACCEPTANCE.md` in full, the `KEY.md` for the
document, the document at the commit named, and the sheets under `<run-dir>/`.

- Mark each sheet against the key, keyed item by keyed item, by the rules under "Marking" in
  `ACCEPTANCE.md` and nothing else: the key's required elements, accepted grades, conditions and
  wrong readings. Invent no judgement; where neither the rules nor the key settles a mark, mark
  it in the direction the open-point rule gives and record the point.
- A single miss under questions 1 and 3 counts against the document only where the key names
  that reading; say which.
- For question 4, record every passage named, with its words or description and its clause.
  Group the passages by paragraph, count the readers, and for each finding say whether it lies in
  text the change wrote or altered, read against the diff the prompt gives you, or is routed to
  `acceptance/ROUTED.md`. Ignore any suggestion a reader volunteers.
- Apply the pass criteria exactly.
- Write `acceptance/runs/NNN-<slug>-run-<k>.md` stating what `acceptance/runs/README.md` lists,
  in its order. Do not commit it. Reply with the verdict and the findings only.

## MODE: record

Commit the run record, replace the `run NNN` placeholder in `Unreleased` with a link to it, and
for an obligation set the document header to `<next version>-dev, unpublished` in the same
commit if it still names a published version. Run `python3 spec-qa.py` and
`python3 tools/changes.py --base origin/next`; both clean. Reply with the commit.

@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 088: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

Answer sheets, each reader's whole output as its process returned it

reader-1

{"duration_api_ms":60484,"stop_reason":"end_turn","session_id":"3600dd2c-00f6-469a-b439-0355eaa25238","total_cost_usd":0.01302453,"usage":{"input_tokens":2,"cache_creation_input_tokens":29129,"cache_read_input_tokens":2153,"output_tokens":14354,"output_tokens_details":{"thinking_tokens":12142},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29129,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":14354,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29129,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29129},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":14354,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29129,"webSearchRequests":0,"costUSD":0.01302453,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":12142,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Before fixing a defect, name the class it belongs to and the hazard that class carries. If no pattern can be expressed, record that conclusion and fix the defect conventionally.\n- Search for other instances using at least two independent techniques, chosen and run before the rule is run, and record what each found.\n- Build a blocking rule in a detector that reads code, not in a test. Prove it fires on the originating instance or a fixture, committed separately from the fix.\n- Run the rule across the whole codebase, record the count, and fix every instance by addressing the hazard, not by silencing the rule.\n- Make the rule permanent through the project's own entry point, failing rather than warning. Its terse message carries a stable identifier that resolves to documentation versioned with the rule.\n- Only then fix the original defect with a test that reproduces it. Suppressions, baselines and leaving instances unfixed belong to the owner, not the practitioner.\n\n## 2. Load-bearing statements\n\n**2a.** The practitioner first attributes the defect to a class, writes its hazard sentence, and runs two independent searches. Then they build and prove the rule, sweep and fix every instance, enforce the rule, and write its documentation. Last, they fix the original defect with a reproducing test.\n- \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\"\n- \"The [Defence] is committed with the originating [Instance] still present, and the fix is committed after it.\"\n- \"The specific [Defect] is still reproduced with a test and proven fixed, exactly as normal.\"\n\n**2b.** Only the owner may adopt a baseline or suppress an instance. An agent may not do either on its own authority.\n- \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\"\n- \"An [Exception] exists only where a human has discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own creates one.\"\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect, and the verdict rests on reproduction. A report alone is not enough.\n- \"**A remediation [Conforms]** if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\"\n- \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer reruns the [Defence] red at the commit that introduced it and green at the final commit, through the project's own entry point for accepting changes, and reruns the originating symptom against the fix.\"\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n|---|---|---|\n| 3.1 | Partial | Commit A records the class, the hazard sentence and the next wider class. It does not record two search techniques or what each found, and it does not say whether a Runner check pins the reported behaviour. The wider class is rejected because \"most are legitimate\", which is not a hazard sentence confirmed by search. |\n| 3.2 | Yes | The rule is in the project's static analyser, which reads code. No test serves as the detector. |\n| 3.3 | Yes | Commit B holds the rule and fixture while `Ledger::total()` is still present, and CI is red with five findings. The fix comes later in commit C. The ignore added in commit C is a suppression, not a narrowing, so it is graded under 3.4 and 3.5. |\n| 3.4 | No | The count of five is recorded but has no independent corroboration. `Report::render()` is left unfixed behind an inline ignore that the agent added on its own authority. |\n| 3.5 | No | The rule is in the checks, fails rather than warns, and the local run passed. But the green run depends on an inline ignore, and the project's recorded decisions say nothing about inline ignores, so no recorded decision covers it. |\n| 3.6 | Partial | The one-line message carries the identifier, file and line, and the wiki URL. The documentation states what is forbidden, why, and the construction to use. But it lives on a company wiki rather than being versioned with the rule. |\n\n**Does the remediation conform?** No. Clauses 3.4 and 3.5 are No, and 3.1 and 3.6 are Partial. Section 7 says partial conformance is not conformance. The verdict also cannot rest on reproduction, because the fixture shows only commit E's description in the pull request, not a reviewer rerunning the red and green runs. The merge commit is not a problem, because all five commits remain in the history.\n\n**Does the defence alone conform?** No. Section 7 requires the defence to be drawn within both bounds and to resolve to documentation versioned alongside it. The wiki fails the second requirement. The first is not shown: the search techniques are not recorded, and whether `Report::render()` carries the hazard is undetermined. If it does not, the rule has a false positive and fails the upper bound. If it does, it is an instance the rule should have led to a fix.\n\n**Decision not the practitioner's to take.** The inline ignore on `Report::render()` in commit C. Section 4 lists \"Suppressing an [Instance]\" and \"Accepting a known [Instance] as unfixed for any reason\" as owner decisions. The project has a named owner, so the default-owner route does not apply, and the agent did not escalate. The 3.1 table also gives the owner the decision on whether the next wider rule is built. That is a secondary point, and it is unclear, as section 4 shows (see section 4 below).\n\n## 4. Confusion\n\n- **Clause 3.1, table, Owner column:** \"Whether the next wider [Rule] is built.\" This conflicts in scope with the section 4 bullet: \"Leaving unbuilt the next wider [Rule] that clause 3.1 required the [Practitioner] to name, where it was rejected because no [Instance] of it exists today rather than because the [Hazard] cannot arise there.\" The table gives the decision to the owner in general, while section 4 covers only one reason for rejecting a wider rule.\n- **Clause 3.1, lower bound:** \"unless the independent search below has found no other [Instance] and the [Practitioner] records that the [Class] is genuinely singular today\", followed later in the same bullet by \"where it stays because the search found nothing wider, the record MUST also name the next wider [Rule]\". The bound gives two different exits that seem to cover the same case.\n- **Clause 3.1:** \"**Both bounds measure breadth within one level; the level itself is a separate question, answered against the report.**\" I could not tell what \"level\" refers to, or how the report answers the question.","ttft_ms":53548,"type":"result","duration_ms":61064,"uuid":"20782c0f-eb98-4cd3-856d-3d56694a3611","ttft_stream_ms":1288,"time_to_request_ms":590,"first_content_frame_ms":1289,"queued_turn_count":0,"result_index":0}

reader-2

{"duration_api_ms":47132,"stop_reason":"end_turn","session_id":"e874a072-7cdd-41bb-b949-f606be772475","total_cost_usd":0.01122143,"usage":{"input_tokens":2,"cache_creation_input_tokens":29131,"cache_read_input_tokens":2153,"output_tokens":10747,"output_tokens_details":{"thinking_tokens":8718},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29131,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":10747,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29131,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29131},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":10747,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29131,"webSearchRequests":0,"costUSD":0.01122143,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":8718,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Don't fix the defect yet. Treat it as evidence of a class and name that class, with the harm it carries (3.1).\n- Express the class as a blocking rule in a static detector, not as a test (3.2).\n- Run at least two independent searches for other instances, chosen before the rule runs. Then prove the rule fires, in its own commit, on the instance or a fixture (3.1, 3.3).\n- Sweep the whole codebase, record the count, and fix every instance. Only the Owner may baseline, suppress, or leave an instance unfixed (3.4, section 4).\n- Make the rule permanent and failing in the project's own entry point, with a terse message and a stable identifier that resolves to documentation versioned with the rule (3.5, 3.6).\n- Then fix the original defect with a reproducing test, and record any deferred defect where the project keeps its decisions (section 2).\n\n## 2. Load-bearing statements\n\n**2a.** The practitioner attributes the defect to a class, builds and proves the rule, sweeps and fixes every instance, then enforces it. Only after that do they fix the original defect with a test. The method \"runs *before* a [Defect] is fixed\". Within that, the sweep count comes first: \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything.\" The original defect is then fixed \"exactly as normal\": \"The specific [Defect] is still reproduced with a test and proven fixed, exactly as normal.\"\n\n**2b.** The Owner, who is always a human, may adopt a baseline or suppress an instance. An agent may not do either on its own authority. \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\" Also: \"An [Exception] exists only where a human has discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own creates one.\" The default-Owner rule in section 3 does not apply here, because the fixture says the project has a named owner.\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect: \"A remediation [Conforms] if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction.\" A reviewer's verdict must rest on reproduction, not the report: \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer reruns the [Defence] red at the commit that introduced it and green at the final commit, through the project's own entry point for accepting changes.\"\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n| --- | --- | --- |\n| 3.1 | No | Commit A names the class and its hazard. But no two independent search techniques are recorded as chosen before the rule ran. The rejection of the wider class (\"most are legitimate\") is not a hazard sentence confirmed by search. |\n| 3.2 | Yes | Commit B expresses the class as a rule in the project's static analyser, which reads code. |\n| 3.3 | Yes | Commit B holds the rule and the instance together, and CI is red on it. The fix is in a later commit (C), and the merge kept all five commits. A fresh-checkout reproduction is not shown. |\n| 3.4 | No | The count of five includes the fixture, so it is four instances, and it is not independently corroborated. `Report::render()` is left unfixed behind an inline ignore, which is suppression, not a fix. |\n| 3.5 | No | The rule fails rather than warns, and the local run through the project's entry point passed (commit E). But the green run depends on an inline ignore that no recorded decision covers. |\n| 3.6 | No | The message is terse and carries the identifier. But the documentation is on a company wiki, not versioned in the repository with the rule, and so does not meet the requirement. |\n\n**Remediation conforms under section 7?** No. Clauses 3.1, 3.4, 3.5 and 3.6 are not met. The verdict also cannot rest on reproduction. The pull-request description and commit E's report are the report, not a rerun of the red and green runs.\n\n**Does the defence alone conform?** No. A defence conforms only if 3.1, 3.2, 3.3, 3.5 and 3.6 are satisfied. Three of those fail: 3.1 (no two recorded searches, and an unsupported rejection of the wider class), 3.5 (suppression without a recorded decision), and 3.6 (documentation outside the repository).\n\n**Decision not the practitioner's to take.** The inline `// ignore proj.caught-then-used` on `Report::render()` in commit C. Section 4 lists \"Suppressing an [Instance]\" as an Owner decision, and says an agent \"MUST NOT ... suppress ... on its own authority\". The fixture also says the agent \"did not raise it with anyone\". A secondary candidate is the decision in commit A not to build the wider class. It was rejected on a hazard judgement (\"most are legitimate\") that section 3.3 says goes to the Owner when the practitioner is not sure. Section 4's own bullet on unbuilt wider rules covers only the cases where no instance exists or the hazard cannot arise, so this one is arguable.\n\n## 4. Confusion\n\n- **Clause 3.1**: \"A [Rule] drawn to the exact value or name found in the [Defect] is the same [Narrowing] however it is spelt.\" This uses \"Narrowing\" for a rule drawn too narrowly, which conflicts with the glossary's definition of narrowing as reducing scope to stop reporting code that lacks the hazard.\n- **Section 4**, the bullet beginning \"Leaving unbuilt the next wider [Rule]\": \"The absence of an [Instance] is the [Practitioner]'s reason not to widen unasked; it is not authority to exclude the extension, and recording it as a known gap does not change whose decision it is.\"\n- **Clause 3.1**: \"Both bounds measure breadth within one level; the level itself is a separate question, answered against the report.\"","ttft_ms":40218,"type":"result","duration_ms":47635,"uuid":"336d682b-a38c-477f-942e-829ef1cf8bab","ttft_stream_ms":1318,"time_to_request_ms":508,"first_content_frame_ms":1319,"queued_turn_count":0,"result_index":0}

reader-3

{"duration_api_ms":64697,"stop_reason":"end_turn","session_id":"f156293d-13af-4b00-9271-9cfa97807ae3","total_cost_usd":0.01357723,"usage":{"input_tokens":2,"cache_creation_input_tokens":29130,"cache_read_input_tokens":2153,"output_tokens":15459,"output_tokens_details":{"thinking_tokens":13325},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29130,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":15459,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29130,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29130},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":15459,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29130,"webSearchRequests":0,"costUSD":0.01357723,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":13325,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Before fixing a defect, find the class it belongs to: a pattern, with one sentence naming the hazard, bounded both ways.\n- Try to express the class as a blocking rule in a detector that reads code. A test is never the detector. If no pattern exists, record one sentence naming at least two techniques tried.\n- Run at least two independent searches for other instances before running the rule, until the last one adds nothing. If the rule misses instances the searches found, widen it.\n- Prove the rule fires in its own commit, with the originating instance still present. Narrow it only with a written sentence showing the hazard cannot arise in the excluded code. Any doubt goes to the owner.\n- Sweep the whole codebase, record the count, and fix every instance. Make the rule permanent and failing through the project's own entry point, with a terse message whose identifier resolves to documentation versioned with the rule.\n- Then fix the defect with a test that reproduces it. Baselines, suppressions, and leaving instances unfixed belong to the owner, not the practitioner.\n\n## 2. Load-bearing statements\n\n**2a.** The practitioner builds and proves the rule, sweeps and counts, and fixes every instance, all before the defect's own fix. Section 2: \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\" Clause 3.4: \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\" Appendix A: \"Only then fix the original [Defect] in the normal way, with a test that reproduces it.\"\n\n**2b.** The owner may adopt a baseline or suppress an instance. An agent may not, on its own authority. Section 4: \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\" The definition of Owner says the owner is \"Always a human.\" Section 4 also says: \"Nothing an [Agent] concludes on its own creates [an Exception].\"\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect, and the verdict rests on reproduction. Section 7: \"A remediation [Conforms] if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction.\" Section 7 also says: \"A report that reads as [Conforming] has not been shown to be.\"\n\n## 3. Application\n\n- **3.1 Attribute to a class: Partial.** Commit A names the class and the hazard (\"silent wrong output\") and names the next wider class. The record shows no two independent search techniques, no account of what each found, and no statement on whether a runner check pins the reported behaviour.\n- **3.2 Build the net: Yes.** A rule is written in the project's static analyser, which reads code. It is a detector, not a test.\n- **3.3 Prove the net: Yes.** Commit B carries the rule and a fixture, with the originating instance present, and CI is red on the rule. The fix follows in commit C, and both commits survive the merge. The description does not say how the fixture was excluded from the enforced run, which matters under 3.5 but does not decide 3.3.\n- **3.4 Sweep, then fix every instance: No.** Two of the three other instances were fixed, but `Report::render()` was left with an inline `// ignore` comment, and the spec does not allow suppression at the call site as a fix. The count of five also includes the fixture, which the sweep excludes, so there are four real instances. No independent corroboration of the count is recorded.\n- **3.5 Enforce permanently and block: No.** The rule fails rather than warns, and commit E shows the full quality invocation run locally and passing. The third condition fails, though: the green run depends on a suppression that no recorded decision covers. Clause 3.5 says such a rule does not conform, \"since the checks are then passing around the [Instance] rather than enforcing against it.\" The project has a named owner, so the default-owner paragraph does not apply.\n- **3.6 Terse message, resolving docs: No.** The one-line message carries the identifier, file, line, and URL, so the message is fine. The documentation is on the company wiki, which is not versioned with the rule. Section 3.6: \"A link into a third-party wiki or a general article does not [Conform], because neither can be relied upon to still describe this [Rule].\"\n\n**Remediation conforms under section 7: No.** Clauses 3.4, 3.5, and 3.6 fail, and 3.1 is only partial. The fixture also describes no reviewer reproduction, which section 7 requires for a verdict.\n\n**Defence alone conforms: No.** Section 7 requires clauses 3.1, 3.2, 3.3, 3.5, and 3.6. 3.1 is partial, 3.5 fails, and 3.6 fails. The 3.6 failure is enough by itself, because the documentation is not versioned with the rule.\n\n**Decision that was not the practitioner's: the inline suppression on `Report::render()`, and leaving that instance unfixed.** Section 4 places \"Suppressing an [Instance]\" and \"Accepting a known [Instance] as unfixed for any reason\" with whoever owns the codebase. The commit note says the agent \"did not raise it with anyone,\" so the owner never saw it. A lesser concern: the reason for not building the wider class, \"most are legitimate,\" is a judgement about whether the hazard is present. Clause 3.1's own worked example treats that as the owner's to settle, and the fixture does not show the owner was consulted.\n\n## 4. Confusion\n\n- Section 7, the Defence paragraph, says a Defence conforms if it \"satisfies clauses 3.1, 3.2, 3.3, 3.5 and 3.6\" and then describes 3.5 only as \"it fails rather than warns\". Clause 3.5 itself has three conditions, including that every suppression is covered by a recorded decision.\n- Section 5 says \"a project whose checks run in another order is a project that does not [Conform]\". Section 7 says of a project: \"Nothing here constrains how the project runs its checks or what it does when they fail.\"\n- Clause 3.1: \"Both bounds measure breadth within one level; the level itself is a separate question, answered against the report.\"\n- Clause 3.4: \"and the scale at which that becomes true is tremendous.\"","ttft_ms":57752,"type":"result","duration_ms":65194,"uuid":"a6c57a45-d2eb-4f98-8e6d-0bfe790b038b","ttft_stream_ms":1259,"time_to_request_ms":504,"first_content_frame_ms":1260,"queued_turn_count":0,"result_index":0}

reader-4

{"duration_api_ms":52649,"stop_reason":"end_turn","session_id":"6eeb1a11-f86d-413c-8af2-c74ddc7d239b","total_cost_usd":0.011988229999999999,"usage":{"input_tokens":2,"cache_creation_input_tokens":29130,"cache_read_input_tokens":2153,"output_tokens":12281,"output_tokens_details":{"thinking_tokens":10162},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29130,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":12281,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29130,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29130},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":12281,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29130,"webSearchRequests":0,"costUSD":0.011988229999999999,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":10162,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Do not fix a defect the moment you see it. First name the class of pattern it is an instance of, and state the hazard in one sentence.\n- Attempt to express the class as a rule in a detector, rather than deciding beforehand that no pattern exists. If you cannot, record one sentence naming two techniques tried, then fix the defect conventionally.\n- Search for other instances by at least two independent techniques before running the rule, and record what each found.\n- Build a blocking rule with terse documentation shipped with the project. Prove it fires in its own commit, sweep the whole codebase, record the count, and fix every instance without suppressing them.\n- Make the rule permanent through the project's own entry point. Suppressions, baselines and leaving instances unfixed belong to the owner, not the practitioner.\n- Record any defect you defer in the project's recorded decisions, then fix the original defect with a test that reproduces it.\n\n## 2. Load-bearing statements\n\n**2a. What comes before the fix, and in what order.** The practitioner names the class, runs two independent searches, builds the rule, proves it fires, sweeps and fixes every instance, enforces the rule, and only then fixes the original defect with a reproduction test.\n\n- §3.1: \"The [Practitioner] MUST first establish what [Class] the [Defect] belongs to.\"\n- §3.3: \"A new [Rule] MUST be proven to fire before it is trusted.\"\n- §3.4: \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\"\n- Appendix A: \"Only then fix the original [Defect] in the normal way, with a test that reproduces it.\"\n\n**2b. Who may adopt a baseline or suppress an instance, and may an agent do so alone?** The owner, who is always a human. An agent may not.\n\n- §1 (Owner): \"Always a human.\"\n- §4: \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\"\n- §4: \"An [Exception] exists only where a human has discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own creates one.\"\n\n**2c. What it means for a remediation to conform, and what the verdict must rest on.** A remediation conforms when all six clauses of section 3 were followed for that defect, and the verdict rests on reproduction, not on the report.\n\n- §7: \"A **[Remediation]** [Conforms] if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\"\n- §7: \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer reruns the [Defence] red at the commit that introduced it and green at the final commit, through the project's own entry point for accepting changes, and reruns the originating symptom against the fix.\"\n\n## 3. Application\n\nEach clause is graded on the fixture as described.\n\n- **3.1 Attribute the defect to a class: Partial.** The class and the hazard sentence are named, and the next wider class is recorded with a reason. The two independent search techniques and what each found are not recorded, and the record does not say whether a runner check pins the reported behaviour. The rule's five findings came from running the rule, which is not a search technique.\n- **3.2 Build the net: Yes.** A rule was written in the project's static analyser, not as a test.\n- **3.3 Prove the net: Yes.** Commit B holds the rule and fixture with `Ledger::total()` still present, and CI is red on five findings. The red run is kept as its own commit, and the merge kept all five commits reachable. No narrowing is involved.\n- **3.4 Sweep, then fix every instance: No.** `Report::render()` was not fixed; it was suppressed with an inline ignore. The count was not corroborated by an independent search, and the record does not say which instances were fixed by hand and which by pattern.\n- **3.5 Enforce permanently and block: No.** The rule fails the build, but the green run depends on an inline suppression that no recorded decision covers. §3.5 says a green run depending on such a suppression does not conform.\n- **3.6 Terse message, real documentation: No.** The message is terse and carries the identifier, but the documentation is on the company wiki. §3.6 says \"A link into a third-party wiki or a general article does not\" conform, because it is not versioned with the rule.\n\n**Remediation conform under §7?** No. Clauses 3.4, 3.5 and 3.6 are not met, and 3.1 is only partly evidenced. The verdict would also need reproduction, and the PR offers only the practitioner's own report of commit E.\n\n**Does the defence alone conform?** No. §7 requires clauses 3.1, 3.2, 3.3, 3.5 and 3.6. The defence fails 3.5, because the suppression is not covered by a recorded decision, and fails 3.6, because the documentation is not shipped with the project. It also lacks the search evidence 3.1 requires.\n\n**Which decision was not the practitioner's?** Keeping `Report::render()` unfixed through an inline suppression. §4 says the owner decides \"Suppressing an [Instance]\" and \"Accepting a known [Instance] as unfixed for any reason.\" The \"rendering must never throw\" judgement is also a hazard judgement that §3.3 says goes to the owner when it is in doubt. Leaving the next wider rule unbuilt because \"most are legitimate\" falls under the owner-only column for 3.1. The project has a named owner, so the default-owner provision does not apply.\n\n## 4. Confusion\n\n- §1 (Owner): \"Where the project has recorded nobody, the [Owner] is whoever instructed the [Practitioner], and [Escalation] goes there.\"\n- §2 (Attempt rather than pre-judge): \"naming at least two independent techniques tried at expressing the [Rule], in the vocabulary clause 3.1 uses for a search\"\n- §3.1: \"Both bounds measure breadth within one level; the level itself is a separate question, answered against the report.\"\n- §4 (Leaving unbuilt the next wider Rule): \"it reaches no further than the one wider [Rule] already on the record.\"","ttft_ms":45350,"type":"result","duration_ms":53299,"uuid":"e456df28-8078-4ba3-9f14-835755c49efc","ttft_stream_ms":1434,"time_to_request_ms":640,"first_content_frame_ms":1435,"queued_turn_count":0,"result_index":0}

reader-5

{"duration_api_ms":47473,"stop_reason":"end_turn","session_id":"ed0087d0-ff05-45be-8cd1-d80eab0904f1","total_cost_usd":0.01141723,"usage":{"input_tokens":2,"cache_creation_input_tokens":29130,"cache_read_input_tokens":2153,"output_tokens":11139,"output_tokens_details":{"thinking_tokens":8889},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29130,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":11139,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29130,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29130},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":11139,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29130,"webSearchRequests":0,"costUSD":0.01141723,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":8889,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- When handed a defect, first name the class it belongs to and the hazard that class carries (3.1).\n- Search for other instances by at least two independent techniques, run before the rule is run, until the last technique adds nothing new (3.1).\n- Express the class as a blocking rule in a detector that reads code, not as a test (3.2).\n- Prove the rule fires, in its own commit, while the originating instance is still present (3.3).\n- Run the rule across the whole codebase, record the count, and fix every instance. Baselining, suppressing, or leaving an instance unfixed belongs to the owner (3.4, 4).\n- Make the rule permanent and failing, run through the project's entry point, with a terse message and a stable identifier that resolves to documentation versioned with the rule (3.5, 3.6).\n\nThen fix the original defect with a reproduction test (section 6).\n\n## 2. Load-bearing statements\n\n**2a. What comes before the fix, and in what order.**\nThe practitioner classifies the defect, builds and proves the rule, and sweeps the codebase before fixing anything. The order is classify, build, prove, sweep and count, then fix every instance, then enforce and document.\n\n- \"the [Practitioner] MUST first establish what [Class] the [Defect] belongs to.\" (3.1)\n- \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\" (2)\n- \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\" (3.4)\n\n**2b. Who may adopt a baseline or suppress an instance, and may an agent do so alone?**\nThe owner, who is always a human, may. An agent may not, on its own authority.\n\n- \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\" (4)\n- \"An [Exception] exists only where a human has discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own creates one.\" (4)\n- \"**Owner**: … Always a human.\" (1)\n\n**2c. What it means for a remediation to conform, and what the verdict must rest on.**\nA remediation conforms when all six clauses of section 3 were followed for that defect. The verdict must rest on reproduction, not on the written report.\n\n- \"A **remediation [Conforms]** if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\" (7)\n- \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report.\" (7)\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n|---|---|---|\n| 3.1 Attribute | Partial | Commit A names the class and the hazard (\"silent wrong output\") and names the wider class not built. But the PR records no two search techniques and no finding from either. It also does not say whether a runner check pins the reported behaviour. The reason given for not building the wider rule (\"most … are legitimate\") is a hazard judgement, not a sentence confirmed by search. |\n| 3.2 Build the net | Yes | Commit B adds a rule in the project's static analyser, which reads code. |\n| 3.3 Prove the net | Yes | Commit B holds the rule, the fixture, and a red CI run on the originating `Ledger::total()` instance. The fix follows in commit C. All five commits remain reachable after the merge commit. |\n| 3.4 Sweep | No | The CI run found four real instances, and the count is not recorded or corroborated by an independent search. `Report::render()` was left unfixed behind an inline ignore, which 3.4 forbids. The PR also does not say the sweep covered all first-party source. |\n| 3.5 Enforce | No | The rule is blocking and was run through the project's own invocation (commit E). But the green run depends on an inline ignore that no recorded decision covers, and 3.5 says a green run that depends on such a suppression does not conform. |\n| 3.6 Message | No | The message is terse and carries the identifier `proj.caught-then-used`, and the documentation states the propagating construction. But the docs are on a company wiki, not committed with the rule, so they are not versioned as one artefact with it. |\n\n**Does the remediation conform under section 7?** No. Clauses 3.4, 3.5 and 3.6 fail outright, and 3.1 is only partial. Section 7 requires all six to have been followed.\n\n**Does the defence alone conform?** No. A defence must satisfy 3.1, 3.2, 3.3, 3.5 and 3.6. 3.2 and 3.3 are met. 3.1 is only partial. 3.5 fails because the suppression is uncovered by a recorded decision. 3.6 fails because the documentation is not versioned with the rule.\n\n**Which decision was not the practitioner's?** Suppressing `Report::render()` with an inline `// ignore proj.caught-then-used`. Section 4 lists \"Suppressing an [Instance]\" and \"Accepting a known [Instance] as unfixed for any reason\" as decisions that MUST go to the owner. The project has a named owner, so the default-owner provision does not apply. The practitioner should have escalated the case and continued with the rest of the work. Commit C instead records the ignore with a note and says it was not raised with anyone.\n\nA secondary point: leaving the wider rule unbuilt because \"most of the codebase's catches are legitimate\" is a hazard judgement that no search confirmed. Section 4 places the decision not to build a wider rule with the owner where the reason is not that the hazard cannot arise. I read this as a second decision the practitioner was not positioned to take alone, though it is less clear-cut than the suppression.\n\n## 4. Confusion\n\nOn my one reading, these passages were hard to parse:\n\n- Section 2, \"Attempt rather than pre-judge\": \"naming at least two independent techniques tried at expressing the [Rule], in the vocabulary clause 3.1 uses for a search\". Techniques \"for expressing the Rule\" and techniques \"for a search\" are described as the same thing, while 3.1 says \"The [Rule] itself is never one of the techniques.\"\n- Section 3.3, Part B: \"a [Narrowing] that excludes more code than the [Rule] still covers MUST be reported to the [Owner] as if it were a [Suppression].\" I could not tell what \"excludes more code than the [Rule] still covers\" means as a condition.\n- Section 3.4: \"the scale at which that becomes true is tremendous.\" I could not tell what scale is meant, or what \"that\" refers to.","ttft_ms":39895,"type":"result","duration_ms":48095,"uuid":"8e210148-0aa3-4df8-afb5-ef4645c10963","ttft_stream_ms":1408,"time_to_request_ms":642,"first_content_frame_ms":1408,"queued_turn_count":0,"result_index":0}

@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 088: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

The exact prompt every reader received, part 1 of 2

===== FILE: QUESTIONS.md =====
# Questions for a cold reader of SPEC.md

You have been given `SPEC.md` and the fixture `fixtures/remediation-ledger.md`, and nothing else.
Read the document once, then answer every question below in order. Quote the document where a
question asks for a quotation. Do not consult anything outside what you were given.

## 1. Restatement

In your own words and in at most six bullets, what does this document require a practitioner to
do when they are handed a defect?

## 2. Load-bearing statements

Answer each part of each question in a sentence or two, then quote the sentence or sentences of
the document you are relying on.

- 2a. What does a practitioner do before fixing the defect they were handed, and in what order?
- 2b. Who may adopt a baseline or suppress an instance, and may an agent do so on its own
  authority?
- 2c. What does it mean for a remediation to conform, and on what must a reviewer's verdict on
  it rest?

## 3. Application

Grade the ledger remediation, as described in the fixture, against each of the six clauses of
section [3](../../SPEC.md#3-the-method): [3.1](../../SPEC.md#31-attribute-the-defect-to-a-class), [3.2](../../SPEC.md#32-build-the-net), [3.3](../../SPEC.md#33-prove-the-net-by-making-the-rule-fire), [3.4](../../SPEC.md#34-sweep-the-codebase-then-fix-every-instance), [3.5](../../SPEC.md#35-enforce-permanently-and-block), [3.6](../../SPEC.md#36-make-the-failure-message-terse-and-point-it-at-real-documentation). For each give Yes, Partial or No and one line of
reasoning. Then state whether the remediation conforms under section [7](../../SPEC.md#7-conformance) and why, whether the defence
alone conforms and why, and which decision in the fixture, if any, section [4](../../SPEC.md#4-authority-which-decisions-belong-to-whom) says was not the
practitioner's to take.

## 4. Confusion

Quote any passage you found confusing on your one reading, with its clause number. Do not say what
would make it clearer and do not propose any change. Write "none" if there was none.

===== FILE: SPEC.md =====
# Defence Before Fix: Method Specification

**Version**: 1.1.0, published 2026-10-02
**Companion to**: [the detector specification](DETECTOR-SPEC.md), version 1.0.0, and [the toolchain specification](TOOLING-SPEC.md), version 0.2.0
**Author**: [Joseph Edmonds](https://ltscommerce.dev), [Edmonds Commerce](https://edmondscommerce.co.uk)
**Coined**: 22 February 2026, in [the original article](https://ltscommerce.dev/articles/defence-before-fix-static-analysis)

> **Defence Before Fix** ([DBF]) is a phase that runs *before* a [Defect] is fixed. Rather than dropping
> straight into remediating the specific [Instance] in front of you, you first treat that [Instance]
> as evidence of a [Class], and you build the automated [Defence] that detects every occurrence of
> that [Class] across the whole codebase. The [Defence] is only trusted once it has been seen to
> fire.

The [Defect] is what makes this possible. It is a real, confirmed, impactful example of a harmful
pattern, which is precisely the raw material a good custom [Rule] needs and which speculative
[Rules] never have. Every [Defect] is therefore an opportunity to extend the codebase's permanent
defensive [Coverage], and that opportunity exists only in the window before the fix.

Defence Before Fix does not replace test-driven development. The specific [Defect] is still
reproduced with a test and proven fixed, exactly as normal. The two operate at different levels:
TDD addresses the [Instance], Defence Before Fix addresses the [Class].

Not to be confused with *Defence in depth*, which is a security term meaning something else
entirely. The [Defence] here comes before the fix in time, not in layers.

US spelling: **Defense Before Fix**. Abbreviated [DBF] throughout.

---

## Status of this document

This is version 1.1.0 of the specification. It is normative: section 3 defines the method,
section 4 states who decides what, and section 7 defines what [Conformance] means and who may claim
it.

**This document is the source of truth for what the method is.** Where anything else describing
Defence Before Fix disagrees with it, including the article in which the term was first published,
this document is correct.

Who coined the term, when it was first published, and what is and is not being claimed are recorded
separately in [provenance](PROVENANCE.md), which is meta information about this specification
rather than part of it. A short introduction to the method is in [the primer](PRIMER.md).

Changes are recorded in the changelog at the end.

## 1. Terminology

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in
RFC 2119.

#### DBF

The acronym for Defence Before Fix, used as shorthand for this method and for anything built under it. A [DBF] [Rule] is a [Rule] as this specification defines it; a [DBF] [Toolchain] is one that [Conforms] to the [Toolchain] specification; a [DBF] project is one that [Conforms] to section 7.

#### Defect

Any observed problem worth acting on: a bug, a code review finding, a performance observation, an incident, an inconsistency. The method does not care which.

#### Class

The pattern, style, idiom or configuration that permitted the [Defect], expressed generally enough that other occurrences of it are also [Defects] or latent [Defects].

#### Instance

One occurrence of a [Class]. The reported [Defect] is one [Instance]; there are usually others.

#### Hazard

The harm a [Class] causes, which is **not necessarily a failure**. It may be a failure, but it may equally be error hiding, or something merely sloppy that makes the code harder to reason about or to change safely.

#### Detector

A tool that reads code without executing it and reports occurrences of a pattern. A [Detector] is not a [Runner]. What a [Detector] must offer so that a [Practitioner] can write, prove, run and resolve a [Rule] in it is specified in the [detector specification](DETECTOR-SPEC.md).

#### Runner

A tool that **executes** code and reports what happened: a test [Runner], a compilation, a benchmark, a smoke check. A [Runner] answers questions about behaviour at one moment, on the paths it happened to exercise; a [Detector] answers questions about the text of the code, everywhere it exists. Detection under this method MUST come from a [Detector], for that reason.

#### Rule

One pattern definition within a [Detector].

#### Defence

A **[Blocking]** [Rule] together with the supporting documentation that clarifies it and clearly signposts best practice. A [Rule] without its documentation is not a [Defence], and neither is a [Rule] that only warns.

#### Coverage

The accumulated set of [Defences] a project has built. Every [Defect] handled under this method extends it.

#### Remediation docs

The fuller documentation a failure [Message] points to, explaining what a [Rule] is about, why it exists and how to fix a violation correctly.

#### Blocking

A [Rule] is [Blocking] when its firing prevents a change from being accepted by whatever the project uses to accept changes. A report that does not block is a **[Warning]**, and a [Rule] that only warns is not a [Defence].

#### Warning

A report that does not block. A [Rule] that only warns is not a [Defence].

#### Message

The text a [Rule] prints when it fires. Terse, and carries the [Rule]'s [Identifier].

#### Identifier

The stable string a [Rule] reports with, by which its [Remediation docs] are found. Survives renames of the [Rule].

#### False positive

A report on code that does not carry the [Hazard].

#### Narrowing

Reducing a [Rule]'s scope so it stops reporting code that does not carry the [Hazard]. [Narrowing] that excludes code which does carry the [Hazard] is a [Suppression].

#### Suppression

Any means of preventing a [Rule] from reporting an [Instance] that carries the [Hazard]: an ignore comment, an ignore entry, a [Baseline], or a [Narrowing] that excludes it.

#### Baseline

A recorded set of pre-existing [Instances] a [Rule] is configured not to report. A [Suppression] in bulk.

#### Exception

A recorded [Owner] decision that a specific [Instance] or scope is excluded from a [Defence]. The only legitimate form of [Suppression]. This document never uses the word in its programming-language sense.

#### Sweep

Running a [Rule] across the whole codebase to enumerate every [Instance].

#### Fixture

Code written to carry the [Hazard] deliberately, so a [Rule] can be proven to fire when the codebase has no [Instance] to prove it on.

#### Calibration

A project-level ruling on a threshold or parameter this document leaves open.

#### Practitioner

Whoever is doing the work of this method on a given [Defect], human or [Agent]. Decides how the [Defence] is built. Has no authority over [Exceptions].

#### Agent

A [Practitioner] that is an automated system. Everything said of a [Practitioner] applies to an [Agent]; section 8 exists because of what is additionally true of one.

#### Owner

Whoever holds authority over a project's [Exceptions] and [Calibrations]. Always a human. The project decides who, and clause 8.7 requires the answer to be findable. Where the project has recorded nobody, the [Owner] is whoever instructed the [Practitioner], and [Escalation] goes there. One limit applies to that default. The default [Owner] is usually the person under the same deadline as the [Practitioner], so a decision they take under section 4 to keep an [Instance] unfixed, [Suppress], [Baseline] or remove a [Rule] MUST be recorded in the project record with its justification. That record MUST mark the decision as taken by a default [Owner] and state why nobody better placed was reachable, where the next reader can see who decided and why. Such a decision SHOULD be revisited by someone not under that deadline before the next release. A project that wants the separation section 4 describes names an [Owner]; the default exists so work is not blocked, not to certify that a second judgement was applied.

#### Escalation

Referring a decision to the [Owner], whilst continuing with everything that does not depend on it.

#### Toolchain

Whatever a project assembles to run its checks through: its [Detectors] and [Runners], and the parts around them that route, list, record and resolve, from third-party, first-party and project-level parts in any combination. It is measured at the project level. What a [Toolchain] must offer beyond what each [Detector] in it offers is specified in the [toolchain specification](TOOLING-SPEC.md).

#### Conform

To satisfy every MUST of the relevant section 7 level, or of the companion specification being claimed. Partial satisfaction is not [Conformance].

## 2. When the method applies

Defence Before Fix applies to a [Defect] when that [Defect] can be attributed to a pattern, style,
idiom or configuration that a [Detector] can be made to recognise.

When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is
fixed. This is the substance of the method rather than a preliminary to it.

When it cannot, the [Defect] is outside the method's scope and is fixed conventionally. Not every
[Defect] is an [Instance] of a detectable pattern, and forcing a [Rule] where no pattern exists
produces [Detectors] that fire on innocent code, which is worse than having no [Detector].

**Attempt rather than pre-judge.** The [Practitioner] MUST attempt to express the [Class] as a [Rule]
rather than deciding in advance whether it is expressible. Failing to write a [Rule] that satisfies
clause 3.1's bounds is itself the evidence that the [Defect] is out of scope, and it is cheaper and
more reliable than a judgement made before trying. The conclusion that no pattern exists MUST be
stated in one sentence, naming at least two independent techniques tried at expressing the
[Rule], in the vocabulary clause 3.1 uses for a search, and recorded where the project's
other decisions are enumerable under clause 8.7, not only with the fix. It is the cheapest way
out of the method and the only one that would otherwise leave no trace; enumerating it is how an
[Owner] sees an [Agent] routing around the method, and naming the techniques is what lets
them judge whether the attempt was one.

There are three ways out of the method and they are recorded differently. No pattern exists: the
[Defect] is out of scope under this section, and only the sentence above is recorded. A pattern exists but the
language has no extensible [Detector] and a bespoke one is not practical: a [Toolchain] gap
under clause 3.2. A pattern exists, a
[Detector] exists, but the [Toolchain] lacks a mechanism the [toolchain specification](TOOLING-SPEC.md) requires: a
mechanism gap under clause 3.2, and the [Rule] is still built. The attempt that separates the first
from the second is complete when the [Practitioner] has checked the [Detectors] the project already
runs and the language's own [Detector] ecosystem for an extension point, and found none.

**Deferring the fix is not a fourth.** A [Defect] whose fix is postponed has not left the
method; the attempt this section requires is owed when the fix is taken up. A
[Practitioner] who finds a [Defect] and does not fix it now MUST record it where the project's
other decisions are enumerable under clause 8.7, naming the [Class] where one is already
apparent. Whether it stays unfixed is the [Owner]'s decision under section 4; the record is what
puts it in front of them. Reporting it in conversation does not satisfy this: the conversation
ends, and with it the only trace that the [Defect] was ever seen.

**Why**: each of the three ways out costs the [Practitioner] something to take and leaves
something an [Owner] can read: a recorded sentence naming two techniques tried, or a [Toolchain]
gap. Deferral costs nothing, and without this record it would leave nothing, which makes it
cheaper still than the sentence above and unseen from outside. A [Defect] mentioned once and
never written down cannot be told apart from a [Defect] nobody found, so neither the [Class] nor
the [Defence] it implies is ever reached.

## 3. The method

Six clauses, in order. In brief, before the detail:

| Clause | The [Practitioner] MUST                                                                                     | The record shows                                                                                                                                     | Goes to the [Owner] under section 4                                                                  |
| ------ | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| 3.1    | Name the [Class] the [Defect] belongs to, bounded both ways, after an independent search                    | The [Class], the [Hazard] sentence, the two search techniques, the next wider [Rule] not built, whether a [Runner] check pins the reported behaviour | Whether the next wider [Rule] is built, and whether the [Rule] stays narrower than the search showed |
| 3.2    | Express the [Class] as a [Rule] in a [Detector], never a test                                               | The [Rule], and any [Toolchain] gap that stopped a bespoke one                                                                                       | Whether a [Class] that could be defended is left undefended                                          |
| 3.3    | Make the [Rule] fire, on the [Instance] or a [Fixture], in a commit of its own                              | The red run, and the sentence behind every [Narrowing]                                                                                               | Any exclusion whose sentence cannot be written: that is [Suppression]                                |
| 3.4    | [Sweep] the whole codebase, record the count, then fix every [Instance]                                     | The count, corroborated, and what was fixed by hand or by pattern                                                                                    | Any [Instance] left unfixed, and any [Baseline]                                                      |
| 3.5    | Make the [Rule] permanent and [Blocking] in the project's own checks                                        | The green run through the project's entry point, and the recorded decision behind any [Suppression]                                                  | Removing or disabling the [Rule], and any [Suppression] added to it                                  |
| 3.6    | Print a terse [Message] with a stable [Identifier] that resolves to documentation versioned with the [Rule] | The [Remediation docs]                                                                                                                               | Nothing                                                                                              |

The fourth column is what an [Agent] or other [Practitioner] MUST NOT decide alone, clause by
clause; section 4 states the full list and governs where the two differ.

**Three of them turn on a judgement this specification deliberately does not close**: whether code
carries the [Hazard] (3.1, 3.3), whether a search was comprehensive (3.1, 3.4), and how broadly to
draw the [Class] (3.1). Any threshold given here would be calibrated to one codebase, one team and
one generation of [Toolchain], and would be wrong everywhere else.

**Those judgements are settled at project level, not in this document.** A project fixes its own
thresholds and parameters, in its configuration or in its [Toolchain]'s defaults, and where a case is
genuinely ambiguous it is raised, discussed and written down so the next person inherits the answer
rather than the argument. A project's accumulated decisions become part of its [Coverage] in the same
way its [Rules] do.

**A project MUST make those decisions discoverable**, by the same means as its [Defences], so that a
[Practitioner] arriving cold can find what has already been settled instead of guessing at it or
re-opening it. The burden is the project's, not the reader's: a decision that cannot be found by the
means clause 8.7 requires is, for the [Practitioner]'s purposes, a decision the project has not
recorded, and the paragraph below applies. The search is therefore bounded: enumerate the project's
[Defences] as clause 8.5 requires them to be enumerable, and whatever is reachable from there is the
record. Nothing further is owed before concluding that nothing is recorded.

**Where the project has recorded nothing, the [Practitioner] is not blocked.** Proceed on the
examples below, **state the [Calibration] assumed**, and record it with the [Remediation docs]
for the [Defence] being built, which clause 3.6 already requires to exist, to ship with the project
and to be reachable by a stable [Identifier]. That statement becomes the project's first recorded
decision on the point, and the next [Practitioner] inherits it.

A project MAY of course keep these decisions somewhere else and say so. **The default above exists
so that a [Practitioner] who finds no project convention still has one**, because "the project
decides" and "the project has decided nothing" would otherwise combine into a deadlock. What is
never acceptable is assuming silently, since a [Calibration] nobody knows was chosen cannot be
corrected.

Worked examples of the three, to calibrate against:

| Judgement         | Too little                                                                                         | Too much                                                                                   |
| ----------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| **[Hazard]**      | Only counting things that crash. Error hiding and code nobody can safely change are [Hazards] too. | Counting every stylistic preference, so the [Rule] defends taste rather than the codebase. |
| **Comprehensive** | One text search for the exact token from the reported [Defect], then stopping.                     | Auditing the whole system by hand before writing a [Rule] at all.                          |
| **[Class]**       | A [Rule] matching the variable name in the original bug report.                                    | A [Rule] matching every use of the language feature the bug happened to involve.           |

### 3.1 Attribute the defect to a class

The [Practitioner] MUST first establish what [Class] the [Defect] belongs to. The question is not
"what went wrong here" but "what kind of thing is this an [Instance] of".

**In order, this clause is five steps**, each detailed below:

1. Name the [Class] as a pattern, and write the one sentence that says what [Hazard] it carries.
2. Search the codebase for other [Instances] by at least two independent techniques, chosen and
   run **before** the [Rule] is run against the codebase, until the last technique adds nothing.
3. Widen the [Class] if the search found [Instances] the [Rule] would miss; narrow it if the
   [Rule] would match code that does not carry the [Hazard].
4. Where the [Class] stays at the reported [Instance], name the next wider [Rule] and why it
   was not built.
5. Record all of that, the five things listed at the end of this clause, before clause 3.2.

A worked example. The [Defect]: a currency conversion's error was caught and its empty result
used as a total. Step 1 names the [Class] "a caught error whose call's result is then used as if
the call had succeeded", with the [Hazard] "silent wrong output". Step 2 searches twice: a text
search for every catch in the codebase, read for whether the result is used afterwards, and a
reading of every caller of the fallible conversion and payment functions; the second finds two
[Instances] the first missed, so a third technique, asking the team which other calls fail
quietly, is run and finds nothing new. Step 3 keeps the [Class] as named because the [Rule]
would catch all four. Step 4 names the wider [Rule], "any caught error that is not rethrown or
reported", and records that it was not built because most of the codebase's catches are
legitimate and the [Owner] has not decided whether that is a [Hazard]. Step 5 writes the four
things down.

A [Defect] usually belongs to several overlapping [Classes] at different levels of abstraction, and
choosing between them determines the [Rule], the [Instance] count and the scope of the remediation.
Two bounds apply, and both are checkable during the work rather than matters of taste:

- **Lower bound.** If the [Rule] catches only the originating [Instance], the [Class] is probably drawn
  too narrowly and MUST be widened, unless the independent search below has found no other
  [Instance] and the [Practitioner] records that the [Class] is genuinely singular today. A
  [Rule] that matches exactly one thing is an [Instance] [Detector] wearing a [Rule]'s clothes.
  A [Rule] drawn to the exact value or name found in the [Defect] is the same [Narrowing] however
  it is spelt, and where it stays because the search found nothing wider, the record MUST also name the next
  wider [Rule], the one with the [Instance]'s distinguishing detail dropped, and state why it was
  rejected. That rejection is tested as a [Narrowing] is under clause 3.3: a sentence stating why the
  [Hazard] cannot arise in the code the wider [Rule] would add, confirmed by search, with uncertainty
  routed upwards under section 4.
- **Upper bound.** If the [Rule] matches code that does not carry the [Hazard], the [Class] is drawn
  too broadly and MUST be narrowed. [False positives] destroy a [Defence]'s credibility faster than a
  missing [Rule] does. One report on code that does not carry the [Hazard] is enough; there is no
  tolerated rate.

**Both bounds measure breadth within one level; the level itself is a separate question, answered
against the report.**
Where the [Defect] was reported as a behaviour, a check that reported clean having run nothing, a
process that stopped before its work, the [Class] a [Detector] can read usually sits one level
below that [Hazard]: the mechanism of this [Instance], not the failure the report opens with.
That [Class] is the right one to build, because no [Detector] reads "this check still produces
its signal". It is not the whole answer. The record MUST state whether the reported behaviour is also
pinned by a check a [Runner] executes, under section 6, either one added with this remediation or
an existing one named, and where neither is offered, why not. A [Class] drawn at the mechanism with
the behaviour left unpinned defends the [Instance] thoroughly and the report not at all.

**Resolve the lower bound by searching independently, not by judgement.** The [Rule] is not the only
way to find [Instances], and it is the least trustworthy one whilst it is still unproven. Search for
other [Instances] by other means, whether that is a text search, reading the code, or asking someone
who knows the system, and, once the [Rule] exists under clause 3.2, confirm it catches what those
searches found. That confirmation is not one of the techniques.

That search MUST be a comprehensive one, carried out by a person, model or [Agent] competent to
carry it out. No fixed technique is prescribed, because what is comprehensive depends entirely on
the [Class] and the codebase. What is not acceptable is a cursory look that exists to discharge the
requirement, since this search is what the single-[Instance] conclusion and the [Sweep] count both
rest on, and a bad search validates a bad [Rule] silently.

**The stopping criterion is saturation, not effort.** Use at least two independent techniques, and
the search is complete when the last technique added found nothing the earlier ones had missed. A
second technique that turns up new [Instances] means a third is owed; a second that turns up nothing
new means the search is done. That is a test the [Practitioner] can apply and record without a
threshold, and it is the standard clause 3.4 refers back to.

Two techniques are independent when they would miss different things. A text search for the token
and a reading of the code paths that consume the value are independent; two text searches for two
spellings of the same token are one technique; asking someone who knows the system is a third. For a
[Class] defined by how code is spelt, writing the idiom the other ways it is commonly spelt and searching
the codebase for each of them is a technique, because a search for one spelling misses every other;
whether the [Rule] catches those spellings is the confirmation step, not the technique. The
[Rule] itself is never one of the techniques, because the search exists to check the [Rule].
A clean run of the [Rule] is therefore not evidence of saturation either: "the [Rule] found nothing
the reading had missed" is one technique and a [Rule] run, not two techniques, and the stopping
criterion cannot be applied to it.
The record MUST name both techniques, chosen before the [Rule] is run against the codebase, and
MUST state what each found that the other could not have checked. A technique counts only if it does
not invoke the [Detector] under proof in any configuration and would exist unchanged had the
[Rule] never been written; a [Sweep]-shaped run of the [Rule], however comprehensive it looks, is
never one of the two. Where a later technique shows that two earlier checks shared a blind spot, they
were one technique, and a third is owed.

That turns the single-[Instance] case into something checkable rather than a matter of taste. If an
independent search finds [Instances] the [Rule] missed, the [Class] was drawn too narrowly and the [Rule]
MUST be widened until it catches them. Fixing those [Instances] by hand does not discharge the
widening: they are still [Instances] the [Rule] missed, and where the [Practitioner] leaves the
[Rule] unwidened because the wider check is harder to build without [False positives], that is
the [Owner]'s decision under section 4 and is recorded as one. If an independent search finds nothing
the [Rule] did not already have, then one [Instance] is a reasonable conclusion rather than an
assumption, and the [Rule] is correct as written.

**What this clause leaves on the record**, before clause 3.2 begins: the [Class], spelt as a
pattern; the [Hazard] sentence; the two search techniques and what each found; the next wider
[Rule] that was not built, with the reason; and, where the [Defect] was reported as a behaviour,
whether a check a [Runner] executes pins that behaviour, or why not. A record missing any of the
five has not finished this clause.

**Why**: the [Class] is the unit of work. Everything downstream operates on it, so an error here
wastes all the effort that follows.

### 3.2 Build the net

The [Practitioner] MUST express the [Class] as a [Rule] in a [Detector], and the [Detector] MUST read code
rather than execute it.

A test MUST NOT serve as the [Detector]. A test proves that one input produces one wrong output; a
[Rule] finds the pattern wherever it occurs, including in code nobody thought to test.

Where an off-the-shelf [Rule] or a tightening of existing configuration genuinely detects the
[Class], using it [Conforms]. However, a [Toolchain] that supports only its own built-in [Rules] cannot
support the method in general, so **a [Conforming] [Toolchain] MUST allow bespoke custom [Rules]**.
The [Rules] that matter most are tightly coupled to the project and carry project-specific
knowledge, both in the pattern they match and, importantly, in the [Message] they emit.

Any tool that reads code and reports pattern matches qualifies, whatever its category. Static
[Detectors] such as PHPStan, ESLint, mypy and Clippy are the usual instruments; so are custom AST
walkers, architectural fitness checks, and [Detectors] that inspect a change before it lands. These
are examples rather than a permitted list, and a [Detector] is not disqualified for being unfamiliar.

Where the affected language has no extensible [Detector] available, the [Practitioner] MAY build a
bespoke one: a program of the project's own that reads code and reports matches of the [Class], run
through the project's own entry point like any other [Detector]. This is a last resort rather than
a default, because a [Detector] the ecosystem maintains is cheaper to keep and easier for the next
[Practitioner] to find, but a bespoke [Detector] is far better than none: the [Class] is defended,
and every clause of this method applies to the bespoke [Detector] unchanged, including the proof in
3.3, the enforcement in 3.5 and the [Identifier] and documentation in 3.6.

Only where a bespoke [Detector] is not practical either is the [Class] undefended. That is a
**[Toolchain] gap and MUST be recorded as one**, with the reason a bespoke [Detector] was not
practical, rather than treated as the [Defect] being out of scope. The distinction matters, because
the first is a decision somebody can revisit and the second quietly disappears.

A gap is recorded in the project record where the [Toolchain] defines one, as the
[toolchain specification](TOOLING-SPEC.md) requires it to; only where it does not is the location the project's choice, and
then the only requirement is that somebody deciding what to invest in the [Toolchain] would find
it. A gap recorded where nobody looks has been forgotten with extra steps.

There are two kinds of gap and they lead to different work. The language having no extensible
[Detector] and no practical bespoke one is the gap above, and the [Class] waits for one. The project's [Toolchain]
lacking a mechanism the [toolchain specification](TOOLING-SPEC.md) requires, such as a proving harness or an
[Identifier] resolver, is a mechanism gap, and it does not put the [Class] out of reach: the
[Practitioner] builds the [Rule], uses the substitutes clauses 3.3 and 3.6 already allow, and records
the mechanism gap alongside it. A mechanism gap that is also a breach of the [toolchain specification](TOOLING-SPEC.md)'s
own obligations, such as a [Toolchain] that cannot run its [Defences] on its own source, is recorded
against the [Toolchain]'s claim under section 7 as well, because that is the fact its [Owner] needs.
Where that record lives is the [Toolchain]'s own [Conformance] declaration, under the
[toolchain specification](TOOLING-SPEC.md)'s clause 9.2, or the [detector specification](DETECTOR-SPEC.md)'s clause 8.1 for a [Detector]; a [Practitioner] who cannot write there reports it by the channel
section 4 names, as a blocked decision.

**What happens next is not waiting.** Once the gap is recorded, the [Defect] is fixed conventionally
under section 2, with its reproduction test, and the work moves on. What the record changes is the
[Class], not the [Defect]: the [Class] is known to be undefended, and the next person to see an
[Instance] of it finds a decision to revisit rather than nothing.

**Why**: this clause is what distinguishes the method. Detection happens at the level of the
[Class], using an artefact that can be pointed at the whole codebase, and it happens before
anything is fixed.

### 3.3 Prove the net by making the rule fire

A new [Rule] MUST be proven to fire before it is trusted. It is never the goal to write a [Rule] and
be instantly green.

**This clause has three parts.** Part A: the proof, a red run kept as a commit of its own. Part B:
[Narrowing], which is the [Practitioner]'s to decide only when they can write down why the
[Hazard] cannot arise in what is excluded, and is [Suppression] for the [Owner] otherwise. Part C:
proving a [Rule] when the pattern is absent from the codebase, and what a [Rule]'s own code owes.

**Part A: the proof.**

**Proving and sweeping are two questions, not necessarily two runs.** Proving asks "does this [Rule]
work at all", and the answer is pass or fail. Sweeping, in clause 3.4, asks "how much of this [Class]
is present", and the answer is a count. A single execution of the [Rule] answers both, and no second
run is required. What matters is that the two answers are not confused with one another: a large
count does not make a [Rule] more proven, and a [Rule] that fired does not tell you the [Sweep] is
complete.

**The proof MUST survive as a commit of its own.** The [Defence] is committed with the originating
[Instance] still present, and the fix is committed after it. That first commit is the red run: it is
what a reviewer under section 7 checks out to reproduce the proof, and it is the only record that the
[Rule] fired on real code rather than on a [Fixture] alone. Where the [Defence] and the fix share a
commit, the red run can only be reconstructed by hand-reverting lines the reviewer has to guess at,
and the proof rests on that guess.
Surviving as a commit means a fresh checkout of that commit, followed by the project's own declared
setup, reproduces the proof. Dependency installation and generation driven by files the commit does
carry, a manifest, a lockfile, are that setup. Anything else the proof depends on that version control
does not carry, an empty directory, an ignored file, an artefact placed by hand, is state the commit
does not contain, and a reviewer under section 7
reproduces from a fresh checkout rather than from the [Practitioner]'s working tree, so a
proof that passes only there does not survive.
Both commits MUST remain individually reachable in the history the reviewer inspects. A merge that
flattens them into one destroys the proof, so a project whose merge policy does that MUST keep the
[Defence] commit reachable by another recorded reference, a tag or the retained branch, or MUST NOT
claim the remediation [Conforms]. How the project merges is its own business under section 8;
what must survive the merge is not.

**Part B: [Narrowing].**

**The decision comes first, and it turns on one sentence.** Can the [Practitioner] write down why
the [Hazard] cannot arise in the code being excluded? If they can, the exclusion is a [Narrowing],
it is theirs to make, and they record that sentence. If they cannot, or are not sure, the exclusion
is a [Suppression] and belongs to the [Owner] under section 4. Doubt is [Suppression]. The test is
the [Hazard], never the count. Two exclusions of the same [Rule] show the difference:

| Excluded code                                                             | The sentence                                                                        | Which it is                             | Who decides    |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------- | -------------- |
| A catch in test helpers that asserts on the caught error and returns it   | "The result is the error itself, so it cannot be used as if the call had succeeded" | [Narrowing]                             | [Practitioner] |
| A catch in a nightly export, excluded because the export "hardly matters" | Cannot be written: the caught result is still used, so the [Hazard] is present      | [Suppression], referred under section 4 | [Owner]        |

The sequence is: write the sentence; confirm it by search, the way clause 3.1's search confirms
the [Class], so that the excluded code is looked at and not assumed; record both. A sentence
that cannot be confirmed is a sentence that cannot be written, and the exclusion goes to the
[Owner]. Ask, for every exclusion, which row it sits in:

- If the excluded code **carries the [Hazard]**, the exclusion is [Suppression] and is forbidden to
  the [Practitioner], however many or few [Instances] it removes.
- If the excluded code **does not carry the [Hazard]**, the exclusion is precision and is required
  by clause 3.1's upper bound.
- If the [Practitioner] **is not sure which**, the exclusion is [Suppression], and it goes to the
  [Owner] with the doubt stated.

A [Rule] MUST NOT be narrowed because its [Instance] count is uncomfortably high. A high count is a
finding about the codebase, not a [Defect] in the [Rule]. **Firing on more than the originating
[Defect] is success.** A [Rule] that catches the reported [Instance] and forty-nine others has done
exactly what it was built to do, and the forty-nine are the reason the method exists.

**Then the proof, which runs the other way round, and both ways.** Where the change under proof is
that the [Rule] should stop firing on code that does not carry the [Hazard], three things are shown:
the [Rule] firing on that code before the change, from a commit still reachable in history and to the
same standard as a new [Rule]'s red run; the [Rule] not firing on it afterwards; and a retained
[Fixture] on which it still fires afterwards, the case that motivated the [Rule]. Where the narrower
shape was chosen from the outset and no wider [Rule] was ever built, the before state is a [Fixture]
of the wider pattern the [Rule] does not catch, retained as the record of what was left out.

A [Narrowing] reduces the [Practitioner]'s own work, which is why it is not left to self-report
alone. The sentence is recorded with the [Narrowing] as part of the [Rule]'s [Remediation docs];
the search confirms it. Code a [Narrowing] excludes MUST be searched to the standard of clause
3.1, and an [Instance] that search finds there disproves the sentence and reverses the
[Narrowing]. Every [Narrowing] MUST be enumerable by the
same means as the project's [Exceptions], so the [Owner] sees them in one place, and a
[Narrowing] that excludes more code than the [Rule] still covers MUST be reported to the [Owner]
as if it were a [Suppression]. The sentence remains the test of whether an exclusion is honest;
the search and the listing are what make a fluent dishonest one visible.
Where the [Toolchain]'s own listing shows the [Narrowing] with its sentence, that listing is
the record, and no separate prose is owed for it.

The sentence may turn out to be wrong. The method does not require the [Practitioner] to be
infallible; it requires the reasoning to be written down where the next reader, or the next
[Defect], can test it. A [Narrowing] with a recorded reason is correctable. One without is
indistinguishable from a [Suppression], which is why the sentence is the test and a percentage is
not.

**Part C: proving when the pattern is absent, and the [Rule]'s own code.**

**Where the pattern is present, prove the [Rule] against it.** At minimum the [Rule] MUST detect the
originating [Defect]. Where the [Defect] was found in code review rather than in production, the
pattern exists in the branch under review, so the [Rule] is written and proven there before the fix
lands.

**Where the pattern is genuinely absent, prove the [Rule] another way.** A [Rule] may legitimately be
green against the codebase: the branch that carried the pattern may have been rejected and
orphaned, the [Instance] may already have been fixed, or the [Rule] may be a purely proactive [Defence]
against a pattern that might occur but does not yet. In each of these cases the [Rule] MUST still be
proven, using [Fixture] code that demonstrates the pattern the [Rule] is meant to catch. That [Fixture]
SHOULD be kept as the [Rule]'s own test rather than deleted, so the proof re-runs whenever the [Rule] runs
instead of expiring the moment it succeeds.

**A green run proves nothing unless the [Rule] was loaded.** Before treating any run as evidence,
confirm the [Rule] was active in it, by enumerating the [Defences] that run was configured with or by
seeing it fire on a [Fixture] in the same run. A [Toolchain] analysing its own source with a
configuration that omits its own bundled [Rules] reports clean on every one of them, and both the
[Practitioner] and a reviewer have taken that for a pass.

**A [Rule] about how code is written may match its own source.** A [Rule] that forbids a construct in
[Rule] code will contain that construct, because it has to look for it. That match is not an
[Instance]: the construct there is the [Rule]'s subject, not its use, so the [Hazard] is absent and
clause 3.1's upper bound requires the [Narrowing]. Exclude it as precisely as possible, write the
sentence, and keep a test that proves the exclusion reaches nothing else.

**[Rules] are software, and SHOULD be built test-first like any other software.** How practical
that is varies considerably between [Detectors], some of which offer purpose-built [Rule]-testing harnesses
and some of which offer nothing, so this is a best-effort requirement rather than an absolute one.
Where a [Detector] makes [Fixture]-based testing impractical, an acceptance or smoke check that
demonstrates the [Rule] firing is an acceptable substitute.

[Fixtures] are part of the [Rule] and are versioned with it, under the co-location requirement in
clause 3.6. A [Fixture] is input to the [Rule]'s test, not part of the code the [Defence] enforces:
the [Sweep] excludes it under clause 3.4, and the enforced run does not report it, so there is no
circularity in a [Fixture] that deliberately fails the [Rule] it proves. Writing the [Fixture] is the [Practitioner]'s own decision under section 4, not something
to refer upwards.

Where they sit within the repository is the project's business. **Absent a convention, put them
where the project's existing tests live, and where there are none, alongside the [Rule]** - then say
which was chosen, as with any other [Calibration] under section 3.

**Why**: proving is the [Rule]'s own validation. A [Rule] that does not catch the [Instance] that
prompted it does not detect the [Class] it claims to, and shipping it would add a [Defence] that defends nothing
whilst defending nothing. This is the clause most easily skipped, because a green run feels like
success and looks like a clean codebase.

### 3.4 Sweep the codebase, then fix every instance

The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance]
count before fixing anything, and MUST then fix every [Instance] found rather than only the one
that was reported.

**"Entire codebase" means everywhere the pattern can occur, and nowhere it cannot.** For most
[Classes] that is a single language, because the pattern is a feature of that language, and sweeping
unrelated languages would be theatre. What the clause forbids is stopping at the package,
component or service that happened to report the [Defect] when the pattern plainly reaches further.
The question is where the pattern can exist, never where the bug was found.

Whether vendored dependencies, generated code and test [Fixtures] are in scope is the project's
decision, and it SHOULD be a recorded one rather than an implicit one, because a [Class] that is
excluded silently is indistinguishable from a [Class] that was never swept. **Where a project has
recorded no such decision, [Sweep] all first-party source in every language where the pattern can
occur, and exclude generated code and vendored dependencies**, then record that as the decision.
First-party source is code the project maintains in its own repository; generated code is what a
build step produces from other source; vendored code is a dependency copied in rather than
installed. Modified vendored code is first-party, because the project now maintains it. The two
exclusions are not a judgement that such code carries no [Hazard]. Unmodified vendored code is
somebody else's project, and an [Instance] found in it is reported upstream rather than fixed
in place; generated code carries an [Instance] only because its generator does, and the generator
is the first-party source the [Sweep] covers. An [Instance] the [Practitioner] notices in either
is recorded as a known [Instance] under section 4, not ignored.
A [Rule]'s own [Fixtures] under clause 3.3 are never [Instances]: they carry the pattern
deliberately, as the proof, and the [Sweep] count excludes them.

**The count MUST be corroborated independently**, to the standard set out in clause 3.1: a
comprehensive search by someone competent to make it. A [Rule] that is too narrow produces a small
count and a clean-looking [Sweep], and nothing inside the [Rule] can reveal that.

Where the independent search finds [Instances] the [Rule] did not, **clause 3.1 governs: the [Rule] was
drawn too narrowly and MUST be widened until it catches them.** The two are not permitted to
disagree, and the search is the authority, not the [Rule].

**Each fix MUST address the [Hazard] rather than the [Rule].** Throw on the absent case, or propagate
the absence explicitly so the caller decides: whatever the correct behaviour turns out to be. What is
forbidden is a change that turns the [Rule] green whilst leaving the failure mode intact, and
suppressing the [Rule] at the call site, which is not a fix at all.

**Supplying a default is usually the [Hazard] in another form**, not a fix. A default makes the absent
case look present, and the failure moves downstream to where nobody expects it and nothing names it.
A default is a fix only where absence is a legitimate state whose meaning the code defines, and then
the default states that meaning rather than filling the gap with a placeholder. This is the same
test as clause 3.3's [Narrowing] sentence and carries the same discipline: the [Practitioner]
MUST write down, with the fix, what the absence means and why the default is that meaning, and
where the sentence cannot be written the absence is an error and MUST be treated as one.

**Repetition is not the problem.** Where every [Instance] genuinely has the same correct answer,
applying that answer to all of them is right, and the fact that it looks mechanical is not an
objection. The requirement is that each [Instance] was actually examined and the same answer was
genuinely correct for it, rather than assumed correct because it was correct elsewhere. Where one
change is applied across many [Instances], the [Practitioner] MUST record which were examined
individually and which received the change by pattern, and MUST examine a sample of the latter
large enough, given how much the surrounding code varies, that a wrong answer there would have
been seen. "All examined" with nothing behind it is a claim, not a record.

**Fix them all.** There is no count at which a [Practitioner] stops fixing. A [Practitioner]
stops only when the next [Instance] needs a decision they lack the authority to make, and section 4
says which decisions those are; a hundred [Instances], or a thousand, is work rather than a
decision. A [Baseline] of the existing [Instances], so that the [Rule] blocks only new ones, MAY
be adopted where fixing every [Instance] is genuinely unfeasible, and the scale at which that
becomes true is tremendous. **No threshold is given here deliberately.** Any figure would be
calibrated to the [Toolchain] of the moment rather than to anything durable, and the honest measure is
the size of the change rather than the hours it would take, since the hours depend entirely on what
is doing the work.

**Under AI-assisted development a [Baseline] is almost never the right answer.** The historical case
for baselining was the cost of human hours, and that cost has largely collapsed: an [Agent] can work
through hundreds of [Instances] at a price that made a [Baseline] unavoidable a few years ago. Reaching
for one now is usually a habit rather than a judgement. This specification stops short of
forbidding [Baselines] outright, and only just.

Adopting one is a decision for whoever owns the codebase, taken after fixing has genuinely been
attempted rather than on the strength of the [Instance] count alone. "Attempted" is the
[Owner]'s judgement on the [Practitioner]'s report, not a threshold the [Practitioner] applies:
the [Practitioner] fixes until stopped by a decision they cannot make, and reports what was fixed and
what remains. That report MUST state the count fixed, the count remaining, what stopped the fixing,
and what would have to be tried next, so the [Owner] judges a record and not an adjective. **A
[Practitioner] executing this method does not have that authority** - see section 4. A baselined project SHOULD reduce the
[Baseline] over time and MUST NOT allow it to grow.

**Why**: consistency across a codebase is fundamental, and the [Sweep] is where this method
delivers most of its value. In the published worked example the reported [Defect] was one of
twenty-three; the other twenty-two were bugs waiting to surface in different contexts, reported
by different customers, at different times. A project that knows about twenty-three [Instances]
and fixes one has produced a documented list of [Defects] it has chosen to keep.

### 3.5 Enforce permanently, and block

The [Rule] MUST become a permanent part of the project's quality checks, and it MUST **fail** rather
than warn. Three things must hold for this clause to be met: the [Rule] is in the checks the
project runs to accept changes; it fails rather than warns; and every [Suppression] it carries is
covered by a recorded decision. A [Rule] that reports a violation without failing does not
[Conform], and neither does a [Rule] whose green run depends on a [Suppression] that no recorded
decision covers, since the checks are then passing around the [Instance] rather than enforcing
against it.

**Where those checks run, and what the project does when they fail, is out of scope.** Continuous
integration, git hooks, branch policy and release process are the project's own business; see
section 8. What this clause requires is that the [Defence] is permanent, applies to everyone rather
than to whoever remembers it, and produces a failure rather than a remark.

Removing a [Rule], or adding a [Suppression] for an [Instance], MUST be a recorded decision rather
than a silent edit, and belongs to the [Owner] under section 4, which governs. Where a [Rule]
carries a [Suppression] that no recorded decision covers, this clause is not followed until the
decision is recorded or the [Suppression] removed.

**Enforcement is demonstrated through the project's own entry point.** Running the [Detector]
directly shows that the [Rule] can fire; it does not show that the project's checks will run it.
The [Practitioner] MUST run the invocation the project uses to accept changes, over everything
that invocation covers, and see the [Rule] reported there. A [Toolchain] that wraps its
[Detectors] in its own command is asking for that command to be used, and a [Practitioner]
who bypasses it has proven the [Rule] and not the [Defence].

For a [Bundled defence], the project whose entry point demonstrates it is any project the
[Toolchain] is genuinely installed into, the [Consuming project] included, since that is
where the [Rule] will be enforced. A mechanism gap that stops the [Toolchain] running its own entry
point on its own source moves the demonstration to such a project; it does not weaken it. The
substitutes clause 3.2 allows, for proving under clause 3.3 and for resolution under clause 3.6, do
not extend to this clause: the demonstration here is through an entry point or it is not made. A
project created for the purpose, with the [Toolchain] installed into it from the source under
test, is such a project, so a [Toolchain] with no consumer yet is not excused.

**Why**: the purpose is that the mistakes of the past become structurally impossible to repeat, and
a [Warning] is not structure. A [Warning] is a suggestion, and suggestions decay under deadline
pressure, which is the condition under which the original [Defect] was written.

### 3.6 Make the failure message terse, and point it at real documentation

The failure [Message] MUST be terse, and it MUST carry a stable [Identifier] that resolves to
[Remediation docs] shipped with the project.

That documentation MUST state three things: what the [Rule] is about, why it exists, and how to
fix a violation correctly using the project's preferred approach.

A [Practitioner] who finds that an existing [Rule]'s printed [Identifier] does not resolve records
it where clause 3.2's gaps are recorded, naming the [Rule] and the [Identifier], with no [Rule] build of
their own to attach it to. It does not block the remediation. Where the mechanism cannot resolve
[Identifiers] at all, it is a mechanism gap under clause 3.2, not a [Toolchain] gap, and since it
also breaches the [toolchain specification](TOOLING-SPEC.md)'s own obligations it blocks the
[Toolchain]'s claim under section 7 until fixed; where the mechanism resolves others and only this [Rule]'s documentation is missing, it is a
fault in that [Rule]'s [Remediation docs], owned by whoever wrote the [Rule], and bears on
the project's claim rather than the [Toolchain]'s.

**The split between the two is by job, not by length.** The [Message] carries what was detected,
where, and the [Identifier]; it is read under interruption by somebody trying to get on with
something else. The documentation carries the reasoning and the remedy; it is read once, by
somebody who has decided to understand the [Rule], and it is maintained as the project's thinking
develops. Anything that would grow over time belongs in the documentation.

**A [Rule] and its documentation are one artefact and MUST be versioned as one.** They live in the
same repository and are committed together, or in a library the project depends on at a clearly
tracked version. A [Rule] carries knowledge specific to the project it defends, and its
documentation is where most of that knowledge actually sits, so anything that lets the two drift
apart destroys the value of both. The same applies to a [Rule]'s [Fixtures] under clause 3.3.

An external URL [Conforms] only where the project controls it and versions it alongside the [Rule]. A
link into a third-party wiki or a general article does not, because neither can be relied upon to
still describe this [Rule].

**Repository structure is left to the project.** Where these artefacts sit, how they are foldered,
whether [Rules] are vendored or depended upon: none of that is this specification's business. What
is required is that they move together.

The [Identifier] MAY be a [Rule] ID resolved by a command, an anchor in a documentation file, or a
URL. It MUST continue to resolve for as long as any released version can emit it, which is a
stronger requirement than surviving the current release. Where a [Rule] is redesigned such that its
meaning materially changes, a new [Identifier] SHOULD be issued and the old one SHOULD keep
resolving to an explanation of what became of it.

A [Message] that names a pattern without leading anywhere does not [Conform]. "Pattern X detected"
teaches nothing.

**Why**: the [Message] is read at the moment of failure, by someone who wants to get on with their
work, so it has to be short enough to read and specific enough to act on, whilst the reasoning
has to live somewhere it can be maintained and can grow. Splitting it this way keeps the [Message]
terse without losing the teaching, and the documentation becomes the project's accumulated
engineering knowledge rather than a comment nobody revisits. The best custom [Rules] are
opinionated documentation encoded as automation.

## 4. Authority: which decisions belong to whom

The method is frequently executed by somebody who does not own the codebase, including an [Agent]
acting under instruction. This section states which decisions that person takes alone and which
they do not.

**Decisions the [Practitioner] takes alone**, without seeking approval:

- What [Class] the [Defect] belongs to, and where its bounds sit (clause 3.1).
- Which [Detector] to use and how to express the [Rule] in it (clause 3.2).
- How to prove the [Rule], and what [Fixture] to prove it against (clause 3.3).
- What the correct behaviour is at each [Instance], and therefore what each fix should be (clause
  3.4).
- The wording of the failure [Message] and the [Remediation docs] (clause 3.6).

**Decisions that MUST go to whoever owns the codebase:**

- Adopting a [Baseline], in whole or in part (clause 3.4).
- Suppressing an [Instance], or removing or disabling an existing [Rule] (clauses 3.4 and 3.5).
- Accepting a known [Instance] as unfixed for any reason.
- Deciding that a [Class] will not be defended at all, where a [Rule] for it is achievable.
- Leaving unbuilt the next wider [Rule] that clause 3.1 required the [Practitioner] to name, where
  it was rejected

@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 088: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

The exact prompt every reader received, part 2 of 2

because no [Instance] of it exists today rather than because the [Hazard] cannot
  arise there. The absence of an [Instance] is the [Practitioner]'s reason not to widen unasked; it
  is not authority to exclude the extension, and recording it as a known gap does not change whose
  decision it is. This does not reopen a [Class] whose bounds a [Hazard] sentence has settled, and
  it reaches no further than the one wider [Rule] already on the record.
- Leaving a [Rule] narrower than an independent search under clause 3.1 showed it should be, where
  the reason is that the wider check is harder to build without [False positives] rather
  than that the [Hazard] is absent from what it would add. The [Instances] the search found are
  fixed regardless; what stays with the [Owner] is the [Class] left partly undefended.

The dividing line is that the [Practitioner] decides **how the [Defence] is built** and the [Owner]
decides **what the codebase is permitted to keep**. An [Agent] MUST NOT [Baseline], suppress or
knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to
be.

**The default position is that none of these are permitted at all.** [Suppressions], [Baselines] and
every other means of evading a [Defence] are not techniques with a threshold governing their use; they are
[Exceptions] to the method, and the standing answer is no. An [Exception] exists only where a human has
discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own
creates one.

**Uncertainty is itself an [Escalation] trigger.** Where the [Practitioner] is not confident that code
they are about to exclude is free of the [Hazard], that exclusion is a [Suppression] and belongs to the
[Owner], whatever it is called. This is deliberately asymmetric: [Narrowing] is the [Practitioner]'s
decision only whilst they are sure, and doubt routes it upwards. Without this, the [Hazard] judgement
in clause 3.3 would let a [Practitioner] suppress an [Instance] whilst never touching the clause that
would have escalated it.

**Where the work is entirely [Agent]-driven, do the work.** Fixing [Instances] is cheap now, and an
[Agent] that reaches for an [Exception] has almost always found a shortcut rather than a genuine
obstacle.

**What to do whilst waiting.** A decision awaiting the [Owner] MUST NOT stall the rest of the work.
The [Practitioner] completes everything within their own authority, reports the blocked decision
with the [Instance] count and what it would cost to fix, and leaves the [Rule] unmerged rather than
merged in a weakened form. Where the only thing pending is whether one matched case carries the
[Hazard], the [Rule] MAY merge at full width with that case recorded as a known [Instance]
awaiting the [Owner], enumerable under clause 8.7: the [Rule] is not weakened, the case is not
hidden, and the [Practitioner] is not pushed towards declaring that no pattern exists. Reporting a
[Sweep] of four hundred [Instances] and stopping is a useful outcome; quietly baselining them is not.

Report by whatever channel carries the rest of the [Practitioner]'s work: for an [Agent], its
output to whoever instructed it. An [Instance] in code the [Practitioner] is not permitted to
change is reported the same way, as a blocked decision with its count, and is never a reason to
narrow the [Rule] so that it stops seeing it.

**The [Instance] count alone never triggers [Escalation].** A large [Sweep] is work, not a blocked
decision. Stopping to report is what the [Practitioner] does when an [Owner] decision is genuinely
required, which means an [Exception] is being contemplated, or when they are uncertain whether an
exclusion carries the [Hazard]. It is not what they do because the number is uncomfortable. Five
hundred [Instances] with no [Exception] needed is five hundred [Instances] to fix.

A remediation MAY be delivered across several changes where the volume warrants it, **provided the
[Rule] does not become [Blocking] until every [Instance] is fixed**, since a [Blocking] [Rule] merged over a
codebase that still violates it either fails continuously or has been weakened to avoid doing so.
No [Instance] may be knowingly left unfixed at the end without the [Owner]'s decision.

**Why**: without this, an executing [Agent] either stalls at the first judgement call it cannot
authorise or takes the decision unilaterally, and the second failure is much harder to notice than
the first. Both were observed in cold readings of an earlier draft of this specification.

## 5. Ordering the quality checks

Defence Before Fix presumes quality checks ordered as follows, and the ordering is enforcing rather
than advisory:

1. [Detectors] - type checking, linting, custom [Rules]
2. Automated tests - unit, integration, functional
3. Build verification - services start, dependencies resolve
4. Human acceptance testing - visual review, workflow validation

[Detectors] MUST run first, and a failure at that level MUST stop the levels below it from
being treated as meaningful.

This constrains the **sequence of checks**, not the infrastructure that runs them. How the project
expresses that sequence is out of scope, per section 8. It is a property of the project, not a step
in any one remediation: a [Practitioner] handling a [Defect] is not required to reorder the
project's checks, and a project whose checks run in another order is a project that does not
[Conform], which is a finding to record, not work to do on the way to a fix.

**Why**: a [Detector] is preventive and a test is diagnostic. A [Detector] reads every file, every
time it runs, so it cannot miss a file merely because nobody thought to write a test for it.
Failures at a lower level also produce confusing results at the levels above, so any other order
costs time diagnosing symptoms of a problem the first level would have named directly.

## 6. Relationship to test-driven development

Defence Before Fix neither replaces nor competes with TDD. They operate at different levels, and
a full remediation uses both: a test, executed by a [Runner], pins the [Instance]; a [Rule], evaluated by
a [Detector], catches the [Class].

| Concern            | TDD                                | Defence Before Fix                           |
| ------------------ | ---------------------------------- | -------------------------------------------- |
| Operates on        | The specific [Defect]              | The [Class] the [Defect] belongs to          |
| Artefact           | A test                             | A [Rule] in a [Detector]                     |
| Proves correctness | For one behaviour, by executing it | For a pattern, by reading the code           |
| Answers            | "Is this bug fixed?"               | "Can this kind of bug still exist anywhere?" |

The specific [Defect] is still reproduced with a test and proven fixed, exactly as normal. What
this method adds is the phase before that work begins, in which the codebase's permanent [Defence]
[Coverage] is extended using the evidence the [Defect] has just supplied.

The name records the ordering. The [Defence] comes first, because once the fix has landed the
evidence is gone and the opportunity closes with it.

## 7. Conformance

[Conformance] is claimed at one of four levels. Partial [Conformance] MUST NOT be described as
[Conformance].

**A remediation [Conforms]** if all six clauses of section 3 were followed for that [Defect], and
the verdict that they were rests on reproduction, as this section closes by requiring, not on the
report.

**A [Defence] [Conforms]** if it satisfies clauses 3.1, 3.2, 3.3, 3.5 and 3.6: it is drawn to a
[Class] within both bounds and not to the reported [Instance], it is evaluated by reading code, it
was proven to fire against either the originating [Instance] or [Fixture] code, it fails rather
than warns, and its [Message] is terse and resolves to [Remediation docs] versioned alongside
it. A [Defence] can [Conform] whilst the [Sweep] is incomplete; what that describes is a
[Conforming] [Defence] over a codebase with known [Instances], which section 4 requires to be
recorded, not a [Conforming] remediation.

**A [Toolchain] [Conforms]** if it permits bespoke custom [Rules], supports the ordering in section 5,
and satisfies clauses 8.1 to 8.5 and 8.7: the [Practitioner] can run it, the result reaches them in
the output they are reading, [Identifiers] resolve mechanically, documentation states the correct
construction, and both the [Defences] and the project's recorded decisions can be enumerated. Clause
8.6 is a SHOULD and does not bear on [Conformance], deliberately: it is the one clause that acts
before a mistake rather than after, so no failing run can prove it absent, and [Conformance] is
claimed only over what a run can prove. The [toolchain specification](TOOLING-SPEC.md) names a
[Toolchain] that also satisfies it as [Conforming] with [Agent] support, which is the claim to
make when it is true. Those obligations fall in two documents: what each [Detector] the
[Toolchain] routes a [Defence] through must offer is stated in the
[detector specification](DETECTOR-SPEC.md), and what the assembled [Toolchain] must add is stated
in the [toolchain specification](TOOLING-SPEC.md), which requires the first as its own opening clause,
carries the ordering in section 5 and the correct construction of clause 8.4 as clauses of its own,
and states the listing and the project record that clauses 8.5 and 8.7 require.

**A project [Conforms]** if its [Defences] and [Remediation docs] [Conform], and if its recorded
decisions under section 3 are discoverable. Nothing here constrains how the project runs its checks
or what it does when they fail.

[Toolchain] [Conformance] is stated in full in the [detector specification](DETECTOR-SPEC.md)
and the [toolchain specification](TOOLING-SPEC.md) together, which give each of the obligations above as a
clause with its own reasoning, and add what a [Detector] and a [Toolchain] must provide so
that a project can meet its own. The summary here is normative and sufficient to judge a
[Toolchain] by; the companion documents are where a [Detector] maintainer and a
[Toolchain] author should work from.

A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer
reruns the [Defence] red at the commit that introduced it and green at the final commit, through the
project's own entry point for accepting changes, and reruns the originating symptom against the fix.
Where the [Rule] was proven against a [Fixture] under clause 3.3, the red run is against the
retained [Fixture]. Where the originating symptom cannot be reproduced through the entry point,
an incident or an observation at production scale, the reviewer says so and verifies the red and green
runs alone. A verdict on a [Toolchain] or a project rests on the reviewer exercising the clauses
named above, not on the claimant's report of having done so. A report that reads as [Conforming]
has not been shown to be.

A [Toolchain] MAY additionally audit its own [Rules] against clause 3.6, failing its own release if any
[Rule] fails something without resolving to [Remediation docs]. This is the strongest
available demonstration that the clause is honoured rather than asserted.

## 8. Operating a defence under AI-assisted development

*This section is normative.*

### The argument these clauses rest on

For a human developer, a failure [Message] that teaches is good practice. They may read it, may
internalise it, may ignore it, and which of those happens depends on their seniority, their
workload and how many times they have seen the [Message] before.

For an [Agent], the failure [Message] is the entire remediation loop. It is consumed as instruction,
in the same turn, every time, with no fatigue and no seniority gradient. A [Message] that resolves
to documentation explaining the correct approach does not simply block the [Agent], it redirects it,
and it does so identically on the thousandth occurrence as on the first.

That reframes the usual complaint about AI-written code. The difficulty was never that [Agents] make
mistakes, since people do too. The difficulty is that nobody built the channel to correct them at
the level of the [Class] rather than the [Instance].

If the value of the method depends on that loop closing, then what closing it requires has to be
stated rather than assumed. The clauses below are what a [Defence] must do to be usable by an [Agent]
at all.

### Scope: this specifies a defence, not a pipeline

**Continuous integration, git hooks, branch policy, review process and release management are out
of scope.** How a project chooses to run its quality checks, and what it does when they fail, is
the project's own business and no part of this specification.

What is in scope is the [Defence] itself: a [Rule] that reads code, the documentation that explains it,
and the requirement that the [Practitioner] can run it and act on the result. A specification that
told projects how to run their checks would be overreaching, and would be ignored for it.

### 8.1 The practitioner MUST be able to run the defence themselves

A [Rule] that only reports through infrastructure the [Practitioner] cannot invoke is not usable by
them, whatever it does for anyone else.

**Why**: an [Agent] that cannot check its own work against a [Defence] cannot iterate against it, so
the loop never closes in the turn where the mistake was made, which is the only moment it is cheap
to fix.

### 8.2 The result MUST reach the practitioner in the output they are already reading

The [Detector]'s own output, at the point of the work. Not exclusively a dashboard, a report artefact or
a summary elsewhere.

**Why**: a [Message] that teaches nobody, because nobody sees it, is the same as no [Message].

### 8.3 The identifier MUST resolve without a human

By a command the [Practitioner] can run, a file they can read, or a URL they can fetch.

**Why**: clause 3.6 requires the [Identifier] to resolve. This requires it to resolve *for the
reader*, which for an [Agent] means mechanically. An explanation that lives in a colleague's head
resolves for nobody at three in the morning either.

### 8.4 Documentation MUST state the correct construction, not only the prohibition

Explaining why the pattern is dangerous is not sufficient. The documentation has to show what to do
instead, specifically enough to act on.

**Why**: this is the difference between [Blocking] an [Agent] and redirecting it. A prohibition alone
leaves it to guess at the replacement, and it will guess.

### 8.5 A project's defences MUST be enumerable

A [Practitioner] MUST be able to list what defends this codebase, and read each [Defence]'s
documentation, without triggering it first.

**Why**: an [Agent] arriving at a codebase has no colleague to ask and no memory of last time.
Without this, a project's standards can only be learned by violating them one at a time.

### 8.6 A project SHOULD publish a summary of its defences suitable for an agent's context

One terse line per [Defence], stating the [Rule] as a standing instruction rather than as a failure
report, each linked to its full documentation. *"Error hiding is forbidden"* is the shape.

**Why**: everything else in this method operates after the mistake. This operates before it. A
summary small enough to sit in an [Agent]'s working context turns the accumulated [Defences] from a
series of ambushes into a description of how this project expects code to be written, and it costs
one table.

### 8.7 Recorded project decisions MUST be discoverable by the same means

The judgements section 3 delegates to project level, once agreed and written down, MUST be
reachable exactly as the [Defences] are.

**Why**: section 3 says a project's accumulated decisions become part of its [Coverage]. [Coverage]
nobody can find is not [Coverage], and every [Practitioner] who arrives after a decision will otherwise
re-open it.

## 9. Citation

> Edmonds, Joseph. *Defence Before Fix*, version 1.1.0. First published 22 February 2026.
> <https://ltscommerce.dev>

## Appendix A: Instructing an agent

Where an [Agent] is expected to follow this method, give it the clauses rather than the article.
This appendix restates sections 2 to 4; where the two differ, the sections govern.

> When you find a [Defect] of any kind, do not fix it yet.
>
> First work out what [Class] it belongs to: the pattern, style, idiom or configuration that allowed
> it. Do not decide in advance whether that is possible, attempt it. If you cannot write a [Rule]
> for it, say so and fix the [Defect] conventionally.
>
> Write a custom [Rule] that detects the [Class], in a tool that reads code rather than running it.
> Draw it so that it catches more than the single [Instance] you started from, but never so broadly
> that it matches code which does not carry the [Hazard].
>
> Do not trust the [Rule] as your only way of finding [Instances]. Search independently as well, by
> text search and by reading the code, and check the [Rule] catches what you found by hand. Make that
> search a thorough one rather than a gesture, because everything downstream rests on it. If your
> own search turns up [Instances] the [Rule] missed, widen the [Rule] until it catches them; the search
> wins, not the [Rule]. If it turns up nothing new, then one [Instance] is a supported conclusion rather
> than a guess.
>
> The [Hazard] is whatever harm the [Class] does, and it need not be a failure. Error hiding counts.
> So does something merely sloppy that makes the code harder to reason about safely.
>
> Prove the [Rule] fires before you trust it. It must catch the originating [Defect]. If the pattern
> is not present in the codebase, because it was already fixed or because you are defending
> against it pre-emptively, prove the [Rule] against [Fixture] code that demonstrates the pattern and
> keep that [Fixture] as the [Rule]'s test.
>
> Then run the [Rule] everywhere the pattern can occur, which usually means one language but never
> just the component the bug was reported in, and report the [Instance] count. Catching far more
> [Instances] than you expected is success, not evidence the [Rule] is too broad. Only ever narrow a
> [Rule] to exclude code that does not carry the [Hazard]; never to make the number smaller.
>
> Fix every [Instance]. Examine each one; where the same answer is genuinely right for all of them,
> applying it to all of them is correct. Never satisfy the [Rule] whilst leaving the [Hazard] in place,
> and never suppress the [Rule] at the call site.
>
> You do not have the authority to [Baseline], to suppress an [Instance], or to leave a known [Instance]
> unfixed, however large the count turns out to be. Those belong to whoever owns the codebase, and
> the standing answer is no unless a human has already agreed and documented an [Exception] for this
> project. If you hit one, finish everything else, then report the count and what fixing it would
> take, and leave the [Rule] unmerged rather than merging it weakened.
>
> A [Defect] you find and do not fix now is recorded where the project keeps its other decisions,
> naming the [Class] if you can already see it. Mentioning it in your output is not a record.
>
> If you are unsure whether code you want to exclude carries the [Hazard], you are suppressing it, so
> refer it upwards rather than deciding. Narrow only where you are confident.
>
> Otherwise, just do the work. Fixing [Instances] is cheap for you, and reaching for an [Exception] is
> almost always a shortcut rather than a real obstacle.
>
> Make the [Rule] a permanent part of the project's quality checks, failing rather than [Warning]. Write
> its failure [Message] terse, carrying a stable [Identifier] that resolves to documentation shipped
> with the project saying what the [Rule] is about, why it exists and how to fix a violation
> correctly. Check you can run the [Rule] yourself and read its output, because if you cannot, nor can
> the next [Agent].
>
> Only then fix the original [Defect] in the normal way, with a test that reproduces it.

## Changelog

| Version | Date       | Change                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1.0.0   | 2026-09-08 | Initial specification, formalising the method published on 22 February 2026. Revised before publication after three independent cold readers understood the method correctly and still could not execute its judgement calls.                                                                                                                                                                                                                                                                                                                                                        |
| 1.0.1   | 2026-09-08 | Clarity, no obligation changed: section 3 opens with a map of the six clauses; clause 3.1 opens with its five steps and closes with what it leaves on the record; clause 3.3 is in three named parts and its [Narrowing] part opens with the decision. The header and the terminology entries for [Detector], [Toolchain] and [Conform] name the [detector specification](DETECTOR-SPEC.md) 1.0.0 alongside the [toolchain specification](TOOLING-SPEC.md) 0.2.0, and [Conform] extends to the companion specification being claimed. Accepted under [ACCEPTANCE.md](ACCEPTANCE.md). |
| 1.1.0   | 2026-10-02 | New obligation: a [Defect] whose fix is deferred is recorded where the project's other decisions are enumerable under clause 8.7, naming the [Class] where apparent; whether it stays unfixed is the [Owner]'s decision under section 4, and a mention in conversation is not a record. Appendix A carries the sentence for an [Agent]. Section 7's definition of a [Conforming] remediation names reproduction in the same sentence. Accepted under [ACCEPTANCE.md](ACCEPTANCE.md).                                                                                                 |

<!-- Term link definitions -->

[agent]: #agent
[agents]: #agent
[baseline]: #baseline
[baselines]: #baseline
[blocking]: #blocking
[bundled defence]: TOOLING-SPEC.md#bundled-defence
[calibration]: #calibration
[calibrations]: #calibration
[class]: #class
[classes]: #class
[conform]: #conform
[conformance]: #conform
[conforming]: #conform
[conforms]: #conform
[consuming project]: TOOLING-SPEC.md#consuming-project
[coverage]: #coverage
[dbf]: #dbf
[defect]: #defect
[defects]: #defect
[defence]: #defence
[defences]: #defence
[detector]: #detector
[detectors]: #detector
[escalation]: #escalation
[exception]: #exception
[exceptions]: #exception
[false positives]: #false-positive
[fixture]: #fixture
[fixtures]: #fixture
[hazard]: #hazard
[hazards]: #hazard
[identifier]: #identifier
[identifiers]: #identifier
[instance]: #instance
[instances]: #instance
[message]: #message
[narrowing]: #narrowing
[owner]: #owner
[practitioner]: #practitioner
[remediation docs]: #remediation-docs
[rule]: #rule
[rules]: #rule
[runner]: #runner
[runners]: #runner
[suppress]: #suppression
[suppression]: #suppression
[suppressions]: #suppression
[sweep]: #sweep
[toolchain]: #toolchain
[warning]: #warning

===== FILE: fixtures/remediation-ledger.md =====
# Fixture: the ledger remediation

A fictional remediation, described as the pull request and its commits would show it. Grade it
against the document you were given. Where the description does not say, answer from what is
written.

## What the pull request shows

A defect: `Ledger::total()` silently returned zero when a currency conversion failed, because the
conversion call's error was caught and the empty result treated as a valid amount.

The practitioner, a coding agent, did the following, in this order, over five commits.

1. **Commit A.** Wrote a note in the pull request naming the class as "a fallible call whose
   error is caught and whose result is then used as if the call had succeeded", stating that
   the hazard is silent wrong output rather than a crash, and naming the next wider class it
   did not build, "any caught error that is not rethrown or reported", as not attempted because
   the codebase has hundreds of such catches and most are legitimate.
2. **Commit B.** Wrote a rule in the project's static analyser, identifier `proj.caught-then-used`,
   that flags a caught error followed by a use of the call's result in the same function. Added
   the rule to the project's analyser configuration. The commit also contains a fixture file
   reproducing the `Ledger::total()` shape, and the CI run for this commit is red on that rule
   with five findings: the fixture, `Ledger::total()`, and three other functions.
3. **Commit C.** Fixed `Ledger::total()` and two of the three others, so that the failure is
   propagated. For the third, `Report::render()`, the agent added an inline `// ignore proj.caught-then-used` comment with the note "rendering must never throw", and did not raise
   it with anyone. The CI run is green.
4. **Commit D.** Wrote the rule's documentation as a page on the company wiki, at a URL the rule's
   failure message prints in full. The page states what the rule forbids and why, and shows the
   propagating construction to write instead. The failure message is one line: the identifier,
   the file and line, and the wiki URL.
5. **Commit E.** Confirmed in the pull request description that the rule is set to fail the
   build, not warn, and that the whole quality invocation was run locally and passed.

The pull request was merged with a merge commit; all five commits remain in the history.

The project's recorded decisions say nothing about inline ignores. The project has a named owner.

LTSCommerce and others added 2 commits October 12, 2026 15:40
Made under the acceptance process of pull request 22 at 40f7895. One reader of five
leaves out attributing the defect to a class under 2a, quoting section 2's
"building the Defence" sentence, clause 3.4 and Appendix A; question 2 needs every
reader. Questions 1 and 3 are five of five. No reader names the clause 3.6
paragraph this change altered; the five findings lie in untouched text and are
noted against their rows in acceptance/ROUTED.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er, from attribution

Run 088 failed on question 2a: a reader relying on section 2's sentence
requiring the defence before the fix took the rule, its proof and the
sweep, but not the first step, attributing the defect to a class. A
sentence after "This is the substance of the method" now names section 3's
clauses in order, starting with attribution. Clarity edit: no MUST, SHOULD
or MAY added or removed. Changelog line updated, naming runs 34 and 88.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LTSCommerce and others added 3 commits October 12, 2026 15:44
…ope the MUST; 3.6 reason and record

Section 2's added sentence no longer says what building the Defence is
or lists only four of section 3's six clauses; it points to section 3's
clauses in order and names the first, working out the Defect's Class
under clause 3.1. Clause 3.6 now gives the breach as a reason, in its own
sentence, and says the mechanism gap is recorded against the Toolchain's
claim where clause 3.2 says, so its record agrees with 3.2's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sentence after the defence-before-fix requirement no longer counts
section 3 as six clauses in order straight after the MUST, which could be
read as making all of section 3 part of what must precede the fix. It now
says the method begins at clause 3.1 and follows section 3 in order.
CHANGELOG line updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reached

The sentence added to section 2 could be read as saying every remediation
runs all of section 3, which clashes with the exits that end it early. It
now says the method begins at clause 3.1 and that section 3 gives the order
of the steps that follow. The changelog line matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 089: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

Command and working directory

ccy-claude -p --model haiku --safe-mode --tools '' --setting-sources '' --strict-mcp-config --no-session-persistence --output-format json < prompt.txt, each reader in a fresh empty directory made by mktemp -d outside the repository

Model version each reader reported

reader 1: claude-haiku-5-5
reader 2: claude-haiku-5-5
reader 3: claude-haiku-5-5
reader 4: claude-haiku-5-5
reader 5: claude-haiku-5-5

The delegated marker's task prompt

MODE: mark

Worktree: <worktree> (branch `editor/gap-terms`). The readers read commit 30fc055. Read, at that commit and in that worktree: ACCEPTANCE.md in full (it is the process as pull request 22 proposes it, merged into this branch; the run is made under that process change before it merges, at commit 40f7895, named here before the readers started), acceptance/runs/README.md, acceptance/method/QUESTIONS.md, acceptance/method/KEY.md, acceptance/fixtures/remediation-ledger.md and SPEC.md. You may read acceptance/runs/088-gap-terms-method-run-2.md for the shape of a record only; ACCEPTANCE.md says a judgement an earlier run recorded is not a rule. Do not take any judgement from acceptance/runs/034-gap-terms-method-run-1.md, which was made under the earlier rules.

Run: 089. Document: SPEC.md. Change: gap terms, pull request 18, a clarity edit: clause 3.6 now calls a mechanism that cannot resolve identifiers at all a mechanism gap under clause 3.2, not a toolchain gap, and says that because it also breaches the toolchain specification's own obligations it is recorded against the toolchain's claim as well, where clause 3.2 says, and blocks that claim under section 7 until fixed; and, after run 088 failed on a keyed miss on 2a, section 2's "When it can" paragraph gains a sentence saying the method begins at clause 3.1, working out what class the defect belongs to, and that section 3 gives the order of the steps that follow. No key or question changed: no key quotes either altered paragraph and the change adds no obligation. Pull request: 18, https://github.com/Defence-Before-Fix/defence-before-fix.github.io/pull/18. Run within the pull request: 3 (run 34 under the earlier process and run 88 under this one came before). Record file to write: <worktree>/acceptance/runs/089-gap-terms-method-run-3.md. Do not commit it.

Sheets: <run-dir>/sheet-1.md to sheet-5.md, each the `result` of reader-N.json. Prompt the readers received: <run-dir>/prompt.txt; its sha256 is 5107692ff60d3f875239d122ee15ad46f4f1c00b37dbf4872ac76ccc064cb31c, and it is byte for byte the question set, SPEC.md and the fixture at 30fc055, each after one `===== FILE: <name> =====` line, one blank line between files. Model: every reader reported claude-haiku-5-5 (<run-dir>/models.txt); the cohort's model named for the run is claude-haiku-5-5. Every reader's JSON has is_error false and subtype success; no reader is set aside.

Facts the editor supplies for the record: cohort of five readers, none reused from any earlier run; each a separate `claude -p` process started through the `ccy-claude` wrapper (which only reattaches the session's credential) in a fresh empty directory made by `mktemp -d` outside any repository, with the packet on standard input, command `ccy-claude -p --model haiku --safe-mode --tools '' --setting-sources '' --strict-mcp-config --no-session-persistence --output-format json < prompt.txt`; no tools, no settings, no project context, no saved session; the machine applies no managed settings; the editor attests the rest of the isolation. Marked by a delegated marker, a fresh `dbf-spec-editor` agent (Claude Opus) given this instruction and its agent definition, having seen neither the drafting nor the review.

Text the change wrote or altered, for "Where a finding lies": the output of `git -C <worktree> diff 9e914fe 30fc055 -- SPEC.md` (9e914fe is the merge base with `next`). Run it and read it. A finding in a paragraph that diff adds, rewords, moves, deletes a sentence from, or changes a word, term or link in, a paragraph the change parts from what it refers to, or that a reader sets against such a paragraph, blocks; any other finding is routed to acceptance/ROUTED.md, and the record says so for each.

Mark every sheet, keyed item by keyed item, by ACCEPTANCE.md's Marking section and the key alone. Write the record stating what acceptance/runs/README.md lists, in its order, with the marks table, each miss with the reader's reasoning in brief, every passage named under question 4 with its words or description, clause and readers, each finding and where it lies, any open point and the direction you marked it, the process-change commit 40f7895, the verdict, and a placeholder line for the link to the posted sheets. Reply with the verdict and the findings only, under thirty lines.

The agent definition the marker ran under

---
name: dbf-spec-editor
description: Drafts, reviews, key-updates, marks or records a change to the Defence Before Fix specification, one mode per dispatch, following the specification repository's CONTRIBUTING.md, SCOPE.md and ACCEPTANCE.md exactly. Dispatched by the dbf-spec-change skill; it cannot run a cohort itself.
tools: Read, Write, Edit, Grep, Glob, Bash
model: opus
---

You work on one change to the Defence Before Fix specification, in the worktree path you are
given. The process is defined by that repository's `CONTRIBUTING.md`, `SCOPE.md`,
`ACCEPTANCE.md` and `acceptance/README.md`; read the ones your mode needs, in full, before you
act. You invent no steps. You never merge, push to `next` or `main`, release, tag or publish,
and you never change a question, threshold or fixture. Your prompt names one mode.

Write as the specification is written: British spelling, no em dashes, defined terms linked as
`spec-qa.py` requires, every clause or section number linked to its heading. Commit on the
worktree's branch.

## MODE: draft

1. Read `SCOPE.md`, `DECLINED.md`, and every clause the change touches, with the terminology
   entries they use.
2. Decide the kind. A change to the sentences carrying MUST, SHOULD or MAY in a numbered section
   or appendix is an obligation; anything else is a clarity edit.
3. For an obligation, answer the five SCOPE tests in a sentence or two each, and name the
   section the obligation extends. If a test fails, or an existing clause already covers it, say
   so and stop without editing.
4. Make the smallest edit that does the job, in the clause that owns the subject.
5. Add one line under `Unreleased` in `CHANGELOG.md`, saying what changed and why, with a
   placeholder `run NNN`. Change no version.
6. If Appendix A changed, run `python3 build-agent-surfaces.py` and commit its outputs.
7. Run `python3 spec-qa.py` and `python3 -m unittest discover -s tools -p 'test_*.py'`; both
   clean.
8. Reply with: kind, SCOPE answers, files changed, commit, QA result, and a filled
   `.github/PULL_REQUEST_TEMPLATE.md` body. Under forty lines.

## MODE: review

You have not seen the drafting. Read the whole changed document and the diff against
`origin/next`. Report, numbered, with clause numbers: contradictions with other clauses, terms
used outside their definition, a sentence a reader could take two ways, an obligation an owner
could not verify from the record. Do not edit.

## MODE: keys

Read `acceptance/README.md` and the `KEY.md` for each changed document. Update every quotation of
a changed sentence to the new wording, verbatim, in a new commit; run `python3 spec-qa.py` until
`key-quote-stale` is gone. If the change adds a load-bearing obligation no keyed question
covers, propose the question and its key in your reply; add it only if your prompt says the owner
has decided to.

## MODE: mark

You have not seen the drafting or the review. Read `ACCEPTANCE.md` in full, the `KEY.md` for the
document, the document at the commit named, and the sheets under `<run-dir>/`.

- Mark each sheet against the key, keyed item by keyed item, by the rules under "Marking" in
  `ACCEPTANCE.md` and nothing else: the key's required elements, accepted grades, conditions and
  wrong readings. Invent no judgement; where neither the rules nor the key settles a mark, mark
  it in the direction the open-point rule gives and record the point.
- A single miss under questions 1 and 3 counts against the document only where the key names
  that reading; say which.
- For question 4, record every passage named, with its words or description and its clause.
  Group the passages by paragraph, count the readers, and for each finding say whether it lies in
  text the change wrote or altered, read against the diff the prompt gives you, or is routed to
  `acceptance/ROUTED.md`. Ignore any suggestion a reader volunteers.
- Apply the pass criteria exactly.
- Write `acceptance/runs/NNN-<slug>-run-<k>.md` stating what `acceptance/runs/README.md` lists,
  in its order. Do not commit it. Reply with the verdict and the findings only.

## MODE: record

Commit the run record, replace the `run NNN` placeholder in `Unreleased` with a link to it, and
for an obligation set the document header to `<next version>-dev, unpublished` in the same
commit if it still names a published version. Run `python3 spec-qa.py` and
`python3 tools/changes.py --base origin/next`; both clean. Reply with the commit.

@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 089: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

Answer sheets, each reader's whole output as its process returned it

reader-1

{"duration_api_ms":64079,"stop_reason":"end_turn","session_id":"47969ca2-8e7d-453c-9fb6-e75507219396","total_cost_usd":0.008041199999999998,"usage":{"input_tokens":2,"cache_creation_input_tokens":339,"cache_read_input_tokens":31020,"output_tokens":15326,"output_tokens_details":{"thinking_tokens":13176},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":339,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":15326,"cache_read_input_tokens":31020,"cache_creation_input_tokens":339,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":339},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":15326,"cacheReadInputTokens":31020,"cacheCreationInputTokens":339,"webSearchRequests":0,"costUSD":0.008041199999999998,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":13176,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Don't fix a handed defect yet. First establish the class it belongs to, bounded both ways, and the hazard that class carries.\n- Search for other instances by at least two independent techniques, run before the rule is run, until the last technique adds nothing new.\n- Express the class as a blocking rule in a detector that reads code, never a test. Prove it fires, in its own commit, before trusting it.\n- Sweep the whole codebase, record the count, and fix every instance by addressing the hazard. Narrow a rule only with a written sentence showing the hazard cannot arise in the excluded code; doubt is suppression.\n- Make the rule permanent through the project's own entry point, failing rather than warning, with a terse message whose identifier resolves to documentation versioned with the rule.\n- Record anything deferred or left unbuilt where decisions are discoverable. Suppressing, baselining, or leaving an instance unfixed belongs to the owner, not an agent.\n\n## 2. Load-bearing statements\n\n**2a.** Attribute the defect to a class, search independently for other instances, then build and prove the rule with the originating instance still present, and only then fix. Sweep and fix every instance.\n\n> \"When it can, building the Defence is not optional and MUST be attempted before the Defect is fixed.\" (section 2)\n>\n> \"The Practitioner MUST first establish what Class the Defect belongs to.\" (3.1)\n>\n> \"The Defence is committed with the originating Instance still present, and the fix is committed after it.\" (3.3, Part A)\n\n**2b.** Only a human owner may. An agent may not, on its own authority, adopt a baseline, suppress an instance, or leave one unfixed.\n\n> \"Whoever holds authority over a project's Exceptions and Calibrations. Always a human.\" (Terminology, Owner)\n>\n> \"An Agent MUST NOT Baseline, suppress or knowingly leave an Instance unfixed on its own authority, whatever the Instance count turns out to be.\" (4)\n\nThe fixture says the project has a named owner, so the default-owner rule in the Owner entry does not apply.\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect. The verdict must rest on reproduction: a reviewer reruns the defence red at the introducing commit and green at the final commit, through the project's own entry point, and reruns the symptom against the fix.\n\n> \"A remediation Conforms if all six clauses of section 3 were followed for that Defect, and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\" (7)\n>\n> \"A verdict on a remediation or a Defence MUST rest on reproduction, not on the report: the reviewer reruns the Defence red at the commit that introduced it and green at the final commit, through the project's own entry point for accepting changes, and reruns the originating symptom against the fix.\" (7)\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n| --- | --- | --- |\n| 3.1 | Partial | Commit A names the class, the hazard sentence, and the wider class not built. But it names no two search techniques and says what each found, and it has no statement on whether a Runner check pins the reported behaviour. \"Most are legitimate\" is not the hazard sentence 3.1 requires for rejecting the wider rule. The five findings came from the rule itself, which 3.1 excludes as a technique. |\n| 3.2 | Yes | The rule is in the project's static analyser, which reads code. It is a custom rule in the project's own configuration, not a test. |\n| 3.3 | Yes | Commit B holds the rule and fixture while `Ledger::total()` is still present, and CI is red with five findings before the fix in commit C. There is no narrowing. The merge keeps both commits reachable. |\n| 3.4 | No | The count is the rule's own output, uncorroborated by any independent search. `Report::render()` was not fixed. An inline ignore was added instead, which 3.4 excludes as \"suppressing the Rule at the call site, which is not a fix at all.\" |\n| 3.5 | Partial | The rule fails the build, not warns, and the full local invocation passed. But the inline ignore is a Suppression with no recorded decision, and the project records say nothing about inline ignores. 3.5 requires all three conditions to hold, so the clause is not met. |\n| 3.6 | No | The one-line message carries the identifier, but the documentation is on a company wiki that is not versioned with the rule. Section 3.6 says \"A Rule and its documentation are one artefact and MUST be versioned as one,\" and an external URL conforms only if it is versioned alongside the rule. |\n\n**Section 7, the remediation:** It does not conform. Clause 3.4 is No, and 3.1 and 3.5 are not met, so not all six clauses were followed. The fixture also gives no reviewer reproduction.\n\n**Section 7, the defence alone:** It does not conform. It needs 3.1, 3.2, 3.3, 3.5 and 3.6 satisfied. 3.6 fails outright, 3.5 is not met because of the uncovered suppression, and 3.1 is only partial. The incomplete sweep does not by itself stop the defence from conforming, since section 7 allows that.\n\n**Decision not the practitioner's:** The inline ignore on `Report::render()`. It is a Suppression, and section 4 reserves \"Suppressing an Instance\" and \"Accepting a known Instance as unfixed for any reason\" for the owner. The permitted route was different. Section 4 says \"the Rule MAY merge at full width with that case recorded as a known Instance awaiting the Owner.\" Secondarily, leaving the wider rule unbuilt because \"most are legitimate\" is a hazard judgement, and section 3.1 and the worked example send that to the owner. The fixture does not show that happened.\n\n## 4. Confusion\n\n- Section 2: \"naming at least two independent techniques tried at expressing the Rule, in the vocabulary clause 3.1 uses for a search\"\n- Clause 3.3, Part B: \"**Then the proof, which runs the other way round, and both ways.**\"\n- Clause 3.5: \"The substitutes clause 3.2 allows, for proving under clause 3.3 and for resolution under clause 3.6, do not extend to this clause\"\n- Terminology, Owner: \"That record MUST mark the decision as taken by a default [Owner] and state why nobody better placed was reachable, where the next reader can see who decided and why.\"","ttft_ms":57810,"type":"result","duration_ms":64686,"uuid":"1960aadb-bd42-49e0-9a31-5aae4df27d80","ttft_stream_ms":1441,"time_to_request_ms":602,"first_content_frame_ms":1442,"queued_turn_count":0,"result_index":0}

reader-2

{"duration_api_ms":71060,"stop_reason":"end_turn","session_id":"304592b1-82f5-412b-8e1c-82ee2979c55d","total_cost_usd":0.01432243,"usage":{"input_tokens":2,"cache_creation_input_tokens":29206,"cache_read_input_tokens":2153,"output_tokens":16919,"output_tokens_details":{"thinking_tokens":14600},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29206,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":16919,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29206,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29206},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":16919,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29206,"webSearchRequests":0,"costUSD":0.01432243,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":14600,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Don't fix a handed defect first. Work out the class it belongs to: the pattern, idiom or configuration that allowed it (2, 3.1).\n- If a detector can recognise that pattern, building a defence is mandatory and must be attempted before the fix. Don't pre-judge whether it's expressible (2).\n- Name the class with a hazard sentence, bounded both ways, after at least two independent searches chosen before the rule is run (3.1).\n- Build the rule in a detector, not a test. Prove it fires in its own commit, with the instance still present. Narrow only with a written sentence showing the hazard can't arise (3.2, 3.3).\n- Sweep the whole codebase, record the count, and fix every instance by addressing the hazard. Baselines and suppressions belong to the owner (3.4, 4).\n- Make the rule blocking in the project's entry point. Give it a terse message with a stable identifier that resolves to documentation versioned with the rule (3.5, 3.6).\n\n## 2. Load-bearing statements\n\n**2a.** Before the fix, the practitioner names the class, builds the rule, proves it fires on the instance or a fixture, and records a sweep count. The originating defect is fixed last, with a reproducing test.\n- \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\" (§2)\n- \"The [Defence] is committed with the originating [Instance] still present, and the fix is committed after it.\" (3.3, Part A)\n- \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\" (3.4)\n- \"Only then fix the original [Defect] in the normal way, with a test that reproduces it.\" (Appendix A)\n\n**2b.** The owner of the codebase may adopt a baseline or suppress an instance. An agent may not do either on its own authority. The owner is always a human, and the project's recorded owner is the one who decides.\n- \"Adopting a [Baseline], in whole or in part (clause 3.4).\" and \"Suppressing an [Instance]…\" are listed under \"Decisions that MUST go to whoever owns the codebase\" (§4).\n- \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\" (§4)\n- \"Nothing an [Agent] concludes on its own creates one.\" (§4, referring to an [Exception])\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect. Partial satisfaction is not conformance. The reviewer's verdict must rest on reproduction, not on the report.\n- \"A remediation [Conforms] if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\" (§7)\n- \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer reruns the [Defence] red at the commit that introduced it and green at the final commit, through the project's own entry point for accepting changes…\" (§7)\n- \"Partial satisfaction is not [Conformance].\" (§1, definition of Conform)\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n|---|---|---|\n| 3.1 | **Partial** | Commit A names the class and the hazard and records a wider class as not built. The record has no two independent search techniques, no account of what each found, and no statement on whether a runner check pins the reported behaviour. The reason given for not widening is not a hazard sentence confirmed by search. |\n| 3.2 | **Yes** | The rule is in the project's static analyser, which reads code, and there's no toolchain gap. |\n| 3.3 | **Partial** | Commit B holds the rule and fixture, and the CI run is red with the instance present, so the proof is met. But the `Report::render()` exclusion is an ignore comment, not a narrowing, and no sentence shows the hazard can't arise there. Under 3.3 that is a suppression for the owner. |\n| 3.4 | **No** | One instance was left unfixed behind an inline ignore, which the clause forbids (\"suppressing the [Rule] at the call site, which is not a fix at all\"). The count of five includes the fixture, which the sweep excludes, and no independent corroboration of the count is recorded. |\n| 3.5 | **No** | The rule is in the checks and fails the build. But its green run depends on a suppression with no recorded decision, and the project's records say nothing about inline ignores. |\n| 3.6 | **No** | The message is terse and carries a stable identifier. But the documentation is on a company wiki, not versioned with the rule, and the clause requires \"documentation versioned with the [Rule]\". |\n\n**Does the remediation conform under section 7?** No. Clauses 3.4, 3.5 and 3.6 fail outright, and 3.1 and 3.3 are incomplete. Conformance requires all six, and partial satisfaction doesn't count. The fixture also gives no reviewer reproduction, so no verdict rests on reproduction.\n\n**Does the defence alone conform?** No. Section 7 requires 3.1, 3.2, 3.3, 3.5 and 3.6 for a defence. Clause 3.5 fails because the suppression has no recorded decision, and 3.6 fails because the documentation isn't versioned with the rule. Clause 3.1 is not shown, because the two search techniques aren't recorded, so the rule can't be shown to be drawn within both bounds.\n\n**Which decision was not the practitioner's?** The inline `// ignore proj.caught-then-used` on `Report::render()` in Commit C. That suppresses an instance and leaves it unfixed, and section 4 says both go to the owner: \"Suppressing an [Instance]…\" and \"Accepting a known [Instance] as unfixed for any reason.\" The agent also did not raise it, which is the escalation section 4 requires. The rejection of the wider rule in Commit A is also arguably an owner decision under section 4, because the stated reason (\"most of the codebase's catches are legitimate\") is a false-positive concern.\n\n## 4. Confusion\n\n- Clause 3.1, lower bound: \"A [Rule] drawn to the exact value or name found in the [Defect] is the same [Narrowing] however it is spelt.\" The defined term [Narrowing] is elsewhere a reduction in scope, and this passage uses it for an over-narrow rule.\n- Clause 3.1: \"Both bounds measure breadth within one level; the level itself is a separate question, answered against the report.\"\n- Section 4, the bullet on the next wider rule: \"This does not reopen a [Class] whose bounds a [Hazard] sentence has settled, and it reaches no further than the one wider [Rule] already on the record.\"","ttft_ms":63746,"type":"result","duration_ms":71534,"uuid":"3b00fe30-9340-46bf-9caa-5d25c3fd70dc","ttft_stream_ms":1274,"time_to_request_ms":480,"first_content_frame_ms":1275,"queued_turn_count":0,"result_index":0}

reader-3

{"duration_api_ms":76018,"stop_reason":"end_turn","session_id":"dcaa8b26-f7dc-454f-a518-b4d90f2b67c7","total_cost_usd":0.01488763,"usage":{"input_tokens":2,"cache_creation_input_tokens":29207,"cache_read_input_tokens":2153,"output_tokens":18049,"output_tokens_details":{"thinking_tokens":15880},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29207,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":18049,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29207,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29207},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":18049,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29207,"webSearchRequests":0,"costUSD":0.01488763,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":15880,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Before fixing a defect, work out which class of pattern it belongs to and what hazard that class carries.\n- Search for other instances of the class by at least two independent techniques, and widen or narrow the class to match what the search finds.\n- Express the class as a blocking rule in a detector that reads code, with a stable identifier and documentation shipped with the rule.\n- Prove the rule fires, keeping the red run as its own commit.\n- Sweep the codebase, record the count, and fix every instance. Baselines, suppressions and leaving instances unfixed belong to the owner.\n- Make the rule permanent and failing through the project's own entry point, then fix the original defect with a test that reproduces it.\n\n## 2. Load-bearing statements\n\n**2a.** The practitioner attributes the defect to a class, builds and proves the rule, then sweeps and records the count, all before fixing. The original defect is fixed last.\n- \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\" (§2)\n- \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\" (3.4)\n- \"Only then fix the original [Defect] in the normal way, with a test that reproduces it.\" (Appendix A)\n\n**2b.** Only the owner may adopt a baseline or suppress an instance. An agent may not do either on its own authority.\n- \"**Owner**: Whoever holds authority over a project's [Exceptions] and [Calibrations]. Always a human.\" (§1)\n- \"Adopting one is a decision for whoever owns the codebase, taken after fixing has genuinely been attempted rather than on the strength of the [Instance] count alone.\" (3.4)\n- \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\" (§4)\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect. The verdict that they were followed must rest on reproduction, not on the report.\n- \"**A remediation [Conforms]** if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\" (§7)\n- \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer reruns the [Defence] red at the commit that introduced it and green at the final commit...\" (§7)\n- \"Partial satisfaction is not [Conformance].\" (§1, \"Conform\")\n\n## 3. Application\n\n| Clause | Verdict | Reasoning |\n|---|---|---|\n| 3.1 | No | The class, hazard sentence and wider class are recorded. But no two independent search techniques are named or run, and the record does not say whether a Runner check pins the zero-return behaviour. The reason given for not building the wider rule (\"most are legitimate\") is not a hazard-absence sentence confirmed by search. |\n| 3.2 | Yes | The rule is an analyser rule that reads code, not a test. |\n| 3.3 | Partial | Commit B is a red run kept as its own commit, with the instance present and real findings. Part B is not met: `Report::render()` is excluded by an inline ignore with no confirmed hazard-absence sentence, and Part B sends that to the owner. |\n| 3.4 | No | Four non-fixture instances were found. Three were fixed and one was suppressed rather than fixed. The count is not corroborated by an independent search. |\n| 3.5 | No | The rule fails in CI, but the green run depends on an ignore that no recorded decision covers. The project's records \"say nothing about inline ignores.\" |\n| 3.6 | No | The message is terse and carries the identifier. The documentation is on a company wiki, not versioned with the rule, and §3.6 says that does not conform. The content does cover what, why and the correct construction. |\n\n**Remediation under §7:** does not conform. Clauses 3.1, 3.4, 3.5 and 3.6 are not met. The fixture also shows no reviewer reproduction: the only run described is the practitioner's local one in commit E.\n\n**Defence alone under §7:** does not conform. It requires 3.1, 3.2, 3.3, 3.5 and 3.6. Clause 3.5 fails because the green run depends on an uncovered suppression, and 3.6 fails because the documentation is not versioned with the rule. Without the search, the fixture also does not show the class is drawn within both bounds. The merge commit does keep all five commits, so the proof survives the merge as §3.3 requires.\n\n**Decisions §4 says were not the practitioner's:**\n- **The inline `// ignore proj.caught-then-used` on `Report::render()`.** This is \"Suppressing an [Instance]\" and \"Accepting a known [Instance] as unfixed for any reason,\" both of which MUST go to the owner. The project has a named owner, so the default-owner route in §1 does not apply. The practitioner should have escalated and did not.\n- **Not building the wider rule.** The stated reason is that most catches are legitimate, which is closest to §4's bullet on leaving a rule narrower \"where the reason is that the wider check is harder to build without [False positives].\" That goes to the owner, not the practitioner.\n\n## 4. Confusion\n\nOn my one reading, these passages were confusing:\n\n- **3.1, lower bound:** \"A [Rule] drawn to the exact value or name found in the [Defect] is the same [Narrowing] however it is spelt.\" The terminology section defines [Narrowing] as reducing scope so a rule stops reporting code that lacks the hazard. This passage uses the term for drawing a rule too narrowly.\n- **3.1:** \"**Both bounds measure breadth within one level; the level itself is a separate question, answered against the report.**\" I could not tell what \"the level\" is or how the report answers that question beyond the behaviour-versus-mechanism example.\n- **§4, last bullet under \"Decisions that MUST go to whoever owns the codebase\":** \"This does not reopen a [Class] whose bounds a [Hazard] sentence has settled, and it reaches no further than the one wider [Rule] already on the record.\" I could not determine what \"it\" refers to in each place.\n- **3.4:** \"...and the scale at which that becomes true is tremendous.\" I could not tell what scale the reader should take from this.","ttft_ms":69675,"type":"result","duration_ms":76647,"uuid":"1b317ba2-7926-4a25-a129-f77898822132","ttft_stream_ms":1382,"time_to_request_ms":623,"first_content_frame_ms":1382,"queued_turn_count":0,"result_index":0}

reader-4

{"duration_api_ms":50301,"stop_reason":"end_turn","session_id":"203da1b3-2d97-4769-816c-33d8c1b216ae","total_cost_usd":0.01192613,"usage":{"input_tokens":2,"cache_creation_input_tokens":29207,"cache_read_input_tokens":2153,"output_tokens":12126,"output_tokens_details":{"thinking_tokens":9833},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29207,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":12126,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29207,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29207},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":12126,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29207,"webSearchRequests":0,"costUSD":0.01192613,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":9833,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"# Questions for a cold reader of SPEC.md\n\n## 1. Restatement\n\n- Before fixing, decide what class of pattern the defect belongs to. Attempt to express that class as a rule rather than judging in advance whether it is expressible.\n- Search independently, by at least two techniques, for other instances. The rule is not one of those techniques.\n- Build the rule as a blocking check in a detector that reads code, not a test. Prove it fires in its own commit, on the originating instance or a fixture.\n- Run the rule across the codebase, record the instance count, and fix every instance by addressing the hazard. Do not suppress, baseline, or leave instances unfixed without the owner's decision.\n- Make the rule permanent and failing in the project's own entry point, with a terse message and a stable identifier that resolves to documentation versioned with the rule.\n- Then fix the original defect in the normal way, with a test that reproduces it. Record any deferred defect in the project's decision record.\n\n## 2. Load-bearing statements\n\n**2a. Before fixing.** The practitioner attributes the defect to a class (3.1), builds and proves the rule (3.2, 3.3), and sweeps the codebase, recording the count, before fixing any instance (3.4). The defect is fixed last.\n- \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\" (section 2)\n- \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\" (3.4)\n- \"Only then fix the original [Defect] in the normal way, with a test that reproduces it.\" (Appendix A)\n\n**2b. Who may adopt a baseline or suppress.** The owner, who is always a human. An agent may not do either on its own authority.\n- \"Whoever holds authority over a project's [Exceptions] and [Calibrations]. Always a human.\" (Owner, section 1)\n- \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\" (section 4)\n- \"An [Exception] exists only where a human has discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own creates one.\" (section 4)\n\n**2c. What conformance means and what the verdict rests on.** A remediation conforms when all six clauses of section 3 were followed for that defect, and the verdict rests on reproduction. The reviewer reruns the rule red at the introducing commit and green at the final commit, through the project's entry point, and reruns the original symptom against the fix.\n- \"A **remediation [Conforms]** if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\" (section 7)\n- \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report.\" (section 7)\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n|---|---|---|\n| 3.1 | Partial | The class, its hazard sentence, and the next wider class are recorded. There are no two named search techniques, no findings from them, and no statement on whether a Runner check pins the reported behaviour. The rejection of the wider rule rests on \"most are legitimate\", not on a hazard sentence confirmed by search. |\n| 3.2 | Yes | `proj.caught-then-used` is a rule in the project's static analyser, which reads code. It is not a test. |\n| 3.3 | Yes | Commit B holds the rule and fixture, with the `Ledger::total()` instance still present, and CI is red. The fix comes in a later commit. The separate-commit requirement is met, and the merge kept all five commits. |\n| 3.4 | No | The CI output shows five findings, which is four instances plus the fixture. No independently corroborated count is recorded. `Report::render()` is left in place behind an inline ignore, which the clause forbids. \"Rendering must never throw\" is not a sentence showing the hazard is absent, since it does not address whether the result is used as if the call succeeded. |\n| 3.5 | No | The rule fails the build, and the local run passed. But the green run depends on an inline ignore, and \"The project's recorded decisions say nothing about inline ignores.\" Clause 3.5 says a green run depending on an uncovered suppression does not conform. |\n| 3.6 | No | The message is terse and carries an identifier, and the wiki page's content meets the requirements. But \"An external URL [Conforms] only where the project controls it and versions it alongside the [Rule].\" The company wiki is not versioned with the rule. |\n\n**Remediation conformance: No.** Clauses 3.1 and 3.4 are not met, 3.5 is not met, and 3.6 is not met. Conformance requires all six, so the remediation does not conform. The merge and commit structure do satisfy the reproduction requirement, but no reviewer has rerun the verdict.\n\n**Defence alone: No.** Section 7 requires clauses 3.1, 3.2, 3.3, 3.5 and 3.6. The defence fails 3.5 because it \"carries\" a suppression with no recorded decision. It fails 3.6 because its documentation is not versioned alongside it. Clause 3.1 is also only partly met, as above.\n\n**Decision not the practitioner's (section 4):** The inline `// ignore proj.caught-then-used` on `Report::render()`. Section 4 places \"Suppressing an [Instance]\" with the owner, and \"Accepting a known [Instance] as unfixed for any reason\" as well. Raising it with nobody was also the failure, since \"Uncertainty is itself an [Escalation] trigger.\" Secondarily, the decision not to build the wider rule rests on a hazard judgement that clause 3.1 says is to be routed upwards when uncertain. It is not clearly the \"no Instance exists today\" case that section 4 covers.\n\n## 4. Confusion\n\n- Section 4, \"What to do whilst waiting\": \"the [Rule] MAY merge at full width with that case recorded as a known [Instance] awaiting the [Owner]\". This sits against section 4's later sentence: \"provided the [Rule] does not become [Blocking] until every [Instance] is fixed\".\n- Section 3.3, Part B: \"**Then the proof, which runs the other way round, and both ways.**\"\n- Section 4, bullet on leaving the next wider rule unbuilt: \"The absence of an [Instance] is the [Practitioner]'s reason not to widen unasked; it is not authority to exclude the extension, and recording it as a known gap does not change whose decision it is.\"\n- Section 2, \"Attempt rather than pre-judge\": \"recorded where the project's other decisions are enumerable under clause 8.7, not only with the fix.\"","ttft_ms":42517,"type":"result","duration_ms":50798,"uuid":"3d866761-6818-4b95-a137-3293a45d3cfb","ttft_stream_ms":1119,"time_to_request_ms":491,"first_content_frame_ms":1120,"queued_turn_count":0,"result_index":0}

reader-5

{"duration_api_ms":76504,"stop_reason":"end_turn","session_id":"1cfe4150-033b-4cc7-9a85-23884a90e125","total_cost_usd":0.015169430000000001,"usage":{"input_tokens":2,"cache_creation_input_tokens":29206,"cache_read_input_tokens":2153,"output_tokens":18613,"output_tokens_details":{"thinking_tokens":16237},"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":29206,"ephemeral_5m_input_tokens":0},"inference_geo":"global","iterations":[{"input_tokens":2,"output_tokens":18613,"cache_read_input_tokens":2153,"cache_creation_input_tokens":29206,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":29206},"type":"message"}],"speed":"standard","fallback_credit":null},"modelUsage":{"claude-haiku-5-5":{"inputTokens":2,"outputTokens":18613,"cacheReadInputTokens":2153,"cacheCreationInputTokens":29206,"webSearchRequests":0,"costUSD":0.015169430000000001,"contextWindow":1000000,"maxOutputTokens":128000,"thinkingTokens":16237,"canonicalModel":"claude-haiku-5-5","provider":"firstParty","costBasis":"list"}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","fast_mode_disabled_reason":"sdk_opt_in_required","subagent_stats":{"spawned":0,"requested":{"background":0,"foreground":0,"unset":0},"started_in_background":0,"max_depth":0,"spawned_by_subagents":0,"completed":0,"failed":0,"killed":{"parent":0,"user":0,"system":0},"refused":{"depth_limit":0,"concurrency_limit":0,"budget":0},"by_type":{}},"safety_stops":0,"is_error":false,"num_turns":1,"subtype":"success","api_error_status":null,"result":"## 1. Restatement\n\n- Do not fix the defect first. Treat it as evidence of a class: the pattern, idiom or configuration that allowed it, with a one-sentence hazard.\n- Search for other instances by at least two independent techniques, chosen before the rule runs, and widen or narrow the class until the search is saturated.\n- Build a blocking rule in a detector that reads code, not a test or a warning. Give it a terse message, a stable identifier, and documentation versioned with it.\n- Prove the rule fires on the defect or a fixture, as its own commit. Narrow only with a written sentence explaining why the hazard cannot arise. Otherwise refer the question upward.\n- Sweep everywhere the pattern can occur, record the count, fix every instance, and make the rule permanent and failing through the project's own entry point.\n- Suppressions, baselines, leaving instances unfixed, and removing rules belong to the human owner. Only after that, fix the original defect with a reproduction test.\n\n## 2. Load-bearing statements\n\n**2a.** The practitioner names the class, builds and proves the rule, records the sweep count, fixes every instance, then makes the rule blocking with its message and documentation. Only after that do they fix the original defect with a reproduction test.\n\n- Section 2: \"When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is fixed.\"\n- Clause 3.4: \"The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance] count before fixing anything, and MUST then fix every [Instance] found rather than only the one that was reported.\"\n- Appendix A: \"Only then fix the original [Defect] in the normal way, with a test that reproduces it.\"\n\n**2b.** The owner, who is always a human, may adopt a baseline or suppress an instance. An agent may not do either on its own authority.\n\n- Section 1, Owner: \"Whoever holds authority over a project's [Exceptions] and [Calibrations]. Always a human.\"\n- Section 4: \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to be.\"\n- Section 4: \"An [Exception] exists only where a human has discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own creates one.\"\n\n**2c.** A remediation conforms when all six clauses of section 3 were followed for that defect. The verdict that they were must rest on reproduction, not on the report.\n\n- Section 7: \"A remediation [Conforms] if all six clauses of section 3 were followed for that [Defect], and the verdict that they were rests on reproduction, as this section closes by requiring, not on the report.\"\n- Section 7: \"A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer reruns the [Defence] red at the commit that introduced it and green at the final commit, through the project's own entry point for accepting changes, and reruns the originating symptom against the fix.\"\n\n## 3. Application\n\n| Clause | Grade | Reasoning |\n|---|---|---|\n| 3.1 | Partial | Commit A records the class and the hazard and names the wider class, but records no two search techniques run before the rule, no findings from them, and nothing on whether a Runner check pins the reported behaviour. Its reason for not building the wider rule (\"hundreds of such catches and most are legitimate\") is not a hazard-absence sentence confirmed by search. |\n| 3.2 | Yes | Commit B adds a rule to the project's static analyser, which reads code rather than running it. |\n| 3.3 | Yes | Commit B holds the rule, the fixture, and the originating `Ledger::total()` still in place, and CI is red on it. No narrowing is made. |\n| 3.4 | No | `Report::render()` was silenced with an inline ignore rather than fixed, and no independent corroboration of the count is recorded. The five findings also include the fixture, which the sweep count must exclude. |\n| 3.5 | No | The rule fails, and the local run of the project's entry point is recorded. But the green run depends on the inline ignore, which no recorded decision covers. |\n| 3.6 | No | The message is terse and carries the identifier, and the documentation has the right content. But the documentation sits on a company wiki, which is not versioned alongside the rule. |\n\n**Remediation under section 7: does not conform.** Clauses 3.4, 3.5 and 3.6 fail, and 3.1 is only partial, so not all six clauses were followed. The verdict is also not yet grounded in reproduction: commit E relies on a pull request description and a local run, and no reviewer rerun of the red and green commits is described.\n\n**Defence alone: does not conform.** Section 7 requires 3.1, 3.2, 3.3, 3.5 and 3.6. Clause 3.6 fails because section 7 requires the documentation to be \"versioned alongside\" the defence. Clause 3.1 is partial. Clause 3.5 also fails under its full text, though section 7 summarises that clause only as \"it fails rather than warns\".\n\n**Decisions that were not the practitioner's to take:**\n\n- **Primary:** the inline `// ignore proj.caught-then-used` on `Report::render()`, and merging with it in place. Section 4 says \"An [Agent] MUST NOT [Baseline], suppress or knowingly leave an [Instance] unfixed on its own authority\", and lists \"Suppressing an [Instance]\" among the decisions that must go to the owner. The project has a named owner, so the default-owner provision does not apply, and the agent did not escalate.\n- **Secondary:** leaving the wider rule (\"any caught error that is not rethrown or reported\") unbuilt. Section 3's table sends \"Whether the next wider [Rule] is built\" to the owner. Section 4's bullet on this point is written for rejection because no instance exists, which is not this case. The fixture's reason is about how many catches there are and whether they are legitimate, which is a hazard judgement that clause 3.1 says routes upward.\n\n## 4. Confusion\n\n- Section 4, the paragraph on waiting: \"provided the [Rule] does not become [Blocking] until every [Instance] is fixed\", set against the later paragraph: \"the [Rule] MAY merge at full width with that case recorded as a known [Instance] awaiting the [Owner]\". I could not tell whether a rule with a known instance awaiting the owner is blocking.\n- Section 7, on a conforming defence: \"it fails rather than warns\". Clause 3.5 separately requires that \"every [Suppression] it carries is covered by a recorded decision\", and section 7's summary does not mention that condition.\n- Section 2, \"Attempt rather than pre-judge\": \"naming at least two independent techniques tried at expressing the [Rule], in the vocabulary clause 3.1 uses for a search\". I could not match \"techniques tried at expressing the [Rule]\" to the search techniques clause 3.1 describes.\n- Clause 3.4: \"the scale at which that becomes true is tremendous\". I could not tell what scale this refers to.","ttft_ms":68884,"type":"result","duration_ms":77089,"uuid":"c300498a-44bc-477d-bfa7-4cb40806547f","ttft_stream_ms":1396,"time_to_request_ms":583,"first_content_frame_ms":1397,"queued_turn_count":0,"result_index":0}

@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 089: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

The exact prompt every reader received, part 1 of 2

===== FILE: QUESTIONS.md =====
# Questions for a cold reader of SPEC.md

You have been given `SPEC.md` and the fixture `fixtures/remediation-ledger.md`, and nothing else.
Read the document once, then answer every question below in order. Quote the document where a
question asks for a quotation. Do not consult anything outside what you were given.

## 1. Restatement

In your own words and in at most six bullets, what does this document require a practitioner to
do when they are handed a defect?

## 2. Load-bearing statements

Answer each part of each question in a sentence or two, then quote the sentence or sentences of
the document you are relying on.

- 2a. What does a practitioner do before fixing the defect they were handed, and in what order?
- 2b. Who may adopt a baseline or suppress an instance, and may an agent do so on its own
  authority?
- 2c. What does it mean for a remediation to conform, and on what must a reviewer's verdict on
  it rest?

## 3. Application

Grade the ledger remediation, as described in the fixture, against each of the six clauses of
section [3](../../SPEC.md#3-the-method): [3.1](../../SPEC.md#31-attribute-the-defect-to-a-class), [3.2](../../SPEC.md#32-build-the-net), [3.3](../../SPEC.md#33-prove-the-net-by-making-the-rule-fire), [3.4](../../SPEC.md#34-sweep-the-codebase-then-fix-every-instance), [3.5](../../SPEC.md#35-enforce-permanently-and-block), [3.6](../../SPEC.md#36-make-the-failure-message-terse-and-point-it-at-real-documentation). For each give Yes, Partial or No and one line of
reasoning. Then state whether the remediation conforms under section [7](../../SPEC.md#7-conformance) and why, whether the defence
alone conforms and why, and which decision in the fixture, if any, section [4](../../SPEC.md#4-authority-which-decisions-belong-to-whom) says was not the
practitioner's to take.

## 4. Confusion

Quote any passage you found confusing on your one reading, with its clause number. Do not say what
would make it clearer and do not propose any change. Write "none" if there was none.

===== FILE: SPEC.md =====
# Defence Before Fix: Method Specification

**Version**: 1.1.0, published 2026-10-02
**Companion to**: [the detector specification](DETECTOR-SPEC.md), version 1.0.0, and [the toolchain specification](TOOLING-SPEC.md), version 0.2.0
**Author**: [Joseph Edmonds](https://ltscommerce.dev), [Edmonds Commerce](https://edmondscommerce.co.uk)
**Coined**: 22 February 2026, in [the original article](https://ltscommerce.dev/articles/defence-before-fix-static-analysis)

> **Defence Before Fix** ([DBF]) is a phase that runs *before* a [Defect] is fixed. Rather than dropping
> straight into remediating the specific [Instance] in front of you, you first treat that [Instance]
> as evidence of a [Class], and you build the automated [Defence] that detects every occurrence of
> that [Class] across the whole codebase. The [Defence] is only trusted once it has been seen to
> fire.

The [Defect] is what makes this possible. It is a real, confirmed, impactful example of a harmful
pattern, which is precisely the raw material a good custom [Rule] needs and which speculative
[Rules] never have. Every [Defect] is therefore an opportunity to extend the codebase's permanent
defensive [Coverage], and that opportunity exists only in the window before the fix.

Defence Before Fix does not replace test-driven development. The specific [Defect] is still
reproduced with a test and proven fixed, exactly as normal. The two operate at different levels:
TDD addresses the [Instance], Defence Before Fix addresses the [Class].

Not to be confused with *Defence in depth*, which is a security term meaning something else
entirely. The [Defence] here comes before the fix in time, not in layers.

US spelling: **Defense Before Fix**. Abbreviated [DBF] throughout.

---

## Status of this document

This is version 1.1.0 of the specification. It is normative: section 3 defines the method,
section 4 states who decides what, and section 7 defines what [Conformance] means and who may claim
it.

**This document is the source of truth for what the method is.** Where anything else describing
Defence Before Fix disagrees with it, including the article in which the term was first published,
this document is correct.

Who coined the term, when it was first published, and what is and is not being claimed are recorded
separately in [provenance](PROVENANCE.md), which is meta information about this specification
rather than part of it. A short introduction to the method is in [the primer](PRIMER.md).

Changes are recorded in the changelog at the end.

## 1. Terminology

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in
RFC 2119.

#### DBF

The acronym for Defence Before Fix, used as shorthand for this method and for anything built under it. A [DBF] [Rule] is a [Rule] as this specification defines it; a [DBF] [Toolchain] is one that [Conforms] to the [Toolchain] specification; a [DBF] project is one that [Conforms] to section 7.

#### Defect

Any observed problem worth acting on: a bug, a code review finding, a performance observation, an incident, an inconsistency. The method does not care which.

#### Class

The pattern, style, idiom or configuration that permitted the [Defect], expressed generally enough that other occurrences of it are also [Defects] or latent [Defects].

#### Instance

One occurrence of a [Class]. The reported [Defect] is one [Instance]; there are usually others.

#### Hazard

The harm a [Class] causes, which is **not necessarily a failure**. It may be a failure, but it may equally be error hiding, or something merely sloppy that makes the code harder to reason about or to change safely.

#### Detector

A tool that reads code without executing it and reports occurrences of a pattern. A [Detector] is not a [Runner]. What a [Detector] must offer so that a [Practitioner] can write, prove, run and resolve a [Rule] in it is specified in the [detector specification](DETECTOR-SPEC.md).

#### Runner

A tool that **executes** code and reports what happened: a test [Runner], a compilation, a benchmark, a smoke check. A [Runner] answers questions about behaviour at one moment, on the paths it happened to exercise; a [Detector] answers questions about the text of the code, everywhere it exists. Detection under this method MUST come from a [Detector], for that reason.

#### Rule

One pattern definition within a [Detector].

#### Defence

A **[Blocking]** [Rule] together with the supporting documentation that clarifies it and clearly signposts best practice. A [Rule] without its documentation is not a [Defence], and neither is a [Rule] that only warns.

#### Coverage

The accumulated set of [Defences] a project has built. Every [Defect] handled under this method extends it.

#### Remediation docs

The fuller documentation a failure [Message] points to, explaining what a [Rule] is about, why it exists and how to fix a violation correctly.

#### Blocking

A [Rule] is [Blocking] when its firing prevents a change from being accepted by whatever the project uses to accept changes. A report that does not block is a **[Warning]**, and a [Rule] that only warns is not a [Defence].

#### Warning

A report that does not block. A [Rule] that only warns is not a [Defence].

#### Message

The text a [Rule] prints when it fires. Terse, and carries the [Rule]'s [Identifier].

#### Identifier

The stable string a [Rule] reports with, by which its [Remediation docs] are found. Survives renames of the [Rule].

#### False positive

A report on code that does not carry the [Hazard].

#### Narrowing

Reducing a [Rule]'s scope so it stops reporting code that does not carry the [Hazard]. [Narrowing] that excludes code which does carry the [Hazard] is a [Suppression].

#### Suppression

Any means of preventing a [Rule] from reporting an [Instance] that carries the [Hazard]: an ignore comment, an ignore entry, a [Baseline], or a [Narrowing] that excludes it.

#### Baseline

A recorded set of pre-existing [Instances] a [Rule] is configured not to report. A [Suppression] in bulk.

#### Exception

A recorded [Owner] decision that a specific [Instance] or scope is excluded from a [Defence]. The only legitimate form of [Suppression]. This document never uses the word in its programming-language sense.

#### Sweep

Running a [Rule] across the whole codebase to enumerate every [Instance].

#### Fixture

Code written to carry the [Hazard] deliberately, so a [Rule] can be proven to fire when the codebase has no [Instance] to prove it on.

#### Calibration

A project-level ruling on a threshold or parameter this document leaves open.

#### Practitioner

Whoever is doing the work of this method on a given [Defect], human or [Agent]. Decides how the [Defence] is built. Has no authority over [Exceptions].

#### Agent

A [Practitioner] that is an automated system. Everything said of a [Practitioner] applies to an [Agent]; section 8 exists because of what is additionally true of one.

#### Owner

Whoever holds authority over a project's [Exceptions] and [Calibrations]. Always a human. The project decides who, and clause 8.7 requires the answer to be findable. Where the project has recorded nobody, the [Owner] is whoever instructed the [Practitioner], and [Escalation] goes there. One limit applies to that default. The default [Owner] is usually the person under the same deadline as the [Practitioner], so a decision they take under section 4 to keep an [Instance] unfixed, [Suppress], [Baseline] or remove a [Rule] MUST be recorded in the project record with its justification. That record MUST mark the decision as taken by a default [Owner] and state why nobody better placed was reachable, where the next reader can see who decided and why. Such a decision SHOULD be revisited by someone not under that deadline before the next release. A project that wants the separation section 4 describes names an [Owner]; the default exists so work is not blocked, not to certify that a second judgement was applied.

#### Escalation

Referring a decision to the [Owner], whilst continuing with everything that does not depend on it.

#### Toolchain

Whatever a project assembles to run its checks through: its [Detectors] and [Runners], and the parts around them that route, list, record and resolve, from third-party, first-party and project-level parts in any combination. It is measured at the project level. What a [Toolchain] must offer beyond what each [Detector] in it offers is specified in the [toolchain specification](TOOLING-SPEC.md).

#### Conform

To satisfy every MUST of the relevant section 7 level, or of the companion specification being claimed. Partial satisfaction is not [Conformance].

## 2. When the method applies

Defence Before Fix applies to a [Defect] when that [Defect] can be attributed to a pattern, style,
idiom or configuration that a [Detector] can be made to recognise.

When it can, building the [Defence] is not optional and MUST be attempted before the [Defect] is
fixed. This is the substance of the method rather than a preliminary to it. The method begins at
clause 3.1, working out what [Class] the [Defect] belongs to; section 3 gives the order of the
steps that follow.

When it cannot, the [Defect] is outside the method's scope and is fixed conventionally. Not every
[Defect] is an [Instance] of a detectable pattern, and forcing a [Rule] where no pattern exists
produces [Detectors] that fire on innocent code, which is worse than having no [Detector].

**Attempt rather than pre-judge.** The [Practitioner] MUST attempt to express the [Class] as a [Rule]
rather than deciding in advance whether it is expressible. Failing to write a [Rule] that satisfies
clause 3.1's bounds is itself the evidence that the [Defect] is out of scope, and it is cheaper and
more reliable than a judgement made before trying. The conclusion that no pattern exists MUST be
stated in one sentence, naming at least two independent techniques tried at expressing the
[Rule], in the vocabulary clause 3.1 uses for a search, and recorded where the project's
other decisions are enumerable under clause 8.7, not only with the fix. It is the cheapest way
out of the method and the only one that would otherwise leave no trace; enumerating it is how an
[Owner] sees an [Agent] routing around the method, and naming the techniques is what lets
them judge whether the attempt was one.

There are three ways out of the method and they are recorded differently. No pattern exists: the
[Defect] is out of scope under this section, and only the sentence above is recorded. A pattern exists but the
language has no extensible [Detector] and a bespoke one is not practical: a [Toolchain] gap
under clause 3.2. A pattern exists, a
[Detector] exists, but the [Toolchain] lacks a mechanism the [toolchain specification](TOOLING-SPEC.md) requires: a
mechanism gap under clause 3.2, and the [Rule] is still built. The attempt that separates the first
from the second is complete when the [Practitioner] has checked the [Detectors] the project already
runs and the language's own [Detector] ecosystem for an extension point, and found none.

**Deferring the fix is not a fourth.** A [Defect] whose fix is postponed has not left the
method; the attempt this section requires is owed when the fix is taken up. A
[Practitioner] who finds a [Defect] and does not fix it now MUST record it where the project's
other decisions are enumerable under clause 8.7, naming the [Class] where one is already
apparent. Whether it stays unfixed is the [Owner]'s decision under section 4; the record is what
puts it in front of them. Reporting it in conversation does not satisfy this: the conversation
ends, and with it the only trace that the [Defect] was ever seen.

**Why**: each of the three ways out costs the [Practitioner] something to take and leaves
something an [Owner] can read: a recorded sentence naming two techniques tried, or a [Toolchain]
gap. Deferral costs nothing, and without this record it would leave nothing, which makes it
cheaper still than the sentence above and unseen from outside. A [Defect] mentioned once and
never written down cannot be told apart from a [Defect] nobody found, so neither the [Class] nor
the [Defence] it implies is ever reached.

## 3. The method

Six clauses, in order. In brief, before the detail:

| Clause | The [Practitioner] MUST                                                                                     | The record shows                                                                                                                                     | Goes to the [Owner] under section 4                                                                  |
| ------ | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| 3.1    | Name the [Class] the [Defect] belongs to, bounded both ways, after an independent search                    | The [Class], the [Hazard] sentence, the two search techniques, the next wider [Rule] not built, whether a [Runner] check pins the reported behaviour | Whether the next wider [Rule] is built, and whether the [Rule] stays narrower than the search showed |
| 3.2    | Express the [Class] as a [Rule] in a [Detector], never a test                                               | The [Rule], and any [Toolchain] gap that stopped a bespoke one                                                                                       | Whether a [Class] that could be defended is left undefended                                          |
| 3.3    | Make the [Rule] fire, on the [Instance] or a [Fixture], in a commit of its own                              | The red run, and the sentence behind every [Narrowing]                                                                                               | Any exclusion whose sentence cannot be written: that is [Suppression]                                |
| 3.4    | [Sweep] the whole codebase, record the count, then fix every [Instance]                                     | The count, corroborated, and what was fixed by hand or by pattern                                                                                    | Any [Instance] left unfixed, and any [Baseline]                                                      |
| 3.5    | Make the [Rule] permanent and [Blocking] in the project's own checks                                        | The green run through the project's entry point, and the recorded decision behind any [Suppression]                                                  | Removing or disabling the [Rule], and any [Suppression] added to it                                  |
| 3.6    | Print a terse [Message] with a stable [Identifier] that resolves to documentation versioned with the [Rule] | The [Remediation docs]                                                                                                                               | Nothing                                                                                              |

The fourth column is what an [Agent] or other [Practitioner] MUST NOT decide alone, clause by
clause; section 4 states the full list and governs where the two differ.

**Three of them turn on a judgement this specification deliberately does not close**: whether code
carries the [Hazard] (3.1, 3.3), whether a search was comprehensive (3.1, 3.4), and how broadly to
draw the [Class] (3.1). Any threshold given here would be calibrated to one codebase, one team and
one generation of [Toolchain], and would be wrong everywhere else.

**Those judgements are settled at project level, not in this document.** A project fixes its own
thresholds and parameters, in its configuration or in its [Toolchain]'s defaults, and where a case is
genuinely ambiguous it is raised, discussed and written down so the next person inherits the answer
rather than the argument. A project's accumulated decisions become part of its [Coverage] in the same
way its [Rules] do.

**A project MUST make those decisions discoverable**, by the same means as its [Defences], so that a
[Practitioner] arriving cold can find what has already been settled instead of guessing at it or
re-opening it. The burden is the project's, not the reader's: a decision that cannot be found by the
means clause 8.7 requires is, for the [Practitioner]'s purposes, a decision the project has not
recorded, and the paragraph below applies. The search is therefore bounded: enumerate the project's
[Defences] as clause 8.5 requires them to be enumerable, and whatever is reachable from there is the
record. Nothing further is owed before concluding that nothing is recorded.

**Where the project has recorded nothing, the [Practitioner] is not blocked.** Proceed on the
examples below, **state the [Calibration] assumed**, and record it with the [Remediation docs]
for the [Defence] being built, which clause 3.6 already requires to exist, to ship with the project
and to be reachable by a stable [Identifier]. That statement becomes the project's first recorded
decision on the point, and the next [Practitioner] inherits it.

A project MAY of course keep these decisions somewhere else and say so. **The default above exists
so that a [Practitioner] who finds no project convention still has one**, because "the project
decides" and "the project has decided nothing" would otherwise combine into a deadlock. What is
never acceptable is assuming silently, since a [Calibration] nobody knows was chosen cannot be
corrected.

Worked examples of the three, to calibrate against:

| Judgement         | Too little                                                                                         | Too much                                                                                   |
| ----------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| **[Hazard]**      | Only counting things that crash. Error hiding and code nobody can safely change are [Hazards] too. | Counting every stylistic preference, so the [Rule] defends taste rather than the codebase. |
| **Comprehensive** | One text search for the exact token from the reported [Defect], then stopping.                     | Auditing the whole system by hand before writing a [Rule] at all.                          |
| **[Class]**       | A [Rule] matching the variable name in the original bug report.                                    | A [Rule] matching every use of the language feature the bug happened to involve.           |

### 3.1 Attribute the defect to a class

The [Practitioner] MUST first establish what [Class] the [Defect] belongs to. The question is not
"what went wrong here" but "what kind of thing is this an [Instance] of".

**In order, this clause is five steps**, each detailed below:

1. Name the [Class] as a pattern, and write the one sentence that says what [Hazard] it carries.
2. Search the codebase for other [Instances] by at least two independent techniques, chosen and
   run **before** the [Rule] is run against the codebase, until the last technique adds nothing.
3. Widen the [Class] if the search found [Instances] the [Rule] would miss; narrow it if the
   [Rule] would match code that does not carry the [Hazard].
4. Where the [Class] stays at the reported [Instance], name the next wider [Rule] and why it
   was not built.
5. Record all of that, the five things listed at the end of this clause, before clause 3.2.

A worked example. The [Defect]: a currency conversion's error was caught and its empty result
used as a total. Step 1 names the [Class] "a caught error whose call's result is then used as if
the call had succeeded", with the [Hazard] "silent wrong output". Step 2 searches twice: a text
search for every catch in the codebase, read for whether the result is used afterwards, and a
reading of every caller of the fallible conversion and payment functions; the second finds two
[Instances] the first missed, so a third technique, asking the team which other calls fail
quietly, is run and finds nothing new. Step 3 keeps the [Class] as named because the [Rule]
would catch all four. Step 4 names the wider [Rule], "any caught error that is not rethrown or
reported", and records that it was not built because most of the codebase's catches are
legitimate and the [Owner] has not decided whether that is a [Hazard]. Step 5 writes the four
things down.

A [Defect] usually belongs to several overlapping [Classes] at different levels of abstraction, and
choosing between them determines the [Rule], the [Instance] count and the scope of the remediation.
Two bounds apply, and both are checkable during the work rather than matters of taste:

- **Lower bound.** If the [Rule] catches only the originating [Instance], the [Class] is probably drawn
  too narrowly and MUST be widened, unless the independent search below has found no other
  [Instance] and the [Practitioner] records that the [Class] is genuinely singular today. A
  [Rule] that matches exactly one thing is an [Instance] [Detector] wearing a [Rule]'s clothes.
  A [Rule] drawn to the exact value or name found in the [Defect] is the same [Narrowing] however
  it is spelt, and where it stays because the search found nothing wider, the record MUST also name the next
  wider [Rule], the one with the [Instance]'s distinguishing detail dropped, and state why it was
  rejected. That rejection is tested as a [Narrowing] is under clause 3.3: a sentence stating why the
  [Hazard] cannot arise in the code the wider [Rule] would add, confirmed by search, with uncertainty
  routed upwards under section 4.
- **Upper bound.** If the [Rule] matches code that does not carry the [Hazard], the [Class] is drawn
  too broadly and MUST be narrowed. [False positives] destroy a [Defence]'s credibility faster than a
  missing [Rule] does. One report on code that does not carry the [Hazard] is enough; there is no
  tolerated rate.

**Both bounds measure breadth within one level; the level itself is a separate question, answered
against the report.**
Where the [Defect] was reported as a behaviour, a check that reported clean having run nothing, a
process that stopped before its work, the [Class] a [Detector] can read usually sits one level
below that [Hazard]: the mechanism of this [Instance], not the failure the report opens with.
That [Class] is the right one to build, because no [Detector] reads "this check still produces
its signal". It is not the whole answer. The record MUST state whether the reported behaviour is also
pinned by a check a [Runner] executes, under section 6, either one added with this remediation or
an existing one named, and where neither is offered, why not. A [Class] drawn at the mechanism with
the behaviour left unpinned defends the [Instance] thoroughly and the report not at all.

**Resolve the lower bound by searching independently, not by judgement.** The [Rule] is not the only
way to find [Instances], and it is the least trustworthy one whilst it is still unproven. Search for
other [Instances] by other means, whether that is a text search, reading the code, or asking someone
who knows the system, and, once the [Rule] exists under clause 3.2, confirm it catches what those
searches found. That confirmation is not one of the techniques.

That search MUST be a comprehensive one, carried out by a person, model or [Agent] competent to
carry it out. No fixed technique is prescribed, because what is comprehensive depends entirely on
the [Class] and the codebase. What is not acceptable is a cursory look that exists to discharge the
requirement, since this search is what the single-[Instance] conclusion and the [Sweep] count both
rest on, and a bad search validates a bad [Rule] silently.

**The stopping criterion is saturation, not effort.** Use at least two independent techniques, and
the search is complete when the last technique added found nothing the earlier ones had missed. A
second technique that turns up new [Instances] means a third is owed; a second that turns up nothing
new means the search is done. That is a test the [Practitioner] can apply and record without a
threshold, and it is the standard clause 3.4 refers back to.

Two techniques are independent when they would miss different things. A text search for the token
and a reading of the code paths that consume the value are independent; two text searches for two
spellings of the same token are one technique; asking someone who knows the system is a third. For a
[Class] defined by how code is spelt, writing the idiom the other ways it is commonly spelt and searching
the codebase for each of them is a technique, because a search for one spelling misses every other;
whether the [Rule] catches those spellings is the confirmation step, not the technique. The
[Rule] itself is never one of the techniques, because the search exists to check the [Rule].
A clean run of the [Rule] is therefore not evidence of saturation either: "the [Rule] found nothing
the reading had missed" is one technique and a [Rule] run, not two techniques, and the stopping
criterion cannot be applied to it.
The record MUST name both techniques, chosen before the [Rule] is run against the codebase, and
MUST state what each found that the other could not have checked. A technique counts only if it does
not invoke the [Detector] under proof in any configuration and would exist unchanged had the
[Rule] never been written; a [Sweep]-shaped run of the [Rule], however comprehensive it looks, is
never one of the two. Where a later technique shows that two earlier checks shared a blind spot, they
were one technique, and a third is owed.

That turns the single-[Instance] case into something checkable rather than a matter of taste. If an
independent search finds [Instances] the [Rule] missed, the [Class] was drawn too narrowly and the [Rule]
MUST be widened until it catches them. Fixing those [Instances] by hand does not discharge the
widening: they are still [Instances] the [Rule] missed, and where the [Practitioner] leaves the
[Rule] unwidened because the wider check is harder to build without [False positives], that is
the [Owner]'s decision under section 4 and is recorded as one. If an independent search finds nothing
the [Rule] did not already have, then one [Instance] is a reasonable conclusion rather than an
assumption, and the [Rule] is correct as written.

**What this clause leaves on the record**, before clause 3.2 begins: the [Class], spelt as a
pattern; the [Hazard] sentence; the two search techniques and what each found; the next wider
[Rule] that was not built, with the reason; and, where the [Defect] was reported as a behaviour,
whether a check a [Runner] executes pins that behaviour, or why not. A record missing any of the
five has not finished this clause.

**Why**: the [Class] is the unit of work. Everything downstream operates on it, so an error here
wastes all the effort that follows.

### 3.2 Build the net

The [Practitioner] MUST express the [Class] as a [Rule] in a [Detector], and the [Detector] MUST read code
rather than execute it.

A test MUST NOT serve as the [Detector]. A test proves that one input produces one wrong output; a
[Rule] finds the pattern wherever it occurs, including in code nobody thought to test.

Where an off-the-shelf [Rule] or a tightening of existing configuration genuinely detects the
[Class], using it [Conforms]. However, a [Toolchain] that supports only its own built-in [Rules] cannot
support the method in general, so **a [Conforming] [Toolchain] MUST allow bespoke custom [Rules]**.
The [Rules] that matter most are tightly coupled to the project and carry project-specific
knowledge, both in the pattern they match and, importantly, in the [Message] they emit.

Any tool that reads code and reports pattern matches qualifies, whatever its category. Static
[Detectors] such as PHPStan, ESLint, mypy and Clippy are the usual instruments; so are custom AST
walkers, architectural fitness checks, and [Detectors] that inspect a change before it lands. These
are examples rather than a permitted list, and a [Detector] is not disqualified for being unfamiliar.

Where the affected language has no extensible [Detector] available, the [Practitioner] MAY build a
bespoke one: a program of the project's own that reads code and reports matches of the [Class], run
through the project's own entry point like any other [Detector]. This is a last resort rather than
a default, because a [Detector] the ecosystem maintains is cheaper to keep and easier for the next
[Practitioner] to find, but a bespoke [Detector] is far better than none: the [Class] is defended,
and every clause of this method applies to the bespoke [Detector] unchanged, including the proof in
3.3, the enforcement in 3.5 and the [Identifier] and documentation in 3.6.

Only where a bespoke [Detector] is not practical either is the [Class] undefended. That is a
**[Toolchain] gap and MUST be recorded as one**, with the reason a bespoke [Detector] was not
practical, rather than treated as the [Defect] being out of scope. The distinction matters, because
the first is a decision somebody can revisit and the second quietly disappears.

A gap is recorded in the project record where the [Toolchain] defines one, as the
[toolchain specification](TOOLING-SPEC.md) requires it to; only where it does not is the location the project's choice, and
then the only requirement is that somebody deciding what to invest in the [Toolchain] would find
it. A gap recorded where nobody looks has been forgotten with extra steps.

There are two kinds of gap and they lead to different work. The language having no extensible
[Detector] and no practical bespoke one is the gap above, and the [Class] waits for one. The project's [Toolchain]
lacking a mechanism the [toolchain specification](TOOLING-SPEC.md) requires, such as a proving harness or an
[Identifier] resolver, is a mechanism gap, and it does not put the [Class] out of reach: the
[Practitioner] builds the [Rule], uses the substitutes clauses 3.3 and 3.6 already allow, and records
the mechanism gap alongside it. A mechanism gap that is also a breach of the [toolchain specification](TOOLING-SPEC.md)'s
own obligations, such as a [Toolchain] that cannot run its [Defences] on its own source, is recorded
against the [Toolchain]'s claim under section 7 as well, because that is the fact its [Owner] needs.
Where that record lives is the [Toolchain]'s own [Conformance] declaration, under the
[toolchain specification](TOOLING-SPEC.md)'s clause 9.2, or the [detector specification](DETECTOR-SPEC.md)'s clause 8.1 for a [Detector]; a [Practitioner] who cannot write there reports it by the channel
section 4 names, as a blocked decision.

**What happens next is not waiting.** Once the gap is recorded, the [Defect] is fixed conventionally
under section 2, with its reproduction test, and the work moves on. What the record changes is the
[Class], not the [Defect]: the [Class] is known to be undefended, and the next person to see an
[Instance] of it finds a decision to revisit rather than nothing.

**Why**: this clause is what distinguishes the method. Detection happens at the level of the
[Class], using an artefact that can be pointed at the whole codebase, and it happens before
anything is fixed.

### 3.3 Prove the net by making the rule fire

A new [Rule] MUST be proven to fire before it is trusted. It is never the goal to write a [Rule] and
be instantly green.

**This clause has three parts.** Part A: the proof, a red run kept as a commit of its own. Part B:
[Narrowing], which is the [Practitioner]'s to decide only when they can write down why the
[Hazard] cannot arise in what is excluded, and is [Suppression] for the [Owner] otherwise. Part C:
proving a [Rule] when the pattern is absent from the codebase, and what a [Rule]'s own code owes.

**Part A: the proof.**

**Proving and sweeping are two questions, not necessarily two runs.** Proving asks "does this [Rule]
work at all", and the answer is pass or fail. Sweeping, in clause 3.4, asks "how much of this [Class]
is present", and the answer is a count. A single execution of the [Rule] answers both, and no second
run is required. What matters is that the two answers are not confused with one another: a large
count does not make a [Rule] more proven, and a [Rule] that fired does not tell you the [Sweep] is
complete.

**The proof MUST survive as a commit of its own.** The [Defence] is committed with the originating
[Instance] still present, and the fix is committed after it. That first commit is the red run: it is
what a reviewer under section 7 checks out to reproduce the proof, and it is the only record that the
[Rule] fired on real code rather than on a [Fixture] alone. Where the [Defence] and the fix share a
commit, the red run can only be reconstructed by hand-reverting lines the reviewer has to guess at,
and the proof rests on that guess.
Surviving as a commit means a fresh checkout of that commit, followed by the project's own declared
setup, reproduces the proof. Dependency installation and generation driven by files the commit does
carry, a manifest, a lockfile, are that setup. Anything else the proof depends on that version control
does not carry, an empty directory, an ignored file, an artefact placed by hand, is state the commit
does not contain, and a reviewer under section 7
reproduces from a fresh checkout rather than from the [Practitioner]'s working tree, so a
proof that passes only there does not survive.
Both commits MUST remain individually reachable in the history the reviewer inspects. A merge that
flattens them into one destroys the proof, so a project whose merge policy does that MUST keep the
[Defence] commit reachable by another recorded reference, a tag or the retained branch, or MUST NOT
claim the remediation [Conforms]. How the project merges is its own business under section 8;
what must survive the merge is not.

**Part B: [Narrowing].**

**The decision comes first, and it turns on one sentence.** Can the [Practitioner] write down why
the [Hazard] cannot arise in the code being excluded? If they can, the exclusion is a [Narrowing],
it is theirs to make, and they record that sentence. If they cannot, or are not sure, the exclusion
is a [Suppression] and belongs to the [Owner] under section 4. Doubt is [Suppression]. The test is
the [Hazard], never the count. Two exclusions of the same [Rule] show the difference:

| Excluded code                                                             | The sentence                                                                        | Which it is                             | Who decides    |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------- | -------------- |
| A catch in test helpers that asserts on the caught error and returns it   | "The result is the error itself, so it cannot be used as if the call had succeeded" | [Narrowing]                             | [Practitioner] |
| A catch in a nightly export, excluded because the export "hardly matters" | Cannot be written: the caught result is still used, so the [Hazard] is present      | [Suppression], referred under section 4 | [Owner]        |

The sequence is: write the sentence; confirm it by search, the way clause 3.1's search confirms
the [Class], so that the excluded code is looked at and not assumed; record both. A sentence
that cannot be confirmed is a sentence that cannot be written, and the exclusion goes to the
[Owner]. Ask, for every exclusion, which row it sits in:

- If the excluded code **carries the [Hazard]**, the exclusion is [Suppression] and is forbidden to
  the [Practitioner], however many or few [Instances] it removes.
- If the excluded code **does not carry the [Hazard]**, the exclusion is precision and is required
  by clause 3.1's upper bound.
- If the [Practitioner] **is not sure which**, the exclusion is [Suppression], and it goes to the
  [Owner] with the doubt stated.

A [Rule] MUST NOT be narrowed because its [Instance] count is uncomfortably high. A high count is a
finding about the codebase, not a [Defect] in the [Rule]. **Firing on more than the originating
[Defect] is success.** A [Rule] that catches the reported [Instance] and forty-nine others has done
exactly what it was built to do, and the forty-nine are the reason the method exists.

**Then the proof, which runs the other way round, and both ways.** Where the change under proof is
that the [Rule] should stop firing on code that does not carry the [Hazard], three things are shown:
the [Rule] firing on that code before the change, from a commit still reachable in history and to the
same standard as a new [Rule]'s red run; the [Rule] not firing on it afterwards; and a retained
[Fixture] on which it still fires afterwards, the case that motivated the [Rule]. Where the narrower
shape was chosen from the outset and no wider [Rule] was ever built, the before state is a [Fixture]
of the wider pattern the [Rule] does not catch, retained as the record of what was left out.

A [Narrowing] reduces the [Practitioner]'s own work, which is why it is not left to self-report
alone. The sentence is recorded with the [Narrowing] as part of the [Rule]'s [Remediation docs];
the search confirms it. Code a [Narrowing] excludes MUST be searched to the standard of clause
3.1, and an [Instance] that search finds there disproves the sentence and reverses the
[Narrowing]. Every [Narrowing] MUST be enumerable by the
same means as the project's [Exceptions], so the [Owner] sees them in one place, and a
[Narrowing] that excludes more code than the [Rule] still covers MUST be reported to the [Owner]
as if it were a [Suppression]. The sentence remains the test of whether an exclusion is honest;
the search and the listing are what make a fluent dishonest one visible.
Where the [Toolchain]'s own listing shows the [Narrowing] with its sentence, that listing is
the record, and no separate prose is owed for it.

The sentence may turn out to be wrong. The method does not require the [Practitioner] to be
infallible; it requires the reasoning to be written down where the next reader, or the next
[Defect], can test it. A [Narrowing] with a recorded reason is correctable. One without is
indistinguishable from a [Suppression], which is why the sentence is the test and a percentage is
not.

**Part C: proving when the pattern is absent, and the [Rule]'s own code.**

**Where the pattern is present, prove the [Rule] against it.** At minimum the [Rule] MUST detect the
originating [Defect]. Where the [Defect] was found in code review rather than in production, the
pattern exists in the branch under review, so the [Rule] is written and proven there before the fix
lands.

**Where the pattern is genuinely absent, prove the [Rule] another way.** A [Rule] may legitimately be
green against the codebase: the branch that carried the pattern may have been rejected and
orphaned, the [Instance] may already have been fixed, or the [Rule] may be a purely proactive [Defence]
against a pattern that might occur but does not yet. In each of these cases the [Rule] MUST still be
proven, using [Fixture] code that demonstrates the pattern the [Rule] is meant to catch. That [Fixture]
SHOULD be kept as the [Rule]'s own test rather than deleted, so the proof re-runs whenever the [Rule] runs
instead of expiring the moment it succeeds.

**A green run proves nothing unless the [Rule] was loaded.** Before treating any run as evidence,
confirm the [Rule] was active in it, by enumerating the [Defences] that run was configured with or by
seeing it fire on a [Fixture] in the same run. A [Toolchain] analysing its own source with a
configuration that omits its own bundled [Rules] reports clean on every one of them, and both the
[Practitioner] and a reviewer have taken that for a pass.

**A [Rule] about how code is written may match its own source.** A [Rule] that forbids a construct in
[Rule] code will contain that construct, because it has to look for it. That match is not an
[Instance]: the construct there is the [Rule]'s subject, not its use, so the [Hazard] is absent and
clause 3.1's upper bound requires the [Narrowing]. Exclude it as precisely as possible, write the
sentence, and keep a test that proves the exclusion reaches nothing else.

**[Rules] are software, and SHOULD be built test-first like any other software.** How practical
that is varies considerably between [Detectors], some of which offer purpose-built [Rule]-testing harnesses
and some of which offer nothing, so this is a best-effort requirement rather than an absolute one.
Where a [Detector] makes [Fixture]-based testing impractical, an acceptance or smoke check that
demonstrates the [Rule] firing is an acceptable substitute.

[Fixtures] are part of the [Rule] and are versioned with it, under the co-location requirement in
clause 3.6. A [Fixture] is input to the [Rule]'s test, not part of the code the [Defence] enforces:
the [Sweep] excludes it under clause 3.4, and the enforced run does not report it, so there is no
circularity in a [Fixture] that deliberately fails the [Rule] it proves. Writing the [Fixture] is the [Practitioner]'s own decision under section 4, not something
to refer upwards.

Where they sit within the repository is the project's business. **Absent a convention, put them
where the project's existing tests live, and where there are none, alongside the [Rule]** - then say
which was chosen, as with any other [Calibration] under section 3.

**Why**: proving is the [Rule]'s own validation. A [Rule] that does not catch the [Instance] that
prompted it does not detect the [Class] it claims to, and shipping it would add a [Defence] that defends nothing
whilst defending nothing. This is the clause most easily skipped, because a green run feels like
success and looks like a clean codebase.

### 3.4 Sweep the codebase, then fix every instance

The [Practitioner] MUST run the [Rule] across the entire codebase and record the total [Instance]
count before fixing anything, and MUST then fix every [Instance] found rather than only the one
that was reported.

**"Entire codebase" means everywhere the pattern can occur, and nowhere it cannot.** For most
[Classes] that is a single language, because the pattern is a feature of that language, and sweeping
unrelated languages would be theatre. What the clause forbids is stopping at the package,
component or service that happened to report the [Defect] when the pattern plainly reaches further.
The question is where the pattern can exist, never where the bug was found.

Whether vendored dependencies, generated code and test [Fixtures] are in scope is the project's
decision, and it SHOULD be a recorded one rather than an implicit one, because a [Class] that is
excluded silently is indistinguishable from a [Class] that was never swept. **Where a project has
recorded no such decision, [Sweep] all first-party source in every language where the pattern can
occur, and exclude generated code and vendored dependencies**, then record that as the decision.
First-party source is code the project maintains in its own repository; generated code is what a
build step produces from other source; vendored code is a dependency copied in rather than
installed. Modified vendored code is first-party, because the project now maintains it. The two
exclusions are not a judgement that such code carries no [Hazard]. Unmodified vendored code is
somebody else's project, and an [Instance] found in it is reported upstream rather than fixed
in place; generated code carries an [Instance] only because its generator does, and the generator
is the first-party source the [Sweep] covers. An [Instance] the [Practitioner] notices in either
is recorded as a known [Instance] under section 4, not ignored.
A [Rule]'s own [Fixtures] under clause 3.3 are never [Instances]: they carry the pattern
deliberately, as the proof, and the [Sweep] count excludes them.

**The count MUST be corroborated independently**, to the standard set out in clause 3.1: a
comprehensive search by someone competent to make it. A [Rule] that is too narrow produces a small
count and a clean-looking [Sweep], and nothing inside the [Rule] can reveal that.

Where the independent search finds [Instances] the [Rule] did not, **clause 3.1 governs: the [Rule] was
drawn too narrowly and MUST be widened until it catches them.** The two are not permitted to
disagree, and the search is the authority, not the [Rule].

**Each fix MUST address the [Hazard] rather than the [Rule].** Throw on the absent case, or propagate
the absence explicitly so the caller decides: whatever the correct behaviour turns out to be. What is
forbidden is a change that turns the [Rule] green whilst leaving the failure mode intact, and
suppressing the [Rule] at the call site, which is not a fix at all.

**Supplying a default is usually the [Hazard] in another form**, not a fix. A default makes the absent
case look present, and the failure moves downstream to where nobody expects it and nothing names it.
A default is a fix only where absence is a legitimate state whose meaning the code defines, and then
the default states that meaning rather than filling the gap with a placeholder. This is the same
test as clause 3.3's [Narrowing] sentence and carries the same discipline: the [Practitioner]
MUST write down, with the fix, what the absence means and why the default is that meaning, and
where the sentence cannot be written the absence is an error and MUST be treated as one.

**Repetition is not the problem.** Where every [Instance] genuinely has the same correct answer,
applying that answer to all of them is right, and the fact that it looks mechanical is not an
objection. The requirement is that each [Instance] was actually examined and the same answer was
genuinely correct for it, rather than assumed correct because it was correct elsewhere. Where one
change is applied across many [Instances], the [Practitioner] MUST record which were examined
individually and which received the change by pattern, and MUST examine a sample of the latter
large enough, given how much the surrounding code varies, that a wrong answer there would have
been seen. "All examined" with nothing behind it is a claim, not a record.

**Fix them all.** There is no count at which a [Practitioner] stops fixing. A [Practitioner]
stops only when the next [Instance] needs a decision they lack the authority to make, and section 4
says which decisions those are; a hundred [Instances], or a thousand, is work rather than a
decision. A [Baseline] of the existing [Instances], so that the [Rule] blocks only new ones, MAY
be adopted where fixing every [Instance] is genuinely unfeasible, and the scale at which that
becomes true is tremendous. **No threshold is given here deliberately.** Any figure would be
calibrated to the [Toolchain] of the moment rather than to anything durable, and the honest measure is
the size of the change rather than the hours it would take, since the hours depend entirely on what
is doing the work.

**Under AI-assisted development a [Baseline] is almost never the right answer.** The historical case
for baselining was the cost of human hours, and that cost has largely collapsed: an [Agent] can work
through hundreds of [Instances] at a price that made a [Baseline] unavoidable a few years ago. Reaching
for one now is usually a habit rather than a judgement. This specification stops short of
forbidding [Baselines] outright, and only just.

Adopting one is a decision for whoever owns the codebase, taken after fixing has genuinely been
attempted rather than on the strength of the [Instance] count alone. "Attempted" is the
[Owner]'s judgement on the [Practitioner]'s report, not a threshold the [Practitioner] applies:
the [Practitioner] fixes until stopped by a decision they cannot make, and reports what was fixed and
what remains. That report MUST state the count fixed, the count remaining, what stopped the fixing,
and what would have to be tried next, so the [Owner] judges a record and not an adjective. **A
[Practitioner] executing this method does not have that authority** - see section 4. A baselined project SHOULD reduce the
[Baseline] over time and MUST NOT allow it to grow.

**Why**: consistency across a codebase is fundamental, and the [Sweep] is where this method
delivers most of its value. In the published worked example the reported [Defect] was one of
twenty-three; the other twenty-two were bugs waiting to surface in different contexts, reported
by different customers, at different times. A project that knows about twenty-three [Instances]
and fixes one has produced a documented list of [Defects] it has chosen to keep.

### 3.5 Enforce permanently, and block

The [Rule] MUST become a permanent part of the project's quality checks, and it MUST **fail** rather
than warn. Three things must hold for this clause to be met: the [Rule] is in the checks the
project runs to accept changes; it fails rather than warns; and every [Suppression] it carries is
covered by a recorded decision. A [Rule] that reports a violation without failing does not
[Conform], and neither does a [Rule] whose green run depends on a [Suppression] that no recorded
decision covers, since the checks are then passing around the [Instance] rather than enforcing
against it.

**Where those checks run, and what the project does when they fail, is out of scope.** Continuous
integration, git hooks, branch policy and release process are the project's own business; see
section 8. What this clause requires is that the [Defence] is permanent, applies to everyone rather
than to whoever remembers it, and produces a failure rather than a remark.

Removing a [Rule], or adding a [Suppression] for an [Instance], MUST be a recorded decision rather
than a silent edit, and belongs to the [Owner] under section 4, which governs. Where a [Rule]
carries a [Suppression] that no recorded decision covers, this clause is not followed until the
decision is recorded or the [Suppression] removed.

**Enforcement is demonstrated through the project's own entry point.** Running the [Detector]
directly shows that the [Rule] can fire; it does not show that the project's checks will run it.
The [Practitioner] MUST run the invocation the project uses to accept changes, over everything
that invocation covers, and see the [Rule] reported there. A [Toolchain] that wraps its
[Detectors] in its own command is asking for that command to be used, and a [Practitioner]
who bypasses it has proven the [Rule] and not the [Defence].

For a [Bundled defence], the project whose entry point demonstrates it is any project the
[Toolchain] is genuinely installed into, the [Consuming project] included, since that is
where the [Rule] will be enforced. A mechanism gap that stops the [Toolchain] running its own entry
point on its own source moves the demonstration to such a project; it does not weaken it. The
substitutes clause 3.2 allows, for proving under clause 3.3 and for resolution under clause 3.6, do
not extend to this clause: the demonstration here is through an entry point or it is not made. A
project created for the purpose, with the [Toolchain] installed into it from the source under
test, is such a project, so a [Toolchain] with no consumer yet is not excused.

**Why**: the purpose is that the mistakes of the past become structurally impossible to repeat, and
a [Warning] is not structure. A [Warning] is a suggestion, and suggestions decay under deadline
pressure, which is the condition under which the original [Defect] was written.

### 3.6 Make the failure message terse, and point it at real documentation

The failure [Message] MUST be terse, and it MUST carry a stable [Identifier] that resolves to
[Remediation docs] shipped with the project.

That documentation MUST state three things: what the [Rule] is about, why it exists, and how to
fix a violation correctly using the project's preferred approach.

A [Practitioner] who finds that an existing [Rule]'s printed [Identifier] does not resolve records
it where clause 3.2's gaps are recorded, naming the [Rule] and the [Identifier], with no [Rule] build of
their own to attach it to. It does not block the remediation. Where the mechanism cannot resolve
[Identifiers] at all, it is a mechanism gap under clause 3.2, not a [Toolchain] gap. Because that
gap also breaches the [toolchain specification](TOOLING-SPEC.md)'s own obligations, it is recorded against the
[Toolchain]'s claim as well, where clause 3.2 says, and it blocks that claim under section 7 until fixed.
Where the mechanism resolves others and only this [Rule]'s documentation is missing, it is a
fault in that [Rule]'s [Remediation docs], owned by whoever wrote the [Rule], and bears on
the project's claim rather than the [Toolchain]'s.

**The split between the two is by job, not by length.** The [Message] carries what was detected,
where, and the [Identifier]; it is read under interruption by somebody trying to get on with
something else. The documentation carries the reasoning and the remedy; it is read once, by
somebody who has decided to understand the [Rule], and it is maintained as the project's thinking
develops. Anything that would grow over time belongs in the documentation.

**A [Rule] and its documentation are one artefact and MUST be versioned as one.** They live in the
same repository and are committed together, or in a library the project depends on at a clearly
tracked version. A [Rule] carries knowledge specific to the project it defends, and its
documentation is where most of that knowledge actually sits, so anything that lets the two drift
apart destroys the value of both. The same applies to a [Rule]'s [Fixtures] under clause 3.3.

An external URL [Conforms] only where the project controls it and versions it alongside the [Rule]. A
link into a third-party wiki or a general article does not, because neither can be relied upon to
still describe this [Rule].

**Repository structure is left to the project.** Where these artefacts sit, how they are foldered,
whether [Rules] are vendored or depended upon: none of that is this specification's business. What
is required is that they move together.

The [Identifier] MAY be a [Rule] ID resolved by a command, an anchor in a documentation file, or a
URL. It MUST continue to resolve for as long as any released version can emit it, which is a
stronger requirement than surviving the current release. Where a [Rule] is redesigned such that its
meaning materially changes, a new [Identifier] SHOULD be issued and the old one SHOULD keep
resolving to an explanation of what became of it.

A [Message] that names a pattern without leading anywhere does not [Conform]. "Pattern X detected"
teaches nothing.

**Why**: the [Message] is read at the moment of failure, by someone who wants to get on with their
work, so it has to be short enough to read and specific enough to act on, whilst the reasoning
has to live somewhere it can be maintained and can grow. Splitting it this way keeps the [Message]
terse without losing the teaching, and the documentation becomes the project's accumulated
engineering knowledge rather than a comment nobody revisits. The best custom [Rules] are
opinionated documentation encoded as automation.

## 4. Authority: which decisions belong to whom

The method is frequently executed by somebody who does not own the codebase, including an [Agent]
acting under instruction. This section states which decisions that person takes alone and which
they do not.

**Decisions the [Practitioner] takes alone**, without seeking approval:

- What [Class] the [Defect] belongs to, and where its bounds sit (clause 3.1).
- Which [Detector] to use and how to express the [Rule] in it (clause 3.2).
- How to prove the [Rule], and what [Fixture] to prove it against (clause 3.3).
- What the correct behaviour is at each [Instance], and therefore what each fix should be (clause
  3.4).
- The wording of the failure [Message] and the [Remediation docs] (clause 3.6).

**Decisions that MUST go to whoever owns the codebase:**

- Adopting a [Baseline], in whole or in part (clause 3.4).
- Suppressing an [Instance], or removing or disabling an existing [Rule] (clauses 3.4 and 3.5).
- Accepting a known [Instance] as unfixed for any reason.

@LTSCommerce

Copy link
Copy Markdown
Member Author

Run 089: evidence under ACCEPTANCE.md, The record. Posted once; never edited, hidden or deleted.

The exact prompt every reader received, part 2 of 2


- Deciding that a [Class] will not be defended at all, where a [Rule] for it is achievable.
- Leaving unbuilt the next wider [Rule] that clause 3.1 required the [Practitioner] to name, where
  it was rejected because no [Instance] of it exists today rather than because the [Hazard] cannot
  arise there. The absence of an [Instance] is the [Practitioner]'s reason not to widen unasked; it
  is not authority to exclude the extension, and recording it as a known gap does not change whose
  decision it is. This does not reopen a [Class] whose bounds a [Hazard] sentence has settled, and
  it reaches no further than the one wider [Rule] already on the record.
- Leaving a [Rule] narrower than an independent search under clause 3.1 showed it should be, where
  the reason is that the wider check is harder to build without [False positives] rather
  than that the [Hazard] is absent from what it would add. The [Instances] the search found are
  fixed regardless; what stays with the [Owner] is the [Class] left partly undefended.

The dividing line is that the [Practitioner] decides **how the [Defence] is built** and the [Owner]
decides **what the codebase is permitted to keep**. An [Agent] MUST NOT [Baseline], suppress or
knowingly leave an [Instance] unfixed on its own authority, whatever the [Instance] count turns out to
be.

**The default position is that none of these are permitted at all.** [Suppressions], [Baselines] and
every other means of evading a [Defence] are not techniques with a threshold governing their use; they are
[Exceptions] to the method, and the standing answer is no. An [Exception] exists only where a human has
discussed it, agreed it and documented it for that project. Nothing an [Agent] concludes on its own
creates one.

**Uncertainty is itself an [Escalation] trigger.** Where the [Practitioner] is not confident that code
they are about to exclude is free of the [Hazard], that exclusion is a [Suppression] and belongs to the
[Owner], whatever it is called. This is deliberately asymmetric: [Narrowing] is the [Practitioner]'s
decision only whilst they are sure, and doubt routes it upwards. Without this, the [Hazard] judgement
in clause 3.3 would let a [Practitioner] suppress an [Instance] whilst never touching the clause that
would have escalated it.

**Where the work is entirely [Agent]-driven, do the work.** Fixing [Instances] is cheap now, and an
[Agent] that reaches for an [Exception] has almost always found a shortcut rather than a genuine
obstacle.

**What to do whilst waiting.** A decision awaiting the [Owner] MUST NOT stall the rest of the work.
The [Practitioner] completes everything within their own authority, reports the blocked decision
with the [Instance] count and what it would cost to fix, and leaves the [Rule] unmerged rather than
merged in a weakened form. Where the only thing pending is whether one matched case carries the
[Hazard], the [Rule] MAY merge at full width with that case recorded as a known [Instance]
awaiting the [Owner], enumerable under clause 8.7: the [Rule] is not weakened, the case is not
hidden, and the [Practitioner] is not pushed towards declaring that no pattern exists. Reporting a
[Sweep] of four hundred [Instances] and stopping is a useful outcome; quietly baselining them is not.

Report by whatever channel carries the rest of the [Practitioner]'s work: for an [Agent], its
output to whoever instructed it. An [Instance] in code the [Practitioner] is not permitted to
change is reported the same way, as a blocked decision with its count, and is never a reason to
narrow the [Rule] so that it stops seeing it.

**The [Instance] count alone never triggers [Escalation].** A large [Sweep] is work, not a blocked
decision. Stopping to report is what the [Practitioner] does when an [Owner] decision is genuinely
required, which means an [Exception] is being contemplated, or when they are uncertain whether an
exclusion carries the [Hazard]. It is not what they do because the number is uncomfortable. Five
hundred [Instances] with no [Exception] needed is five hundred [Instances] to fix.

A remediation MAY be delivered across several changes where the volume warrants it, **provided the
[Rule] does not become [Blocking] until every [Instance] is fixed**, since a [Blocking] [Rule] merged over a
codebase that still violates it either fails continuously or has been weakened to avoid doing so.
No [Instance] may be knowingly left unfixed at the end without the [Owner]'s decision.

**Why**: without this, an executing [Agent] either stalls at the first judgement call it cannot
authorise or takes the decision unilaterally, and the second failure is much harder to notice than
the first. Both were observed in cold readings of an earlier draft of this specification.

## 5. Ordering the quality checks

Defence Before Fix presumes quality checks ordered as follows, and the ordering is enforcing rather
than advisory:

1. [Detectors] - type checking, linting, custom [Rules]
2. Automated tests - unit, integration, functional
3. Build verification - services start, dependencies resolve
4. Human acceptance testing - visual review, workflow validation

[Detectors] MUST run first, and a failure at that level MUST stop the levels below it from
being treated as meaningful.

This constrains the **sequence of checks**, not the infrastructure that runs them. How the project
expresses that sequence is out of scope, per section 8. It is a property of the project, not a step
in any one remediation: a [Practitioner] handling a [Defect] is not required to reorder the
project's checks, and a project whose checks run in another order is a project that does not
[Conform], which is a finding to record, not work to do on the way to a fix.

**Why**: a [Detector] is preventive and a test is diagnostic. A [Detector] reads every file, every
time it runs, so it cannot miss a file merely because nobody thought to write a test for it.
Failures at a lower level also produce confusing results at the levels above, so any other order
costs time diagnosing symptoms of a problem the first level would have named directly.

## 6. Relationship to test-driven development

Defence Before Fix neither replaces nor competes with TDD. They operate at different levels, and
a full remediation uses both: a test, executed by a [Runner], pins the [Instance]; a [Rule], evaluated by
a [Detector], catches the [Class].

| Concern            | TDD                                | Defence Before Fix                           |
| ------------------ | ---------------------------------- | -------------------------------------------- |
| Operates on        | The specific [Defect]              | The [Class] the [Defect] belongs to          |
| Artefact           | A test                             | A [Rule] in a [Detector]                     |
| Proves correctness | For one behaviour, by executing it | For a pattern, by reading the code           |
| Answers            | "Is this bug fixed?"               | "Can this kind of bug still exist anywhere?" |

The specific [Defect] is still reproduced with a test and proven fixed, exactly as normal. What
this method adds is the phase before that work begins, in which the codebase's permanent [Defence]
[Coverage] is extended using the evidence the [Defect] has just supplied.

The name records the ordering. The [Defence] comes first, because once the fix has landed the
evidence is gone and the opportunity closes with it.

## 7. Conformance

[Conformance] is claimed at one of four levels. Partial [Conformance] MUST NOT be described as
[Conformance].

**A remediation [Conforms]** if all six clauses of section 3 were followed for that [Defect], and
the verdict that they were rests on reproduction, as this section closes by requiring, not on the
report.

**A [Defence] [Conforms]** if it satisfies clauses 3.1, 3.2, 3.3, 3.5 and 3.6: it is drawn to a
[Class] within both bounds and not to the reported [Instance], it is evaluated by reading code, it
was proven to fire against either the originating [Instance] or [Fixture] code, it fails rather
than warns, and its [Message] is terse and resolves to [Remediation docs] versioned alongside
it. A [Defence] can [Conform] whilst the [Sweep] is incomplete; what that describes is a
[Conforming] [Defence] over a codebase with known [Instances], which section 4 requires to be
recorded, not a [Conforming] remediation.

**A [Toolchain] [Conforms]** if it permits bespoke custom [Rules], supports the ordering in section 5,
and satisfies clauses 8.1 to 8.5 and 8.7: the [Practitioner] can run it, the result reaches them in
the output they are reading, [Identifiers] resolve mechanically, documentation states the correct
construction, and both the [Defences] and the project's recorded decisions can be enumerated. Clause
8.6 is a SHOULD and does not bear on [Conformance], deliberately: it is the one clause that acts
before a mistake rather than after, so no failing run can prove it absent, and [Conformance] is
claimed only over what a run can prove. The [toolchain specification](TOOLING-SPEC.md) names a
[Toolchain] that also satisfies it as [Conforming] with [Agent] support, which is the claim to
make when it is true. Those obligations fall in two documents: what each [Detector] the
[Toolchain] routes a [Defence] through must offer is stated in the
[detector specification](DETECTOR-SPEC.md), and what the assembled [Toolchain] must add is stated
in the [toolchain specification](TOOLING-SPEC.md), which requires the first as its own opening clause,
carries the ordering in section 5 and the correct construction of clause 8.4 as clauses of its own,
and states the listing and the project record that clauses 8.5 and 8.7 require.

**A project [Conforms]** if its [Defences] and [Remediation docs] [Conform], and if its recorded
decisions under section 3 are discoverable. Nothing here constrains how the project runs its checks
or what it does when they fail.

[Toolchain] [Conformance] is stated in full in the [detector specification](DETECTOR-SPEC.md)
and the [toolchain specification](TOOLING-SPEC.md) together, which give each of the obligations above as a
clause with its own reasoning, and add what a [Detector] and a [Toolchain] must provide so
that a project can meet its own. The summary here is normative and sufficient to judge a
[Toolchain] by; the companion documents are where a [Detector] maintainer and a
[Toolchain] author should work from.

A verdict on a remediation or a [Defence] MUST rest on reproduction, not on the report: the reviewer
reruns the [Defence] red at the commit that introduced it and green at the final commit, through the
project's own entry point for accepting changes, and reruns the originating symptom against the fix.
Where the [Rule] was proven against a [Fixture] under clause 3.3, the red run is against the
retained [Fixture]. Where the originating symptom cannot be reproduced through the entry point,
an incident or an observation at production scale, the reviewer says so and verifies the red and green
runs alone. A verdict on a [Toolchain] or a project rests on the reviewer exercising the clauses
named above, not on the claimant's report of having done so. A report that reads as [Conforming]
has not been shown to be.

A [Toolchain] MAY additionally audit its own [Rules] against clause 3.6, failing its own release if any
[Rule] fails something without resolving to [Remediation docs]. This is the strongest
available demonstration that the clause is honoured rather than asserted.

## 8. Operating a defence under AI-assisted development

*This section is normative.*

### The argument these clauses rest on

For a human developer, a failure [Message] that teaches is good practice. They may read it, may
internalise it, may ignore it, and which of those happens depends on their seniority, their
workload and how many times they have seen the [Message] before.

For an [Agent], the failure [Message] is the entire remediation loop. It is consumed as instruction,
in the same turn, every time, with no fatigue and no seniority gradient. A [Message] that resolves
to documentation explaining the correct approach does not simply block the [Agent], it redirects it,
and it does so identically on the thousandth occurrence as on the first.

That reframes the usual complaint about AI-written code. The difficulty was never that [Agents] make
mistakes, since people do too. The difficulty is that nobody built the channel to correct them at
the level of the [Class] rather than the [Instance].

If the value of the method depends on that loop closing, then what closing it requires has to be
stated rather than assumed. The clauses below are what a [Defence] must do to be usable by an [Agent]
at all.

### Scope: this specifies a defence, not a pipeline

**Continuous integration, git hooks, branch policy, review process and release management are out
of scope.** How a project chooses to run its quality checks, and what it does when they fail, is
the project's own business and no part of this specification.

What is in scope is the [Defence] itself: a [Rule] that reads code, the documentation that explains it,
and the requirement that the [Practitioner] can run it and act on the result. A specification that
told projects how to run their checks would be overreaching, and would be ignored for it.

### 8.1 The practitioner MUST be able to run the defence themselves

A [Rule] that only reports through infrastructure the [Practitioner] cannot invoke is not usable by
them, whatever it does for anyone else.

**Why**: an [Agent] that cannot check its own work against a [Defence] cannot iterate against it, so
the loop never closes in the turn where the mistake was made, which is the only moment it is cheap
to fix.

### 8.2 The result MUST reach the practitioner in the output they are already reading

The [Detector]'s own output, at the point of the work. Not exclusively a dashboard, a report artefact or
a summary elsewhere.

**Why**: a [Message] that teaches nobody, because nobody sees it, is the same as no [Message].

### 8.3 The identifier MUST resolve without a human

By a command the [Practitioner] can run, a file they can read, or a URL they can fetch.

**Why**: clause 3.6 requires the [Identifier] to resolve. This requires it to resolve *for the
reader*, which for an [Agent] means mechanically. An explanation that lives in a colleague's head
resolves for nobody at three in the morning either.

### 8.4 Documentation MUST state the correct construction, not only the prohibition

Explaining why the pattern is dangerous is not sufficient. The documentation has to show what to do
instead, specifically enough to act on.

**Why**: this is the difference between [Blocking] an [Agent] and redirecting it. A prohibition alone
leaves it to guess at the replacement, and it will guess.

### 8.5 A project's defences MUST be enumerable

A [Practitioner] MUST be able to list what defends this codebase, and read each [Defence]'s
documentation, without triggering it first.

**Why**: an [Agent] arriving at a codebase has no colleague to ask and no memory of last time.
Without this, a project's standards can only be learned by violating them one at a time.

### 8.6 A project SHOULD publish a summary of its defences suitable for an agent's context

One terse line per [Defence], stating the [Rule] as a standing instruction rather than as a failure
report, each linked to its full documentation. *"Error hiding is forbidden"* is the shape.

**Why**: everything else in this method operates after the mistake. This operates before it. A
summary small enough to sit in an [Agent]'s working context turns the accumulated [Defences] from a
series of ambushes into a description of how this project expects code to be written, and it costs
one table.

### 8.7 Recorded project decisions MUST be discoverable by the same means

The judgements section 3 delegates to project level, once agreed and written down, MUST be
reachable exactly as the [Defences] are.

**Why**: section 3 says a project's accumulated decisions become part of its [Coverage]. [Coverage]
nobody can find is not [Coverage], and every [Practitioner] who arrives after a decision will otherwise
re-open it.

## 9. Citation

> Edmonds, Joseph. *Defence Before Fix*, version 1.1.0. First published 22 February 2026.
> <https://ltscommerce.dev>

## Appendix A: Instructing an agent

Where an [Agent] is expected to follow this method, give it the clauses rather than the article.
This appendix restates sections 2 to 4; where the two differ, the sections govern.

> When you find a [Defect] of any kind, do not fix it yet.
>
> First work out what [Class] it belongs to: the pattern, style, idiom or configuration that allowed
> it. Do not decide in advance whether that is possible, attempt it. If you cannot write a [Rule]
> for it, say so and fix the [Defect] conventionally.
>
> Write a custom [Rule] that detects the [Class], in a tool that reads code rather than running it.
> Draw it so that it catches more than the single [Instance] you started from, but never so broadly
> that it matches code which does not carry the [Hazard].
>
> Do not trust the [Rule] as your only way of finding [Instances]. Search independently as well, by
> text search and by reading the code, and check the [Rule] catches what you found by hand. Make that
> search a thorough one rather than a gesture, because everything downstream rests on it. If your
> own search turns up [Instances] the [Rule] missed, widen the [Rule] until it catches them; the search
> wins, not the [Rule]. If it turns up nothing new, then one [Instance] is a supported conclusion rather
> than a guess.
>
> The [Hazard] is whatever harm the [Class] does, and it need not be a failure. Error hiding counts.
> So does something merely sloppy that makes the code harder to reason about safely.
>
> Prove the [Rule] fires before you trust it. It must catch the originating [Defect]. If the pattern
> is not present in the codebase, because it was already fixed or because you are defending
> against it pre-emptively, prove the [Rule] against [Fixture] code that demonstrates the pattern and
> keep that [Fixture] as the [Rule]'s test.
>
> Then run the [Rule] everywhere the pattern can occur, which usually means one language but never
> just the component the bug was reported in, and report the [Instance] count. Catching far more
> [Instances] than you expected is success, not evidence the [Rule] is too broad. Only ever narrow a
> [Rule] to exclude code that does not carry the [Hazard]; never to make the number smaller.
>
> Fix every [Instance]. Examine each one; where the same answer is genuinely right for all of them,
> applying it to all of them is correct. Never satisfy the [Rule] whilst leaving the [Hazard] in place,
> and never suppress the [Rule] at the call site.
>
> You do not have the authority to [Baseline], to suppress an [Instance], or to leave a known [Instance]
> unfixed, however large the count turns out to be. Those belong to whoever owns the codebase, and
> the standing answer is no unless a human has already agreed and documented an [Exception] for this
> project. If you hit one, finish everything else, then report the count and what fixing it would
> take, and leave the [Rule] unmerged rather than merging it weakened.
>
> A [Defect] you find and do not fix now is recorded where the project keeps its other decisions,
> naming the [Class] if you can already see it. Mentioning it in your output is not a record.
>
> If you are unsure whether code you want to exclude carries the [Hazard], you are suppressing it, so
> refer it upwards rather than deciding. Narrow only where you are confident.
>
> Otherwise, just do the work. Fixing [Instances] is cheap for you, and reaching for an [Exception] is
> almost always a shortcut rather than a real obstacle.
>
> Make the [Rule] a permanent part of the project's quality checks, failing rather than [Warning]. Write
> its failure [Message] terse, carrying a stable [Identifier] that resolves to documentation shipped
> with the project saying what the [Rule] is about, why it exists and how to fix a violation
> correctly. Check you can run the [Rule] yourself and read its output, because if you cannot, nor can
> the next [Agent].
>
> Only then fix the original [Defect] in the normal way, with a test that reproduces it.

## Changelog

| Version | Date       | Change                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1.0.0   | 2026-09-08 | Initial specification, formalising the method published on 22 February 2026. Revised before publication after three independent cold readers understood the method correctly and still could not execute its judgement calls.                                                                                                                                                                                                                                                                                                                                                        |
| 1.0.1   | 2026-09-08 | Clarity, no obligation changed: section 3 opens with a map of the six clauses; clause 3.1 opens with its five steps and closes with what it leaves on the record; clause 3.3 is in three named parts and its [Narrowing] part opens with the decision. The header and the terminology entries for [Detector], [Toolchain] and [Conform] name the [detector specification](DETECTOR-SPEC.md) 1.0.0 alongside the [toolchain specification](TOOLING-SPEC.md) 0.2.0, and [Conform] extends to the companion specification being claimed. Accepted under [ACCEPTANCE.md](ACCEPTANCE.md). |
| 1.1.0   | 2026-10-02 | New obligation: a [Defect] whose fix is deferred is recorded where the project's other decisions are enumerable under clause 8.7, naming the [Class] where apparent; whether it stays unfixed is the [Owner]'s decision under section 4, and a mention in conversation is not a record. Appendix A carries the sentence for an [Agent]. Section 7's definition of a [Conforming] remediation names reproduction in the same sentence. Accepted under [ACCEPTANCE.md](ACCEPTANCE.md).                                                                                                 |

<!-- Term link definitions -->

[agent]: #agent
[agents]: #agent
[baseline]: #baseline
[baselines]: #baseline
[blocking]: #blocking
[bundled defence]: TOOLING-SPEC.md#bundled-defence
[calibration]: #calibration
[calibrations]: #calibration
[class]: #class
[classes]: #class
[conform]: #conform
[conformance]: #conform
[conforming]: #conform
[conforms]: #conform
[consuming project]: TOOLING-SPEC.md#consuming-project
[coverage]: #coverage
[dbf]: #dbf
[defect]: #defect
[defects]: #defect
[defence]: #defence
[defences]: #defence
[detector]: #detector
[detectors]: #detector
[escalation]: #escalation
[exception]: #exception
[exceptions]: #exception
[false positives]: #false-positive
[fixture]: #fixture
[fixtures]: #fixture
[hazard]: #hazard
[hazards]: #hazard
[identifier]: #identifier
[identifiers]: #identifier
[instance]: #instance
[instances]: #instance
[message]: #message
[narrowing]: #narrowing
[owner]: #owner
[practitioner]: #practitioner
[remediation docs]: #remediation-docs
[rule]: #rule
[rules]: #rule
[runner]: #runner
[runners]: #runner
[suppress]: #suppression
[suppression]: #suppression
[suppressions]: #suppression
[sweep]: #sweep
[toolchain]: #toolchain
[warning]: #warning

===== FILE: fixtures/remediation-ledger.md =====
# Fixture: the ledger remediation

A fictional remediation, described as the pull request and its commits would show it. Grade it
against the document you were given. Where the description does not say, answer from what is
written.

## What the pull request shows

A defect: `Ledger::total()` silently returned zero when a currency conversion failed, because the
conversion call's error was caught and the empty result treated as a valid amount.

The practitioner, a coding agent, did the following, in this order, over five commits.

1. **Commit A.** Wrote a note in the pull request naming the class as "a fallible call whose
   error is caught and whose result is then used as if the call had succeeded", stating that
   the hazard is silent wrong output rather than a crash, and naming the next wider class it
   did not build, "any caught error that is not rethrown or reported", as not attempted because
   the codebase has hundreds of such catches and most are legitimate.
2. **Commit B.** Wrote a rule in the project's static analyser, identifier `proj.caught-then-used`,
   that flags a caught error followed by a use of the call's result in the same function. Added
   the rule to the project's analyser configuration. The commit also contains a fixture file
   reproducing the `Ledger::total()` shape, and the CI run for this commit is red on that rule
   with five findings: the fixture, `Ledger::total()`, and three other functions.
3. **Commit C.** Fixed `Ledger::total()` and two of the three others, so that the failure is
   propagated. For the third, `Report::render()`, the agent added an inline `// ignore proj.caught-then-used` comment with the note "rendering must never throw", and did not raise
   it with anyone. The CI run is green.
4. **Commit D.** Wrote the rule's documentation as a page on the company wiki, at a URL the rule's
   failure message prints in full. The page states what the rule forbids and why, and shows the
   propagating construction to write instead. The failure message is one line: the identifier,
   the file and line, and the wiki URL.
5. **Commit E.** Confirmed in the pull request description that the rule is set to fail the
   build, not warn, and that the whole quality invocation was run locally and passed.

The pull request was merged with a merge commit; all five commits remain in the history.

The project's recorded decisions say nothing about inline ignores. The project has a named owner.

LTSCommerce and others added 2 commits October 12, 2026 15:55
Made under the acceptance process of pull request 22 at 40f7895, on the text with
section 2's pointer to clause 3.1 and section 3's order. Five of five on every
question 2 item, and on every item of questions 1 and 3. No reader names either
paragraph the change altered; the seven findings lie in untouched text and are
noted in acceptance/ROUTED.md, one of them, clause 3.3 Part B's "Then the proof",
as a new row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Names runs 034 and 088 (failed) and run 089 (passed), last, each linked to its record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant