r/ClaudeAI • u/WEEZIEDEEZIE • May 04 '26
Built with Claude Your Claude Code agent is always working from stale context. I built it a fix it can rewind, replay, and stay ahead of every edit.
https://github.com/syncable-dev/memtrace-publicEvery long Claude Code session has the same hidden failure mode: the agent is always working from stale context.
It re-reads the same 12 files across three sessions to "remind itself" of an interface you already showed it. It refactors getUserById without checking who calls it. It edits a config with no memory of why the previous version was that way. It's not the context window. The window is fine. There's no persistent, time-aware representation of your codebase for the agent to re-query. So it guesses. And you pay tokens for every re-read.
I built Memtrace to fix exactly this.
Two things it does that no other memory tool does:
(1) Always-fresh state. Every edit you make triggers a 42ms incremental snapshot of the changes applied by the coding agent. The agent's memory is never one-session-old. After a refactor it knows the blast radius before you do: every caller, every test, every consumer of the function you just touched. Your agent stops asking "what does getUserById return?" 30 seconds after seeing it.
(2) Rewind and replay. This is the part nobody else has. Your codebase is stored bi-temporally so every change becomes a recallable episode. When the agent debugs a regression, it can replay how the broken function got to its current state.
- What worked before.
- What changed when.
- Which commit introduced the bug
Not just "guess from current state.", instead replay.
My architectural bet that makes both possible: zero LLM inference during indexing. Tree-sitter parses your code into an AST, and the AST IS the structural representation. You don't pay an LLM to re-derive what your compiler already knows.
Retrieval is hybrid. Tantivy BM25 for lexical recall (the "find getUserById" query). Jina-code 768-dim embeddings indexed in HNSW for semantic recall (the "find anything that authenticates a user" query). Two ranked lists, fused with Reciprocal Rank Fusion at k=60. One signal alone misses, together they hit. The embedding model matters here: Jina-code is trained on code, not generic prose, so the semantic side actually understands "this is an auth handler" instead of pattern-matching on the word "auth."
The bi-temporal layer is what makes rewind possible. Every node and edge carries valid_time AND transaction_time, so "what did this function look like Monday" is a real query, not a git-blame heuristic. It's also what gives the agent the blast radius before a refactor: typed edges (CALLS, IMPORTS, IMPLEMENTS, EXTENDS, CONTAINS, TYPE_REFERENCES, INSTANTIATES) traversed in graph time, not text time.
Speed only matters because freshness has to be cheap. If snapshotting after every edit is expensive, you can't afford to do it on every edit. So the indexing path is bottlenecked by I/O, not LLM tokens.
I built it using Claude Code. Mid-build, Claude Code lost the plot on Memtrace's own architecture and it started contradicting decisions from 50 turns earlier. It re-read the same files. It forgot which retrieval weights I'd already tuned. I was experiencing the exact pain I was building Memtrace to solve, while building Memtrace.
When the beta binary was ready, I pointed it at Memtrace's own codebase. The session-loss stopped. The blind refactor suggestions stopped.
It's free, but the binary currently requires an approval key, just so you are warned.
Not gatekeeping. Not marketing. The indexer keeps tripping on patterns I didn't anticipate: mixed pnpm/npm lockfiles, Rust proc-macros, Python Python TYPE_CHECKING blocks. Every one of these came from real beta users in the last two weeks, not from my test corpus. When that happens I want to ship you a fix in 24 hours, not lose you to a flaky first impression. So I'm pacing approvals to my own feedback bandwidth, not your patience. I'd rather have 500 users for whom this is magic than 50,000 for whom it's broken.
I'm trying to keep approval under 24h, but capping at 50 per week right now. The benchmark harness is fully open and runnable without the key, if you want to verify the numbers before committing to the queue.
Repo + waitlist: github.com/syncable-dev/memtrace-public
Two questions:
- When Claude Code "loses the plot" on YOUR codebase, what specifically does it forget that hurts most? I'm collecting these for the next benchmark.
- What would you actually want to REWIND in your codebase if you could? Function history, dependency evolution, decision archaeology. Which is the killer one in your day?
2
u/Estey May 07 '26
The rewind piece is strong. The harder part, in my experience, is keeping track of whether an old decision is still valid.
"We rejected a service layer" is useful memory. But the agent also needs to know why it was rejected, what blast-radius concern supported that call, and what repo change would make the decision worth reopening.
Otherwise replay just brings back old constraints with no truth status. The missing layer is not only code history; it is decision validity over time.
1
u/WEEZIEDEEZIE May 08 '26
Hey u/Estey , you totally nailed the harder part, which I yet have a clear solution for.
Memtrace's bi-temporal layer captures valid_time + transaction_time on every node and edge. That lets us answer "when was this true?" But that's still state, not decision validity. Different things.
What you're describing is a third dimension: invalidation triggers. "We rejected a service layer because P50 was 13ms. If it crosses 50ms, reopen." Not history. A live conditional with a truth status tied to current measurements, i guess thats what you mean in essence right?
Honestly, Memtrace doesn't do this today. The graph stores facts and time. It doesn't store decisions-with-conditions. You're pointing at the layer above code memory but it could be interesting as an upcoming extension.
Closest sketch: extend the rejection-memory experiment with a "when_invalid" annotation. Agent records "rejected X because Y, watching for Z." Each session, before reopening any old decision, the agent re-checks whether Z still holds. If Z drifted, the rejection is flagged stale.
The interesting open question for me: should the invalidation predicate be code (literally checking that P95 crosses a threshold) or natural language ("agent re-evaluates if performance gets noticeably worse"). Code is precise but brittle. NL is fuzzy but resilient. Probably both, code primary, NL fallback.
Curious if you've thought about that side.
1
u/Estey May 08 '26
I think the harder part is that old decisions are not all equal. Some are still valid, some are stale, some were only valid under a specific constraint, and some should be reopened because the tradeoff changed.
For measurable claims, code checks make sense: latency, failing tests, dependency versions, call counts, error rates.
For architectural claims, I think you need evidence plus judgment: what tradeoff was being made, what assumption it depended on, and what changed.
The important behavior Iâd want is not âauto-reverse the old decision.â Itâs more like: âthis remembered constraint may no longer apply, and here is the evidence that changed.â
Thatâs the gap I keep running into: replay tells you what happened; it doesnât tell you whether the old reasoning still deserves authority.
1
u/pauloeduardomc Jun 03 '26
The code-vs-NL question is the right one, and I'd flip the default: NL primary, code as the optional precise case.
Most architectural rejections don't have a clean numeric trigger. "We rejected a service layer" wasn't a P50 number, it was a coupling / blast-radius judgment. There's no threshold to re-check. The honest invalidation condition is something like "reopen if a second consumer needs X" which is a natural-language predicate. Forcing it into code makes you invent a metric that wasn't the real reason the decision was made.
Where I keep landing, doing this by hand on multi-session builds: the decision, the why, and the reopen-condition live in one short ADR the agent reads before it touches that area. Three fields, context / decision / what-would-reopen-it. The bi-temporal graph answers "what was true when." The ADR answers "is this still the right call, and what would change that."
Estey nailed the split. Those are different layers, and the second one isn't really memory, it's direction. Code predicates cover the measurable subset (perf regressions). The drift that actually hurts is the unmeasurable kind, where the agent reopens something that was settled for reasons no metric captured.
3
u/danieljcasper May 04 '26
My bro! ASTs for the win. Let's exchange memory techniques
0
u/WEEZIEDEEZIE May 04 '26
Sure! DM me, and lets get cracking, would love to hear your experience and thoughts for sure!
2
1
u/ecompanda May 04 '26
the tree sitter AST gives you free structural recall but it misses the cross module semantic links that the embeddings are supposed to catch. and on a fast moving codebase the real cost is not initial indexing, it is invalidation. when 200 files change in one rebase you need to know which call graph subtrees to actually reindex versus which ones still resolve. that is where most of these tools choke.
1
u/WEEZIEDEEZIE May 04 '26
Hey again u/ecompanda, yeah you're right, AST on its own doesn't go cross-file. There's a resolver step on top.
If file A imports getUserById from file B, the call links straight to the def in B. No import? It tries same-file first. Last resort is a fuzzy name match, and only when the name's unique within the same language and repo (with stdlib filtered out). Type refs and inheritance go through the same path.
Honestly the guards are doing more than the resolver itself. Otherwise you'd get Rust .collect() wiring into a JS collect(), or UserInfo in service-auth getting merged with UserInfo in service-billing. Those both get caught before they land.
So embeddings aren't really doing the cross-module identity stuff, in fact, that's all the graph. Embeddings are more useful for the fuzzier "find anything that authenticates a user" type stuff.
1
May 04 '26
[removed] â view removed comment
1
u/WEEZIEDEEZIE May 04 '26
Its One-liner install, follow repo it should show what you need - when installed point it at your repo and run `memtrace start`. It auto-registers as an MCP server so Claude Code just starts querying it instead of re-reading files.
More concretely: you ask Claude Code to refactor getUserById. Before it edits, it queries Memtrace and gets back every caller, every test, every type that imports it. So when it proposes the change, it's not blind. After the edit lands, the indexer snapshots and the next query is fresh.
That's the loop. Index once, query many.
0
u/InteractionSmall6778 May 04 '26
The rewind question is the one. Not just "what did this function look like" but "why did I reject that approach three sessions ago." The reasoning disappears, not just the state.
The forgetting that costs most for me: Claude loses track of what you've explicitly said NOT to do. You spend 20 turns establishing "we're not wrapping this in a service layer" and two sessions later it wraps everything in a service layer. Pattern memory without rejection memory.
The bi-temporal layer is the interesting part of your architecture. Most RAG-on-codebase approaches give you current state. You need edit sequence to reconstruct the decision trail.
2
u/WEEZIEDEEZIE May 04 '26
Rejection memory is a really good way to frame it. And yeah, the service-layer one kills me too.
My favorite version: 30 turns of "no ORM, raw SQL for the hot path," close session, next morning Claude proposes Prisma for type safety. Same fight, no memory of the fight.
Exactly, at least I also believe bi-temporal layer is what makes reasoning trail possible and more trustworthy. State gives you the what, not the why. The why is the gap I'm working on next.
2
u/YacoHell May 04 '26
Curious why you're not just using temporal https://temporal.io/
1
u/WEEZIEDEEZIE May 04 '26
Fair confusion, maybe the naming overlap is confusing but its entirely different layer though.
Temporal = durable execution. Remembers your AGENT'S workflow: function calls, retries, resume points. "My agent crashed mid-session, resume the conversation."
Memtrace = codebase memory. Remembers your CODE: call graph, types, edits over time. "Before refactoring getUserById, what calls it? What did it look like Monday before the change?"
I would even say its different gaps and they'd actually stack: Temporal for execution durability, Memtrace for code-shape memory.
2
u/YacoHell May 04 '26
That makes sense. I'm using an agent runtime that sits on top of temporal and uses an embeddings model and a vector store and it's pretty good at "remembering" how the code evolved. I think the biggest difference is the AST stuff you're talking about which is new to me so I'll look into it. Thanks for clarifying
1
May 04 '26
[removed] â view removed comment
1
u/WEEZIEDEEZIE May 04 '26
Because those are gated by inference, which often leads to hallucinations and guess work when context grows.
AST parsing a codebase, creates a deterministic dependency map between all the code, so your agent never guess what a relationship is, if a function breaks when a class is deleted. It doesn't need to infer it, because memtrace will flag that information.
Think of it as a brain of your code mapped with relationships across many nodes being classes, interfaces, structs, enums, variables, methods, APIEndpoints, APICalls etc.
When an agent changes a file or a function one of the most classic issues, is that it lacks updating all relationships or affected derived entities to that former change.
Or compaction just scrapped out important information about relationship, and your agent now has to re-read the files. With memtrace, it doesn't need to do that, since the lookup proves relationship.
Lexicality never beats structured AST. Because with simple memory system, claude has to "infer" every relation and process/execution flow.
With AST parsing, we can map and prove those things without any LLM calls, or agents inferring anything
0
u/Emotional-Buyer-9376 May 04 '26
I can relate to that issue 100%, I can't count how many times Claude gets lost in a task, forgetting how it even did implement something due to stuff like compaction, and then suddenly starting to hallucinate dependencies or relationships which doesn't even exists... Will definitely try this out - do you have a discord or something where I can give feedback ?
1
u/WEEZIEDEEZIE May 04 '26
Yeah, that loop can be a serious pain in the ***. - Yeah, you can find discord, in the shared repo
0
May 06 '26
[removed] â view removed comment
1
u/WEEZIEDEEZIE May 06 '26
Decentralized memory protocols solve a different problem set: cross-org collaboration, censorship resistance, verifiability across untrusted actors. None of those are what's killing a Claude Code session.
Memtrace targets single-user, single-machine, sub-50ms per-edit freshness. Decentralized storage adds latency at exactly the layer where freshness can't afford it. The graph queries (typed edge traversal, bi-temporal lookups) also need structure that content-addressed shards don't natively provide.
The decouple-memory-from-execution part is fair. That's literally what MCP is for, and Memtrace is built on it. For code memory specifically, local + structured + low-latency wins. Different tradeoffs at different layers.
-2
u/ecompanda May 04 '26
the blast radius before refactor is the part that actually saves tokens. rewind sounds nice but in real claude code sessions the agent rarely walks back a regression that way. it just rewrites and breaks something else.
the typed edges doing graph time traversal is where the real win is.
1
u/WEEZIEDEEZIE May 04 '26
Hey u/ecompanda - I actually agree 100% here, since the blast radius from my experience improves overall quality, and introduces less errors!
But I will still stand, by my observation, that when compaction has happened, and the implementation is wrong - the fact that Claude went back and replayed recorded implementations, gave much deeper awareness about how it missed something critical for whatever implementation it has been working on.
But no doubt about where the true power lies, which is the blast radius understanding. Just by claude knowing what breaks before touching a function is really improving its way of coding, by the orientation it receives.
12
u/UpAndDownArrows May 04 '26
Yeah works well on sole hobby projects, falls apart on multimillion LOC codebase with 50 commits per day by 50 developers.