How finished turns from every harness become Markdown memory — and what reads it


In one paragraph

A turn is one prompt you send to a harness plus everything its agent does until it stops and hands control back to you. When a turn finishes, HarnessLink writes a short Markdown bank entry: your prompt, the tools the agent called, and its final answer. Entries live in a per-project folder on your disk and upload to HarnessLink Cloud like any other memory file. Recall, briefs, context hooks and the MCP memory tools all read them.

The HarnessLink service produces them by reading each harness's transcripts on disk — omp, pi, Claude Code, Codex and Antigravity. This is autobank. superpi cloud setup turns it on for every one of those harnesses it finds, and the first pass for a harness banks its last 30 days. Cursor is not supported. Entries that HarnessLink's old built-in agent banked in-process stay in the tree and keep syncing; nothing produces new ones.

Background and origin

HarnessLink already runs an append-only memory-gateway pattern: a MemorySyncUploader background process scans the memories tree (~/.superpi/agent/memories/), uploads every .md file it finds to the cloud D1 store, and the reverse pull-memories command rehydrates the local tree from the cloud. This upload/pull pipeline already works and ships zero server changes for storage — any .md file placed in the right directory is picked up automatically.

The memory banking feature builds on that foundation by adding a new write path: when an agent turn settles, a Markdown artifact representing that turn is appended to the project-scoped memory tree. No changes to the upload infrastructure, the D1 schema, or the pull endpoint are required.


Layout

All files live inside the existing memories root (getMemoriesDir(), default ~/.superpi/agent/memories/). Project directories use the same slug encoding as the existing pipeline:

shell
<memoriesRoot>/
  <project-slug>/          ← e.g. --Users-me-Workspace-myapp--
    bank/
      <file-safe-ISO>--<slug>.md   ← one entry per finished turn (append-only)
    MAIN.md                        ← index controller
    summaries/
      <YYYY-MM-DD>.md              ← daily roll-up (additive, never deleted)
    state/
      <harness>--<sessionId>.md    ← latest todos / plan / checkpoints of one harness session

The project is the turn's working directory, so a turn from Claude Code and a turn from omp in the same folder land in the same project.

Bank entry file

Each file is a self-contained Markdown artifact with YAML frontmatter:

markdown
---
id: 2026-08-28T10-30-00-000Z
ts: "2026-08-28T10:30:00.000Z"
title: "Fix the authentication bug"
tags:
  - bash
  - read
  - write
cwd: "/Users/me/Workspace/myapp"
session_id: "01929fae..."
harness: claude-code
turn_key: "claude-code:01929fae...:4b1c..."
source: autobank:claude-code
---

## Prompt

The user's request (capped at 2 000 chars).

## Actions

- bash: {"command":"npm test"}
- write: {"path":"/src/auth.ts","content":"..."}

## Result

The final assistant response (capped at 4 000 chars).

File-safe ISO replaces : and . with - so filenames work on all filesystems. The double-dash -- separator distinguishes the timestamp prefix from the slug, since the timestamp itself contains only single dashes after the replacement. The time is when the turn finished, not when it was banked, so a backfilled entry sorts where it happened.

harness, turn_key and source. Autobank entries carry harness: <id>, turn_key: <harness>:<sessionId>:<turnId> and source: autobank:<harness>. Older entries from HarnessLink's old built-in agent carry source: superpi-autobank and no harness or turn_key.

Per-file byte cap: each entry is checked against MEMORY_UPLOAD_MAX_BYTES (1 MB) — the same ceiling the MemorySyncUploader enforces — and the Result section is trimmed if the file would exceed it. In practice, the character caps (2 000 / 4 000 chars) keep entries well under 10 KB.

MAIN.md

An index controller file per project. Human-written notes above the marker comments are preserved verbatim across every regeneration:

markdown
# Project Memory

Add context, architecture notes, or reminders here.
Everything above the markers is untouched by the banker.

<!-- superpi:index:start -->
- 2026-08-28T10:30:00.000Z — Fix the authentication bug (bank/2026-08-28T10-30-00-000Z--fix-the-authentication-bug.md)
- 2026-08-27T14:00:00.000Z — Add user preferences endpoint (bank/...)
<!-- superpi:index:end -->

The index is rebuilt newest-first, capped at 200 rows, once per autobank pass for each project that pass touched.

Daily summaries

Each project's past days that have bank entries but no summary file are compacted into summaries/YYYY-MM-DD.md:

markdown
# 2026-08-27

- Fix the authentication bug [bash, read, write]
- Add user preferences endpoint [bash]

Compaction is purely additive: existing summary files are never touched — the bank is append-only by design.

Work state (`state/`)

For each harness session, autobank also keeps one file with that session's latest todos, plan and checkpoints: state/<harness>--<sessionId>.md. Frontmatter: harness, session_id, cwd, updated (time of the newest change), kinds. Sections:

  • ## Todos — - [x] done, - [~] in progress, - [!] blocked,

    - [-] abandoned, - [ ] pending

  • ## Plan
  • ## Checkpoints — the last 10
HarnessRead from
omp, pithe todo tool's results and manual todo edits, <session>/local/PLAN.md, checkpoint/rewind calls
Claude Code~/.claude/tasks/<session>/*.json, legacy ~/.claude/todos/…, TodoWrite calls, ~/.claude/plans/<slug>.md, ExitPlanMode plans
Codexupdate_plan calls (not those made inside code-mode exec scripts)

The file is checked whenever the session's transcript grows, rewritten only when its content changes, and never deleted. Like bank entries, it uploads through the normal memory sync.


When banking fires

Autobank

The HarnessLink service runs an autobank pass when it starts, every 30 seconds, every 2 seconds while a backfill is still draining, and once in every superpi cloud sync. A pass reads up to about 8 MB of new transcript per harness. It only reads files; it never writes to a harness's store.

HarnessTranscripts readA turn is finished when
omp~/.omp/agent/sessions/<dir>/*.jsonlthe assistant stops (stop, length, aborted), the next prompt arrives, or the session exits. An error stop waits, because a retry continues the turn
pi~/.pi/agent/sessions/<dir>/*.jsonlsame as omp
Claude Code~/.claude/projects/<slug>/<uuid>.jsonla turn_duration record, the next real prompt, or an interrupt. A plain end_turn stop is not enough on its own, because a Stop hook can still continue the turn
Codex~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonltask_complete. Interrupted turns (turn_aborted) are never banked
Antigravity~/.gemini/antigravity-cli/conversation_summaries.db and history.jsonl— see below

Quiet transcripts. For omp, pi and Claude Code, a turn that has an answer and no tool call still pending is also banked once its transcript has been quiet for 5 minutes, so the last turn of a session is not lost. A turn still waiting on a tool is never banked this way; after 7 days of silence it is dropped. Codex turns settle only on task_complete.

Antigravity keeps no readable per-turn transcript, so it gets **one entry per conversation**: the conversation title, its first prompt, and a result of step count, last activity and up to the last 20 prompts. The entry is dated by the conversation's first prompt. When the conversation's step count or last-modified time changes, the same entry is rewritten in place. Child conversations, conversations with no workspace folder, and conversations last modified before the backfill window are skipped.

Not banked: subagent turns (omp/pi <session>/ subagent files, Claude Code subagents/ and sidechain lines, Codex subagent threads), turns a branched or forked omp/pi session copies from its parent session (they are banked once, from the parent), and Cursor, which is not supported.

Backfill. The first pass after a harness is turned on banks the turns that finished in the last 30 days, then follows new turns. Transcripts not modified in those 30 days are not read. superpi cloud autobank backfill --days=N widens the window and re-reads every transcript once.

Exactly once. Each turn is banked at most once, keyed by its turn_key, across restarts, log rotation and backfills. A pass stopped in the middle of a write loses no turns: another pass takes over its reservation after 10 minutes. Read positions, unfinished turns and banked keys live in ~/.superpi/agent/harness-autobank.db.

Switches. cloud.autobank in config.yml lists the harnesses with autobank on (superpi cloud autobank enable|disable <id>; setup turns it on for every detected one). On a machine set up before autobank existed the key is absent, and autobank follows cloud.harnesses (the harnesses already captured), so updating HarnessLink turns it on without re-running setup; once the key is written — by setup, enable or disable — that list is used, even when empty. Autobank works locally: it does not depend on any sync category, and its files upload through the normal memories category. Turning a harness off — with disable, by removing it from cloud.autobank, or with memory.autoBank: false — pauses it. Entries already banked stay. When it is turned back on, it banks the last 30 days, not everything from the time it was off.

Trivial-turn filter

For every harness except Antigravity, a turn is skipped when:

  • No tool calls were made and the result text is shorter than 200 characters.

This excludes brief conversational exchanges that carry no durable signal — simple acknowledgements, one-line answers, and error recoveries — while banking every meaningful working session. Antigravity conversations are always banked.

The master switch

memory.autoBank (boolean, default true) is the master switch. Setting it to false in config.yml stops all banking and state/ snapshots for every harness. It is read on every autobank pass, so it takes effect without a restart.

Safety invariants

  • Banking never breaks a session. Autobank runs in the HarnessLink service, outside every

    harness, and only reads their files. Errors are caught and logged; a disk-full or permission error produces a log warning, not a crash.

  • New entries reach the cloud on the next memory sync of the HarnessLink service, or at

    once with superpi cloud sync.


Why zero server changes

The MemorySyncUploader scans the entire memories tree for .md files and uploads any that are new or changed. bank/*.md and summaries/*.md are plain Markdown files in the same directory tree. No new file categories, no schema migrations, no API additions are needed. The cloud store receives them on the next scan, and pull-memories delivers them to any other machine that pulls.

The MAIN.md index file is also in the tree and reaches the cloud, giving remote sessions a human-readable overview of the project's banked history.

Consumers of the bank

Banking is the compression step; four surfaces consume it, so the distilled record replaces raw transcripts wherever context is re-established:

  • Recall — superpi cloud recall <query> and GET /v1/memories/index/search run the

    shared FTS core over bank entries (own project + linked.txt projects by default).

  • Context brief — superpi cloud brief (src/cloud/brief.ts) assembles a budgeted brief

    from the human part of MAIN.md, the newest ten bank entries, the harness's own memory files (raw_memories.md, newest rollout summary) and an optional recall section from the local index below. Sections are whole-or-dropped under --budget.

  • Resume broker — superpi resume offers the brief beside native resume once a

    transcript's estimated replay cost reaches 50 000 tokens (src/cli/resume-broker.ts).

  • MCP server — superpi mcp exposes memory_search, memory_get, memory_bank and

    context_brief (plus the aliases recall_memory and record_learning) so any coding harness wired to the server can search, fetch and bank memory inline without leaving the session (src/mcp/server/superpi-server.ts; src/cloud/brief-wiring.ts). The full tool list is in USAGE.md.

  • Context hooks — superpi context-hook injects the brief at session start and up to

    three matching bank entries on each prompt in Claude Code, Codex and omp.

  • Local memory index — ~/.superpi/agent/memory-index.db (src/memories/local-index.ts,

    ADR-43): SQLite FTS5 over the whole tree, a bank entry split into title, tags, prompt, step-log digest and result. The service keeps it current from its file watcher; memory_search, the brief's recall and the hooks read it first, and memory_get reads the file on this machine before asking the cloud. See USAGE.md.

Nothing here changes how banking writes; these are readers of the same tree.


Implementation files

FileRole
packages/superpi/src/memories/banker.tsCore implementation: entries, MAIN.md, summaries, state/ files
packages/superpi/src/cloud/harness-autobank.tsAutobank: the pass over each harness's transcripts, cursors, backfill
packages/superpi/src/cloud/harness/turn-parsers.tsPer-harness turn and work-state parsers
packages/superpi/src/cloud/autobank-cli.tssuperpi cloud autobank
packages/superpi/src/cloud/config.tsmemory.autoBank master switch (read from config.yml, default true)
packages/superpi/test/memory-banker.test.tsUnit tests

Flag notifications