Syncing Agent Memory Across Machines

Tags
AI
AgenticAI
Claude
Description
How I synced agent memory across project clones to carry decisions and working context that Git alone could not.
Published
Published Jul 10, 2026
On this page
While developing Blosharper, I worked on separate clones of the same project across multiple machines. Git took care of the source code. But when I started working with an AI agent on another machine, there was still quite a bit to explain.
Why we had chosen a particular structure, which approaches we had tried and abandoned, what I had asked the agent to fix last time. The code was up to date, but the agent had not been part of those decisions. Instead of picking up where I had left off, I was explaining the background again.
I thought syncing the agent's local memory might reduce that repetition.

What I wanted to carry alongside the code

I wanted to preserve the context of the work. I did not want to reopen decisions we had already made or retry a failed approach without knowing why it had failed.
Harness files such as AGENTS.md and CLAUDE.md describe working rules. Memory accumulates facts learned during the work and the reasons behind decisions. There is some overlap, but it was mainly the latter that I wanted to sync.
A note saying which implementation we chose also needed to explain why we rejected the alternatives. That would be useful when a similar problem came up. Long descriptions of the current code structure, on the other hand, quickly became outdated as the implementation changed. There was little point in duplicating information the agent could get from reading the repository.

From copying conversations to handing over knowledge

With Antigravity, I first tried moving the conversation data. Being able to continue the exact same conversation on another machine seemed like the simplest solution.
It did not work that way. Copying the files did not make the IDE recognize the sessions properly, machine-specific paths caused problems, and the conversation data kept growing.
So I changed what I was syncing. I moved the knowledge organized into Antigravity's KI, or Knowledge Items, along with handover notes. These recorded completed and remaining work, files to look at next, and things to watch out for, so the agent on another machine could refer to them.
I could not bring every conversation back to life, but I could provide the material needed to start the next task. That required deciding what was worth keeping as memory.

A separate repository for project memory

With Claude Code, I initially tried managing memory inside the project repository. Later, in August, I moved project memory into a separate private Git repository. Keeping memory in the project's working tree could entangle it with feature commits and branch operations.
I organized the memory into an index and topic files, then connected each machine's local memory location through symbolic links. Different checkout paths could still point to the same project's memory.
At session start, the setup pulled the latest memory. A Stop hook, triggered when a work response finished, committed changes, incorporated remote updates, and pushed them. The aim was to avoid having to visit the memory repository and run Git commands myself every time.
Some shared instructions and skills were managed alongside it. I chose what to share based on what was needed to continue working across machines.

Picking up the work on another machine

This is the Claude Code setup I used after separating memory into its own repository in August. It is separate from the Antigravity KI sync. Each machine had a clone of the memory repository, with each project's Claude memory location linked to the corresponding project folder inside it.
sequenceDiagram participant A as Claude · Machine A participant L as Local memory A participant R as Remote Git store participant M as Local memory B participant B as Claude · Machine B Note over A,L: Separate from project code<br/>Memory path connected by symlink A->>L: SessionStart hook L->>R: fetch R-->>L: Remote changes Note over L: Apply with ff-only<br/>Otherwise keep local state A->>L: Read relevant memory A->>L: Record decisions and remaining work A->>L: Response finishes · Stop hook L->>L: Commit changes L->>R: pull --rebase alt Rebase succeeds L->>R: push else Rebase conflict L->>L: Abort rebase · keep local commit Note over A,L: Inspect and resolve the conflict end Note over L,R: A failed push also keeps the local commit B->>M: SessionStart hook M->>R: fetch R-->>M: Changes pushed from A Note over M: Apply with ff-only B->>M: Read memory through the symlink Note over M,B: Record and sync the same way
The memory folders in this diagram are local clones, not a shared drive where both machines edit the same remote files. Git carries changes between them. The project code remains in its own repository.
  1. Connect each machine: Create a symbolic link from Claude's project memory location to that project's folder in the local memory clone. Back up any existing memory, then reconcile differing files and index entries. This allows the checkout paths to differ between machines.
  1. Fetch at startup: The SessionStart hook fetches remote changes and applies them only when a fast-forward is possible. A failure does not prevent the session from starting, so the local memory may still be out of date.
  1. Push recorded changes: When Claude updates memory during its work, the Stop hook commits changes in the store, runs pull --rebase, and then pushes. The hook does not decide what to remember or write the notes. It syncs what has already been recorded in files.
Stop runs when Claude finishes responding, not when the application closes. The timing is also described in the official Claude Code hooks guide.

A memory note for the next task

I kept short pointers to topic files in MEMORY.md, with the details in those files. The following is a fictional example of the format, not an excerpt from a real project's memory.
Index - save-recovery.md: Recovery decisions and remaining checks for failed saves Topic file: save-recovery.md - Current goal: Recover the previous data if the app exits during a save - Decision and reason: Write to a temporary file, then replace the old one, to reduce the chance of damaging the existing file during a write - Approach tried: Direct overwrites were put aside because interruption cases were harder to handle - Remaining work: Test forced exits during writes and check error messages - Files to inspect next: src/storage/save.ts, tests/save-recovery.test.ts
That gives the agent on another machine enough context to know what to inspect next. Completed checks need to be reflected in the note, and temporary progress should be distinguished from decisions worth keeping longer.

Handling conflicts and failed syncs

Git can still conflict when two machines edit the same file. My implementation used a union merge for MEMORY.md to keep index entries from both sides, but that did not resolve duplicates or contradictory statements. A rebase conflict in a topic file caused the script to abort the rebase and preserve the local commit.
A commit whose push had failed could be sent by a later hook run. A content conflict still needed inspection and resolution. Because these hooks returned without blocking the work session, a finished response was not proof of a successful sync.
The sharing boundary also matters. Credentials and material that must stay in one environment should not go into the memory store. Machine-specific path connections belong in each machine's setup. Syncing memory should not quietly turn into copying an entire project or account configuration.

Stored memory and memory actually used

Adding synchronization did not solve everything. Having a file on disk and having the agent find and use it at the right moment were separate problems. Rules that had to be followed also needed to be reflected in the harness or validation steps, rather than being left only in memory.
Sync failures needed to be visible, too. Silently swallowing errors to avoid interrupting work could leave me thinking both machines had the same memory when they had actually diverged.
Automatically merging a conflict was not necessarily a resolution. Two statements could both survive and still contradict each other. Blindly choosing one side could discard a useful record. Preserving the content when something failed, and leaving it available for inspection, mattered.
The memory itself needed maintenance. Current progress went stale especially quickly. It had to be updated so completed tasks would not keep appearing as the next thing to do. The most durable notes tended to be the reasons behind decisions and lessons from failures.
📝

Later update · Why I stopped syncing

Both sync setups are now stopped, but for different reasons.
Antigravity sync became less necessary as my main development tool shifted to Claude Code. I stopped Claude memory sync after I was no longer working from a separate project clone on the other machine, and the project was approaching its final stages.
Improvements in remote work also played a large part. I can now check progress and give instructions to the machine at home from my phone or another device. Rather than moving to a separate working environment whenever I change devices, I more often connect to a session in the existing environment.
I still rely on memory. What changed was the need to replicate it across multiple machines.
For anyone working on independent clones of the same project in several places, this approach could still be useful. In my case, memory helped carry the context that moving source code alone could not. I could start the next task on another machine without explaining the reasons behind earlier decisions all over again. That was the problem I wanted to solve.