Files
whetstone_DSL/tools/mcp/synthesize_raw_gap_backlog.py

122 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import json
from collections import Counter, defaultdict
from pathlib import Path
from typing import Dict, List
def load_json(path: Path):
with path.open("r", encoding="utf-8") as f:
return json.load(f)
def main() -> int:
p = argparse.ArgumentParser(description="Synthesize prioritized raw-generator gap backlog from closure ladder outputs.")
p.add_argument("--batch-dir", required=True)
p.add_argument("--out-json", required=True)
p.add_argument("--out-md", required=True)
args = p.parse_args()
batch_dir = Path(args.batch_dir)
ladder_files = sorted(batch_dir.glob("*/closure_ladder_summary.json"))
mode_counts = Counter()
missing_counter = Counter()
by_profile_missing = defaultdict(Counter)
spec_rows: List[Dict] = []
for lf in ladder_files:
try:
summary = load_json(lf)
except Exception:
continue
mode = str(summary.get("selected_mode", ""))
mode_counts[mode] += 1
selected_run = str(summary.get("selected_run", ""))
cov_checks = []
raw_run = ""
raw_rc = ""
# Prefer raw-only attempt artifacts for gap mining.
raw_run_file = lf.parent / "raw_only.run_dir"
raw_rc_file = lf.parent / "raw_only.rc"
if raw_run_file.exists():
raw_run = raw_run_file.read_text(encoding="utf-8", errors="ignore").strip()
if raw_rc_file.exists():
raw_rc = raw_rc_file.read_text(encoding="utf-8", errors="ignore").strip()
target_run = raw_run if raw_run else selected_run
if target_run:
sp = Path(target_run) / "00_summary.json"
if sp.exists():
try:
s = load_json(sp)
cov = (s.get("native_impact_coverage") or {})
cov_checks = list(cov.get("checks") or [])
except Exception:
cov_checks = []
missing_for_spec = []
for c in cov_checks:
pid = str(c.get("id", "unknown"))
for m in (c.get("missing") or []):
m = str(m)
missing_counter[m] += 1
by_profile_missing[pid][m] += 1
missing_for_spec.append({"profile": pid, "missing": m})
spec_rows.append(
{
"run_id": lf.parent.name,
"selected_mode": mode,
"selected_run": selected_run,
"raw_only_run": raw_run,
"raw_only_rc": raw_rc,
"missing_items": missing_for_spec,
}
)
prioritized_missing = [
{"missing": k, "count": int(v)}
for k, v in missing_counter.most_common()
]
profile_priorities = {}
for pid, ctr in by_profile_missing.items():
profile_priorities[pid] = [
{"missing": k, "count": int(v)} for k, v in ctr.most_common()
]
out = {
"status": "ok",
"record_count": len(spec_rows),
"selected_mode_counts": dict(mode_counts),
"prioritized_missing": prioritized_missing,
"profile_priorities": profile_priorities,
"records": spec_rows,
}
with Path(args.out_json).open("w", encoding="utf-8") as f:
json.dump(out, f, indent=2, sort_keys=True)
f.write("\n")
with Path(args.out_md).open("w", encoding="utf-8") as f:
f.write("# Raw Generator Gap Backlog\n\n")
f.write(f"- Record count: {len(spec_rows)}\n")
f.write("- Selected mode counts:\n")
for mode, count in mode_counts.items():
f.write(f" - {mode}: {count}\n")
f.write("\n## Prioritized Missing Signals\n\n")
if not prioritized_missing:
f.write("- None\n")
else:
for row in prioritized_missing:
f.write(f"- {row['missing']}: {row['count']}\n")
print(json.dumps({"status": "ok", "record_count": len(spec_rows), "out_json": args.out_json, "out_md": args.out_md}, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())