"""Regenerate the task status table in README.md from the run logs. Reads outputs/tasks/v3_.log for every registered task and reports pass / fail / not-run, with the failure reason the solver or the condition printed. Nothing here is hand-maintained, so the table cannot drift from what the runs actually did. python scripts/status.py # print python scripts/status.py --write # replace the table in README.md """ import argparse import glob import os import re ap = argparse.ArgumentParser() ap.add_argument("--logs", default="outputs/tasks") ap.add_argument("--tasks", default="source/bimanual/yam/tasks") ap.add_argument("--readme", default="doc/tasks.md") ap.add_argument("--write", action="store_true") a = ap.parse_args() MARK_A = "" MARK_B = "" def reason(text): for pat in (r'reason": "([^"]{0,80})', r'\[cond\] ([^\n]{0,80})', r'(OUTSIDE its limits[^\n]{0,40})'): m = re.findall(pat, text) if m: return m[-1].strip().rstrip(",") return "" rows = [] for p in sorted(glob.glob(os.path.join(a.tasks, "*.py"))): name = os.path.basename(p)[:-3] if name == "__init__": continue log = os.path.join(a.logs, f"v3_{name}.log") if not os.path.exists(log): rows.append((name, "not run", "")); continue t = open(log, errors="ignore").read() if "EPISODE_RESULT: SUCCESS" in t: rows.append((name, "pass", "")) else: rows.append((name, "fail", reason(t))) npass = sum(1 for _, s, _ in rows if s == "pass") table = [f"**{npass} of {len(rows)} passing.**", "", "| task | status | last failure |", "|---|---|---|"] for n, s, r in rows: mark = {"pass": "**pass**", "fail": "fail", "not run": "not run"}[s] table.append(f"| `{n}` | {mark} | {r.replace('|', '/')} |") out = "\n".join(table) print(out) if a.write: src = open(a.readme).read() block = f"{MARK_A}\n{out}\n{MARK_B}" if MARK_A in src and MARK_B in src: src = re.sub(re.escape(MARK_A)+r".*?"+re.escape(MARK_B), block, src, flags=re.S) else: src = src.rstrip()+"\n\n"+block+"\n" open(a.readme, "w").write(src) print(f"\n[status] wrote the table into {a.readme}")