One memory for every Claude Code session, on every machine
Three plain-text layers and a handful of hooks, instead of a memory service. What you tell one session, the next one already knows, on the laptop too.
For people who run several sessions at once, some of them unattended.
01Why every session starts blind
I run Claude Code on a desktop that never sleeps and on a laptop I carry around. On a normal day the desktop has five to ten sessions open: a couple I am typing into, several started by a scheduler, one or two spawned by other sessions. None of them can see the others. Each starts with a clean context and whatever it can find on disk.
That is fine until it is not. A fact I told one session this morning is unknown to the one I open after lunch. A decision made on the laptop is invisible on the desktop. And two sessions, each doing its own job well, change the same thing without knowing. The damage is quiet: nothing crashes, a change just disappears, or a service that was stopped on purpose comes back.
02What a memory service gets wrong
The popular fix is to connect every tool to a hosted memory service and give them the same user id. I ran a hosted memory layer for a few months. It went first for a boring reason (the free tier ran out) and stayed gone for a better one: it solved the wrong problem.
Sessions do not fail for lack of a place to put facts. They fail because the right fact never reaches the right session at the right moment, and because two sessions act on the same thing without knowing. A database of facts fixes neither. A session that is ten tool calls into a fix does not stop to query a memory API, and a memory API has no opinion about who is editing what right now.
What fixed both was three plain-text stores with different jobs, and hooks that put the relevant part in front of every session whether it remembers to look or not.
03Three layers, three jobs
Three stores, three jobs. Nothing leaves your machines, and every change to layers 2 and 3 has a diff. Facts are promoted down the stack; sessions read from all three.
The live blackboard
A single markdown file in the Claude config folder. It has four sections and strict write rules:
## CURRENT STATE
_(the few things true right now; keep to about 5 bullets)_
- Router replaced; new one answers on the usual address.
- Report pages moved off the web server; local mirror serves them.
## STANDING CAUTIONS
_(deliberate states a naive session could undo; one line each)_
- Background network service STOPPED on purpose (kept dropping remote
access). Do not "fix" it by starting it.
- Drive spin-down PAUSED by flag file; one drive drops off after standby.
## ACTIVE WORK
- [sess-b2] rewriting the alert router; touching alerts.yaml, 20 min
## LOG
_(append-only history; NOT injected)_
## LOG goes into every session.A hook injects everything above ## LOG into every session at start and on every prompt. The log is history and stays on disk. That split matters more than it looks: the first version injected nothing and relied on sessions reading the file. A session that is ten tool calls into a fix does not re-read a file. One that has the cautions in its context on every turn does not need to.
Write rules: edit Current state and Active work surgically, never rewrite another session's line; append to the log; when you deliberately stop, disable or revert something another session might "helpfully" undo, add one line to Standing cautions and prune it when the state is back to normal.
Working memory: a private git repo
The blackboard is same-machine only. The bridge to the laptop is a private git repository with a working/ folder: one topic per file, entries dated and signed by the session that wrote them, newest at the top. Pull before you push, commit small and often. The commit log is the audit trail of who wrote what and when.
Git is a better sync layer for agents than a hosted store for two boring reasons. A rejected push is a merge, never a silently lost update; the failure mode of a shared notes folder on a cloud drive is "conflicted copy" files nobody reads. And every change has a diff, so when a session writes something wrong, you can see exactly which session and which commit, and revert one line.
Long-term: plain markdown notes
Durable facts graduate to a folder of markdown notes (I use Obsidian, but any folder of text files works). A weekly job promotes the keepers from the working repo and trims the working copy to a pointer. Rule of thumb: continuing work on the same machine goes on the blackboard; continuing it on another machine goes in the repo; anything true forever goes in the notes.
04Why hooks, not instructions
Everything above could be a paragraph in CLAUDE.md, and for a while it was. Agents load instructions reliably and act on them unreliably. A rule is followed most of the time, which is not the same as every time, and the collisions happen in the gap. So the parts that have to happen every time are hooks, which run on every tool call whether the agent remembers anything or not.
- Tool calledit, shell, MCP
- Pre-write guardreads the live claims and who last wrote the file. Held by another session? Deny once, with the reason; message the holder; the retry goes through.
- Write landsas normal
- Post-write hooksrecord the claim (file, service, area tag); risky? add an Active work line to the blackboard
- Inject the blackboard head at session start and on every prompt, together with a one-line log of what the other sessions on the machine were recently asked. That second part is the cheapest overlap detector there is: a fresh session sees "another session, four minutes ago: fix the alert router" before it starts its own version.
- Claim every write, guard every write. A post-write hook records which session is changing which file or service. A pre-write hook denies a write once, with the reason, when another live session holds the same resource or the file changed since this session last read it. Reads claim nothing. This is the piece most people ask about, so it has its own page.
- Announce risky actions automatically. When a tool call stops a service, reboots a box or edits a scheduled task, a hook adds an Active work line to the blackboard with the session tag and removes it when the session ends. The agent does not have to remember to say so.
- Load lessons by topic. Memory notes carry a tag for the context they belong to. The first time a session turns to that topic, a hook injects one line per tagged note. A correction given once on one machine shows up in the next session on the other.
- Talk to the holder. Claude Code can list the other live sessions on a machine and send them a message. So the guard's denial text does not say "stop"; it says "message the session that holds this, ask whether it is mid-change or done, then retry". Most blocks resolve in one exchange, and the blocked session often hands over something the holder needed.
05Across two machines
- Desktop5 to 10 sessions, scheduler + spawned; the blackboard lives here
- Private git repoworking/, one topic per file; dated + signed entries; tagged lessons; merge, never clobber
- Laptop1 or 2 sessions; pulls at session start; lessons arrive in a minute
The other machine runs a hook that pulls the repo at session start, and a small sync job copies tagged lessons across within a minute of them being written, so a correction given on the laptop at 09:00 is in the desktop's next session at 09:01. The desktop's blackboard does not travel: it is about that machine's live state, and a laptop session that needs it asks the repo instead. Keeping the two apart is deliberate. The moment the blackboard became cross-machine it would carry stale "active work" lines about sessions the other box cannot see.
06What goes where
- A deliberate state change another session could undo: one line in Standing cautions, detail in the log.
- A finding, decision or hand-off another session or machine needs today: a dated, signed entry in the working repo, committed and pushed.
- A correction you give the agent ("never do X, because Y"): a tagged memory note, so it loads by topic everywhere.
- How a device or integration behaves forever: the long-term notes.
- Operational diary for one project: a notes file next to the code. Not a substitute for the shared write.
The default is to broadcast. A session's work is assumed to matter to every other session unless I say it is private. The quiet failure is not writing too much; it is a finding mentioned once in a reply I skimmed and never written anywhere.
07Three failures this fixed
The service that came back
A session stopped a background network service on purpose because it kept dropping my remote connection. Hours later another session, asked to check that everything was healthy, found it stopped and started it again. Nothing in the second session was careless; the reason lived only in the first session's context. This is why Standing cautions exists and why it is injected rather than merely stored.
Two tasks, one resource
Two sessions worked two unrelated tasks that both touched the same set of automation rules. One finished a careful deploy; the other saved over it minutes later and the first had to be fully reverted. Tracking by task cannot catch this, because the tasks really were different. Claims are keyed by resource, plus a coarse subsystem tag so "same area, different file" is caught too.
The warning nobody saw
An early guard returned "ask" instead of "deny". In a permission mode that auto-approves, the ask was resolved without the agent ever seeing the reason: the guard fired, logged correctly, and the write landed anyway. Deny is the only decision that always reaches the agent.
08Prove it still works
Everything here fails open. A hook that crashes must never stop work, so every hook exits cleanly on any error. The price is that a broken hook looks exactly like a quiet one. Three habits cover it:
- Positive control first. Before trusting an empty result ("no collisions today"), seed a collision in a throwaway folder and confirm the guard denies it. The same goes for the sync: a probe checks that the laptop actually pulled recently, not just that the push went out.
- The two-facts test from the hosted-memory world still applies, at the right layer. Tell one machine two unguessable facts, open a session on the other and ask in different words. A plausible guess instead of the fact means the sync is broken, not the model.
- Log every decision in one line. When a session says it was blocked, you can see by whom and why, and when it was not blocked you can see that the guard looked.
09Set it up in an afternoon
- Create the blackboard. One file, the four sections above, in your Claude config folder. Put today's three or four true things in Current state and leave the rest empty.
- Inject it. A
SessionStartand aUserPromptSubmithook that prints everything above## LOG, clipped to a byte budget. Register both insettings.json:{ "hooks": { "SessionStart": [{"hooks": [{"type": "command", "command": "python ~/.claude/hooks/inject_blackboard.py"}]}], "UserPromptSubmit": [{"hooks": [{"type": "command", "command": "python ~/.claude/hooks/inject_blackboard.py"}]}] } }settings.json - Add the write rules to CLAUDE.md. Surgical edits, append the log, one line per standing caution. This is the one place an instruction is enough, because the hook does the reading for it.
- Make a private repo with a
working/folder and a README that states the conventions. Clone it on every machine. Add a session-start hook that runs a plain pull:git -C ~/memory pull --no-rebase --no-edit # never stash, never rebase: a conflict is a merge to read, not state to hidethe session-start pull - Claim and guard writes. A post-write hook that records claims and a pre-write hook that denies once. This is the part with the sharp edges (quoted text, reads, stale copies), which is why I ship it rather than describe it. Start with the free guard.
- Run the two-facts test and keep the probe. If you cannot prove the other machine pulled, you do not have shared memory; you have a push log.
10What a hosted memory service is still good for
Being fair to the approach I left: a memory API gives you semantic recall across thousands of fragments, which plain files do not, and it is the simplest way to join tools from different vendors that cannot share a filesystem or a repo. If your problem is one tool on two machines plus a scheduler, you do not have that problem. You have a coordination problem, and git plus a few hooks is a smaller, auditable answer that keeps everything on hardware you own.
11Questions
- Does any of this send my data anywhere?
- The blackboard and the hooks are local files. The working repo goes wherever you host your private git repos; a self-hosted remote works. Nothing else leaves the machines.
- Why not just use CLAUDE.md for everything?
- CLAUDE.md is loaded once at session start and is the right place for rules. It cannot hold state that changes every few minutes, and it cannot make a session re-read anything. The blackboard is state; the hook is the re-reading.
- Will the injected blackboard eat my context?
- Only if you let it grow. The head is capped at about five bullets per section and clipped to a byte budget by the hook; the log is never injected. Mine runs at a few kilobytes per turn.
- Does this work with scheduled or headless sessions?
- Yes, and that is where it matters most. A scheduled session has nobody watching it. It gets the same injected head, the same guard and the same auto-announce, and its claims are dropped when it ends.
- What about two sessions on different machines editing the same file in the repo?
- Git handles it: the second push is rejected, the session pulls, merges and retries. The guard only covers the same machine, by design; cross-machine edits are rarer and git already refuses to lose them.
- Is this made by Anthropic?
- No. Tudlab is an independent maker. Claude Code is Anthropic's product; everything here works alongside it through its hook system.
12Do this next
The blackboard layout and the hook set in this article are the ones in Tudlab Code. The resource-claims hook and the guard are free as one Python file; the guide and plugin add the blackboard, the auto-announce hooks, the task handshake and the testing chapter. The cross-machine memory kit (the git bridge, the lessons loader, the promote job and the canary) is Tudlab Code: Memory, with the canary free on its own.
- Orchestrate: a 23-page guide and an installable plugin that stops parallel sessions undoing each other's work
- Collision Guard (free): one Python file that stops two sessions writing over the same file
- Memory: a plugin and a 19-page guide that give every session on every machine one shared memory
- Memory Canary (free): one Python file that proves your other machine got what this one wrote
- Orchestrate + Memory: both plugins together for US$39
Everything linked, in one place
- Tudlab Code: the collision guard and the orchestration playbook.
- Claude Code hooks reference: the events used here (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop).
- Claude Code plugins: how a hook set installs in two commands.
- Tudlab Home: the home-automation side of the same estate.
About the maker. Tudlab is one person in Singapore who runs a home estate (automation, cameras, media, a small fleet of scheduled agents) on Claude Code and sells the parts other people keep asking for. Every mechanism in this article is running as written; the incidents are real and anonymised. Claude Code helped draft the text. Tudlab is independent and not affiliated with Anthropic.