2026-02-25 18:52:36 -07:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""Deterministic remediation routing from gate diagnostics to action plan."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
import json
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import Dict, List
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_json(path: str) -> Dict[str, object]:
|
|
|
|
|
p = Path(path)
|
|
|
|
|
if not p.exists():
|
|
|
|
|
return {}
|
|
|
|
|
return json.loads(p.read_text())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def classify(gates: Dict[str, object]) -> Dict[str, object]:
|
|
|
|
|
g = gates.get("gates", {}) if isinstance(gates, dict) else {}
|
|
|
|
|
reasons = list(g.get("failure_reasons", [])) if isinstance(g, dict) else []
|
|
|
|
|
compile_reason = str((g.get("compile") or {}).get("reason", ""))
|
|
|
|
|
tests_reason = str((g.get("tests") or {}).get("reason", ""))
|
2026-02-25 19:07:20 -07:00
|
|
|
diagnostics = gates.get("diagnostics", []) if isinstance(gates, dict) else []
|
|
|
|
|
diag_text = "\\n".join(str(d.get("message", "")) + "\\n" + str(d.get("raw_excerpt", "")) for d in diagnostics if isinstance(d, dict)).lower()
|
2026-02-25 18:52:36 -07:00
|
|
|
|
|
|
|
|
actions: List[Dict[str, object]] = []
|
|
|
|
|
|
|
|
|
|
nonrecoverable = "tool_missing" in compile_reason or "tool_missing" in tests_reason
|
|
|
|
|
if nonrecoverable:
|
|
|
|
|
actions.append(
|
|
|
|
|
{
|
|
|
|
|
"action": "stop_blocked",
|
|
|
|
|
"tool": "none",
|
|
|
|
|
"reason": "missing_toolchain",
|
|
|
|
|
"confidence": 1.0,
|
|
|
|
|
"priority": 100,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
return {
|
|
|
|
|
"blocked": True,
|
|
|
|
|
"blocked_reason": "missing_toolchain",
|
|
|
|
|
"actions": actions,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if any(r.startswith("placeholder_or_todo") for r in reasons):
|
|
|
|
|
actions.append(
|
|
|
|
|
{
|
|
|
|
|
"action": "strengthen_spec",
|
|
|
|
|
"tool": "whetstone_generate_code",
|
|
|
|
|
"hint": "Eliminate placeholders/TODO markers and provide concrete field/method types and bodies.",
|
|
|
|
|
"confidence": 0.95,
|
|
|
|
|
"priority": 90,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if any(r.startswith("compile_failed") for r in reasons):
|
2026-02-25 19:07:20 -07:00
|
|
|
if "defined in header" in diag_text or "did you forget to '#include" in diag_text:
|
|
|
|
|
actions.append(
|
|
|
|
|
{
|
|
|
|
|
"action": "fix_cpp_includes",
|
|
|
|
|
"tool": "apply_cpp_diagnostic_fixes",
|
|
|
|
|
"hint": "Apply deterministic include insertion from compiler diagnostics before re-running gates.",
|
|
|
|
|
"confidence": 0.98,
|
|
|
|
|
"priority": 95,
|
|
|
|
|
}
|
|
|
|
|
)
|
2026-02-25 18:52:36 -07:00
|
|
|
actions.append(
|
|
|
|
|
{
|
|
|
|
|
"action": "diagnose_pipeline",
|
|
|
|
|
"tool": "whetstone_run_pipeline",
|
|
|
|
|
"hint": "Run same-language pipeline pass to collect parse/validation diagnostics and refine signatures/types.",
|
|
|
|
|
"confidence": 0.9,
|
|
|
|
|
"priority": 80,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
actions.append(
|
|
|
|
|
{
|
|
|
|
|
"action": "strengthen_spec",
|
|
|
|
|
"tool": "whetstone_generate_code",
|
|
|
|
|
"hint": "Emit compile-clean code for target language with explicit imports/includes and fully typed declarations.",
|
|
|
|
|
"confidence": 0.85,
|
|
|
|
|
"priority": 70,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if any(r.startswith("tests_failed") for r in reasons):
|
|
|
|
|
actions.append(
|
|
|
|
|
{
|
|
|
|
|
"action": "strengthen_spec",
|
|
|
|
|
"tool": "whetstone_generate_code",
|
|
|
|
|
"hint": "Implement queue behaviors: enqueue/dequeue/peek/size/empty with deterministic semantics.",
|
|
|
|
|
"confidence": 0.9,
|
|
|
|
|
"priority": 75,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
actions.sort(key=lambda a: (-int(a["priority"]), a["action"]))
|
|
|
|
|
return {
|
|
|
|
|
"blocked": False,
|
|
|
|
|
"blocked_reason": "",
|
|
|
|
|
"actions": actions,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
|
|
|
ap = argparse.ArgumentParser()
|
|
|
|
|
ap.add_argument("--gates-json", required=True)
|
|
|
|
|
ap.add_argument("--out", default="")
|
|
|
|
|
args = ap.parse_args()
|
|
|
|
|
|
|
|
|
|
gates = load_json(args.gates_json)
|
|
|
|
|
plan = classify(gates)
|
|
|
|
|
payload = {
|
|
|
|
|
"router_version": "1",
|
|
|
|
|
"input_failure_reasons": (gates.get("gates", {}) or {}).get("failure_reasons", []),
|
|
|
|
|
"blocked": plan["blocked"],
|
|
|
|
|
"blocked_reason": plan["blocked_reason"],
|
|
|
|
|
"actions": plan["actions"],
|
|
|
|
|
}
|
|
|
|
|
text = json.dumps(payload, indent=2, sort_keys=True)
|
|
|
|
|
if args.out:
|
|
|
|
|
out = Path(args.out)
|
|
|
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
out.write_text(text + "\n")
|
|
|
|
|
print(text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|