DEV Community

Riley Xu
Riley Xu

Posted on

Migration Diary: Drain the Transcript Archive Before You Leave a Paid Coding Agent

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.

  1. Export every agent thread that touched the target repository during the last full billing cycle.
  2. Normalize those exports into UTF-8 markdown or JSONL so a local parser can read them.
  3. Extract mentioned paths, decisions, failed commands, and unresolved todos into one leftover markdown file.
  4. Redact tokens, hostnames, and private URLs before that file enters git or shared chat.
  5. 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()
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)