fix(review-suite): detect nested finding-verifier faults; harden render/fault markers
code-reviewer verdict.py: finding-verifier now completes "successfully" with fault markers INSIDE its FINDING_VERIFIER_RESULTS payload (parse_fault's `pipeline-fault` sentinel id, verify_fault / verdict_gate crash-guard notes prefixed PIPELINE-FAULT:), which the old "Agent node failed:" prefix check never saw — a dead parse degraded silently to "unverified" with no fault recorded. Detect them, anchored on the sentinel id / note prefix only (never a substring over the report), counted over the raw entry list so duplicate crash-guard ids are not collapsed. Faults lead the attention list uncapped; the display cap of 5 applies to non-fault entries; the "always-human trigger(s) fired" reason fires only when non-fault triggers exist. code-reviewer render.py: CONTRACT comments at the verdict and summary lines review-gauntlet's gate parses; every LLM-influenced value rendered after the summary line (dropped_titles, rigor, bar_provenance, resolved_surfaces, reason) is newline-sanitised so none can forge a second summary line. finding-verifier verdict_gate.py: the outer crash-guard note carries the PIPELINE-FAULT: prefix (the after-retry UNVERIFIABLE path stays unprefixed — model misbehaviour, not infrastructure); README names all three emitters. deep-research: vet_fault's UNVETTED note tells the critic not to request revision solely because the assessment is unavailable (it matched critique's PREPRINT/UNVERIFIED REVISE trigger verbatim, burning the revision budget on an infra fault); vet_fault and critique_fault dedupe repeated faults and preserve accumulated faults on crash via safe_faults(); question_fault keeps its plan-pinned prefix and appends the lane's question identity so distinct dead lanes no longer collapse in combine_findings' dedupe.
This commit is contained in:
8 files changed
+570
-50
No files matched your search
@@ -16,13 +16,19 @@ def load_state():
|
||||
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
|
||||
|
||||
|
||||
# LLM-influenced values rendered after the summary line: a newline could
|
||||
# forge a second summary line that review-gauntlet/verdict_gate.py parses.
|
||||
def one_line(x):
|
||||
return str(x).replace("\n", " ")
|
||||
|
||||
|
||||
def main():
|
||||
state = load_state()
|
||||
v = state.get("verdict_out") or {}
|
||||
findings = v.get("findings_final") or []
|
||||
counts = v.get("counts") or {}
|
||||
changed = state.get("changed_files") or []
|
||||
rigor = state.get("resolved_rigor") or "production"
|
||||
rigor = one_line(state.get("resolved_rigor") or "production")
|
||||
|
||||
# A fault can leave changed_files empty (a parse fault dies before the
|
||||
# diff is resolved; a verifier fault fires even on an empty diff) — a
|
||||
@@ -39,7 +45,8 @@ def main():
|
||||
lines = [
|
||||
"# Code Review Summary",
|
||||
"",
|
||||
f"**Verdict: {verdict}** — {v.get('reason', '')}",
|
||||
# CONTRACT: review-gauntlet/verdict_gate.py anchors on this wording
|
||||
f"**Verdict: {verdict}** — {one_line(v.get('reason', ''))}",
|
||||
"",
|
||||
"## Walkthrough",
|
||||
state.get("walkthrough") or "(no walkthrough provided)",
|
||||
@@ -96,15 +103,17 @@ def main():
|
||||
m for x in findings for m in
|
||||
__import__("re").findall(r"MISS-\d+", (x.get("title") or "") + (x.get("block") or ""))
|
||||
})
|
||||
dropped = ", ".join(one_line(t) for t in v.get("dropped_titles", [])[:5])
|
||||
lines += [
|
||||
"---",
|
||||
# CONTRACT: review-gauntlet/verdict_gate.py parses this line — keep the format in sync
|
||||
f"*Reviewed {len(changed)} files, found {counts.get('🔴', 0)} critical, "
|
||||
f"{counts.get('🟡', 0)} warnings, {counts.get('🟢', 0)} suggestions, "
|
||||
f"{counts.get('💡', 0)} nitpicks ({v.get('deferred_count', 0)} deferred by quality bar)*",
|
||||
f"*Quality bar: {rigor} — provenance: {state.get('bar_provenance', '?')}; "
|
||||
f"surfaces: {state.get('resolved_surfaces') or 'none'}*",
|
||||
f"*Quality bar: {rigor} — provenance: {one_line(state.get('bar_provenance', '?'))}; "
|
||||
f"surfaces: {one_line(state.get('resolved_surfaces') or 'none')}*",
|
||||
f"*Verdict: {verdict}; verifier: dropped {v.get('dropped_count', 0)} provably-false"
|
||||
+ (f" ({', '.join(v.get('dropped_titles', [])[:5])})" if v.get("dropped_count") else "")
|
||||
+ (f" ({dropped})" if v.get("dropped_count") else "")
|
||||
+ f"; review-miss patterns applied: {', '.join(miss_ids) if miss_ids else 'none relevant'}*",
|
||||
]
|
||||
print(json.dumps({"final_report": "\n".join(lines)}))
|
||||
|
||||
Reference in new issue
Block a user