ADR 0020 B4: retire transitional round-1 research probes and close XML stage separation - #638
Draft
willhea wants to merge 10 commits into
Draft
ADR 0020 B4: retire transitional round-1 research probes and close XML stage separation#638willhea wants to merge 10 commits into
willhea wants to merge 10 commits into
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 showsdiff_bill.pyandmatching.pyare executable-identical to7fdbf62, 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:
db._similarity_pair, which B2 removed;round1_cost.pycalled_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-codingN = 14899and 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 at7fdbf62.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.pyalready checked that everyfrom deltatrack… importresolves. 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_groupstill resolves under its old name, so an attribute-existence guard would have caught six probes and certifiedround1_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_SMOKEshrinks 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:
db._similarity_pair) injected into a real runnable probe → gate red with theAttributeErrorsurfaced;TypeError;The first two ship as in-suite controls; all were reverted.
Documentation closed to the shipped architecture
matching.pystill said "Nothing imports this yet, and that is the design" while round 1 is written against it throughout. Corrected, along withRetrievedPopulation'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_textduplicationClassified 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_ruleand the canonical baseline; B4 does not reopen it.tests/data/round1_legacy_trace.jsonwas not regenerated — byte-identical to7fdbf62, stillsha256 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 gave2.37×and2.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
tests/test_research_probes.pyround1_b3_cost.pyunder the smoke gateround1_b3_cost.pyat full scaleruff check ./ruff format --check .7fdbf62The 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
developright now. The last broad reconciliation (5753875) was 2026-08-11; B2 (#629) and B3 (#632) landed 08-13 and 08-14.AGENTS.mddocs/decisions/0020-matching-stages.mddiff_bill.diff_billsanddiff_pdf._hunk_for_paired_blocksboth do today"docs/architecture.mdTESTING.mdCORPUS_SWEEPbut neither new environment variableUPDATE_ROUND1_TRACEand why it is opt-in, plus the probe execution gate and its closed manifestThe ADR was rewritten in place rather than annotated, per the rule
33d516eestablished: a live record carries no amendment sections, and text does not earn its place by having once been true. ItsStatus: Acceptedis deliberately unchanged —docs/decisions/README.mdis 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.py34 passed, ruff clean.diff_bill.pyandmatching.pyremain AST-identical to7fdbf62with docstrings stripped, andtests/data/round1_legacy_trace.jsonis stillsha256 aa17b5e9…44cc5.Deliberately not changed, and why
docs/research/provision-matching/spike.mdandproblem-framing.mdname_similarity_pairandreconcile_movesin the present tense, and spike.md has a section headed "How matching actually works today". These are dated research artifacts, but unlikeround1-audit.mdthey 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:418carries a comment "Mirrorsdiff_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.pywas described as sizing "what thefused 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_movesatdiff_bill.py:1829,classifyat 1831). The slice-2 section also still asked whether themove-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.mdwas strengthened past a table cell in the same pass. The primary architectureguide 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.mdargued a point on the premise that "ADRs areappend-only". That rule was replaced by
33d516ewith a living record that may be rewritten to stayaccurate — 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 onlylanguage 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.mdchange necessary, and by its criterion — what is stale —that is correct. The change here is an addition:
UPDATE_ROUND1_TRACEandDELTATRACK_PROBE_SMOKEwere 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
docs/architecture.md,AGENTS.mddiff_bill.py,matching.pytests/test_round1_preservation.pytests/data/round1_legacy_trace.jsonDoes 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.mdkeeps 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.