122 lines
4.0 KiB
Python
Executable File
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())
|