Skip to content

ADR 0020 B4: retire transitional round-1 research probes and close XML stage separation - #638

Draft
willhea wants to merge 10 commits into
developfrom
worktree-adr0020-b4-closure
Draft

ADR 0020 B4: retire transitional round-1 research probes and close XML stage separation#638
willhea wants to merge 10 commits into
developfrom
worktree-adr0020-b4-closure

Conversation

@willhea

@willhea willhea commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Final slice of the ADR 0020 round-1 XML work. B0–B3 are already merged (#624, #628, #629, #632); this closes the record behind them.

B4 changes no executable matching policy. The only src/ changes are docstrings. Verified structurally rather than by reading the diff: parsing both revisions and comparing their ASTs with docstrings stripped shows diff_bill.py and matching.py are executable-identical to 7fdbf62, and the comparison was checked against a deliberately mutated copy first so a green result means something.

Retiring the round-1 research probes

Eight probes went, seven of them because they no longer ran:

  • six wrapped db._similarity_pair, which B2 removed;
  • round1_cost.py called _match_collision_group(old, new) with its pre-B1 two-argument signature.

The eighth, round1_candidateset_cost.py, went for the opposite reason: it ran fine, while hard-coding N = 14899 and reporting it as its candidate population — a figure the audit's own §10 disowns. Retiring it rather than renumbering it, because the question it existed to inform (candidate storage scope) was settled in B2 on the semantic argument, not the cost one.

None were ported. §11 of the audit had already ruled these drafts should be promoted into tests by B0 rather than rewritten, and B0–B3 did exactly that. A port would produce a second, ungated answer to a question a test already owns — and not the audit's answer either, since every figure there is scoped to the fused matcher at 0ff0eb1e. The audit's appendix now maps each retired question to the executable invariant that inherited it (frozen oracle trace, the two synthetic fixtures, the tie-direction and CandidateSet-order controls, the round-1 completeness gate). All eight are readable at 7fdbf62.

The remaining runnable round-1 probe is round1_b3_cost.py.

A behavioural gate, because a symbol check could not see this

tests/test_research_probes.py already checked that every from deltatrack… import resolves. That gate was green throughout, for two reasons it cannot address: attribute access bypasses it, and a signature change bypasses every symbol-existence check there is_match_collision_group still resolves under its old name, so an attribute-existence guard would have caught six probes and certified round1_cost.py.

So the gate now executes the probes it declares runnable and takes the exit code as the verdict. The manifest is closed against what is on disk, so a probe added later is either run or the gate fails. DELTATRACK_PROBE_SMOKE shrinks the sample and never the code path, which was the fail-open risk and so the thing tested hardest.

Decisive negative controls demonstrated during local review:

  • a removed private symbol (db._similarity_pair) injected into a real runnable probe → gate red with the AttributeError surfaced;
  • a pre-B1 call signature → gate red with TypeError;
  • a positive control, so the runner is not simply failing everything;
  • the surviving probe's own equivalence assertion mutated → it reddened under smoke mode (205 vs 212 pairings), proving the reduced sample does not make it self-validating.

The first two ship as in-suite controls; all were reverted.

Documentation closed to the shipped architecture

matching.py still said "Nothing imports this yet, and that is the design" while round 1 is written against it throughout. Corrected, along with RetrievedPopulation's "B2 will consume", the preservation harness's "stage names B1 and B2 will introduce", and the audit's storage-scope entry, which said NOT FROZEN after B2 had frozen it. The distinction kept in each case is between a claim about the present, which had to change, and a record of why something was sequenced as it was, which is history and stays.

diff_text duplication

Classified as known performance debt, not an unfinished ADR 0020 architecture requirement, and not touched here. The two calls answer separate stage-owned questions — correspondence evidence computes BODY_UNCHANGED, classification computes the textual diff the output record carries — and #591 quantified and deliberately accepted that duplication as preservation cost rather than routing classification output across the stage boundary. No round-1 invariant depends on the call count. Optimization is deferred unless later end-to-end profiling justifies a separate change, and no call-count gate is added: that belongs with the PR that changes the behaviour.

Closure

No known decision-changing ADR 0020 XML invariant remains untested. Every consequential round-1 invariant — retrieval, evidence, assignment, classification boundary, preservation — has a named executable owner, and each one whose defect is corpus-invisible has a decisive negative control or a synthetic fixture. The Slice-A similarity assignment boundary is independently covered by test_the_extracted_rule_agrees_with_the_pre_refactor_rule and the canonical baseline; B4 does not reopen it.

tests/data/round1_legacy_trace.json was not regenerated — byte-identical to 7fdbf62, still sha256 aa17b5e9…44cc5, last touched by B0.

On the B3 timing ratio

2.37× is the historical B3 reported figure and remains what the audit quotes. Two full re-runs during this work gave 2.37× and 2.30×, so it is stable to a few percent rather than exact, and the absolute microsecond figures are not portable at all (~9.7 µs/group here against ~16 µs/group recorded in B3, on different machine state). The stable conclusion is the relative ordering and the gap — B3 sits between the pre-B3 traversal and collision-routing, keeping about a fifth of the headroom — not an exact timer result. The audit now carries that qualification. No new benchmarking was done and none is proposed.

Verification

Gate Result
tests/test_research_probes.py 57 passed, 1 skipped
round1_b3_cost.py under the smoke gate rc=0
round1_b3_cost.py at full scale rc=0
Fast suite 1939 passed, 4 skipped, 15 xfailed
ruff check . / ruff format --check . clean
Production executable AST vs 7fdbf62 identical (control proven able to fire)

The fast-suite count is 2 lower than before this slice, and both are accounted for: the retired probe's execution and import parametrizations.

Corpus/slow suites were not rerun locally — B4 changes no executable production code — and are left to CI.

Refs: ADR 0020, round-1 closure slice B4

🤖 Generated with Claude Code


Added after review: documentation reconciliation

A documentation audit ahead of merge found that the always-loaded instructional documents had been describing a matcher that no longer exists. None of this drift comes from B4 — it is live on develop right now. The last broad reconciliation (5753875) was 2026-08-11; B2 (#629) and B3 (#632) landed 08-13 and 08-14.

File Was Now
AGENTS.md "unique paths pair directly, collision groups … resolved by normalized division title, then text similarity" — written 2026-04-15, accurate for four months, falsified by B2/B3 The four stages, both orchestrations, which projection returns what, and that the similarity cutoff is a separate later assignment act
docs/decisions/0020-matching-stages.md thresholding "which diff_bill.diff_bills and diff_pdf._hunk_for_paired_blocks both do today" XML half corrected (that condition is what this record existed to remove); PDF half is still accurate and stays
docs/architecture.md Diff row summarised the fused matcher; file cited neither ADR 0019 nor 0020 though every other stage links its ADR Names the four stages and links both records
TESTING.md documented CORPUS_SWEEP but neither new environment variable Documents UPDATE_ROUND1_TRACE and why it is opt-in, plus the probe execution gate and its closed manifest

The ADR was rewritten in place rather than annotated, per the rule 33d516e established: a live record carries no amendment sections, and text does not earn its place by having once been true. Its Status: Accepted is deliberately unchanged — docs/decisions/README.md is explicit that status describes the standing of the decision, not whether it has been built.

Still green after the doc commit: fast suite 1939 passed / 4 skipped / 15 xfailed, tests/test_docs_consistency.py + tests/test_adr_index.py 34 passed, ruff clean. diff_bill.py and matching.py remain AST-identical to 7fdbf62 with docstrings stripped, and tests/data/round1_legacy_trace.json is still sha256 aa17b5e9…44cc5.

Deliberately not changed, and why

  • docs/research/provision-matching/spike.md and problem-framing.md name _similarity_pair and reconcile_moves in the present tense, and spike.md has a section headed "How matching actually works today". These are dated research artifacts, but unlike round1-audit.md they carry no date or commit pin, so "today" does not resolve for a reader. The proportionate fix is a provenance line anchoring each to the commit it measured — not rewriting the analysis. Left for a maintainer's call rather than edited unasked.
  • src/deltatrack/diff_pdf.py:418 carries a comment "Mirrors diff_bill.reconcile_moves", a function that no longer exists. Production source on the PDF track, out of scope here.
  • round1-audit.md §12 gives B3 and B4 an "as shipped" paragraph reconciling plan against reality, but B1 and B2 have none, though their proposed names (retrieve_division_candidates, GroupRetrieval, sole_candidate) never shipped. Nothing there claims them as current, so this is an asymmetry rather than a defect.

Second-reviewer pass on the documentation audit

An independent audit was run over the same question and named three files. Two matched what this
branch had already fixed (AGENTS.md, docs/architecture.md). The third, scripts/README.md,
was a genuine miss
and is now fixed.

The miss is worth recording, because it was a process failure rather than a judgement call: that
file was delegated, came back "no change needed", and the clean bill was accepted without a second
read — while every positive finding in the same sweep was independently re-verified. Verifying
positives and trusting negatives is a one-sided check, and a clean bill is exactly where a second
read is cheapest to skip and most expensive to be wrong about.

The file was also worse than reported. probe_matching_stages.py was described as sizing "what the
fused matching decision costs" and as measuring a "post-classification move pass" — the second is
wrong since #591 moved both retrieval rounds ahead of classification (verified: assign_moves at
diff_bill.py:1829, classify at 1831). The slice-2 section also still asked whether the
move-assignment pass "can be extracted" as its own stage, an open question that was answered by
shipping it. The probes and their methodology notes are preserved verbatim; the section is labelled
historical and points at the current record.

docs/architecture.md was strengthened past a table cell in the same pass. The primary architecture
guide now carries the stage chain and the rule that gives it force — retrieval policy controls
consideration, assignment policy controls correspondence
— plus the two constraints worth knowing
before touching the code: no pairing may be constructed outside candidate admission, and the
similarity cutoff is a separate later assignment act rather than part of the group competition.

One retired governance rule, corrected

docs/research/naming-architecture/README.md argued a point on the premise that "ADRs are
append-only". That rule was replaced by 33d516e with a living record that may be rewritten to stay
accurate — the same rule this PR follows in editing ADR 0020 in place rather than appending a note.
Left standing it is the failure mode that commit's own message names: stale governance text used as
a load-bearing premise, as ADR 0017's retracted packaging claim once was. The premise is corrected
and the conclusion left alone, since it does not rest on the rule.

The append only language in the PDF bakeoff pre-registration documents is deliberately untouched —
that is scientific pre-registration discipline, a different rule sharing a phrase.

Divergence left open for the reviewer

The second audit found no TESTING.md change necessary, and by its criterion — what is stale
that is correct. The change here is an addition: UPDATE_ROUND1_TRACE and
DELTATRACK_PROBE_SMOKE were documented nowhere, and the first is a footgun without its guardrails,
since regenerating the frozen trace to green a refactor destroys the evidence the refactor preserved
anything. It sits outside the minimal patch and can be dropped if a tighter diff is preferred.

Documentation simplification and removal is deliberately out of scope here and tracked separately.
This pass corrected what was false and added only what was load-bearing; several of these documents
are longer than they need to be, and shortening them is a different job from making them true.


Final retention ruling: the round-one research artifacts are deleted

The four-agent adversarial pass proved the probe execution gate works. That was not the question. A correct gate around an artifact that no longer earns permanent residence is still permanent machinery — so the artifact went, and the gate with it.

Deleted docs/research/provision-matching/probes/round1_b3_cost.py. Its timing ratio answers a closed historical question: what B3's unique-path migration cost against the pre-B3 traversal and the collision-routing alternative that was rejected. That is not an SLO, not a methodology result current output depends on, not a security property, not a frozen architecture requirement, and not a quantity CI must reproduce. The rejected counterfactual is not a production invariant. PR #632 holds the measurement. It is not replaced.

Deleted docs/research/provision-matching/round1-audit.md — not condensed again, with no replacement closure summary and no archive map. The two incorrect archive pointers the adversarial review found are the argument for this rather than against it: a tombstone mapping is one more thing that can be wrong, and this one already was.

Removed the round-one half of tests/test_research_probes.py — the manifest, the timeout, the subprocess helpers, the execution parametrization, all three can-fire controls, and the imports only they used. The two pre-existing gates serving the live provision-matching research program are untouched. The module docstring now says plainly that both are static, that neither can see attribute access or a moved signature, and that the limit is accepted because a probe whose question has closed is deleted rather than kept running.

Two source pointers went stale the instant the benchmark was deleted — one in diff_bill.py, one in the preservation suite. Both now carry the claim that survives (B3 sits between the two paths, nearer the cheaper one) and cite PR #632. The exact ratios are deliberately not restated: they move a few percent between runs, which is why they were never a gate.

Permanent ownership after this PR

Concern Home
Durable architectural decisions ADR 0020
Current contributor-facing architecture docs/architecture.md, AGENTS.md
Implementation contracts diff_bill.py, matching.py
Executable preservation + falsification tests/test_round1_preservation.py
Frozen independent expectation tests/data/round1_legacy_trace.json
The investigation Git and PR history

Does any remaining artifact exist solely to preserve the investigative history of B0–B4? No. Every round-one artifact left in the tree is a current decision, an executable invariant, a frozen input, or serves a still-live research question.

AGENTS.md keeps the retention rule, simplified to the durable policy and stripped of B4 narration. The standing requirement to pin a recovery commit for every retired artifact is gone — it was itself an instruction to create the defect this ruling removes.

willhea and others added 10 commits August 14, 2026 09:47
…the rest by running them

Seven probes in docs/research/provision-matching/probes could no longer run. Six reached the
fused matcher as `db._similarity_pair`, which B2 removed; `round1_cost.py` called
`db._match_collision_group(old, new)`, whose signature B1 and B2 extended. All seven failed only
when executed, and nothing executed them.

Each asked a question a B0-B3 executable invariant now owns, so they are removed rather than
ported to the staged machinery -- a port would produce a second, ungated answer to a question a
test already answers, and its numbers would not be the audit's, because the audit is pinned to a
matcher that no longer exists. They remain readable at 7fdbf62, and the audit's appendix records
which invariant inherited each one.

The static import gate could not see either defect. Attribute access bypasses it, and a signature
change bypasses every symbol-existence check there is: `_match_collision_group` still resolves
under its old name. So the new gate executes the probes it declares runnable and takes the exit
code as the verdict, with the manifest closed against what is on disk so a probe added later is
either run or reported.

`DELTATRACK_PROBE_SMOKE` shrinks the sample and never the code path, so the gate cannot pass by
skipping the call whose staleness it exists to detect. Three controls: a clean script must pass,
a removed-symbol script must fail with AttributeError, and a pre-B1 call signature must fail with
TypeError.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four places still described a state that three merged slices have changed, and one of them was
flatly false in a way that would make a closure reviewer doubt the record: matching.py said
"Nothing imports this yet, and that is the design" while diff_bill's round-1 stages are written
against it throughout.

No behaviour changes. The distinction each edit keeps is between a claim about the present, which
had to be corrected, and a record of why something was sequenced the way it was, which is history
and stays: the contracts really were introduced before anything imported them, and that rule
still governs whatever moves next.

The audit's storage-scope entry said NOT FROZEN. B2 froze it comparison-scoped, and the note now
records which argument actually decided it -- the semantic one, since only comparison-scoped
storage can hold one candidate carrying two invocations' proposals. The cost difference did not
decide it and should not be cited as though it had.

Its §10 candidate figures are marked pre-B3 rather than rewritten: 1,701 was true while the
unique path still paired by tuple construction, and B3 added the 14,001 unique pairings. The
correction §10 makes to 14,899 is unaffected and stands.

The `diff_text` double-call #623 flagged for B4 is NOT taken, and §12 and §14 now say why. Its
two call sites are in different stages and the second needs the diff's value rather than its
emptiness, so removing it routes a value across a stage boundary. That is an architecture change
owing a call-count gate that does not exist yet, and bundling it here would put closure evidence
and an optimisation on one result.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… claim forward

The retirement table credited only the invariant that made the redundancy question moot. The
claim itself did not disappear with the probe: round1_b3_cost.py asserts all four arms emit the
same pairing stream before it reports any ratio, and B4's execution gate now runs that assertion
on every test run. Mutating legacy_fast_path to drop its one-sided emissions reddens it.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…y the diff_text residue

round1_candidateset_cost.py ran cleanly, which is why it needed removing rather than repairing.
It hard-coded N = 14899 and reported that as its candidate population -- exactly the figure §10
of the audit disowns -- so B4's new execution gate would have certified that the probe RUNS while
it published a quantity nobody should read as the round-1 candidate population. Running is not
the same as informative, and an execution badge on a stale number is a false green of its own.

Its question is closed. B2 shipped comparison-scoped storage on the semantic argument -- only
that scope can hold one candidate carrying two invocations' proposals -- and the cost difference
did not decide it. The audit now points the CandidateSet scope decision at its durable executable
owners instead of at a synthetic benchmark maintained for a settled choice. The runnable manifest
is round1_b3_cost.py alone, and the bar for a ninth is a still-live research question.

The diff_text double-call is reclassified, not deferred with a caveat. It is known performance
debt, not an unfinished ADR 0020 architecture requirement: correspondence evidence computes
BODY_UNCHANGED, classification computes the textual diff the output record carries, and #591
quantified and accepted that duplication as preservation cost rather than routing classification
output across the stage boundary. No round-1 invariant depends on the call count. Optimization is
deferred unless later end-to-end profiling justifies a separate change, and no call-count gate is
added -- that belongs with the PR that changes the behaviour.

Two accuracy corrections to wording this slice introduced. Six probes reached _similarity_pair,
not seven; the seventh was a signature defect. And a second full-scale run of round1_b3_cost.py
gives 2.30x where the first gave 2.37x, so "reproduced exactly" was overstated: the ratio is
stable to a few percent, the absolute microsecond figures are not portable at all, and what is
invariant across runs is the ordering and the gap.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cture B1-B3 shipped

The last broad documentation reconciliation was 5753875, on 2026-08-11. B2 (#629) and B3 (#632)
landed on 08-13 and 08-14, so the always-loaded documents have been describing a matcher that no
longer exists for two days. None of this drift comes from B4; it is live on develop now.

AGENTS.md said unique paths pair directly and collision groups are resolved "by normalized
division title, then text similarity". That was written 2026-04-15 and was accurate for four
months. B3 put unique paths through the same four stages and B2 deleted `_similarity_pair`, so
both halves are now false -- in the one file every session loads as its architecture brief. It
now describes the four stages, both orchestrations, which projection returns what, and that the
similarity cutoff is a separate later assignment act rather than part of the group competition.

ADR 0020 said thresholding a correspondence score is something `diff_bill.diff_bills` and
`diff_pdf._hunk_for_paired_blocks` "both do today". Half of that is the condition the record
existed to remove, and removing it is what B1-B3 did: diff_bills is now orchestration and the
only round-1 cutoff sits in an assignment act that runs before classification. The PDF half is
still accurate and stays. Rewritten in place rather than annotated, per the rule 33d516e
established -- a live record carries no amendment sections, and text does not earn its place by
having once been true. The Status line is untouched: docs/decisions/README.md is explicit that
status describes the standing of the decision, not whether it has been built.

docs/architecture.md linked a governing ADR from every pipeline stage except this one, and cited
neither 0019 nor 0020 anywhere, while its Diff row still summarised the fused matcher.

TESTING.md documented CORPUS_SWEEP at length but neither environment variable this work
introduced. A contributor with a legitimate round-1 change had no pointer to UPDATE_ROUND1_TRACE
and, worse, none to the reason it is opt-in: regenerating the frozen trace to make a refactor
green destroys the only evidence the refactor preserved anything, and two of the behaviours it
binds move zero of the 27 committed pairs. The probe execution gate and its closed manifest are
documented alongside, including why an import check was not enough.

No executable production line changed; diff_bill.py and matching.py remain AST-identical to
7fdbf62 with docstrings stripped. Frozen trace untouched.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rent architecture

A second reviewer caught what this pass missed. scripts/README.md is a table of dev scripts a
reader treats as current, and two entries still described the engine ADR 0020 changed.

`probe_matching_stages.py` was introduced as sizing "what the fused matching decision costs".
There is no fused matching decision on the XML side any more, and the same sentence called the
move pass "post-classification" -- also wrong since #591, which moved both retrieval rounds ahead
of classification. Verified against diff_bills: assign_moves runs at line 1829, classify at 1831.
The probe is unchanged and still useful; what it measures is now named as the state ADR 0020 set
out to change.

The slice-2 section opened by asking whether the move-assignment pass "can be extracted" as its
own stage. It was, so the open question now reads as unfinished architecture. The section is
labelled historical and points at the current record instead. The probes and their methodology
notes are preserved verbatim -- they are the evidence behind the decision, and the fix is to stop
them posing as a description of the engine, not to rewrite them.

docs/architecture.md gains the boundary itself rather than a table cell. The primary architecture
guide is where a reader should meet the stage chain and the rule that gives it force -- retrieval
policy controls consideration, assignment policy controls correspondence -- plus the two things
worth knowing before touching the code: no pairing may be constructed outside candidate
admission, and the similarity cutoff is a separate later assignment act rather than part of the
group competition.

No executable production line changed.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs/research/naming-architecture/README.md argued that an absolute naming principle "would be
cited against good decisions later, which matters because ADRs are append-only". That rule is
gone: 33d516e replaced it with a living record that may be rewritten to stay accurate, and
docs/decisions/README.md now says so explicitly, down to forbidding the amendment sections the
old rule depended on.

Left as-is this is the failure mode 33d516e's own message names -- stale governance text used as
a load-bearing premise, the way ADR 0017's retracted packaging claim was once used to reject an
alternative fix. The conclusion is untouched, because it does not rest on the rule: an absolute
is citable for as long as it stands, and rewriting it still depends on someone noticing. Only the
premise moved.

The pre-registration documents under docs/research/pdf-backend-bakeoff/ also say "append only",
and are deliberately not touched: that is scientific pre-registration discipline, a different
rule that happens to share a phrase.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…the round-1 audit

Research artifacts are working material, not automatically permanent repository material. At
closure an artifact is retained only if it reproduces a consequential result, enforces an
invariant, documents a durable decision, or serves as a frozen input. Git history preserves the
investigative record, so retention is the exception that needs a reason -- and "it was expensive
to produce" is not one.

AGENTS.md carries the rule, with the two failure modes this repository actually hit. A probe that
no longer runs still reads as evidence: eight in provision-matching referenced a symbol removed a
slice earlier and nothing noticed, because nothing ran them. A probe that DOES run can publish a
quantity the project has since disowned, which is worse, because passing a gate makes it look
current. It also carries the obligation that makes retirement safe: record which executable test
inherited the question, and pin the commit the artifact stays readable at.

round1-audit.md is the first application. Nothing in the repository cites it -- no test, doc or
script -- and §9 says outright that its mutation table is implemented in
tests/test_round1_preservation.py, so the invariants left long ago. 877 lines to 514, by dropping
whole sections rather than rewriting: the pre-work source survey, the pipeline walkthrough now
owned by docs/architecture.md, the rule-by-rule stage map now owned by ADR 0020, proposed
signatures that shipped under other names or not at all, the oracle design that the test file's
own docstrings carry, the slice plan whose slices are all merged, and blockers that resolved.

Everything retained is byte-identical, because a measurement paraphrased is a measurement that can
drift from what was run. Section numbers are unchanged so surviving cross-references still resolve,
and the five that pointed into removed sections were repaired rather than left dangling -- a
reference to something deleted is the defect this rule exists to prevent, not its cost.

Deliberately still here: the corpus-invisibility measurements the synthetic fixtures rest on, the
negative-control table with each mutation's red count, the measured corpus facts, the retirement
map from each retired probe to the test that inherited it, the cost rulings, and the provenance
pin. Those are consequential results or durable decisions; the rest was the investigation.

No executable production line changed.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four agents attacked this branch: one tried to prove the audit prune lost something, one
fact-checked every new documentation claim against source, one attacked the probe gate with live
fault injection, one audited the commit series for contradictions. The gate held -- all five
injections fired, the manifest floor is real, and smoke mode was shown to skip no code path. The
documentation did not fare as well.

Two archive pointers were wrong, which is the exact defect the retention rule this branch adopted
exists to prevent:

- the audit's intro pinned its own pre-cut text to 7fdbf62, which predates 696281d and therefore
  does not contain it. The probes are at 7fdbf62; the document is at 542de8c^. Two artifacts, two
  pins, now stated separately.
- tests/test_round1_preservation.py cited round1_decisive.py as reproducing a measurement. That
  probe was deleted earlier in this same branch. Every sweep run over this work grepped markdown
  for retired probe names, so a live pointer in a .py file went unseen -- a hole in the method,
  not bad luck. The repo-wide sweep across all file types is now clean.

Three claims were wrong about the code:

- "eight probes referenced a private symbol removed a slice earlier" -- six did. One called a
  function whose signature moved, and one ran perfectly and was retired for publishing a disowned
  number. Those are three distinct failure modes, each invisible to the check that catches the one
  before it, which is a better argument for an executing gate than the version it replaces.
- the cross-division fallback was described as consuming what assignment left unclaimed. Its
  population has two sources, and on the committed corpus every participant is the other kind --
  contributed by a one-sided division, never assigned at all. The sentence named the half the
  corpus never exercises.
- scripts/README.md labelled probe_matching_stages.py and the slice-2 probes historical. Both were
  run: they execute against the current engine, and probe_matching_stages reports the same 496
  moved changes diff_bill.py's own docstring carries. Live probes, mislabelled. That came from
  applying an external reviewer's remedy without testing its premise -- the findings were verified,
  the prescription was not.

Two were self-inconsistencies with rules this branch itself wrote:

- the diff_text reclassification was deleted by the retention pass in the same series that made it,
  leaving a durable decision recoverable only from a commit message. It now lives in ADR 0020's
  Implementation section, and both call sites carry a self-contained pointer, which is what
  AGENTS.md prescribes for a defended choice.
- tests/test_research_probes.py said "six" in one docstring and "seven" in another, and its module
  docstring still announced "two checks" after a third was added.

No executable production line changed: the two new comments do not reach the AST, and diff_bill.py
and matching.py remain identical to 7fdbf62 with docstrings stripped, verified with a control that
detects a one-line mutation.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…that maintained them

Reviewer ruling on permanent retention. The four-agent review proved the probe execution gate
works; that is not the question. A correct gate around an artifact that no longer earns permanent
residence is still permanent machinery, and the artifact was the thing to remove.

Deleted round1_b3_cost.py. Its ratio answers a closed historical question -- what B3's unique-path
migration cost against the pre-B3 traversal and the collision-routing alternative that was
rejected. It is not an SLO, not a methodology result current output depends on, not a frozen
architecture requirement, and not a quantity CI must reproduce. The rejected counterfactual is not
a production invariant. PR #632 holds the measurement.

Deleted round1-audit.md rather than condensing it a second time, with no replacement summary and
no archive map. The two wrong archive pointers the adversarial review found are the argument: a
tombstone mapping is itself a thing that can be wrong, and this one already was.

Removed the round-1 half of tests/test_research_probes.py -- the manifest, the timeout, the
subprocess helpers, the execution parametrization and all three can-fire controls -- along with
the imports only they used. The two pre-existing gates that serve the live provision-matching
research program are untouched. The module docstring now states plainly that both are static and
cannot see attribute access or a moved signature, and that this limit is accepted because a probe
whose question has closed is deleted rather than kept running.

Two source pointers went stale the moment the benchmark was deleted, in diff_bill.py and in the
preservation suite. Both now carry the ordering that survives -- B3 sits between the two paths,
nearer the cheaper one -- and cite PR #632 rather than a file that no longer exists. The exact
ratios are deliberately not restated: they move a few percent between runs, which is why they were
never a gate.

TESTING.md keeps the frozen-trace guidance and loses the probe-execution section. AGENTS.md keeps
the retention rule, simplified to the durable policy: what to keep, delete the rest, update live
references, move durable conclusions to their authoritative home, and write no archival summary.
The B4 narration and the requirement to pin a recovery commit for every retired artifact are gone
-- the second was itself a standing instruction to create the defect this ruling removes.

Ownership after this: ADR 0020 for decisions, architecture.md and AGENTS.md for the current
picture, diff_bill.py and matching.py for the contracts, test_round1_preservation.py and its
frozen trace for preservation and falsification, and git history for the investigation.

No executable production line changed. Frozen trace untouched.

Refs: ADR 0020, round-1 closure slice B4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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