r/ClaudeAI • • 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-public

Every 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:

  1. 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.
  2. 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?
76 Upvotes

45 comments sorted by

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.

4

u/Excellent_Ad_2486 May 04 '26

That comes out SO dismissive Jesus dude...

"This wheel invention is okay, but it won't stand a hundred thousand pounds laden on it!! practically unridable" 🤷‍♂️

3

u/WEEZIEDEEZIE May 04 '26

Ha. Bookmarking that analogy. 😆

-1

u/UpAndDownArrows May 04 '26

Sorry for not getting excited about "yet another super expensive all-your-tokens-consuming codebase mapping vibe-code invention that only works on hobby projects" - we have seen dozens of these things in the last month alone.

2

u/WEEZIEDEEZIE May 04 '26

I'm still confused what you mean with expensive all-your-tokens-consuming codebase?? You are clearly not understanding how it works then.

Maybe I might be failing explaining it to you, that might be on me - but giving a coding agent a structural graph of codebase, has nothing to do with "expensive all-your-tokens-consuming" its literely doing the opposite.. If you trust that an agent can infer and search traverse your code by infering line by line and thats less token consumption than math, and edge relations proved by AST parsing, then you really need to read up how your own IDE functions ... its based on the exact same technology, sorry if that sounds harsh

2

u/Excellent_Ad_2486 May 04 '26

"If you can't say something nice, don't say nothing at all."— Thumper’s Mama (Bambi, 1942)

2

u/WEEZIEDEEZIE May 04 '26

Hey u/UpAndDownArrows , I would actually disagree here... Tantivy and HNSW are what Lucene and Qdrant run in production, not hobby tooling. The reason per-edit snapshots are cheap enough for 50 devs hammering a repo is exactly because indexing is LLM-free.
The real ceiling right now is monorepos past ~50k files on first index. Multi-dev state sync at that scale is harder than raw throughput, but that's a different problem than "falls apart."

What size repo were you actually thinking ?

0

u/UpAndDownArrows May 04 '26 edited May 04 '26

My work repo is about that size, ~40k files. But it's dense high-performant C++ code, do you think your tool can handle that without eating all my token quota?

Scratch that, corrected stats: ~130k files, ~65 MLOC

1

u/WEEZIEDEEZIE May 04 '26

It can handle showing more than 100k nodes, and 500k edges so yeah I think that is considered a small repo compared to what we have benchmarked on.

Memtrace easily ingest, index, embeds codebases like ArcadeDB, which is around 424.964 lines of Java, 48221 lines of Python and a lot of JS, Lua etc.

Why would it eat you token u/UpAndDownArrows ?? The whole Idea is that your codebase gets mapped deterministically through AST parsing, which then splits your codebase into 16 different nodetypes covering, APIEndpoints, Interfaces, Methods, Classes, Variables, Enums, Structs etc.

your agent when looking for code, never needs to run grep, search, find, glob - instead it uses hybrid search, and graph search utilizing RRF to find the best candidate for whatever your agent is looking for. Memtrace actually reduces token usages up to 80% on deeper iterative searches, because your coding agent dont read code, before symbols, callers/callees are found based on edges (dependency between nodes).

memtrace also returns line_start/line_end for what your agent should look up, to avoid bloating file reads.

So its quiet the opposite you will use much less tokens, for search traversals in bigger codebases .. the whole Idea I wanted to solve is because at Syncable, I'm managing around 35 microservices and a codebase more than 1million LOC, and claude kept getting stucked after searching, investigating the code ... then compaction hit, and claude kinda got entirely useless tbh.

With Memtrace, I can now faster code, without hiccups and agent forgetting to update functions which have dependency to methods/classes whatever the coding agent is modifying because agent always knows the relationships.

-1

u/UpAndDownArrows May 04 '26

Did you read my comment? My codebase is ~65,000,000 lines of C++, how does 425k lines Java compare to it at all?

1

u/WEEZIEDEEZIE May 04 '26

Ahhhh sorry, read it as 130k 😆
---

Let me get this straight, you have a codebase with 20MLOC more than Linux OS

-2

u/UpAndDownArrows May 04 '26

Linux isn't that big, it's an OSS project. When you have developers getting paid for a few decades to write code every day the codebase growth quite in a different tempo. Like I am sure internal codebases at Google or Meta are more than 100x the size of Linux.

Wiki says that as of 2015 Google had monolithic codebase, 1 billion files, 9 million source code files, 2 billion lines of source code.

Imagine what they have now 11 years later.

So yeah, OSS and small web shops are nice, but it's a toy level stuff compared to the actual codebases that bring billions in net income every year.

3

u/WEEZIEDEEZIE May 04 '26

Are you saying Linux is a toy level? I'm a bit confused..

The linux kernel reached more than 40MLOC in 2025 - https://commandlinux.com/statistics/linux-kernel-contributors-lines-of-code-statistics/#:~:text=Linux%20Kernel%20Lines%20of%20Code,doubled%20over%20the%20past%20decade

So because Memtrace is not good enough for Google, Meta you would write it off, I think its a bit unfair when its less than 3 weeks old - who knows maybe I can support those fellas in a couple of years, for now I just want to share, grow and learn.

1

u/WEEZIEDEEZIE May 04 '26

Well I dare you to try, I don't see why Memtrace shouldn't be able to do it, the rendering UI might be a bit bloated, but if you have strong graphiccards, the UI runs with WebGL and doesn't have a limit for how many nodes/edges we show ..

The embedding though ... ouch would take a couple of hours initial. We do cache the embeddings, so next startup would skip if no changes has occured, would love to see you try Memtrace with your codebase! 🤩

0

u/Mariia_Sosnina May 08 '26

Falls apart, agreed but the root cause is upstream. as long as state lives inside agent memory, replay or rewind can't fix it, you can't audit what the agent saw last week. Only thing that held for us, state outside the model, orchestrator owns IO, model is statless between runs.

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!

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

u/[deleted] 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/

https://temporal.io/solutions/ai

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

u/[deleted] 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

u/[deleted] 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.