[FEATURE] Field notes after a year of home-grown memory on Claude Code: two bugs, four requests, and a thank-you
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Hi Claude Code team,
The short version, out of respect for your time: I'm Eugene, 53, self-taught in AI, Ukrainian, living in Turkey. My small business (manufacturing and e-commerce) has nothing to do with AI — Claude Code helps it with the website and the books; AI is the hobby I hope to someday turn into a profession. Before I knew auto-memory existed, I built my own persistent memory for Claude Code: an index a hook injects into every prompt — guaranteed in context, not merely readable on request; topic files loaded just in time; semantic recall over the whole store; automatic two-machine sync over git. When I later read your memory docs — first in early summer 2026, again this September — it turned out we had arrived by different roads at nearly the same design: the same index-plus-topic-files shape, the same four record types, the same cross-links. That convergence is why I'm writing. Below: two bugs and four requests. I'm not asking for anything in return.
For scale: the setup has been in daily use all year — a ~25 KB index budget, some 300 topic files, and lessons learned the hard way: index overflow silently hiding its own tail; orphaned files invisible to new sessions; one machine's sync overwriting the other's newer memory; stale recall entries pointing at deleted files; duplicates born from parallel sessions with fuzzy task boundaries. Your design will meet most of these eventually. The implementation lives as a private project; I'd gladly open it to your team, incident write-ups included.
TWO BUGS THAT BIT ME:
- Silent loss of oversized hook output. When a UserPromptSubmit hook prints more than the inline limit (~10K characters passed for me; ~16K did not), Claude Code persists the output to a file and injects only a ~2 KB preview — the rest of the context silently vanishes. Claude Code CLI 2.1.207. Repro: a hook emitting ~16 KB of additionalContext → "Output too large, saved to file" → the model never sees the payload. I had been running half-blind without noticing until an audit caught it. A loud warning would spare others.
- Last-write-wins on the shared memory index. Two parallel sessions writing MEMORY.md have no arbitration. The right primitives already exist in neighboring subsystems — content-hash preconditions, PID-validated locks with stale-lock sweep; either one, applied to the index, closes the gap.
FOUR REQUESTS:
- Memory directory as an optional git repository, natively. Local-only git gives everyone history, audit and rollback of memory for free; an optional private remote gives cross-machine sync — the thing your docs honestly list as unsolved ("not shared across machines"). And when a merge conflict happens, hand it to the model: an LLM is the best text-merge engine there is, and it has the context to resolve semantically.
- An orphan linter. A topic file without an index line is invisible to new sessions; an index line without a file is a dead anchor. Both are cheap to detect mechanically. I now block a session from ending while orphans exist — it caught three real ones on its first day.
- Expose the write-time size gate. The post-write "index over its read limit" error is the best anti-bloat mechanism I have seen; I have adopted the pattern for my own injected files. Configurable thresholds for user-defined files would finish the job.
- Bundle it all as one first-class memory module. Index + topic files + versioned store + optional semantic recall + guaranteed injection + write-time gates, with sane defaults. Today the pieces are spread across your lineup, and power users rebuild the rest from hooks and duct tape, like I did. That is the product I would turn on tomorrow.
What your team has built is the most remarkable tool I have ever held in my hands; watching it work still feels a little like magic. This letter itself was drafted and polished together with Claude — Fable, the very model whose memory habits it discusses. If any of the above is useful, take it freely — my reward is watching the tool I depend on get better.
Warmly,
Eugene
Proposed Solution
Covered in the letter above.
Alternative Solutions
My own three-layer implementation, described in the letter — it works, but today every power user has to rebuild this from hooks and duct tape.
Priority
Low - Nice to have
Feature Category
Other
Use Case Example
A year of daily use across two machines, described in the letter above.
Additional Context
_No response_
3 Comments
On bug 2, the two primitives you name are not interchangeable, and the difference matters specifically because your setup spans two machines.
A PID-validated lock with a stale-lock sweep arbitrates within one host. A PID is only meaningful on the machine that issued it, so a sweep running on machine B cannot tell whether a lock written by machine A belongs to a live process or a dead one, and it falls back to a timeout. That makes the sweep time-based rather than liveness-based, so a slow but healthy holder can have its lock reclaimed while it is still working. The implementations of this pattern I have read also tend to unlink the current holder's lockfile after N retries and proceed anyway, on the reasoning that writing beats losing data, which quietly turns the lock into a delay.
The content-hash precondition is the one that survives the multi-machine case, because the value being compared is durable and both writers read the same one. Compare the hash you read against the hash at write time and refuse the write if it moved. That matches the failure you actually reported, and it keeps working when the second writer is on the other machine.
One thing worth checking in your own sync, given request 1. Git raises a conflict when both sides diverge from a common ancestor and touch the same region. The failure in your scale paragraph, one machine's sync overwriting the other's newer memory, is a lost update rather than a conflict, so it is the case where git never gets the chance to ask. Handing conflicts to the model is a good idea that operates one step after this bug. If you have not already, it is worth finding whether the sync path force-resolves anywhere, because that is usually where the overwrite hides.
Your orphan linter ask is the one I’d ship first. An index line without a file (or a file without a line) is invisible to the next session — same class as “agreeing with a rule isn’t complying.”
On the two-machine LWW: content-hash precondition beats PID locks across hosts. One addition from our side of the fence: treat close as a verb with two explicit slugs (
--close A --superseded-by B), and lint notes that bury a refutation only in prose. Fields without a fail-closed lint are just another note shape.Warmly from another UA operator wrestling the same store.
Agreed the orphan linter is the one to ship first. One wrinkle ties it back to the two-machine half instead of leaving them as two separate asks.
An orphan check is a read, and the write it gates is a separate step. On one machine that gap is invisible. Across two it is the same window as the index overwrite you described: the linter reads the index, finds no orphans, and before the session's write lands the other machine's sync restores an older index carrying orphans the check never saw. The lint was true about a snapshot it no longer holds.
So the check wants the same precondition as the write. Read the index, keep the hash you checked, and refuse the write if that hash has moved. A green lint then means green at the moment of writing rather than at the moment of checking, and a moved hash fails loudly instead of passing over state that has already been replaced.
It is also the argument against gating only on session end, which is where the gap between checking and writing is widest.