How to Keep Memory Across Claude, Codex, and OpenCode Without Losing Your Mind
Every harness switch resets state. Here's the local-first pattern — Obsidian vault, qmd retrieval, handoff files — that lets Claude, Codex, Claude Code, and OpenCode read the same canonical context.
Last updated:
Every harness switch is a state reset unless you’ve already moved state out of the model. Claude hits a limit. You /clear. You open Codex. You spend ten minutes re-pasting context. You compact. The new agent makes the same suggestion the last one already rejected. This is the dev-pain that owns your week.
The fix isn’t a better prompt. The fix is that the model stops owning your workflow.
What actually leaks when you switch
| Switch event | What gets lost |
|---|---|
/clear in Claude Code | Project state, decisions made, files inspected, errors triaged |
compact in any harness | Subtle context — why a path was rejected, what a function actually does |
| Limit hit, swap to Codex | Working set of files, current diff, the half-built migration |
| New terminal, new session | Everything not written to disk |
| Switch to OpenCode for a tool the others don’t have | All session memory, every command run, every read file |
The pattern is the same. The state was sitting inside one process. The process ended. The state ended with it.
The local-first memory pattern
The pattern is three layers, all on your machine, all plain files, all readable by every harness.
1. The vault. A directory of Markdown files. One file per concept — a project, a person, a deal, a feedback loop, a workflow. Frontmatter on top (status:, updated:, next_action:), body below. Wikilinks ([[other_note]]) wire it together. This is canonical memory. Obsidian is a good editor for it. Git tracks every diff.
2. Daily logs. A file per day in daily/YYYY-MM-DD.md. Timestamped entries. Flow, not state. What you actually did at 14:32, what broke at 15:10, what you decided at 16:05. This catches the things too small for a vault note.
3. Handoff files. Per-project .handoff/HANDOFF.md + handoff.json. Written at the end of a session by the agent, read at the start of the next session by whichever agent you opened. The agent doesn’t ask “where were we” — it reads the file.
Now every harness — Claude, Codex, Claude Code, OpenCode, Hermes, OpenClaw — reads from the same place. The state survives /clear. It survives the limit hit. It survives the migration from one tool to the next.
Retrieval has to be honest
A vault nobody can search is a graveyard. qmd (hybrid BM25 + vector + LLM rerank) over the vault means any harness can ask “what do we know about X” and get actual notes, not a hallucinated summary. The vault-aware OpenCode plugin pushes top-3 retrieval results into the agent’s context at prompt time. Claude Code’s UserPromptSubmit hook does the same. Both read the same files.
When retrieval is honest, you stop pasting context.
The read-before-write rule
This is the one rule that protects the vault from being silently overwritten by an agent who didn’t read it.
Before any agent updates a note: read the full file. If new info agrees with existing state, bump updated: and move on. If it contradicts, append to a ## Changelog section with the old value, the new value, and a one-line reason. Never overwrite a contradicting value silently.
This is what makes the vault trustworthy across agents. Without the changelog, a Codex session can quietly delete a decision a Claude session made yesterday. With it, the audit trail is intact and every contradiction is dated.
The memory degradation test
Before you change anything that touches memory — routing rules, the vault structure, the qmd index, the plugin — run memory-degradation-test quick. It re-queries known canonical facts. If recall is still passing, the change is safe. If it fails, the change just broke cross-agent recall.
This is the guardrail that lets the system survive its own evolution. Most agent memory setups die because someone “improves” the index and the agents stop finding things, and nobody notices until three days later when a Codex session confidently asserts the opposite of what’s in the vault.
What a working day actually looks like
Open Claude Code at 09:00. SessionStart emits a one-line index of every hot note plus the last four hours of yesterday’s daily log. Around 800 tokens. You know the shape of your week.
Hit a limit at 11:00. Write a handoff. Open Codex. The session-handoff skill reads .handoff/HANDOFF.md. Codex picks up where Claude dropped it. No re-paste.
Switch to OpenCode at 14:00 because it has a tool the others don’t. The vault-aware plugin nudges the relevant brand note into context on the first prompt. The decision Claude made at 10:30 is still in force.
At 18:00 the session-end hook writes a daily-log summary. Tomorrow’s session-start reads it. The model never owned anything. The files did.
What this looks like with oc
oc is the open-source local-first agent OS that wires this pattern. The vault-aware plugin lives in ~/.config/opencode/plugins/vault-context.js. The handoff skills live in the agent skill registry. oc-vault-sync keeps cloud-burst edits from racing the local Obsidian Git auto-commit. The memory-degradation test ships in the same repo.
You can roll your own. The vault is just Markdown. The handoffs are just JSON. The retrieval is just qmd. But the integration is the part that takes a weekend you don’t have, which is what oc is for.
Quick Answers
Why do I lose context every time I switch from Claude to Codex?
Because the state lived inside one chat session. The model owned your workflow. When you switch harnesses, the new harness reads nothing the previous one wrote unless you keep a shared memory layer outside both.
Isn't a project README enough?
No. READMEs don't move with the task. They don't record the last decision, the half-finished migration, the rejected approach, or the file you were editing 30 minutes ago. A vault + handoff file does.
Does this require Obsidian specifically?
No. Obsidian is one good default because it's plain Markdown + git. Any plain-text vault works. The point is that canonical memory is files, not a model's context window.