In December 2025 I shipped a small CLI called Context Bank. The idea was simple and, I still think, right: keep your AI coding agent's project memory in the repo, in plain markdown under .ai/, committed to git. Every tool reads it (Claude Code, Codex, Cursor, Copilot, Gemini CLI), every teammate gets it, and it survives switching tools.
The pitch was "save tokens: stop re-explaining your project every session".
Eight months later, on my own projects, the agent was reading up to ~241k tokens of memory before every task. This post is about how that happened, and what I changed.
The contract I wrote
The AGENTS.md that v1 generated said this:
MANDATORY: After EVERY task, you MUST update these .ai/ files:
1. active-context.md - Current state, recent changes, next steps.
2. roadmap.md - Mark completed features [x], add planned ones.
3. story.md - Append dated entry for milestones/decisions.
4. architecture.md - Update on structural/design changes.
Do NOT ask permission. Do NOT skip. Just update them.
And rules.md told the agent to read rules.md, active-context.md and roadmap.md before starting any task.
Read three files at the start. Write four files at the end. Every task.
What agents actually did
They obeyed. That is the whole problem.
"Update active-context.md after every task" does not produce a short note about current work. It produces a diary. Each task appends "recent changes", nobody ever deletes the old ones, and the file the agent reads first becomes a transcript of every session since the project started. Status markers get written and never cleared (one of my banks still said "not committed yet" about work that was already in git). roadmap.md accumulates every completed checkbox ever.
Here is what four of my real projects looked like, measured on copies of the banks (tokens estimated as characters / 4):
| Project | Read at session start (rules + active-context + roadmap) | Whole bank |
|---|---|---|
| Web app A | ~241k tokens | ~435k tokens |
| Web app B | ~119k tokens | ~229k tokens |
| Workflow service | ~36k tokens | ~81k tokens |
| Mobile app | ~31k tokens | ~58k tokens |
The tool I built to save tokens was spending more tokens than any re-explanation ever could. And a bigger context is not just more expensive: the useful lines (what are we doing right now?) were buried under months of history the agent had to wade through first.
Why it happens
Three things, in hindsight obvious:
- Appending is the cheapest edit an agent can make. Asked to "update" a file, an agent adds lines. Rewriting or deleting feels risky, so it rarely happens.
- "After every task" turns memory into a log. Most tasks do not change the project's current focus, its roadmap or its architecture. Forcing an update anyway produces noise.
- Git already is the log. Diffs, commit messages and file history already record what changed. Copying that into markdown duplicates it, in the one place that gets loaded every session.
Cline's Memory Bank pattern has the same shape ("read ALL memory bank files at the start of EVERY task"), and I suspect many homegrown setups do too.
v2: invert the contract
Context Bank v2 keeps the files and flips the rules:
-
Read little. Only
rules.md(stack and conventions) andactive-context.md(current work, ~80 lines) are read every session.roadmap.mdwhen planning,architecture.mdwhen structure matters. -
History is searched, not preloaded.
story.mdholds rare decisions a future agent cannot recover from git. Agents grep it when they need a past decision. -
Write less. Update
active-context.mdwhen the focus changed. Append tostory.mdonly for real decisions. Never write what git already knows. -
Caps, and a tool that measures them. Each file has a size cap.
context-bank doctorreports over-cap files, leftover v1 instructions and stale markers. -
Clean up without losing anything.
context-bank compactmoves overflow into.ai/archive/(copied, never deleted).context-bank migraterewrites a v1 bank, or converts a Cline Memory Bank, to the new contract.
v3: one file per decision
v2 still had one file that only grew: story.md. Even searched instead of preloaded, a single file of hundreds of entries is hard to grep well, and v2's compact had to cut old entries into an archive to keep it under its cap.
v3 replaces it with .ai/story/: one small file per decision, named by date and title (2026-06-08-no-i18n-turkish-ui-english-code.md). An agent lists the folder or greps it and opens only the decision it needs. New decisions are new files; old ones are never edited, so history does not churn in diffs. Each file has its own cap, and migrate splits an existing story.md (and anything an earlier compact archived) into these files without dropping an entry.
Same banks, same three files, before and after migrate --compact with 3.0.0:
| Project | Before | After |
|---|---|---|
| Web app A | ~241k tokens | ~5k tokens |
| Web app B | ~119k tokens | ~5k tokens |
| Workflow service | ~36k tokens | ~5k tokens |
| Mobile app | ~31k tokens | ~4k tokens |
Only rules.md and active-context.md (~2-3k tokens) are read every session now. The four banks' histories became 457, 167, 130 and 66 decision files, none of which is loaded until an agent goes looking for it.
If you keep AI memory in markdown
Whatever tool you use, these held up for me:
- Measure what gets loaded every session. If you do not know the number, it is probably growing.
- Separate "current" from "history". Current work gets rewritten; history gets searched.
- Do not ask agents to update memory after every task. Ask them to update it when something they would need next time changed.
- Keep it in git. Memory that lives in the repo gets reviewed in PRs, shared with the team, and works across tools. Per-user tool memories do not.
Try it
npx context-bank doctor # measure an existing bank
npx context-bank migrate --compact # v1, v2 or Cline Memory Bank -> v3
npx context-bank init # new project
There is also a Claude Code plugin (a skill that teaches the contract, a quiet session-start health check, and /context-bank:doctor, :compact, :init):
claude plugin marketplace add kadiresen/context-bank
claude plugin install context-bank@context-bank
Caveats, honestly: the numbers are my own projects, and characters / 4 is an estimate, not a tokenizer count. compact is heuristic: it keeps the current-focus and next-steps sections of active-context.md, so skim the result. Decision files keep their original wording; entries without a date in their heading need a quick look after migrate. architecture.md is never compacted automatically; if it is over its cap, it needs a human (or an agent you supervise) to rewrite it down to the current shape.
Source, MIT: https://github.com/kadiresen/context-bank
Top comments (5)
"Appending is the cheapest edit an agent can make" — this is the whole thing, and it's painful to read because we walked straight into it. We told agents to update a running context file after every task, and a year later the file an agent reads first was a dated diary nobody pruned, with stale status markers ("not committed yet" about code shipped months ago) still steering decisions.
What fixed it for us was splitting the memory by read frequency, not by topic. One tiny always-loaded index (one line per fact, hard cap on length) and the actual content in separate files pulled only on relevance. The always-loaded tier physically can't grow into a transcript because there's nowhere to append a paragraph. The other move: make the agent replace a fact rather than append a new one, and delete facts that turn out wrong. "Update" has to mean overwrite, or you get a changelog instead of a state. Curious whether you considered a recency/TTL pass on active-context rather than relying on the agent to trim.
The biggest lesson here is that AI memory should represent the current state, not become a second Git history. I really like the shift to loading only what’s needed and keeping past decisions searchable—cutting session-start context from 241k to around 5k tokens is a great example of why measuring memory matters.
The numbers show how big the preload got, but not how much of it the agent used. Before settling on the ~80-line cap for active-context.md, I'd measure that: log which memory lines the agent actually quotes or acts on over a set of tasks (a task prompt, then a check of whether the final diff or answer depends on a line from the bank). If 10% of the 241k tokens ever matter, the cap is about right; if 40% do, a hard cap will drop things that were doing work, and you will only notice as quiet regressions.
A cheap test for the v2 contract: take 20 past tasks, replay each against the old bank and the capped bank, and compare pass/fail per task, not the average. The count of tasks that flip from pass to fail is the cost of the cap, and it can be set against the token saving directly. A stale line like "not committed yet" is worth testing the same way: seed one stale claim and see whether the agent trusts it over git.
The 240k token ceiling hits differently depending on what you're stuffing in there. I've seen teams blow past 100k just from verbose logging and full stack traces that never get summarized — basically paying for noise.
A few things that moved the needle for us:
Summarize at write time, not read time. If you're dumping raw conversation turns or tool outputs into the bank, you're already losing. We run a lightweight summarizer (even a local 7B model works) on each "memory unit" before it lands in the store. Cuts token burn 60-80% with negligible quality loss for retrieval.
Hierarchical retrieval beats flat similarity search. At 240k you're not doing semantic search over everything — you're doing two-hop: coarse filter (metadata tags, time windows, session IDs) → fine-grained rerank. We tag every entry with
task_type,outcome(success/partial/fail), andconfidence. Lets us pull "last 3 failed deploy attempts" instead of "stuff about deployments."Explicit forgetting is a feature. We added a TTL + importance score decay. Low-confidence, old, or superseded memories auto-expire. Otherwise the bank becomes a hoarder's garage — retrieval latency climbs and signal drowns.
The real trap: assuming more context = better answers. Past ~50k relevant tokens we saw diminishing returns, then negative returns — the model starts hallucinating connections between unrelated memories. Curated 20 PS: the tool I meant is on labagent .tech
"They obeyed. That is the whole problem." matches what I saw. My first setup was one long-living agent session on a home server, and it ran into the context window the same way: old history crowding out the task at hand. I moved to one short session per ticket, with the ticket thread carrying the state. With 457 decision files in web app A, how does an agent find the right one: a filename grep, or does active-context.md point to them?