feat(vuln-hunting): adversarial verification gate, headless hardening, coverage + confirmed-gate integrity
Lands the vuln-hunting pipeline hardening (validated by a blind 7-Zip 26.00
re-run that independently found + PoC-confirmed the GetCuSize shift-overflow
class), in four layers:
Adversarial verification gate (confirmed = independently reproduced):
- New `adversary` agent (leaf, shell+write, memory_record_verification only on
its tool list) re-derives the taint path and re-runs the PoC on the
UNMODIFIED target; records survived/refuted/inconclusive.
- L1 code guard: memory_update_finding(confirmed) is rejected unless a
`survived` verification is on record. require_adversarial_verification flag
(default true) is the escape hatch.
- Migration 2026-07-12-120000 adds subject_finding_id + verification_verdict;
new memory_verification tool + ToolCatalog/operation wiring.
Headless hardening (the "0-output hang" was a visibility gap, not a deadlock):
- B1 provider stream silence-timeout (http.stream_timeout_secs=180).
- B2 tool-call headers/failures -> stderr unconditionally in headless.
- B3 liveness heartbeat every 15s + stall watchdog (VOGE_HEADLESS_STALL_SECS,
default 600s) that aborts instead of hanging forever; RetryAttempt surfaced.
Coverage gate v2 (count-equality census + tree-rooted generalization):
- Coverage completion is covered==census_total over evidence-bound verdicts;
sanctioned verbatim find command, non-narrowable; proof_of_read/safe_citation
verbatim lines re-checked at the gate. CoverageVerdict::Partial added.
- Tree-rooted dangerous-idiom generalization in sage/voge prompts.
- memory_coverage title made optional (was required -> 5 failures/run).
Confirmed + coverage gate integrity v3 (closes two residual gaps found by the
blind re-run, red-team-vetted via workflow):
- Substance-bound verification: each survived verification snapshots a
verified_substance fingerprint (CWE+severity+taint_path, NOT prose).
has_surviving_verification_for_substance walks the supersedes chain and
matches substance — so a narrow ancestor verification cannot cover a broader
descendant (closes verify-narrow-then-confirm-broad). MemoryUpdateFinding
builds the fully-mutated revised version BEFORE gating; rejects is_latest=0
input ids (reusable-token guard). Consolidation can't mint confirmed; evidence
refinement keeps confirmed, only substance changes force re-verify.
- memory_add rejects kind=verification/coverage (centralize behind the validated
tools; closes the verdict=NULL malformed-record hole).
- coverage-supersede: memory_coverage supersedes the prior verdict for the same
file_path so an honest agent CAN flip needs_review->safe (save_memory
fingerprint-dedup used to fork parallel is_latest rows). New read-only
memory_coverage_status tool returns per-path distinct verdicts + counts.
- Completion gate now requires needs_review==0 AND partial==0 AND
total_latest==census_total (denominator — needs_review==0 alone was
satisfiable by never recording coverage for hard files). muse re-derives the
counts itself and refuses a mismatched report.
- Migration 2026-07-13-100000 adds verified_substance (indexed).
Tests: 4 new (substance_fingerprint blind-to-prose, revise defense-in-depth x2,
substance-gate mismatch+chain-walk); 1713 existing pass across voge_domain /
voge_repo / voge_app / voge_services / voge_config. Snapshots updated for the
new tool + config field.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>