You should drain the transcript archive before you cancel a paid coding agent, because the leftovers live in those chats. Git keeps merged code, but it does not keep rejected paths, named constraints, or half-finished repairs. Those fragments sit in agent threads, and they vanish when the vendor session finally dies. This diary treats transcript drain as a required cutover step rather than optional nostalgia for old chats.
What the paid seat was actually holding
Paid coding agents store more than the patches you later merge into the default branch of a repo. They also store file lists you never committed, command outcomes you never logged, and design vetoes you never wrote down. When you export nothing, the next loop on cheaper infrastructure starts blind and repeats expensive mistakes. You then spend scarce budget rediscovering constraints that a paid agent thread had already settled for you.
A useful drain is not a full dump of every retry, joke, or stack trace from those sessions. You want a compact leftover ledger that lists paths, decisions, open defects, and commands that already failed. That ledger becomes the first context pack for whatever loop you run after cancel day. Without that ledger, free infrastructure only looks cheaper because it is silently repeating already billed work.
Cutover plan
Keep the paid seat alive until the leftover ledger survives a dry run against your tree. Work through the following numbered steps in order, and do not skip the redaction pass.
- Export every agent thread that touched the target repository during the last full billing cycle.
- Normalize those exports into UTF-8 markdown or JSONL so a local parser can read them.
- Extract mentioned paths, decisions, failed commands, and unresolved todos into one leftover markdown file.
- Redact tokens, hostnames, and private URLs before that file enters git or shared chat.
- Replay the leftover file against a cheap loop and mark each item done, skipped, or blocked.
The export format will differ by vendor, so do not wait for a perfect official archive API. Copy visible threads into transcripts/raw/ as .md or .jsonl files, and record the export date in the filename. If the vendor offers a zip of chats, drop that zip in the same folder and unpack it once. Name those files after the repository and the thread title, not after the model nickname.
Build a leftover ledger from raw transcripts
The helper below is a proposed local script, not a claim about any vendor's private export schema. Save it as drain_transcripts.py and point that script at a directory of UTF-8 text files. It walks those files, pulls path-like tokens, decision sentences, failed command lines, and TODO markers, then writes CUTOVER_LEFTOVERS.md. Treat every extracted line as a candidate, then delete anything the regex should not have kept.
#!/usr/bin/env python3
"""Drain coding-agent transcripts into a leftover ledger.
Proposed local helper. Adjust regexes to match your export format.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
PATH_RE = re.compile(
r"(?:^|[\s`'\"(])([A-Za-z0-9_./-]+\.(?:py|ts|tsx|js|go|rs|java|rb|md|toml|yml|yaml|json|sql))",
)
DECISION_RE = re.compile(
r"^\s*(?:we(?:'ll| will)|decided|do not|don't|use .+ instead|avoid|must not)\b.*",
re.I,
)
FAIL_RE = re.compile(
r"^\s*(?:\$ |# )?(?:npm|pnpm|yarn|pip|pytest|cargo|go|make|git)\b.*",
re.I,
)
TODO_RE = re.compile(r"\b(TODO|FIXME|BLOCKED|follow-?up)\b[:\s].*", re.I)
SECRET_RE = re.compile(
r"(sk-[A-Za-z0-9]+|ghp_[A-Za-z0-9]+|AKIA[0-9A-Z]{16}|Bearer\s+[A-Za-z0-9._-]+)",
)
def redact(text: str) -> str:
return SECRET_RE.sub("[REDACTED]", text)
def drain(raw_dir: Path) -> str:
paths, decisions, fails, todos = set(), [], [], []
for p in sorted(raw_dir.rglob("*")):
if p.suffix.lower() not in {".md", ".txt", ".jsonl"}:
continue
text = redact(p.read_text(encoding="utf-8", errors="replace"))
for line in text.splitlines():
for m in PATH_RE.finditer(line):
paths.add(m.group(1))
if DECISION_RE.search(line):
decisions.append(f"- {line.strip()[:240]} ({p.name})")
if FAIL_RE.search(line) and re.search(r"error|fail|exit 1|denied", line, re.I):
fails.append(f"- `{line.strip()[:200]}` ({p.name})")
if TODO_RE.search(line):
todos.append(f"- {line.strip()[:240]} ({p.name})")
parts = [
"# Cutover leftovers",
"",
"## Paths mentioned",
*[f"- `{x}`" for x in sorted(paths)] or ["- none"],
"",
"## Decisions and vetoes",
*(decisions[:80] or ["- none"]),
"",
"## Commands that already failed",
*(fails[:80] or ["- none"]),
"",
"## Unresolved follow-ups",
*(todos[:80] or ["- none"]),
"",
"## Replay status",
"| Item | Owner | Status | Notes |",
"| --- | --- | --- | --- |",
"| (fill from lists above) | | done/skipped/blocked | |",
]
return "\n".join(parts) + "\n"
def main() -> None:
raw = Path(sys.argv[1] if len(sys.argv) > 1 else "transcripts/raw")
out = Path(sys.argv[2] if len(sys.argv) > 2 else "CUTOVER_LEFTOVERS.md")
out.write_text(drain(raw), encoding="utf-8")
print(f"wrote {out} from {raw}")
if __name__ == "__main__":
main()
Run the helper like this after you fill the transcripts/raw directory with exported threads.
python3 drain_transcripts.py transcripts/raw CUTOVER_LEFTOVERS.md
# Proposed check: fail the commit if whitespace or conflict markers slipped in.
git add CUTOVER_LEFTOVERS.md
git diff --check
Read the generated markdown before you commit it, because regex extraction is greedy and will catch noise. Delete anything that looks like a customer name, a hostname, or an access token that the redactor missed. Then fill the replay table so cancel day has a queue instead of a blank prompt.
Decision table for each leftover
Use this table while you walk the generated ledger so trivia never enters the next agent loop. It keeps rejected ideas available as vetoes without turning ordinary chat noise into fake requirements. Fill every row before you schedule cancel day, including the rows you already intend to discard.
| Leftover type | Keep in git? | Replay on the next loop? | Discard when |
|---|---|---|---|
| Merged patch already on main | No, git already has it | No | Hash is on the default branch |
| Rejected approach with a named reason | Yes, as a veto note | Only if someone reopens it | The constraint is documented elsewhere |
| Failed command with a non-obvious error | Yes, in the fail list | Yes, after the environment is rebuilt | You reproduced a clean pass |
| Open TODO that still matches the tree | Yes | Yes | The file no longer exists |
| Secrets, cookies, private URLs | Never | Never | Immediately after redaction |
| Banter, retries, token-usage chatter | No | No | After the drain script runs |
You should treat "keep in git" as a documentation choice, not as a backup of the vendor's chat product. The leftover file is a cutover artifact, and it should shrink every week until it is empty. If a row cannot be classified, mark it blocked and leave the paid seat up for that thread only.
Replay without the paid seat
Once the leftover ledger exists, you can replay open items without keeping a paid coding-agent subscription on the critical path. MonkeyCode is one option for that replay: it provides free model access and a free server option you can point at the same ledger. Disclosure: This article was prepared as part of MonkeyCode's product outreach. You still run redaction first, because a free server does not make a leaked secret cheaper.
A practical replay prompt should stay boring on purpose so the new loop cannot wander. Paste the relevant leftover section, the file contents you already listed, and a request for a patch that honors the veto notes. Do not paste the entire raw archive into the new loop, because drain exists to prevent that. Keep each replay focused on a single leftover row so later failures remain easy to attribute.
You are continuing a cutover, not starting a greenfield task.
Honor every veto in the leftovers. Do not reopen rejected libraries.
Work only on the paths listed. Return a unified diff and a test command.
Leftovers:
<paste one unresolved item>
If the free loop cannot finish an item, write blocked in the table and keep a narrow paid thread for that item alone. Canceling every paid thread at once is exactly how leftover context dies during a rushed cutover. Split the cancel work by leftover status, not by calendar pressure or the invoice date.
Limitations
This drain does not reconstruct tool traces, hidden system prompts, or any vendor-side index state. Regexes miss fenced paths, screenshots, and binary attachments, and they over-capture words that look like files. The script is not a compliance archive, and it is not a substitute for your company's retention policy. Vendor export buttons change without notice, so verify the current path on the vendor's own documentation. Do not treat a click path from this diary as a durable instruction for any vendor UI.
You also cannot prove behavioral parity with a leftover list alone, even a tidy one. Pair this ledger with tests you already trust, rather than treating extracted sentences as a specification. If two transcripts disagree, believe the repository and the test suite, not the more confident chat. When a leftover cites a file that no longer exists, drop the row instead of resurrecting dead paths.
Who should not use this approach
Do not use this drain if your transcripts contain regulated data you are not allowed to copy onto a laptop. Do not use it if your employer forbids export of vendor chats, even for migration. Skip this drain when the repository is a short spike with no rejected approaches worth keeping. Teams that already keep architecture decision records and a failing-test list may only need a secret scan, not a full parser.
If you cannot redact with confidence, do not move the files to any shared server, free or paid. The cheapest leftover you will ever manage is the sensitive line that you never copy elsewhere. Encrypted local storage is acceptable during drain week, but unredacted cloud sync is not acceptable.
What cancel day should look like
Cancel day should be a checklist, not a feeling that the new loop merely seems fine. You want an empty replay table, a redacted leftover file, and a vendor export that you could reconstruct from git. Keep one backup of the raw transcripts in an encrypted volume until the first week of cheap-loop work lands on main. Then destroy the raw export, because the leftover value has already been distilled into git.
If the leftover ledger already sits in git, replay blocked rows on a free loop first. Keep the paid seat only for those rows that still fail after the free replay.
Top comments (0)