Files
BFM-decomp/tools/agent_drafts_restore.py
T
Drew T 9480c979a0 docs(phase-32): T3 CHECKPOINT — the launching coordinator overflowed after bank 9; 22 agent verdicts recovered (20 MATCH / 9 NEAR / 2 FAIL of 31), 11 swept Opus deliverables restored + re-verified, cookbook §500 harvest, playbook §S80 addendum-2, agent_drafts_restore.py
- verdict ledger .run/P32/t3/verdicts.jsonl rebuilt from the 31 T3 transcripts (agent_verdicts.py); every unbanked draft
  re-verified with rtu_match in its real TU: 10 MATCH awaiting the gate (main func_80015B6C 120 + func_8002FDE8 73;
  md_SC03_054 func_801EF6D8 604 + six jtbls; md_SC03_053 func_801EF734 44 + func_801EF7E4 72; md_MAIN_007
  func_800CF148/2BC/EEFC/EF94/068) + func_800CF3B0 leaf-exact behind the TU's void/3-arg decl; 9 NEAR at exact length
  (2/6/15/17/27/35/46/49/137), each with its class and inert-lever list
- R48 incident: one agent's `find .run/P32/t3/opus -maxdepth 1 -type f ! -name <mine> -exec mv {} _scratch/` swept 11
  sibling deliverables (two MATCHes among them); found in _scratch/, restored to the contract paths, byte-verified;
  tools/agent_drafts_restore.py (NEW: transcript replay) as the fallback; .gitignore allowlist for .run/P32/** so the
  drafts, ledger and census files are committed (R20)
- harvest (R16/R30): cookbook §500 (10 banked closers, 10 MATCH closers, 9 NEAR classes, two NEW mechanisms — the
  pinned-base-vs-pseudo-address alias basin and #line-equalised ASM_OPERANDS for cross_jump — and the wave-process
  defects); wave-playbook §S80 addendum-2 (per-function work dirs, JSON-only final message, the 20-agent cap, the
  recovery tools); accelerators P32 T3; decision-log P32 S82 (R31); SETUP tooling row (R21); cookbook-index
  regenerated; .run/P32/t3/BRIEF.md output contract amended for the 17 queued launches
- CURRENT_PHASE: T3 row IN PROGRESS, Log entry, 🛑 SESSION CHECKPOINT (census 44 stubs / 5,313 ins with every row's
  state and draft path, the 9-step resume order, the dead session's read-only T4 pre-read); harness task list rebuilt
- no src/ or config/ change in this commit; no fleet R22 has run since the 10 T3 banks — the resume order starts with one
2026-09-05 09:36:43 -06:00

100 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""agent_drafts_restore.py — rebuild a subagent's FINAL deliverable draft from its transcript (P32 T3, 2026-09-05).
WHY. Agent-tool drafters write their deliverable to a shared scratch dir. In the T3 wave one agent's tidy-up
(`find <dir> -maxdepth 1 -type f ! -name <mine> -exec mv {} _scratch/`) swept eleven sibling deliverables away;
two of them were MATCHes. The transcript (`~/.claude/projects/<proj>/<session>/subagents/agent-*.jsonl`) still holds
every Write/Edit the agent made, so the file can be REPLAYED: apply each Write/Edit/heredoc/cp/mv in order and keep
the last state of every path that looks like a deliverable.
python3 tools/agent_drafts_restore.py <subagents-dir> --out <dir> [--pattern REGEX]
Prints one line per agent: the deliverable path(s) rebuilt, the op count, and the number of Edit ops whose old_string
was NOT found (an edit applied to a file the agent had changed via sed/python in between — a replay that reports
misses > 0 may be STALE; verify it with rtu_match and prefer an on-disk copy if one exists). An agent with no
deliverable prints NO-DELIVERABLE so nothing is silently skipped (R32). Replay is a RECOVERY route, never the primary:
verify every rebuilt draft against the real TU before believing its verdict.
"""
import argparse, glob, json, os, re
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/"
def norm(p):
if not p:
return p
p = p.replace(REPO, "")
return p[2:] if p.startswith("./") else p
def replay(tp, pattern):
files, ops = {}, []
for line in open(tp, errors="replace"):
try:
o = json.loads(line)
except Exception:
continue
if o.get("type") != "assistant":
continue
for c in (o.get("message") or {}).get("content") or []:
if c.get("type") != "tool_use":
continue
n, i = c.get("name"), c.get("input") or {}
if n == "Write":
p = norm(i.get("file_path")); files[p] = i.get("content", ""); ops.append(("W", p))
elif n == "Edit":
p = norm(i.get("file_path"))
if p not in files:
ops.append(("EDIT-UNKNOWN", p)); continue
old, new = i.get("old_string", ""), i.get("new_string", "")
if i.get("replace_all"):
files[p] = files[p].replace(old, new); ops.append(("E", p))
elif old in files[p]:
files[p] = files[p].replace(old, new, 1); ops.append(("E", p))
else:
ops.append(("EDIT-MISS", p))
elif n == "Bash":
cmd = i.get("command", "")
for m in re.finditer(r"\b(cp|mv)\s+(?:-\w+\s+)*(\S+)\s+(\S+\.c)\b", cmd):
src, dst = norm(m.group(2)), norm(m.group(3))
if src in files:
files[dst] = files[src]; ops.append((m.group(1), src, dst))
elif os.path.exists(REPO + src):
files[dst] = open(REPO + src, errors="replace").read(); ops.append((m.group(1) + "-disk", src, dst))
else:
ops.append((m.group(1) + "-UNKNOWN", src, dst))
for m in re.finditer(r"cat\s*>\s*(\S+\.c)\s*<<\s*'?(\w+)'?\n(.*?)\n\2\b", cmd, re.S):
p = norm(m.group(1)); files[p] = m.group(3) + "\n"; ops.append(("HEREDOC", p))
deliv = [p for p in files if p and re.search(pattern, p)]
return files, ops, deliv
def main():
ap = argparse.ArgumentParser()
ap.add_argument("subagents_dir")
ap.add_argument("--out", required=True)
ap.add_argument("--pattern", default=r"/(opus|sonnet|haiku)/func_[0-9A-Fa-f]{8}\.c$",
help="regex a path must match to count as a deliverable")
a = ap.parse_args()
os.makedirs(a.out, exist_ok=True)
n_agents = n_files = 0
for tp in sorted(glob.glob(os.path.join(a.subagents_dir, "agent-*.jsonl"))):
n_agents += 1
tag = os.path.basename(tp)[len("agent-"):-len(".jsonl")]
files, ops, deliv = replay(tp, a.pattern)
misses = sum(1 for x in ops if "MISS" in x[0] or "UNKNOWN" in x[0])
if not deliv:
print(f"{tag}: NO-DELIVERABLE ops={len(ops)}")
continue
for p in deliv:
arm = p.split("/")[-2]; fn = os.path.basename(p)[:-2]
outp = os.path.join(a.out, f"{arm}__{fn}__{tag}.c")
open(outp, "w").write(files[p]); n_files += 1
flag = "" if misses == 0 else f" ** {misses} unreplayable op(s): VERIFY, prefer an on-disk copy"
print(f"{tag} {arm} {fn} -> {outp} ({len(files[p])} chars, ops={len(ops)}){flag}")
print(f"agents={n_agents} deliverables rebuilt={n_files} -> {a.out}")
if __name__ == "__main__":
main()