You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Evidence provenance (added 2026-08-22). The run-derived figures in this issue come from a private scan that a reader cannot reproduce (raptor-e2e-20260818, a 9.85-hour run against
a Python target). They are reported for completeness, not as independently checkable evidence.
Every code claim below is pinned to public source at b5019628 and is checkable; where a
conclusion rests on the private numbers rather than on the code, treat it as my report of what
that run showed. This label was missing when the issue was filed.
Summary
FindingVerifier's resume path decides whether a checkpointed unit needs re-verification with _cp_is_error, which returns True only when the checkpoint has no verification dict, or when verification["correct_finding"] == "error". The verify error path writes neither: it writes verification = {"incomplete": True} and puts the error string on the result dict, which the
checkpoint writer does not copy. correct_finding == "error" has no writer on the normal verify
path. Every errored unit therefore looks like finished work on resume and is never retried.
⚠️Corrected 2026-08-22. This paragraph originally read "no writer anywhere in production
code". That is false — finding_verifier.py:882/:921 assigns correct_finding from an
unvalidated LLM field on the Stage-1 consistency path. The value is unwritten on the path that
produced these records, not unwritable. Full correction at the end of this report; the measured
223/223 adoption result is unaffected.
def_cp_is_error(cp_data):
"""A verify checkpoint is errored if verification is missing/empty or correct_finding == 'error'."""ifnotcp_data:
returnTruev=cp_data.get("verification", {})
ifnotv:
returnTruereturnv.get("correct_finding") =="error"# Separate already-done (successful) from to-do (new + errored)results_to_verify= []
_restored_ok=0forrinresults:
key=r.get("unit_id") orr.get("route_key", "unknown")
cp_data=checkpointed.get(key)
ifcp_dataandnot_cp_is_error(cp_data):
# Restore verification data from checkpoint
...
_restored_ok+=1else:
# Either no checkpoint, or an errored one — re-verifyresults_to_verify.append(r)
The error path, finding_verifier.py:761-768:
err_msg=f"{type(e).__name__}: {e}"result["error"] =err_msg# Surface a minimal verification dict marked incomplete so any# consumer that branches on ``verification.incomplete`` also treats# it as needs-review rather than a clean verdict.result.setdefault("verification", {})
result["verification"]["incomplete"] =Trueresult["verification_note"] =f"Verification errored: {err_msg}"
The checkpoint writers, finding_verifier.py:788-792 (sequential) and :819-823 (parallel), copy
three keys and noterror:
So on an errored unit the checkpoint holds verification = {"incomplete": True} — truthy, and
without a correct_finding key. Both guards in _cp_is_error pass.
correct_finding == "error" has readers, and no writer on the normal path
⚠️ Corrected — it IS writable via the consistency path (finding_verifier.py:882/:921). See the correction at the end of this report; the survey below covers the finish/normalise paths only.
core/checkpoint.py:118 # Verify: verification empty or correct_finding == "error"
core/checkpoint.py:121 if not v or v.get("correct_finding") == "error":
core/checkpoint.py:366 # - verify: verification is empty or verification.correct_finding == "error"
core/checkpoint.py:388 # Verify-style: verification empty or correct_finding == "error"
core/checkpoint.py:391 if not v or v.get("correct_finding") == "error":
utilities/finding_verifier.py:605 or correct_finding == 'error'."""
utilities/finding_verifier.py:611 return v.get("correct_finding") == "error"
Three readers, zero assignments.
Key-shape histogram over the live run's checkpoints
RUN=~/.openant/projects/gadievron/raptor-e2e-20260818/scans/7dbf9c691d7/python
python3 - <<'EOF'
import json, glob, os, collections
D = os.path.expanduser("$RUN/verify_checkpoints")
hist = collections.Counter(); n = 0
for f in glob.glob(D + "/*.json"):
if os.path.basename(f) in ("_fingerprint.json", "_summary.json"):
continue
n += 1
v = json.load(open(f)).get("verification", {})
hist[tuple(sorted(v.keys()))] += 1
print("real checkpoints:", n)
for k, c in hist.most_common():
print(c, k)
EOF
The 48 errored records carry onlyincomplete. One of them in full:
{
"verification": { "incomplete": true },
"finding": "vulnerable",
"verification_note": "Verification errored: LLMResponseError: AnthropicAdapter returned no usable content (empty completion); the request may have been filtered or the response was malformed",
"usage": { "input_tokens": 0, "output_tokens": 0, "cost_usd": 0.0 },
"id": "core/audit/condition_smt.py:disprove_integer_overflow"
}
On resume, all 223 checkpoints satisfy cp_data and not _cp_is_error(cp_data), so all 223 are
restored as completed and 0 are re-verified. The else: branch commented "Either no checkpoint, or
an errored one — re-verify" is unreachable for errored units, and the operator-facing message at :637-640 (errored_retries = len(checkpointed) - _restored_ok) computes 0.
The same pattern is repeated in the shared checkpoint helper
core/checkpoint.py:99-122 (load_ids(skip_errors=True)) and :388-391 use the identical verify
test, so they also treat errored verify checkpoints as completed.
Why it matters
The whole point of skip_errors / _cp_is_error is that a resume retries transient failures.
Because the test targets a value nothing writes on the path that produced these records, a resumed verify run adopts the previous run's
hard errors as finished verifications instead of retrying them — the failure is not merely
un-repaired, it is promoted to a result. On this run that is 48 of 223 candidates (21.5%), each of
which keeps its Stage-1 finding: "vulnerable" and is then counted as a confirmed vulnerability by
the metrics recount (filed separately).
It also means that any change intended to reduce verify errors cannot be validated by resuming an
affected scan: the errored units will not be re-attempted at all.
Suggested fix
Make the checkpoint carry the error. Add "error": result.get("error", "") (or verification["error"] = err_msg) to the cp_data dicts at finding_verifier.py:788-792 and :819-823.
Change _cp_is_error to test what the writer actually produces: return bool(cp_data.get("error")) or v.get("correct_finding") == "error". Keep the correct_finding clause for backward compatibility with existing checkpoint files.
Decide explicitly what to do with verification == {"incomplete": True} and nothing else. A unit
whose verification never completed is arguably also a retry candidate; at minimum the decision
should be a named, tested policy rather than a side effect of key absence.
Apply the same change to core/checkpoint.py:388-391 (status) — the live site. Its value
is consumed by the Go CLI at apps/openant-cli/internal/checkpoint/checkpoint.go:112. :99-122 (load_ids) is DEAD CODE — zero callers repo-wide (/usr/bin/grep -rn load_ids
returns only the definition; the two tests/parsers/c/ hits are test_overload_ids…, a
substring false positive). Fixing it changes no behaviour; deleting it is the better move. (Corrected 2026-08-22 — this item previously directed a maintainer at both sites without
saying one of them is dead. The fact was noted in a comment on this issue but had never
reached the body, which is what a triaging maintainer actually reads.)
Add a regression test that writes an errored checkpoint through the real writer, then asserts _cp_is_error returns True for it.
What I am not claiming
I am not claiming a retry would succeed. Whether these 48 units verify on a second attempt is
unmeasured here; the defect is that no second attempt happens.
I am not attributing the 48 errors to any particular cause in this issue. The error string is
quoted verbatim above and is discussed separately.
The errored checkpoint records carry a usage sub-object whose fields are zero. They do not
carry a total_tokens key at all — the successful shapes do.
The 41/134/48 split is from one Python run against a direct Anthropic binding. I have not checked
whether other phases' checkpoint shapes differ.
Correction 2026-08-21 — the "never written" framing is WRONG. The measurement stands; the mechanism claim does not.
This report says _cp_is_error tests "a value the verifier never writes", and a comment of mine
strengthened that to "a value the finish tool's schema cannot emit". Both are too strong.
There is a writer. utilities/finding_verifier.py:882 and :921:
should_be comes from the Stage-1 consistency resolution, an LLM reply parsed out of a schema-less simple_text call (:1029-1031). /usr/bin/grep -n "should_be" over the file returns exactly one
site — :882 — and there is no enum, allow-list, or membership check on it anywhere. So correct_finding can be assigned any string the model returns, including "error".
Why I missed it, stated plainly: I searched for the literal assignment
(correct_finding.*=.*"error") and found only readers. Line 921 assigns a variable, so a literal
search cannot see it. That is a text-match standing in for a semantic question, and it is the same
error class as several other corrections in this corpus.
My enum comment is also narrower than I wrote it. The finish tool's JSON Schema
(finding_verifier.py:158-161) does constrain correct_finding on the finish path. It does not
constrain the consistency path at :921, which bypasses the finish tool entirely.
What survives, and what does not
STANDS — the measurement. All 223 verify checkpoints in the reference run carry correct_finding != "error", and all 223 satisfy cp_data and not _cp_is_error(cp_data), so all
223 are adopted on resume. That is data, not inference.
STANDS — the practical effect. Errored verify units are restored as completed, because nothing
on the normal path writes the value the test looks for.
FALLS — "never writes" / "can never fire" / "structural". The predicate is unwritten in
practice, not unwritable. It is reachable through an unvalidated LLM-supplied field.
Summary
FindingVerifier's resume path decides whether a checkpointed unit needs re-verification with_cp_is_error, which returns True only when the checkpoint has noverificationdict, or whenverification["correct_finding"] == "error". The verify error path writes neither: it writesverification = {"incomplete": True}and puts the error string on the result dict, which thecheckpoint writer does not copy.
correct_finding == "error"has no writer on the normal verifypath. Every errored unit therefore looks like finished work on resume and is never retried.
Evidence
libs/openant-core/utilities/finding_verifier.py:603-630(excerpt — 6 interior lines elided) (HEADb501962):The error path,
finding_verifier.py:761-768:The checkpoint writers,
finding_verifier.py:788-792(sequential) and:819-823(parallel), copythree keys and not
error:So on an errored unit the checkpoint holds
verification = {"incomplete": True}— truthy, andwithout a
correct_findingkey. Both guards in_cp_is_errorpass.correct_finding == "error"has readers, and no writer on the normal pathThree readers, zero assignments.
Key-shape histogram over the live run's checkpoints
The 48 errored records carry only
incomplete. One of them in full:{ "verification": { "incomplete": true }, "finding": "vulnerable", "verification_note": "Verification errored: LLMResponseError: AnthropicAdapter returned no usable content (empty completion); the request may have been filtered or the response was malformed", "usage": { "input_tokens": 0, "output_tokens": 0, "cost_usd": 0.0 }, "id": "core/audit/condition_smt.py:disprove_integer_overflow" }On resume, all 223 checkpoints satisfy
cp_data and not _cp_is_error(cp_data), so all 223 arerestored as completed and 0 are re-verified. The
else:branch commented "Either no checkpoint, oran errored one — re-verify" is unreachable for errored units, and the operator-facing message at
:637-640(errored_retries = len(checkpointed) - _restored_ok) computes 0.The same pattern is repeated in the shared checkpoint helper
core/checkpoint.py:99-122(load_ids(skip_errors=True)) and:388-391use the identical verifytest, so they also treat errored verify checkpoints as completed.
Why it matters
The whole point of
skip_errors/_cp_is_erroris that a resume retries transient failures.Because the test targets a value nothing writes on the path that produced these records, a resumed verify run adopts the previous run's
hard errors as finished verifications instead of retrying them — the failure is not merely
un-repaired, it is promoted to a result. On this run that is 48 of 223 candidates (21.5%), each of
which keeps its Stage-1
finding: "vulnerable"and is then counted as a confirmed vulnerability bythe metrics recount (filed separately).
It also means that any change intended to reduce verify errors cannot be validated by resuming an
affected scan: the errored units will not be re-attempted at all.
Suggested fix
"error": result.get("error", "")(orverification["error"] = err_msg) to thecp_datadicts atfinding_verifier.py:788-792and:819-823._cp_is_errorto test what the writer actually produces:return bool(cp_data.get("error")) or v.get("correct_finding") == "error". Keep thecorrect_findingclause for backward compatibility with existing checkpoint files.verification == {"incomplete": True}and nothing else. A unitwhose verification never completed is arguably also a retry candidate; at minimum the decision
should be a named, tested policy rather than a side effect of key absence.
core/checkpoint.py:388-391(status) — the live site. Its valueis consumed by the Go CLI at
apps/openant-cli/internal/checkpoint/checkpoint.go:112.:99-122(load_ids) is DEAD CODE — zero callers repo-wide (/usr/bin/grep -rn load_idsreturns only the definition; the two
tests/parsers/c/hits aretest_overload_ids…, asubstring false positive). Fixing it changes no behaviour; deleting it is the better move.
(Corrected 2026-08-22 — this item previously directed a maintainer at both sites without
saying one of them is dead. The fact was noted in a comment on this issue but had never
reached the body, which is what a triaging maintainer actually reads.)
_cp_is_errorreturns True for it.What I am not claiming
unmeasured here; the defect is that no second attempt happens.
quoted verbatim above and is discussed separately.
usagesub-object whose fields are zero. They do notcarry a
total_tokenskey at all — the successful shapes do.whether other phases' checkpoint shapes differ.
Correction 2026-08-21 — the "never written" framing is WRONG. The measurement stands; the mechanism claim does not.
This report says
_cp_is_errortests "a value the verifier never writes", and a comment of minestrengthened that to "a value the finish tool's schema cannot emit". Both are too strong.
There is a writer.
utilities/finding_verifier.py:882and:921:should_becomes from the Stage-1 consistency resolution, an LLM reply parsed out of a schema-lesssimple_textcall (:1029-1031)./usr/bin/grep -n "should_be"over the file returns exactly onesite —
:882— and there is no enum, allow-list, or membership check on it anywhere. Socorrect_findingcan be assigned any string the model returns, including"error".Why I missed it, stated plainly: I searched for the literal assignment
(
correct_finding.*=.*"error") and found only readers. Line 921 assigns a variable, so a literalsearch cannot see it. That is a text-match standing in for a semantic question, and it is the same
error class as several other corrections in this corpus.
My enum comment is also narrower than I wrote it. The finish tool's JSON Schema
(
finding_verifier.py:158-161) does constraincorrect_findingon the finish path. It does notconstrain the consistency path at
:921, which bypasses the finish tool entirely.What survives, and what does not
correct_finding != "error", and all 223 satisfycp_data and not _cp_is_error(cp_data), so all223 are adopted on resume. That is data, not inference.
on the normal path writes the value the test looks for.
practice, not unwritable. It is reachable through an unvalidated LLM-supplied field.
_cp_is_errorcorrect is not only about giving it a value that getswritten; the
should_bepath should also be validated, or an LLM reply could setcorrect_findingto an arbitrary string — the same shape as the synthesised-verdict problem inJSON corrector synthesises a verdict from free text and reports it as a successful correction, producing rows no bucket counts #316.
#287 depends on this premise and inherits the correction.