feat: Significant code review suite rewrite with improved graph agent features
This commit is contained in:
41 files changed
+3572
-709
No files matched your search
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Contract gate for the adversary's per-criterion verify chain.
|
||||
|
||||
Validates each criterion verdict against the contract:
|
||||
- a single JSON object with status in {MET, PARTIAL, UNMET, DIVERGED}
|
||||
- MET requires non-empty evidence (the change AND the proving test)
|
||||
- non-MET requires a non-empty complaint
|
||||
|
||||
The authoritative id is stamped from the criterion item — the model's echo is
|
||||
never trusted. Malformed output is rejected back to check_criterion exactly
|
||||
once with the reason; a second failure records PARTIAL (unproven) so a
|
||||
malformed item can never sink the map or masquerade as MET.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
STATUSES = {"MET", "PARTIAL", "UNMET", "DIVERGED"}
|
||||
MAX_RETRIES = 1
|
||||
|
||||
|
||||
def load_state():
|
||||
if path := os.environ.get("GRAPH_STATE_FILE"):
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
|
||||
|
||||
|
||||
def criterion_fields(state):
|
||||
c = state.get("criterion")
|
||||
if isinstance(c, dict):
|
||||
cid = c.get("id")
|
||||
text = c.get("text")
|
||||
return (
|
||||
cid.strip() if isinstance(cid, str) and cid.strip() else "unknown",
|
||||
text.strip() if isinstance(text, str) else "",
|
||||
)
|
||||
return "unknown", ""
|
||||
|
||||
|
||||
def extract_json(text):
|
||||
if not isinstance(text, str) or not text.strip():
|
||||
raise ValueError("criterion-check output is empty or not text")
|
||||
text = re.sub(r"```[a-zA-Z]*", "", text).replace("```", "")
|
||||
start, end = text.find("{"), text.rfind("}")
|
||||
if start == -1 or end <= start:
|
||||
raise ValueError("no JSON object found in criterion-check output")
|
||||
return json.loads(text[start : end + 1])
|
||||
|
||||
|
||||
def validate(obj):
|
||||
if not isinstance(obj, dict):
|
||||
raise ValueError("criterion-check output is not a JSON object")
|
||||
status = obj.get("status")
|
||||
if status not in STATUSES:
|
||||
raise ValueError(f"status must be one of {sorted(STATUSES)}, got {status!r}")
|
||||
evidence = obj.get("evidence") if isinstance(obj.get("evidence"), str) else ""
|
||||
complaint = obj.get("complaint") if isinstance(obj.get("complaint"), str) else ""
|
||||
if status == "MET" and not evidence.strip():
|
||||
raise ValueError(
|
||||
"MET requires non-empty evidence citing the satisfying change AND the "
|
||||
"test proving the behavior (file:line)"
|
||||
)
|
||||
if status != "MET" and not complaint.strip():
|
||||
raise ValueError(
|
||||
f"{status} requires a non-empty complaint (what the diff does/omits at "
|
||||
"file:line, and what would make it conform)"
|
||||
)
|
||||
return status, evidence, complaint
|
||||
|
||||
|
||||
def main():
|
||||
state = load_state()
|
||||
cid, ctext = criterion_fields(state)
|
||||
attempts = state.get("gate_attempts")
|
||||
if not isinstance(attempts, int):
|
||||
attempts = 0
|
||||
|
||||
try:
|
||||
status, evidence, complaint = validate(extract_json(state.get("crit_verdict")))
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"crit_verdict": {
|
||||
"id": cid,
|
||||
"text": ctext,
|
||||
"status": status,
|
||||
"evidence": evidence,
|
||||
"complaint": complaint,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
return
|
||||
except Exception as e: # noqa: BLE001
|
||||
reason = str(e)
|
||||
|
||||
if attempts < MAX_RETRIES:
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"_next": "check_criterion",
|
||||
"gate_attempts": attempts + 1,
|
||||
"gate_feedback": (
|
||||
"PREVIOUS RESPONSE REJECTED by the verdict gate: " + reason +
|
||||
". Respond with ONLY the JSON object described in your instructions."
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"crit_verdict": {
|
||||
"id": cid,
|
||||
"text": ctext,
|
||||
"status": "PARTIAL",
|
||||
"evidence": "",
|
||||
"complaint": (
|
||||
"criterion check output failed machine validation after retry: "
|
||||
+ reason
|
||||
+ " — treat this criterion as unproven"
|
||||
),
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except Exception as e: # noqa: BLE001 — the gate must never crash the chain
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"crit_verdict": {
|
||||
"id": "unknown",
|
||||
"text": "",
|
||||
"status": "PARTIAL",
|
||||
"evidence": "",
|
||||
"complaint": f"criterion gate error: {e} — treat as unproven",
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic diff resolution for the adversary graph.
|
||||
|
||||
Resolves the diff spec (get_diff semantics: staged → unstaged → HEAD~1 for
|
||||
"auto"), lists changed files, and captures the bounded diff text every
|
||||
criterion branch verifies against. Failures degrade into diff_note — the
|
||||
review proceeds fail-closed rather than crashing.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
MAX_DIFF_CHARS = 60000
|
||||
|
||||
|
||||
def load_state():
|
||||
if path := os.environ.get("GRAPH_STATE_FILE"):
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
|
||||
|
||||
|
||||
state = load_state()
|
||||
proj = state.get("project_dir") or "."
|
||||
spec = (state.get("diff_spec") or "auto").strip()
|
||||
|
||||
|
||||
def git(*args):
|
||||
r = subprocess.run(["git", "-C", proj, *args], capture_output=True, text=True, timeout=90)
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError((r.stderr or r.stdout).strip()[:300])
|
||||
return r.stdout
|
||||
|
||||
|
||||
out = {"diff_note": ""}
|
||||
try:
|
||||
if spec in ("auto", ""):
|
||||
text = git("diff", "--cached")
|
||||
if not text.strip():
|
||||
text = git("diff")
|
||||
if not text.strip():
|
||||
text = git("diff", "HEAD~1")
|
||||
elif spec == "staged":
|
||||
text = git("diff", "--cached")
|
||||
elif spec == "worktree":
|
||||
text = git("diff", "HEAD")
|
||||
else:
|
||||
text = git("diff", spec)
|
||||
|
||||
files = sorted({
|
||||
line[6:].strip()
|
||||
for line in text.splitlines()
|
||||
if line.startswith("+++ b/")
|
||||
})
|
||||
out["changed_files"] = files[:300]
|
||||
if len(text) > MAX_DIFF_CHARS:
|
||||
out["diff_note"] = (
|
||||
f" (truncated to {MAX_DIFF_CHARS} chars of {len(text)} — use the fs tools "
|
||||
"to inspect files beyond the cut)"
|
||||
)
|
||||
text = text[:MAX_DIFF_CHARS]
|
||||
out["diff_text"] = text if text.strip() else "(empty diff — nothing changed)"
|
||||
except Exception as e: # noqa: BLE001
|
||||
out["changed_files"] = []
|
||||
out["diff_text"] = "(diff unavailable)"
|
||||
out["diff_note"] = f" (diff resolution failed for spec {spec!r}: {e})"
|
||||
|
||||
print(json.dumps(out))
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic conformance verdict for the adversary graph.
|
||||
|
||||
CONFORMS requires: at least one criterion, every criterion MET, and zero
|
||||
holistic complaints. Everything else — including a missing plan — is
|
||||
DIVERGES (fail-closed). Assembles the exact sentinel format the callers
|
||||
route on. Never crashes into a silent verdict.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
def load_state():
|
||||
if path := os.environ.get("GRAPH_STATE_FILE"):
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
return json.loads(os.environ.get("GRAPH_STATE", "{}"))
|
||||
|
||||
|
||||
def main():
|
||||
state = load_state()
|
||||
verdicts = [v for v in (state.get("crit_verdicts") or []) if isinstance(v, dict)]
|
||||
extra = [c for c in (state.get("extra_complaints") or []) if isinstance(c, dict)]
|
||||
observations = (state.get("observations") or "").strip()
|
||||
n = len(verdicts)
|
||||
|
||||
met = [v for v in verdicts if v.get("status") == "MET"]
|
||||
partial = [v for v in verdicts if v.get("status") == "PARTIAL"]
|
||||
bad = [v for v in verdicts if v.get("status") in ("UNMET", "DIVERGED")]
|
||||
|
||||
lines = []
|
||||
if n == 0:
|
||||
lines.append("ADVERSARIAL_REVIEW: DIVERGES")
|
||||
lines.append("Criteria: none provided.")
|
||||
lines.append("Complaints:")
|
||||
lines.append(
|
||||
"1. No acceptance criteria were provided or extractable — conformance cannot "
|
||||
"be judged without a spec (fail-closed). Supply the plan's Objective/Tasks/"
|
||||
"Acceptance criteria and re-run."
|
||||
)
|
||||
elif not partial and not bad and not extra:
|
||||
lines.append("ADVERSARIAL_REVIEW: CONFORMS")
|
||||
lines.append(f"Criteria: {n}/{n} met (all with tests).")
|
||||
if observations:
|
||||
lines.append("")
|
||||
lines.append("Non-blocking observations:")
|
||||
lines.append(observations)
|
||||
else:
|
||||
lines.append("ADVERSARIAL_REVIEW: DIVERGES")
|
||||
lines.append(
|
||||
f"Criteria: {len(met)}/{n} met, {len(partial)} partial, {len(bad)} unmet/diverged."
|
||||
)
|
||||
lines.append("Complaints:")
|
||||
i = 0
|
||||
for v in verdicts:
|
||||
if v.get("status") == "MET":
|
||||
continue
|
||||
i += 1
|
||||
text = (v.get("text") or "").strip().replace("\n", " ")
|
||||
if len(text) > 140:
|
||||
text = text[:137] + "…"
|
||||
lines.append(
|
||||
f'{i}. Acceptance criterion "{text}" — {v.get("status", "?").title()} — '
|
||||
f'{(v.get("complaint") or "").strip()}'
|
||||
)
|
||||
for c in extra:
|
||||
i += 1
|
||||
lines.append(
|
||||
f"{i}. {c.get('kind', 'violation')} — {c.get('location', '?')} — "
|
||||
f"{c.get('violation', '')} — {c.get('fix', '')}"
|
||||
)
|
||||
if observations:
|
||||
lines.append("")
|
||||
lines.append("Non-blocking observations:")
|
||||
lines.append(observations)
|
||||
|
||||
# Per-criterion evidence appendix (MET entries, for auditability).
|
||||
if met:
|
||||
lines.append("")
|
||||
lines.append("Met criteria (evidence):")
|
||||
for v in met:
|
||||
text = (v.get("text") or "").strip().replace("\n", " ")
|
||||
if len(text) > 100:
|
||||
text = text[:97] + "…"
|
||||
lines.append(f'- "{text}" — {(v.get("evidence") or "").strip()}')
|
||||
|
||||
print(json.dumps({"adv_report": "\n".join(lines)}))
|
||||
|
||||
|
||||
try:
|
||||
main()
|
||||
except Exception as e: # noqa: BLE001 — never a silent verdict
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"adv_report": (
|
||||
"ADVERSARIAL_REVIEW: DIVERGES\n"
|
||||
f"Criteria: verdict computation error: {e} — treat as failed, not as conforming."
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
Reference in new issue
Block a user