feat(TASK-020): finding-verifier + step-runner fail-closed hardening

finding-verifier: parse gains max_attempts 2 + parse_failure capture +
fallback to a new parse_fault marker that rewrites verdicts to a single
UNVERIFIABLE PIPELINE-FAULT entry and continues to done (sentinel
preserved — the caller's verify lane degrades instead of dying);
verify_one gains a branch-local verify_fault marker normalizing the
engine failure text into a schema-conformant UNVERIFIABLE verdict.

step-runner: implement gains fallback end_failure (no retry — reruns
against a mutated tree are unsafe; STEP_FAILED renders the failure via
coder_result); independent_review gains fallback write_handoff with the
handoff instructions and route_review treating an "Agent node failed:"
review_report as review-did-not-run (fail-visible, guard ahead of the
🔴 grep); orient/edge_case_sweep/write_handoff capture their failure
text via state_updates, shrinking the bundled fallback-capture warning
baseline back to zero.

R5 tests: fault-marker normalization + crash paths (raw non-JSON
GRAPH_STATE), route_review.sh fault/critical/clean routing via bash+jq,
and structural wiring tests pinning fallbacks, sentinels, and
end_failure's initial_state-backed template keys.
This commit is contained in:
Dark-Alex-17 committed 2026-09-14 10:50:33 -06:00
1 parent a8496aa50e
commit 4263d684e9
6 files changed
+585 -24

No files matched your search

+21
View File
@@ -32,6 +32,7 @@ initial_state:
verdicts: []
gate_feedback: ''
gate_attempts: 0
parse_failure: ''
start: parse
@@ -92,8 +93,20 @@ nodes:
type: string
description: Repository directory named in the request (echo the prompt default when none).
required: [findings_list, project_dir]
max_attempts: 2
state_updates:
parse_failure: '{{output}}'
fallback: parse_fault
next: verify_each
parse_fault:
id: parse_fault
type: script
description: Fail-closed marker for a dead parse stage (reached only via parse's fallback) — rewrites `verdicts` to a single UNVERIFIABLE PIPELINE-FAULT entry and jumps straight to done, so the FINDING_VERIFIER_RESULTS sentinel still renders and the caller's verify lane degrades instead of dying.
script: scripts/parse_fault.py
timeout: 30
next: done
verify_each:
id: verify_each
type: map
@@ -165,6 +178,7 @@ nodes:
max_attempts: 2
state_updates:
verdict: '{{output}}'
fallback: verify_fault
next: verdict_gate
verdict_gate:
@@ -174,6 +188,13 @@ nodes:
script: scripts/verdict_gate.py
timeout: 30
verify_fault:
id: verify_fault
type: script
description: Fail-closed marker for a dead verify branch (reached only via verify_one's fallback; branch-local). Normalizes the engine's failure text in `verdict` into a schema-conformant UNVERIFIABLE verdict the map collects — a dead verifier lane never sinks the whole map or masquerades as verified.
script: scripts/verify_fault.py
timeout: 30
done:
id: done
type: end