fix(handoff-index): a repo this run could not read IN FULL is not a repo it may DELETE - #1267
Conversation
…epo it may DELETE
REPRODUCED, not inferred. Two repos, one healthy and one whose committed
handoff-doc blob was removed from `.git/objects` (a blobless or partially
fetched clone, an incomplete object store):
badrepo unmeasured=None docs=0 unreadable=('claudedocs/handoff-*.md',)
partial_scope_warnings() == ()
rebuild_refusal() is None
main([--repo good, --repo bad, --rebuild, --write])
-> rc 0, DELETE params [['badrepo','goodrepo']], 2 INSERTs (both goodrepo)
"wrote 2 section row(s)" with no PARTIAL notice
badrepo's stored rows were deleted and zero re-inserted, at rc 0, on the
timer's own argv. The only signal in the whole run was one `⚠ UNREADABLE`
line on stderr.
AND THE FILING UNDERSTATED IT. One bad blob of TWO does the same thing from a
run that looks HEALTHIER, because it writes rows:
zarfrepo unmeasured=None docs=1 unreadable=1
rebuild_delete_labels(...) == ('zarfrepo',)
-> rc 0, DELETE [['zarfrepo']], 2 of 4 sections re-inserted
so the second doc's rows were deleted and never replaced. A fix scoped to the
filed symptom ("every doc unreadable") would have closed the fourth instance
and left the shape open.
THE COMMON SHAPE, ACROSS FOUR SPELLINGS. Authority over a DELETE was granted by
a NEGATIVE predicate — the absence of whichever failure had already been seen —
instead of by a POSITIVE demonstration that the run holds a complete
replacement. An unpredicated TRUNCATE, a scope over the wrong label set, a
refusal that checked "could it be READ" when the risk was "is the config WIDE",
and now a scope keyed on `unmeasured` ("did the ref resolve") when the DELETE
asks "did anything come back". A blacklist re-opens the hole for every failure
nobody has met yet, which is why four rounds of naming one more failure never
converged.
So the default is inverted, in ONE place: `incomplete_reason(d)` /
`may_replace_stored_rows(d)`. A label enters the delete scope only when the run
read that repo IN FULL; any incompleteness, of any kind, withholds it.
`rebuild_delete_labels`, `partial_scope_warnings`, `rebuild_refusal` and
`rebuild_plan_lines` now read that one predicate instead of each re-deriving
"is this repo good" from whatever field was nearest — which is what spelling 3
literally was.
BEHAVIOUR CHANGES
* the DELETE scope shrinks: a repo carrying ANY unreadable doc is out of it.
Its rows are preserved, and every doc that DID read is still upserted (an
ON CONFLICT insert refreshes without destroying).
* `unmeasured` is deliberately NOT set for the unreadable case. Calling a repo
that read 39 of 40 docs "UNMEASURED" would make that field mean both "nothing
came back" and "not everything came back" — the conflation this module has
been burned by three times — and would hide 39 readable docs from
`--offline` search over one corrupt blob. handoff_search's rc 6 / rc 7 split
therefore does NOT move; that coupling only existed for the UNMEASURED route.
* `--prune` now refuses while any repo is incomplete (rc 4), for the same
reason it already refuses an unmeasured one.
* NEW refusal arm, rc 4: "not ONE of the N repo(s) was read COMPLETELY". Before
this, "not every repo is unmeasured" implied "the delete scope is non-empty";
it no longer does, and `PostgresSectionStore.write` RAISES on an empty rebuild
scope by design. Ordered AFTER the zero-rows arm so `rows` is non-empty by
construction and its "re-run without --rebuild and it writes" remedy is true
— the sentence the all-unmeasured arm had to retract in its own state.
* prose: the PARTIAL warning drops "contributed NOTHING" (false of a partially
readable repo), the pre-flight grows a third bucket (a repo in NEITHER list
was invisible), the success line says "not read COMPLETELY", and `--json`
publishes `rebuildable` + `incomplete_reason` so a machine consumer reads the
same value the DELETE reads rather than re-deriving it.
TESTS — red at base a36d3a4, green at HEAD
13 failed, 221 passed @ a36d3a4 (new test file over the base library)
234 passed @ HEAD
11 of the 13 are the new regression tests; the other 2 are the renamed
pre-flight line ("DELETE (measured…" -> "DELETE (read in FULL…"). Six further
new tests are negative controls / invariant guards and are green at BOTH ends,
labelled as such.
The end-to-end test asserts the BOUND DELETE parameters through `main`, not an
exit code: the statement text, the row count and the exit code are identical
between the correct run and the destructive one.
MUTATION SWEEP — fresh tree per mutant, PYTHONDONTWRITEBYTECODE=1
M1 `if d.unreadable:` -> `if False:` KILLED (11 tests)
M2 -> `if d.unreadable and not d.docs:` KILLED (8 tests)
(the "close the 4th instance only" mutant)
M3 scope reverts to `d.unmeasured is None` KILLED (5 tests)
M4 warning reverts to `d.unmeasured` KILLED (3 tests)
P positive control (scope -> every label) KILLED (12 tests)
N negative control (true semantic no-op) SURVIVED, 234 passed
REACHABLE, not merely breakable: `TestTheAuthorityPredicateIsREACHEDByTheDeleteScope`
builds a `RepoDerivation` with `unmeasured=None` and `unreadable` non-empty, so
the earlier check provably does not always win — and M2 dies there with this
guard's own assertion (`assert None is not None`, and a delete scope holding the
extra label), not with a different guard's error.
⚠ No test in this module reaches a live Postgres; the store sits behind
`SectionStore` and the SQL text is what is pinned.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Gate verdicts — merged tree
|
…omplete-read-authority Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Re-gated on the TRUE merged tree — both sandbox tiers now GREEN
Re-merged (
The dev-host tier ( Everything under "Not verified" in the previous comment still stands — no test in |
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
…d in a state home.nix calls SUPPORTED
F1 (the one that mattered). The fourth `rebuild_refusal` arm — "not ONE repo was
read COMPLETELY", rc 4 — fires on the unit's own argv whenever no configured repo
is replaceable, and three absent checkouts plus one corrupt blob is exactly that.
MEASURED on the timer's argv (`--rebuild --write`, unscoped), one handle present
with one corrupt blob of three docs, three absent:
pre-widening : rc 0 DELETE + 18 INSERTs index refreshed, PARTIAL printed
PR head : rc 4 SQL kinds [] nothing written at all
here : rc 0 no DELETE, 18 INSERTs index refreshed, PARTIAL printed
`nix/home.nix` states in as many words that a handle pointing at an absent
checkout is SUPPORTED and that the price is a standing warning, so that arm
turned a supported state into `OnFailure=notify-failure@%n.service` every 6h with
the index frozen — the permanently-red gate this same function's all-unmeasured
arm had to unwind once already. Replaced with `rebuild_downgrade_reason`: the run
takes the remedy that refusal printed as its OWN remedy — write, without the
DELETE. Strictly non-destructive (ON CONFLICT refreshes), and `main` computes the
delete scope only when the downgrade does not fire, so an empty scope can never
reach `write()`. home.nix's enumeration corrected: the prune condition widened
from unmeasured to INCOMPLETE, and the fourth state is documented as a downgrade.
F2 `rebuild_plan_lines` was the one consolidated site still re-deriving from raw
fields — two of its three buckets read `d.unmeasured`. They are now a partition on
a new `incomplete_kind`, and a kind the plan does not enumerate is REPORTED rather
than dropped out of all three buckets.
F3 three surviving mutants killed, two of which produced self-contradicting
operator reports; plus the seam the audit asked for, binding the plan's DELETE
line to the scope `DELETE_SQL` is actually bound with.
F4 `--prune`'s refusal kept, its stated reason fixed: an unreadable doc raises no
spelling ambiguity and its rows are already outside `measured`, so "nothing to put
back" was false. The real reason — a run that could not read its corpus cannot
vouch for it at all — is now what it says.
F5 "`partial_scope_warnings` says so on every output path" was false: it
short-circuited when EVERY repo was incomplete and deferred to a refusal `main`
only consults under `--rebuild`. MEASURED, `--write` without `--rebuild`: rc 0,
24 rows, no PARTIAL anywhere. The claim is now true rather than narrowed.
F6 authority is per DERIVATION and the DELETE binds LABELS, so a healthy twin
grants authority over a broken twin of the same name. Pre-existing and NOT fixed
here (its fix moves the delete scope, the stored key and handoff_search's scoping
together); what is fixed is that the run stopped saying "left untouched" and "kept
their old rows" about rows it had just deleted. Residual recorded in-code and
pinned by a test that fails the day it is fixed.
F7/F8 a disjunction note moved to the arm whose opener is one, and a stale
`--prune` bullet corrected — both pinned structurally.
Mutation sweep: 11 mutants, fresh tree each, PYTHONDONTWRITEBYTECODE=1, no-op
negative control SURVIVED and a known-caught positive control KILLED; all 9 real
mutants KILLED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Round-1 audit: dispositionsMerged
F1 — the finding that matteredI took the fallback rather than narrowing the refusal. MEASURED on the timer's own The downgrade is strictly non-destructive ( What the downgrade costs, stated rather than discovered: with no DELETE, a section F3 — the three mutants, dyingFresh tree per mutant,
The last kills with exactly one failing test, which is also its reachability The seam ( Full sweep: 11 mutants, no-op negative control SURVIVED, a known-caught What remains OPEN
Gate — I am the gate (branch protection is off)All on the merged tree, fix commit
The two dev-host pytest failures, and why they are not this diff — the discriminating
GUARD 10's ATTRIBUTION arm prints |
|
Round 1 audit claims for PR #1267, for the round-2 delta to check against the diff. |
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
…reported as not having happened Six findings from the round-2 delta audit of #1267, plus one the fix work surfaced. The unifying defect in three of them is the same shape: a DECISION taken on the truthiness of a RENDERED STRING rather than on the fact it renders. F-A `partial_scope_warnings` chose its variant with `if not ok:` over a joined label list. A label is `Path(r).name`, so `--repo .` derives "" — and a run whose only replaceable repo is that one joined to "", took the all-bad arm, and printed "NOTHING WAS DELETED" over a bound `DELETE … ANY([''])`. One run, three contradicting statements; the sentence also contradicted itself ("all 2 … were incomplete" then naming one). Now `nothing_was_read_completely` — ONE owner, read by both `rebuild_downgrade_reason` and this arm, so the two spellings that disagreed cannot. Swept the SHAPE, not the site: the plan's `', '.join(x) or '(none)'` printed "(none)" for a non-empty bucket, and `derive_repo`'s `label or root.name` silently discarded an explicit "" and substituted the directory name — a third site, found by a fixture that asserted itself. F-B `incomplete_reason`'s trailing `return` was an unguarded fall-through, and `incomplete_kind`'s docstring told a reader a third kind "has exactly one place to be added". Adding one there alone rendered `docs-unreadable (0 of 3)` for a repo with ZERO unreadable docs — the mechanism built to make an unenumerated kind LOUD explained it with a confident wrong reason, published beside the true kind in adjacent `--json` fields. Now a branch table with no fall-through; an unenumerated kind reports itself as one and still withholds authority. Its guard patched `incomplete_kind` AND `incomplete_reason` together — structurally blind, on a justification that was measurably false (`may_replace_stored_rows` is already False with the kind alone). It now patches only the kind. F-C Three mutants that survived a full green suite, two on code this round added: the plan header ignoring `downgraded` (parameter, header string and main's call-site argument were ALL deletable with the suite green), the `kept` bucket reverted to the raw field, and the downgrade message dropping its stated cost. Each now has a guard; each shown dying by that guard. F-D Two comments named guards that do not exist, so a reader who greps one finds nothing and concludes the invariant is unguarded. Fixed — and pinned: `TestEveryGuardThisModuleNamesByNameActuallyExists` fails the suite on a dangling citation. It immediately found a third, pre-existing one (a truncated real name) that the audit had not. F-E The downgrade's stated cost was an order of magnitude too small. "A section REMOVED from a doc" reads as trailing-ordinal drift; the DELETE is per-repo-LABEL and the key is (repo, slug, section, ordinal), so what persists is EVERY row not re-derived — whole DELETED docs, and both slugs of a RENAMED one, returned side by side. One constant, `DOWNGRADE_COST`, quoted verbatim by nix/home.nix and pinned as a whole normalised string, so correcting one surface and not the others is now a failing test. F-F `--json` publishes `rebuild_would_be_downgraded`, so a machine consumer no longer word-matches "NOTHING WAS READ COMPLETELY" out of prose — the guard-on-WORDS this module refuses everywhere else. A distinct exit code (drift-check's rc 16 idiom) was CONSIDERED AND NOT TAKEN: this file is git-delivered (the unit runs the working-tree copy) while SuccessExitStatus is switch-delivered, so between a pull and the next switch the unit would be RED in a state home.nix calls SUPPORTED — the regression the previous commit exists to undo, re-entered through the deploy gap. Reasoning and the named residual (a shell consumer reading neither prose nor --json still cannot see it) are recorded at the decision site. Red→green at 6e818df: 13 tests red at the merge base, all green at HEAD. Mutation sweep, fresh tree per mutant, PYTHONDONTWRITEBYTECODE=1: 9 mutants all KILLED, no-op negative control SURVIVED (281 passed), positive control KILLED. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Round-2 delta audit — all six findings fixed, plus one the fix work surfacedHead Per-finding disposition
F-A — and the shape, not just the site
Consolidated into
F3's seam fixture widened as asked — F-B — the guard was blind, and its justification was falseThe old guard patched F-D — the pin immediately earned itselfFixed both dangling citations, then pinned the class. The new checker immediately found a third, pre-existing one the audit had not: F-F — why no new exit code
⚠ Residual, recorded in source: a shell consumer reading neither prose nor VerificationRed→green, base Mutation sweep — fresh
M8's three sites (parameter, header string, call-site argument) are each independently killed. M4 not chased — equivalent under reachable states, as the audit said. ⚠ M10 is an INVARIANT guard, labelled as one, not regression coverage. Its discriminating state (a third kind claiming a repo whose Gate — merged tree
Still open
|
|
Round 2 audit claims for PR #1267, for the round-3 delta to check against the diff. |
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
…guarded, and its docstring cited a check the same commit deleted
Final fix round. Four findings, all small; every code change below is one
expression.
F1 `nothing_was_read_completely`'s `bool(derivations) and` conjunct had no
test. The mutant that drops it — `return not any(...)` — SURVIVED a full
281-test run, because every fixture in the file passes at least one
derivation, so nothing ever reached the empty input. `any()` over an empty
sequence is False, so the mutant returns the vacuous True the conjunct
exists to refuse: `nothing_was_read_completely([])` flips False->True,
dragging `rebuild_downgrade_reason([])` from None to a REASON and
`derivation_json([])["rebuild_would_be_downgraded"]` to True. All three are
`__all__` exports and `handoff_search` imports the module; `main` is
insulated only because it returns RC_USAGE first. Now pinned on all three
surfaces.
(b) And the docstring is why it went unguarded: it said
`rebuild_downgrade_reason` "kept" an `if not derivations: return None` of
its own — the same commit had DELETED it, so `grep -n "if not derivations"`
matched the docstring making the claim and nothing else. Retracted: the
guard now lives here alone, and the docstring says so.
F2 The citation checker could not match ANY `test_` name containing a capital.
`test_[a-z0-9_]+` cannot cross an uppercase letter and `\b` cannot fire
mid-identifier, so such a name matched NOTHING — not a truncated prefix, no
match. Three real citations were invisible, TWO written by the previous
round; none dangled, so the checker was green and would have stayed green
through any rename. Widened to `[A-Za-z0-9_]`: 10 names scanned -> 13, still
zero dangling. The module's stated blind spot named only the non-`test_`
shape and is widened to what the code actually missed, and the repo-wide
dangling assertion grows a message naming the citation and the fix.
F3 The class's headline seam test built its empty-label fixture without the
self-assertion its sibling helper carries. Under the `derive_repo`-revert
mutant `empty.label` becomes `brindlemossrepo`, so cases[0] collapses into
cases[2] and cases[1] into cases[6]: the 7-case matrix silently becomes 5
and the "disagreeing input" the docstring names is gone, while the
`verdicts == {True, False}` control still passes. MEASURED: the seam test
passed ALONE under that mutant before this, and fails alone after.
F4 Six sites decided on the truthiness of a `str | None` whose owner spells the
question `is None`. Not divergent today — but that is the same "they agree
only while X is non-empty" argument that held for `if not ok:` until
`--repo .` made X empty, and it is the fourth instance of the shape this
branch has swept. Mechanical `is not None` at all of them
(`incomplete_kind`, `authority_label_collisions`, `partial_scope_warnings`
x2, `rebuild_delete_qualification`); no producer emits "" today, so no
behaviour changes.
Recorded, not fixed:
* the empty label renders blank on FOUR display surfaces, not the two the
tests exercise; count corrected at `derive_repo`. The fix belongs at `main`
(reject/normalise an empty --repo label), never in a renderer — a
`label or "(unnamed)"` there would re-introduce the exact falsy-string shape
just swept.
* M10's guard is labelled an INVARIANT GUARD in as many words, and stays
correctly out of the red-at-base matrix.
* authority-per-derivation vs DELETE-per-label, and the live-Postgres write
path, remain open and remain disclosed.
Mutation battery: fresh tree per mutant, PYTHONDONTWRITEBYTECODE=1, caches
cleared, `.git` detached from every copy. no-op control SURVIVED (283 passed);
inverted-authority positive control DIED (17 failures). F1's mutant now dies by
its own assertion (`assert True is False`); F2's dies on the narrow regex; F3's
seam test fails ALONE.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Claude-Session-Id: 90310ba1-6dc2-4779-a616-2ccfe89457d7
Round-3 delta audit — dispositionsFinal fix round. Four findings fixed, three recorded. Every code change is one expression; the behaviour-bearing diff is 5 lines, all of them Merged FIXEDF1 — the new guard was itself unguarded, and its docstring cited a check the same commit deleted. F2 — the citation checker could not match any F3 — the headline seam test built its empty-label fixture without the self-assertion its sibling carries. F4 — truthiness on a RECORDED, NOT FIXED
STILL OPEN (disclosed, not closed by this round)
VerificationMutation battery: fresh tree per mutant,
GateBase sha Dev-host tier (
That failure is proven environmental, not diagnosed by plausibility. Cheap discriminating control: the same test was run on a pristine Sandbox tier (the one Tekton runs and the merge is gated on) — built one at a time, never combined:
Both required contexts are green on the merged tree. Branch protection is currently off, so this run is the gate; the verdicts above are the runners' own |
Closes the exposure
rebuild_delete_labels' own docstring filed and deliberatelydeferred in #1209: a repo that MEASURES successfully but whose docs git cannot
produce has its rows DELETED and zero re-inserted, at rc 0, with no PARTIAL
notice — on the timer's own argv.
The reproduction (re-measured here, not taken from the filing)
goodrepo(1 committed handoff doc) +badrepo(1 committed doc whose blob wasremoved from
.git/objects— a blobless or partially-fetched clone, anincomplete object store):
The pre-flight actively asserted the opposite:
KEPT (configured but UNMEASURED): (none). The only signal anywhere in the run was one⚠ UNREADABLEline on stderr.The filing understated it
Same fixture with one blob of two removed — a run that looks healthier,
because it writes rows:
The second doc's stored rows are deleted and never replaced. A guard scoped to
the filed symptom ("every doc unreadable") would have closed the fourth instance
and left the shape open — so the guard here is on any unreadable doc.
The four-spelling shape, and whether this closes it
The four are: an unpredicated
TRUNCATE; a delete scope computed over the wronglabel set; a refusal that checked whether a repo could be read when the risk
was whether the config was wide; and this one, a scope keyed on
unmeasured("did the ref resolve") when the DELETE asks "did anything come back".
The common shape is not "someone forgot a field." Authority over the DELETE
was granted by a negative predicate — the absence of whichever failure had
already been seen — instead of by a positive demonstration that the run holds
a complete replacement for the rows it destroys. A blacklist re-opens the hole
for every failure nobody has met yet, which is why four rounds of naming one more
failure never converged. Spelling 3 is literally two sites disagreeing about
which question they were answering.
So this inverts the default in one place:
A label enters the delete scope only when this run read that repo in full;
any incompleteness, of any kind including kinds not yet invented, withholds it.
rebuild_delete_labels,partial_scope_warnings,rebuild_refusalandrebuild_plan_linesnow read that predicate instead of each re-deriving "isthis repo good" from whatever field was nearest.
That is a claim about the shape, so state its limit honestly: it closes the shape
for the rebuild delete scope. It does not make every future predicate in this
module positive by construction — what it does is leave exactly one place where
"may I destroy this repo's rows" is decided, so a fifth spelling has to be a
deliberate edit to that function rather than an omission at a new call site.
Behaviour changes, and which exit codes move
Its stored rows are preserved and every doc that did read is still upserted
(
ON CONFLICT … DO UPDATErefreshes without destroying).unmeasuredis deliberately NOT set for the unreadable case — a considereddeparture from the obvious fix, which would have been "reclassify it as
UNMEASURED and let the existing machinery cover it". Two reasons:
unmeasuredmean both "nothing came back" and "noteverything came back" — the exact conflation this module has already been
burned by three times (
handoff_paths_in_ref'sNone-vs-(),DiskScan'sthree meanings of
(), anddocs == 0's two mechanisms);--offlinesearch over one corruptblob, because an UNMEASURED repo contributes zero rows.
handoff_search's rc 6 / rc 7 split does NOT move. The deferral noteassumed it must ("the two have to move together"), and that coupling exists
only for the UNMEASURED route. Under this design the search front end reads
unmeasuredandunreadableseparately and already renders both correctly;all of its existing rc 6 / rc 7 tests pass unchanged. Neither front end now
claims a repo measured completely when it did not.
--prunerefuses (rc 4RC_REFUSED) while any repo is incomplete, forthe same reason it already refuses an unmeasured one: the run cannot replace
what it would delete. Message opens
came back UNMEASURED or INCOMPLETE— adisjunction rather than a conditional, so it is true in every state it fires
in, with the per-repo reason in the detail. Interaction with
prune_config_refusalis unchanged: that guard still runs first (configwidth is a precondition for
--prune's whole premise) and the two messageskeep their disjoint discriminating tokens, pinned by the existing matrix test.
not ONE of the N repo(s) was read COMPLETELY.Before this, "not every repo is unmeasured" implied "the delete scope is
non-empty"; it no longer does, and
PostgresSectionStore.writeraises onan empty rebuild scope by design. A traceback out of a 6h timer is a worse
report than a refusal that names the repos and a remedy. It is ordered after
the zero-rows arm so
rowsis non-empty by construction — which is what makesits "re-run without
--rebuildand it writes" remedy true. That is the exactsentence the all-unmeasured arm had to retract as false in its own state, so
it is also asserted behaviourally, not just written down.
unmeasured config, and a complete
--pruneall behave exactly as before —each has a negative-control test. The only new refusals require a repo whose
committed doc git cannot produce, which is a broken object store.
contributed NOTHING(falseof a partially readable repo, which contributes rows in the very transaction
that prints it); the pre-flight grows a third bucket, because a repo in
neither list was invisible to the operator; the success line says
not read COMPLETELY;--jsonpublishesrebuildableandincomplete_reasonso amachine consumer reads the same value the DELETE reads rather than re-deriving
it.
What it costs, stated rather than discovered
A single corrupt blob freezes that repo's rows until the object store is
repaired. The rows are preserved, not lost, every readable doc is still
upserted, and the PARTIAL warning says so on every output path — the
conservative direction. If every configured repo is incomplete the rebuild
refuses (rc 4), which on the 6h timer means a failure toast; that state is a
broken machine, and the refusal names
git fsckand re-fetch.Tests — red before, green after
Base sha
a36d3a40(the merge base this work started from), new test filerun against the base library:
11 of the 13 are the new regression tests. The other 2 are the renamed
pre-flight line (
DELETE (measured…→DELETE (read in FULL…). Six further newtests are negative controls / invariant guards and are green at both ends —
labelled as such, not counted as regression coverage.
The end-to-end test asserts the bound DELETE parameters through
main, notan exit code: the statement text, the row count and the exit code are identical
between the correct run and the destructive one, so the bound scope is the only
place the difference shows.
Fixture note: the first draft of
_TRIADreusedDOC_FULLtwice, and becausegit addresses a blob by content hash the two docs were one object — deleting
"one" blob took out two documents and the partial case silently became the total
one. The three bodies are now distinct, and the counts (
docs=2,unreadable=1,total=3) are pairwise distinct and distinct from everyconstant the assertions name.
Mutation sweep — fresh tree per mutant,
PYTHONDONTWRITEBYTECODE=1Mutating the narrowest expression that can be wrong (the
unreadableclause):if d.unreadable:→if False:if d.unreadable and not d.docs:(the "close the 4th instance only" mutant)d.unmeasured is Noned.unmeasuredReachable, not merely breakable.
TestTheAuthorityPredicateIsREACHEDByTheDeleteScopebuilds aRepoDerivationdirectly with
unmeasured=Noneandunreadablenon-empty, so the earlier checkprovably does not always win — and M2 dies there, with this guard's own
assertion (
assert None is not None, and a delete scope holding the extralabel), not with a different guard's error.
Verification limits
⚠ No test in this module reaches a live Postgres. The store sits behind
handoff_index.SectionStore; what is pinned is the SQL text and the boundparameters, not that a server accepts them. Stated in the test file's own header
already, repeated here because this change is entirely about what a
DELETEbinds.