The Token Tax: Making AI Memory Actually Efficient
My agent memory system was eating 50-80k tokens before I'd typed a word. Here's how Obsidian CLI, Haiku subagents, and a file deduplication hook cut that by 80%. Part 4 of the AI Memory series.
My agent memory system was eating 50-80k tokens before I'd typed a word. Here's how Obsidian CLI, Haiku subagents, and a file deduplication hook cut that by 80%. Part 4 of the AI Memory series.
Part 4 of the AI Memory series. Part 1: AI Memory Without the Overhead | Part 2: Making AI Memory Stick | Part 3: When AI Agents Dream
In which the agent learns to ask a cheaper friend before bothering the expensive one, and I discover my memory system was eating my quota for breakfast.
I ended Part 3 with "Part 4 will probably be the semantic search layer." I lied. Something more urgent came up.
My Claude Code sessions were burning through quota like a student with their first credit card. And it wasn't Anthropic's fault. Well - not entirely.
If you were anywhere near the Claude ecosystem around March 23rd, you saw the meltdown. Max subscribers watching their 5-hour session windows drain in 90 minutes. People paying $200 a month hitting 100% usage on a single prompt. Reddit threads with 330+ comments in 24 hours. Anthropic eventually confirmed they'd adjusted peak-hour limits to manage demand; diplomatic speak for "we have more users than GPUs."
The timing was brutal. The QuitGPT movement had driven millions of ex-ChatGPT users to Claude after the OpenAI Pentagon contract. Anthropic's off-peak 2x promotion ended on March 28th, so people who'd gotten used to doubled capacity hit a wall. And underneath it all, a prompt caching bug was causing sessions to reprocess entire conversation histories at full cost instead of reading from cache.
But everyone was focused on Anthropic's infrastructure. I was staring at a different problem. Mine.
I'd been working across three personal projects through March; Float Platform (CDK landing zone), WAFR Discovery (Python inventory tool), and agent-memory-cc-v2 (the system you're reading about). Switching between them constantly, UK evenings, theoretically off-peak.
And I noticed something. Every time I opened a new Claude Code session, the memory system I'd been so proud of in Parts 2 and 3 was pulling in 50-80k tokens before I'd typed a single instruction. The SessionStart hook would fire, Claude would dutifully call MCP search_notes for the project slug, and MCP would dump the full content of every matching note into the context window. Eight session notes at 800 tokens each. Plus the decisions log. Plus any working files. Plus the learnings folder matches.
All of that on Sonnet. All of it before I'd even said "continue where we left off."
I'd built a memory system that remembers everything and tells you all of it whether you asked or not. That colleague who answers "how was your weekend" with a 45-minute monologue. That's what I'd built.
The irony of a token-efficient agent memory system that itself wastes tokens? Yeah. I'm aware.
MCP-Obsidian's search_notes has one mode: dump everything. Search for "float-platform" and it returns full note content for every match. No "just give me the file names." No "show me the matching lines." No "read only the frontmatter." All or nothing.
Fine in Part 1 when I had three session notes and a context file. By Part 3, with a decisions log, codebase archaeology data, dream consolidation outputs, and 35 sessions for Float Platform alone, "all" had become quite a lot of tokens.
The main model was doing retrieval, filtering, summarising, and the actual task. All in the same context window, all at the expensive model's token cost. That's like sending your senior architect to do the filing before they start designing.
Obsidian 1.12 shipped a full CLI in late February. Anything you can do in the app, you can do from the terminal. The bit that caught my eye was the search commands:
# Just file paths - no content
obsidian search query="float-platform" path="5 Agent Memory" format=json limit=10
# Just the matching lines with context - not full notes
obsidian search:context query="float-platform" path="5 Agent Memory" format=json
# Just one frontmatter field - no body
obsidian property:read name="decisions" path="5 Agent Memory/sessions/by-project/float-platform/2026-03-28-sra-gaps.md"
# Note structure without content
obsidian outline path="5 Agent Memory/sessions/by-project/float-platform/2026-03-28-sra-gaps.md"
Progressive disclosure. Start cheap, escalate only when needed. The opposite of MCP's "here's everything, good luck."
The CLI also gives you graph traversal (backlinks, links), task queries (tasks path=... todo), and tag analysis (tags counts). None of which MCP-Obsidian exposes.
The catch: Obsidian needs to be running. It's still early access behind a Catalyst licence. And on Windows you need a terminal redirector from the Discord. Because of course you do.
Even with CLI replacing MCP for reads, the main model (Sonnet or Opus on my Max sub) was still the one searching, filtering, and deciding what's relevant. That's grunt work. Haiku handles it identically.
Claude Code has native subagent support. Each subagent gets its own context window and its own model. You can specify model: haiku in the definition. Haiku on Max subscription. No API costs. The raw CLI output; potentially thousands of tokens of search results, lives and dies in Haiku's context window. The main model only sees what Haiku sends back.
So I built two subagents.
"'Member when we decided to use CDK TypeScript? Oh I 'member!"
memberberry's job is simple. Search the vault, filter for relevance, return a concise summary. It follows a strict escalation:
search - file paths only (cheapest)search:context - matching lines with surrounding contextproperty:read - frontmatter fields without contentbacklinks / links - graph traversalread - full note (last resort, max 2 notes)The system prompt includes "The calling agent is on an expensive model. Every token you return costs more in their context window. Be ruthlessly concise."
Haiku takes this seriously. memberberry returns maybe 200 tokens of targeted context where the old MCP flow was dumping 6,000+.
Is it named after the South Park member berries? Yes. Does the name make it instantly recognisable in logs and /agents output? Also yes. Does my professional reputation survive this?
It depends.
blackbox handles pre-compaction checkpoint capture. Before context gets compacted, it reads the session transcript, pulls out decisions and open items, and writes a structured checkpoint to the vault.
Previously the main model did this. But the main model's context is already full; that's why compaction is happening. Asking it to also summarise the session before losing context is like asking someone to pack while the house is on fire.
blackbox runs on Haiku, in its own context window. Reads the transcript, extracts what matters, writes the checkpoint via CLI. The main model never thinks about it.
The SessionStart hook got a complete rewrite. Instead of injecting pointers that tell Claude "go search the vault" (which triggered the MCP dumps), it runs a handful of CLI calls directly and injects a compact state snapshot:
# Git state
git branch --show-current
git status --short
git log --oneline -5
# Project status (one line from the index)
obsidian search:context query="$SLUG" path="5 Agent Memory/project-index.md"
# Open tasks (just the task lines)
obsidian search:context query="- [ ]" path="5 Agent Memory/sessions/by-project/$SLUG"
# Working files (just names)
obsidian search query="$SLUG" path="5 Agent Memory/working" format=json
# Corrections flag
obsidian search query="$SLUG" path="5 Agent Memory/learnings/corrections" format=json total
Total output: about 200-300 tokens. The main model gets what branch it's on, when the last session was, what's unfinished, whether there's a working file to pick up, whether corrections exist, and how deep the session history goes. All signal, no noise.
The last line: "→ For prior context, delegate to memberberry agent."
Quick task? The model reads the 300 tokens and starts working. Complex resumption? It delegates to memberberry, which runs its CLI calls in Haiku's context and returns a filtered summary. The main model never sees raw vault output.
CLAUDE.md is loaded every turn. Every single message. So anything in there that's dynamic; current priorities, open items, recent decisions, is both stale (not updated since last session) and expensive (charged every turn).
New rule: CLAUDE.md holds what the project is. SessionStart holds what's happening with it right now.
<!-- memory:project-slug=float-platform -->
<!-- memory:area=AWS -->
# Float Platform
## Architecture
Three-account AWS Orgs. CDK TypeScript. GitHub OIDC CI/CD.
Zero long-lived credentials. IAM Identity Center + Entra ID.
## Conventions
- CDK for IaC, TypeScript throughout
- Pragmatic over perfect
- Test with cdk synth + cfn-lint before deploy
## Memory System
- Use **memberberry** agent for prior context retrieval
- Use **blackbox** agent for checkpoint capture
- Do NOT call MCP search_notes or read vault notes directly
Target: ~500 tokens. Down from whatever it was before. I didn't measure. "Too many" is a safe bet.
While solving the memory problem, I found another token drain hiding in plain sight. Claude Code re-reads source files constantly. Read a file, edit it, read it again to verify. Read a config file, work on something else, read the same config file ten minutes later.
Each re-read of an unchanged file is pure waste. The content is already in the context window.
The Boucle framework has a read-once hook that tracks file reads within a session and blocks redundant ones. Changed file? Shows just the diff instead of the full re-read. A 3-line change in a 200-line CDK stack file costs ~30 tokens instead of ~2,000.
It's a PreToolUse hook on the Read tool. Completely separate from the memory system; different layer, no conflict with the CLI calls (which use Bash, not Read). The one integration point: the PreCompact hook clears the read-once cache so files can be re-read fresh after compaction.
60-90% reduction in Read tool token usage. For CDK sessions where I'm iterating on stack files, that adds up fast.
| Layer | What Changed | Estimated Savings |
|---|---|---|
| CLAUDE.md | Static architecture only, ~500 tokens | ~60% per turn |
| SessionStart | CLI snapshot, ~300 tokens | ~50-70% vs MCP dump |
| memberberry | Haiku context, filtered output | ~80% on memory reads |
| blackbox | Haiku checkpoint, own context | ~90% on pre-compaction |
| read-once | Block redundant source reads | ~60-90% on Read tool |
I don't have exact before/after numbers yet. The v3 system is freshly specced and heading into implementation. But the 50-80k tokens on session start? Most of that was MCP dumping full note content. The CLI + subagent approach returns maybe 500 tokens of SessionStart context plus 200 tokens of memberberry output when needed.
That's the difference between hitting quota mid-afternoon and having headroom for the evening session.
| Part 1 | Part 2 | Part 3 | Part 4 |
|---|---|---|---|
| MCP for everything | MCP + hooks | MCP + hooks + dream | CLI reads, MCP writes |
| Main model retrieves | Main model retrieves | Main model retrieves | Haiku retrieves |
| - | - | - | Haiku checkpoints |
| Full notes loaded | Full notes loaded | Full notes loaded | Progressive disclosure |
| - | - | - | Source file deduplication |
| Fat CLAUDE.md | Fat CLAUDE.md | Fat CLAUDE.md | Slim CLAUDE.md (~500 tokens) |
Is this over-engineered? Possibly. Three blog posts ago I said "just point Claude at your vault." Now I have two named subagents, a rewritten SessionStart hook, a vendored read-once system, and a spec document in my vault that's longer than most of my customer proposals.
But the problem is real. 50-80k tokens on session start is real. Quota draining in 90 minutes is real (even if some of that was Anthropic's side, not mine). And the solution; make the cheap model do the grunt work, give the expensive model only what it needs, is sound regardless of what Anthropic does with pricing.
The read-once hook is the immediate win. Install it today, save tokens today. The CLI + subagent architecture is the structural win. It changes how the memory system operates, from "dump everything" to "ask memberberry to find what matters."
Is it perfect? Come on. You know the answer by now.
Does it mean I can spend my quota on actual work instead of watching Claude re-read files and search vaults? Yes. That's the whole point.
This is Part 4 of the AI Memory series. Part 1 was the vault. Part 2 was the enforcement. Part 3 was the consolidation. Part 4 is the optimisation. Part 5 will probably be the semantic search layer I've been threatening since Part 2. Or it'll be about something else entirely. At this point the series writes itself. I just point Claude at my vault and try to keep up.
The v3 spec is at agent-memory-cc-v2 once implementation lands. read-once is from the Boucle framework. Obsidian CLI docs are at help.obsidian.md/cli. If you've found a better way to stop your memory system from eating your quota, I'd genuinely like to hear about it. My subscription depends on it.