TLDR: Two AI savers writing the same memory files in parallel will eat each other's work. The fix isn't a lock — it's an idempotent, anchor-keyed rewrite and a re-read before you clear anything.

The Setup

Apollo — my AI personal assistant system — has two modes running simultaneously: a manual apollo-save I trigger mid-session to flush staged learnings into memory, and an autonomous iMessage daemon that runs its OWN save on its own schedule.

Both write to the same files.

Same MEMORY.md. Same memory/_inbox.md. Same individual memory .md files in the vault.

Nobody told them to coordinate.

The Wall

I started seeing inbox entries I'd just staged... vanish.

Not in any commit. Not rolled back. Just GONE.

And occasionally a git status at the end of a save would come back clean — "nothing to commit" — even though I knew I'd just promoted a batch of learnings.

My first instinct: corruption. Something's wrong with the git state. Maybe a hook? Maybe a merge conflict swallowing changes silently?

I went looking for a bug that wasn't there.

What Was Actually Happening

The autonomous iMessage Apollo was firing its own apollo-save at the exact same moment I was running mine.

Both agents would:

  1. Read _inbox.md
  2. Process staged entries
  3. Write updates to memory files
  4. Clear the inbox

The Edit tool — which I was using for MEMORY.md inserts — takes a read snapshot and then applies a diff. If the other saver had already touched the file between the snapshot and the write, the guard would trip. Silently. No error. The write just didn't land.

And that "nothing to commit"? That wasn't failure. My files had already been swept into the other saver's commit. The work was there — I just couldn't see it from my side.

That debugging spiral cost me 20 minutes. Knowing it's a benign race — not corruption — would have saved all of it.

The Fix That Actually Works

Three rules, applied together:

  • For shared file inserts: atomic Python read-modify-write, keyed on an anchor string, with an idempotency guard (if pointer not in s: insert). Never use the Edit tool for files another agent might touch concurrently. The Edit tool's snapshot model assumes exclusive access. It doesn't have it.
  • Before clearing the inbox: re-read _inbox.md first. The other saver may have already drained the lines you were about to clear. Clear only what's still there. Never blind-truncate.
  • "Nothing to commit" ≠ failure. Confirm your files landed — in your commit OR the other agent's. Both are fine. The work persisted either way.

Why This Shape Keeps Showing Up

Same problem surfaced in my Obsidian RAG indexer (retrieval-augmented search over my memory vault): a ThreadPoolExecutor with 5 workers all trying to write the same SQLite database. Fix there was WAL journal mode + PRAGMA busy_timeout — and critically, don't hold a BEGIN transaction open across a slow LLM or embedding call. Short lock windows. Idempotent writes.

Exact same principle as the memory race, different substrate.

The moment you have two agents writing shared state, you don't have a bug — you have a design gap. The race is the feature. Build around it, or it'll eat your work in the quietest possible way.