# AI Agent Memory Stack Everyone Must Use in 2026 (Builder's Guide)
**作者**: Avid
**日期**: 2026-04-20T19:23:58.000Z
**来源**: [https://x.com/Av1dlive/status/2048800691943309698](https://x.com/Av1dlive/status/2048800691943309698)
---

## here is the truth nobody tells ai builders.
you do not need to build your own model
## all you need to build is
memory layer that lives underneath all of them.
> TLDR; if you don't wanna read it, give this link to your agent and ask it questions: ➡️ github.com/codejunkie99/brain
## The thesis: memory should be portable
A store that's the same in Claude Code or Cursor. An index that survives a session reset, a model deprecation, a tool switch. A protocol that lets every harness talk to the same brain.
Harrison Chase nailed this in three lines on April 21:
> **Harrison Chase@hwchase17**: [原文链接](https://x.com/hwchase17/status/2046308913939919232)
>
> memory will be the great lock in
> and everyone knows it, so are rushing to get there first
> memory should be open!
>
> 
Taranjeet from Mem0 sharpened it on April 24:
> **Taranjeet@taranjeetio**: [原文链接](https://x.com/taranjeetio/status/2047788130623971410)
>
> Chamath is right: being locked into one foundation model is quickly becoming a bottleneck for enterprises.
> But there’s another lock-in forming one layer deeper: memory lock-in.
> It’s not just models that need to be swappable. The context that makes agents useful needs to be
I agree with both. I just pushed it one step further.
Memory shouldn't just be open. It shouldn't just be portable. It should be typed, indexed, audited, synced, and safe. The agent deserves more than a markdown blob and a vector store you can't inspect.
I spent a a little over month and half building exactly that:
- A git-backed event log. One commit per note.
- A SQLite FTS5 derived index with BM25 ranking and prefix matching.
- An MCP stdio server that exposes five tools to any compatible agent.
- A CLI and a full-screen TUI for the human in the loop.
This article is how I built it and why every piece exists.
If you just want to install it: github.com/codejunkie99/brain. Works with Claude Code, Cursor, Codex, OpenClaw, Hermes or any tool that speaks MCP or shell.
If you want to understand what you're installing, keep reading. ⬇️
## The Shape of the Project
Seven Rust crates. Five adapter directories. ~9,000 lines of code. 143 regression tests. One binary on your path.
I didn't design this from scratch. I converged on it after trying layouts that didn't work.
The key insight, the thing Harrison's post crystallized for me, is the separation:
- Git is the source of truth.
- SQLite is a disposable index rebuilt from git on demand.
- Orchestration sits above both and handles the two-phase write.
- Adapters are thin glue so each harness calls into the same shared brain.
Which means:
- Swap the harness tomorrow → lose nothing.
- Swap the model tomorrow → lose nothing.
- Lose the index entirely → rebuild from git in seconds.
The only thing that accumulates value is the git log. And that's a folder of plain JSON files.
## Why git is the right store
Every alternative had a fatal flaw:
- SQLite-only. Fast, but now you're building your own replication, conflict resolution, and audit trail. You've rebuilt half of git, badly.
- Markdown files. Good for humans, bad for agents. Parsing is slow. Updates are racy. No natural unit of write.
- DuckDB or PostgreSQL. Overkill for single-user memory. Doesn't help you sync between laptops.
- Git. One commit per event, one file per event. Free audit log. Free sync via push/pull. Free merge semantics. Free diff visualization. Free backup via any git host.
The unit of write is a commit, which is atomic. Every event has a permanent OID that never changes.
Here's what an append looks like in brain-store/src/repo.rs:
The retry loop with exponential backoff and the idempotency-key pre-check make this durable under concurrent writers. A naive "just commit" version dies the first time two agents write at the same millisecond.
## Why SQLite FTS5 for retrieval
Git is the truth, but git is slow to search. git log -S "authlib" works, but it takes seconds on a 10,000-event repo because it re-diffs every commit.
So I added a derived index. The brain-index crate keeps a SQLite FTS5 index with three weighted columns:
- Title: 10x weight
- Body: baseline
- Tags: 5x weight
BM25 ranks the result. On write: an INSERT OR REPLACE. On search: top-5 hits in under a millisecond at 100,000 events.
The two tables that matter (in brain-index/src/schema.rs):
Critical detail: the index is derived. If it gets corrupted, the schema bumps, or you delete it on purpose, brain doctor --deep rebuilds it from git in one pass.
Git history is the only truth that matters. The index is a cache.
I tried embeddings first. Slower. More complex. Required a model dependency. For a single-user system with under 100,000 events, BM25 on FTS5 produced indistinguishable retrieval quality.
The simple weighted formula won.
## The 10 event kinds
Memory isn't one thing. A preference isn't a lesson. A claim isn't a fact. They need different retrieval rules, different visibility semantics, different update policies.
Each variant has its own payload struct:
- Claims carry schema_type + key + content + supersedes for chain-aware retrieval.
- Prefs have category + key + value.
- Redacts carry a reason and trigger projection rollback.
The typing is the point. current_pref("auth", "provider") returns the current value as a SQL lookup against a materialized projection — not a string match on a markdown blob.
Typed memory is queryable memory.
Every event also carries metadata: event_id (UUIDv7, sortable by time), actor.kind + actor.id + actor.harness, chain_id, time_observed, time_recorded, layer, authority, signature_state, classification, schema_version, idempotency_key.
The actor.harness field is what makes cross-tool attribution work. A note Claude Code wrote is indistinguishable from a Cursor note at the content level. The harness field tells you which tool produced it.
## Redact rolls back, doesn't erase
When you redact the current Pref tip, the naive thing is to delete the projection row. That leaves current_pref empty — meaning the user lost ALL prior values too.
The correct behavior is rollback. Redact Pref B → projection restores Pref A, the predecessor. Like git revert for memory.
In brain-index/src/ingest.rs:
The ORDER BY time_recorded DESC, event_id DESC clause is there because same-millisecond inserts were picking predecessors non-deterministically. UUIDv7's random tail breaks ties.
## The secret prefilter
Every serialized event gets scanned through a RegexSet before it touches git.
- 18 patterns
- NFKC Unicode normalization to defeat fullwidth lookalikes
- Zero-width stripping to defeat smuggled ZWSP characters
In brain-store/src/secrets.rs:
The scan runs in three passes:
1. Scan raw input.
2. Strip zero-width characters and rescan — blocks s\u{200B}k-ant-... smuggling.
3. NFKC-normalize (folding fullwidth ASCII and mathematical alphanumerics to plain ASCII), strip zero-width again, rescan.
The pem-private-key pattern is a backstop.
- A GCP service-account blob pasted into an Observe.content field gets JSON-escaped: "type":"service_account" becomes \"type\":\"service_account\", breaking the structured gcp-service-account pattern.
- But -----BEGIN PRIVATE KEY----- has no quotes, survives JSON escaping unchanged, and the backstop catches it.
I learned this on codex review pass 11.
The commit subject gets scrubbed too. For Observe, Lesson, and Redact (the three free-form variants), the subject uses the event_id instead of echoing user text.
Otherwise a secret that snuck past the prefilter would get promoted to git log --oneline.
## Forgery defenses I didn't know I needed
Commit-level tampering is harder to defend against than I thought. Three cheap defenses, all in one function called event_from_commit:
- Trailer-block parsing. The event_id: trailer is only parsed from the last paragraph of the commit message, after the final blank line. A user note ending with \n\nevent_id: <forged> cannot smuggle a trailer.
- Blob-trailer cross-check. The blob's internal event_id field must match the trailer's event_id and the filename. Prevents "trailer points at the wrong blob" forgery.
- Filename-in-parent skip. Every git commit carries the cumulative events/ tree. A forged commit reusing an old event's filename would inflate event_count and poison find_by_idempotency_key.
- Fix: if events/<id>.json exists in any parent tree, this commit didn't introduce the event. Skip it entirely.
Forgery only matters in practice if someone else can write to your brain dir. But it's a 20-line defense and the tests are trivial.
I ran 13 rounds of adversarial codex review. This class of attack kept surfacing until the third defense landed.
To be honest: this doesn't stop a determined attacker with shell access. It stops accidents and tampered commits pulled from a malicious mirror. That's what the threat model is for.
## Two-phase write and catch-up reconciliation
Git commits are slow (10ms on SSD). SQLite index writes are fast (1ms). A naive single-transaction approach forces every write to the slower of the two.
Instead:
1. Commit to git first (source of truth, durable).
2. Ingest to the index (best-effort, fast).
3. If the process dies between the two, catch_up_index reconciles on next open.
This is what the approach looks in production ⬇️
Three reconciliation paths:
- Fast path. Watermark matches HEAD AND event counts agree. Nothing to do.
- Orphan path. Index has events git doesn't. History was rewritten or a foreign index was imported. Full rebuild from git.
- Replay path. Strict subset of git in the index. Replay from the first missing event forward, relying on ingest idempotency to keep projection ordering correct.
The "replay from first-missing forward" detail took codex review pass 5 to get right.
The obvious version "just ingest the missing events" breaks when missing events are interleaved with indexed ones.
You overwrite current-tip projections with stale data.
Replaying from the earliest missing event forward keeps projection ordering monotonic.
## The MCP server: tool descriptions matter more than implementations
Brain exposes five MCP tools. Once you wire it in, every compatible AI tool gets them.
In brain-mcp/src/lib.rs:
The agent reads the description. It decides whether to call a tool based on the description.
A description that says "when in doubt, call me" gets called more than one that says "returns top 5 matches."
Every tool description includes WHEN to call, not just WHAT it does. That's the leverage point.
## FTS5 query rewriting
FTS5's default behavior is exact-token match. Typing fast returns nothing even when you have notes about fastapi.
That's a fatal UX bug.
The fix lives in brain-index/src/query.rs.
If the query has no explicit operators, split on tokenizer boundaries and append * to each token. Bare words become prefix queries.
Now:
- ask fast → fast* → matches fastapi
- ask fastapi-users → fastapi* users* → matches either token
- ask cargo build → cargo* build* → implicit AND, finds notes with both
- ask "exact phrase" → passes through unchanged
- ask auth* → passes through unchanged (no double-star)
FTS5 parse errors get caught silently in the search path and return empty results instead of bubbling up as a 500. A stray foo(bar from an MCP client doesn't crash the session.
## Sync via git push and pull
Brain is a git repo. Sync is git push and git pull. I wrapped both as CLI commands.
Shelling out to git gets you SSH keys, HTTPS credential managers, 2FA, macOS Keychain, and corporate SSO for free.
No config. No daemon. No cloud. Git does all the work.
## The cross-tool test
Once all five harnesses point at the same ~/.brain, a note Claude Code writes is visible to Cursor, Codex, OpenClaw, and Hermes.
This is the test I use to verify a working install:
Three different tools. Three different processes. One shared memory.
The thing I wanted.
## What happened over the build
Week 1. Built the core: types, store, index. Nothing compiled for two days because libgit2's Repository is !Send and I needed to thread it through async code. Solved by spawn_blocking at the orchestration layer instead of fighting the lifetime.
Weeks 2–3. Built the MCP server. First version had tool descriptions that said "Save a note." The agent never called it without prompting. I rewrote the descriptions to be prescriptive with explicit "CALL THIS WHENEVER" lists. The agent started using them unprompted.
> Tool descriptions matter more than tool implementations for MCP.
Week 3 was also when I almost gave up. brain ask fast returned nothing even though I had ten notes mentioning fastapi. I rebuilt the index three times. I wrote a test that ingested 100 events and asserted retrieval. The test passed. The CLI still returned nothing.
Then I read the FTS5 docs more carefully. Default behavior is exact-token match. I wrote the query rewriter that night. The system became 10x more useful by morning.
Weeks 4–5. Codex review passes 5 through 11. Twelve fixes ranging from secret-prefilter bypasses to commit-trailer injection to watermark race conditions. Added 30 regression tests.
The pattern was always the same: codex would point at a five-line block, I'd think "that's fine," I'd write a test trying to break it, and the test would break it.
Week 6. Built the adapters for all five harnesses. Most of this is writing system-prompt templates and getting each harness's include syntax right. Spent half a day fighting Cursor's .mdc frontmatter (you need literal ["**/*"] globs for alwaysApply to work reliably).
Week 7. Wrote brain push, brain pull, brain remote. Took thirty minutes because git does all the work.
Week 8. TUI polish. Day groups, tool glyphs, filter keys. Prompted by realizing I had 200 notes from four tools and no way to see them by tool.
Week 9. I hit the wall. The source-pollution bug. Every note was matching every query. brain ask brain returned every note. brain ask fastapi returned every note.
> I don't want to overstate this. The system isn't magic. It isn't even particularly clever. What it is, is consistent.
> It commits every note. It rebuilds the index when the index is wrong. It refuses to write a secret. It doesn't have bad days where it forgets what it learned last week.
> That consistency, compounded over months, produces something that feels qualitatively different from a stateless agent , even though the underlying model is the same.
## What to watch out for
Index drift.
- Problem: Index has events git doesn't, or vice versa. Search returns ghosts or misses real notes.
- Solution: brain doctor --deep. Rebuilds the index from git in one pass.
Source-field FTS pollution.
- Problem: A metadata field gets indexed into the body column. Every query matches every note.
- Solution: Explicit allow-list of fields that hit FTS. Test with a query for a token that should match nothing.
Detached HEAD writes.
- Problem: A write that lands on a detached HEAD evaporates on the next branch checkout.
- Solution: Reject detached HEAD at open time and append time. Brain refuses to write until you reattach.
Concurrent-writer races.
- Problem: Two agents commit at the same millisecond, one loses the HEAD compare-and-swap.
- Solution: Retry loop with exponential backoff. Idempotency-key pre-check prevents double-landing on retry.
Schema mismatch on upgrade.
- Problem: A new brain version bumps the FTS schema. Old index file is incompatible.
- Solution: Rebuild self-heal. Detect the mismatch on open, delete the old sqlite file, rebuild from git.
Secret leaked into commit subject.
- Problem: Free-form user text in a commit subject gets promoted to git log --oneline and is permanent.
- Solution: Subject scrubbing for Observe, Lesson, Redact. Use the event_id, never echo user text.
## Things I'd do differently
Write the retrieval test on day one. The source-pollution bug would have been caught immediately with a test that said "a note with no mention of brain should not match ask brain." I didn't write that test until week 9.
> Write tests that validate the user experience, not just unit correctness.
Run adversarial review every 500 lines, not every 5,000. I batched 13 codex passes at the end. If I'd run one after each major feature, most fixes would have been 5-line changes instead of 50-line refactors.
Start with typed errors. StoreError::InvalidState { detail: String } stuffed six distinct conditions into one stringly-typed variant. Callers couldn't distinguish recoverable from corruption.
Make the tool descriptions prescriptive from the start. Generic descriptions ("Save a note") don't get called. Descriptions that tell the agent WHEN to call get used proactively. Single highest-leverage change I made.
Don't split repos until the API stabilizes. I tried to split brain out of the agentic-stack parent repo before the code stabilized. Re-synced three times. Should have waited.
## The bottom line
Right now this system holds memory for one person across one set of tools. But the tools come and go. The model gets deprecated. The harness gets a new IDE plug-in and the old one bit-rots. Your skills evolve with the projects.
The only thing that accumulates value over years is the memory : what you decided, what you tried, what failed, what worked, what you'd never do again.
- The model you can swap whenever something better ships.
- The skills and protocols you can rewrite as the work evolves.
- The memory you cannot replace. It encodes your specific mistakes, your specific decisions, your specific way of working.
Own your memory. Own your index. Keep them in plain files and git where no company can take them from you.
## Credits
Thesis inspired by Harrison Chase (CEO of LangChain) and sharpened by Taranjeet at Mem0, who put the words "memory lock-in" on it before I knew that was the framing I needed.
I built the first version in an afternoon. The complete stack took three months. It has been getting better every week since, without me touching it.
## Disclaimers
This article was researched and written by the author, edited by Minimax-M2.7 The thumbnail was taken off Pinterest.
Harrison Chase "memory should be open!" — https://x.com/hwchase17/status/2046308913939919232Harrison
Chase :"Your Harness, Your Memory" — https://www.langchain.com/blog/your-harness-your-memory
Vivek Trivedi :"The Anatomy of an Agent Harness" — https://www.langchain.com/blog/the-anatomy-of-an-agent-harness
## 相关链接
- [Avid](https://x.com/Av1dlive)
- [@Av1dlive](https://x.com/Av1dlive)
- [127K](https://x.com/Av1dlive/status/2048800691943309698/analytics)
- [github.com/codejunkie99/brain](https://github.com/codejunkie99/brain)
- [Apr 21](https://x.com/hwchase17/status/2046308913939919232)
- [36K](https://x.com/hwchase17/status/2046308913939919232/analytics)
- [Apr 25](https://x.com/taranjeetio/status/2047788130623971410)
- [2.1K](https://x.com/taranjeetio/status/2047788130623971410/analytics)
- [github.com/codejunkie99/brain](https://github.com/codejunkie99/brain)
- [actor.id](https://actor.id/)
- [https://x.com/hwchase17/status/2046308913939919232Harrison](https://x.com/hwchase17/status/2046308913939919232Harrison)
- [https://www.langchain.com/blog/your-harness-your-memory](https://www.langchain.com/blog/your-harness-your-memory)
- [https://www.langchain.com/blog/the-anatomy-of-an-agent-harness](https://www.langchain.com/blog/the-anatomy-of-an-agent-harness)
- [Upgrade to Premium](https://x.com/i/premium_sign_up)
- [12:25 AM · Apr 28, 2026](https://x.com/Av1dlive/status/2048800691943309698)
- [127.5K Views](https://x.com/Av1dlive/status/2048800691943309698/analytics)
- [View quotes](https://x.com/Av1dlive/status/2048800691943309698/quotes)
---
*导出时间: 2026/4/28 12:51:20*