Skip to content

fix(verify): the consistency-rewritten row reaches its envelope bucket; the CSV exports the Stage-1 verdict (#681+#682) - #752

Open
gadievron wants to merge 3 commits into
masterfrom
fix/681-682-consistency-provenance
Open

gadievron wants to merge 3 commits into
masterfrom
fix/681-682-consistency-provenance

Conversation

@gadievron

Copy link
Copy Markdown
Collaborator

Summary

A Stage-2 consistency reply that volunteers findings_to_update can rewrite an agreed row's finding (e.g. vulnerable -> protected) — and the rewritten row previously reached no scanner-envelope bucket (#681): the envelope's protected = analyze.protected + disagreed_protected counts neither term (the analysis metrics counted the row vulnerable at Stage 1; the rewrite is agree=True, not a disagreement), so the envelope said protected=0 while the recount (which reads the finding field) said protected=1 — two numbers across three surfaces, the envelope the outlier. And the CSV's stage1_verdict column exported the rewritten finding as the Stage-1 verdict (#682).

The fix (the #654-named shape): source the envelope from the buckets it already owns.

  • core/verifier.py — _count_verification_outcomes gains the consistency-rewrite buckets by destination (consistency_protected / consistency_safe / consistency_inconclusive), gated on the consistency_update record the apply loop already writes (never a guess from the verdict delta; a disagreed row never lands in them).
  • core/scanner.py — the post-verify envelope (extracted into _post_verify_metrics, one testable authority) folds the buckets into their columns — the envelope and the recount agree; the sum-to-total property restored.
  • core/schemas.py — VerifyResult carries the counts; to_dict() and verify_step_summary() serialize them (the standalone verify surface agrees too).
  • report/csv_export.py — get_stage1_verdict reads the consistency_update.from provenance the verifier already records — after the Changed from note parse (the ordering matters: on a disagreed-then-rewritten row the record's from holds Stage-2's correction, and the note — which always carries the true Stage-1 original — must win; the review's F1 caught the inverse ordering before it shipped).

The tests

Declared notes (honest)

Fixes #681, fixes #682. Cross-linked: #654 (the named fix), #622 (the sibling bucket), #679 (the stack base).

…, never a safe fold (#679)

An off-enum corrected finding ('Probably Fine', a model-drift spelling, or
a corrupted 'error' string) fell into the plain-disagreed arm — which the
scanner folds into safe (safe += disagreed) — so every operator surface
read the unit as safe while the recount ladder counted it as an error.
The false-clean class: the envelope, the recount, and the report disagreed
about the same row.

The fix, consumer-side per the maintainer's preference (no new counter):
- verifier: an unparseable corrected finding reaches error_count BEFORE
  the agreement arms — agreed or not, an unclassifiable verdict is a
  visible error. The existing error-marker path (a top-level r['error'])
  is unchanged.
- report (cli.py): the unknown-verdict rows join a visible ERROR group
  (a local display group — the canonical enum is untouched) and the
  false-clean remediation message is suppressed when unparseable rows
  exist ('N unit(s) have an unrecognized verdict').
- the scanner needs no change: with the off-enum no longer 'disagreed',
  the safe fold never sees it.

RED->GREEN: test_off_enum_corrected_counts_as_error_never_disagreed and
test_off_enum_disagreement_is_not_a_false_positive_eliminated FAIL on
pristine 6240682, PASS at HEAD. The sibling suites (#622/#623/#286) hold.
Full suite: 4273 passed / 2 failed (the pre-existing SDK-env pins,
reproduced identically at the merge-base).

Fixes #679

Agent: ISSUES-TO-PR ses_f3e7372a5ffe3b2I6lP4Wm3w2d
… safe fold (#679)

An off-enum corrected finding ('Probably Fine', a model-drift spelling, or
a corrupted 'error' string) fell into the plain-disagreed arm — which the
scanner folds into safe (safe += disagreed) — so the envelope read safe
while the recount read error (the false-clean class: every surface
disagreed about the same row).

The fix, consumer-side per the maintainer's preference (no new counter):
- verifier: an unparseable corrected finding reaches error_count BEFORE
  the agreement arms — agreed or not, an unclassifiable verdict is a
  visible error. The error-marker path (a top-level r['error']) is
  unchanged.
- report (cli.py): the visible not-classified group carries BOTH the
  canonical error rows and the off-enum spellings (the T1 F1 split: the
  canonical 'error' is RECOGNIZED — the wording distinguishes 'N errored'
  from 'M with an unrecognized verdict', the html_report convention);
  the false-clean message is suppressed when either exists. Both via the
  extracted helpers _unrecognized_verdict_rows + _remediation_for_unrecognized.
- the scanner: unchanged — the off-enum no longer lands in disagreed so
  the safe fold never sees it. The stale #622-era docstrings updated
  (verifier: the residual arm is now safe-only; scanner: same).

RED->GREEN: the off-enum counter tests FAIL on pristine 6240682, PASS at
HEAD; the real-helper tests drive the extraction (the T1 F5 fix: the
for-pass stub removed). 19 tests in the harness file; the sibling suites
(#622/#623/#286) hold; the full suite 4273/2 (the SDK-env pins,
identical at base).

Declared residues (the T1 round): the Go chart/stats still iterate the
canonical verdict_order only (the error group is visible in the findings
list but not the pie — the html_report standalone already charts errors;
the gap is the Go renderer's, named for the follow-up); the
html_report.py standalone is direct-invocation-only (no production
callers) and partially handles the off-enum message already.

Fixes #679

Agent: ISSUES-TO-PR ses_f3e7372a5ffe3b2I6lP4Wm3w2d
Comment thread libs/openant-core/tests/test_issue681_consistency_envelope.py Fixed
…e CSV exports the Stage-1 verdict — the counter gains the consistency_{protected,safe,inconclusive} buckets (gated on the record the apply loop writes), the envelope folds them (extracted _post_verify_metrics), the CSV reads the consistency_update.from AFTER the note parse (the F1 precedence: on disagreed-then-rewritten rows the record's from is Stage-2's correction); the VerifyResult serialization threads the counts; the #622 suite honestly narrowed
@gadievron
gadievron force-pushed the fix/681-682-consistency-provenance branch from ece77c3 to 35b6ca3 Compare September 26, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant