#!/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
-maxdepth 1 -type f ! -name -exec mv {} _scratch/`) swept eleven sibling deliverables away;
two of them were MATCHes. The transcript (`~/.claude/projects///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 --out [--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()