Keep agent memory in the right project, machine, or team
A memory tool that works is a memory tool that can put the wrong fact in front of the wrong agent. A note about a client's staging database shows up in an open-source repo. A personal preference becomes a team rule. A key meant for one project reaches another.
The question is also "where does that memory live, and who else can read it?"
Checked against each tool's documentation on 26–27 October 2026.
The three scopes people mean#
Name the boundary before you choose a configuration.
| Scope | What it means | The failure it causes |
|---|---|---|
| Machine | A file or store on this computer. Every session on the machine may read it. | A personal preference becomes a rule in every repository you open. |
| Project | One repository, or one workspace folder. | A note from project A answers a question in project B. |
| Team | Several people, or several agents, sharing one store. | Someone reads a project they were not meant to see. |
Claude Code: project, user, and organization#
Claude Code loads several instruction files, in a documented order. CLAUDE.md is the markdown file Claude Code reads at the start of a session; it can live at several scopes. The memory docs list four:
- Managed policy: a file deployed by IT, for every user on the machine.
- User instructions:
~/.claude/CLAUDE.md, for all of your projects. - Project instructions:
./CLAUDE.mdor./.claude/CLAUDE.md, shared through source control. - Local instructions:
./CLAUDE.local.md, for you in this project, normally gitignored.
Files above the working directory load at launch. A CLAUDE.md in a subdirectory loads when Claude reads or edits a file there. Path-scoped rules in .claude/rules/ load when a matching file is touched; a path-scoped rule is a rule file that stays out of context until a file matches its pattern.
Auto memory is Claude Code's own set of notes about a project, written by the model as it works. It is on by default in local sessions, stored on the machine, and keyed to the project: the directory is derived from the git repository. A worktree is a second checkout of the same repository; all worktrees and subdirectories of one repository share one memory directory. Outside a git repository, the project root is used. The first 200 lines of MEMORY.md, or 25KB, load at the start of every session; topic files are read on demand. The docs say auto memory is machine-local and is not shared across machines or cloud environments.
Status, 2026-10-26: the docs say all worktrees share one store; an open report says loading is inconsistent in worktree sessions, with some getting the full index and some getting none (#81833).
Two settings can widen that boundary by accident:
autoMemoryDirectorymoves the store. It accepts an absolute path or a path starting with~/. Point it at a shared folder and every project that uses it writes into one store.CLAUDE_CODE_PROJECT_DIR_NAME, set besideCLAUDE_CONFIG_DIR, makes projects launched with that config directory share one auto memory directory (Claude Code v2.1.234 or later).
What to check#
- Run
/contextand look under Memory files. If a file you expected is missing, Claude cannot see it. - Keep personal preferences in
~/.claude/CLAUDE.md. Keep project rules in the repository file. Do not put a client rule in the user file. - In a monorepo, add
claudeMdExcludesfor other teams' files. - If a repository must not write machine-level auto memory, set
autoMemoryEnabledtofalsein that project's settings. The docs also supportCLAUDE_CODE_DISABLE_AUTO_MEMORY=1.
Who can read it#
A project CLAUDE.md is shared with everyone who can read the repository. A user file stays on your machine. A managed file reaches every user the policy covers. Auto memory is plain markdown under ~/.claude/projects/<project>/memory/; anyone with access to your user login can read it.
Codex: global AGENTS.md and a local memory store#
Codex reads AGENTS.md before doing work: a markdown file in the repository or the Codex home. The AGENTS.md guide describes a chain:
- A global file in
$CODEX_HOME(default~/.codex):AGENTS.override.mdif it exists, otherwiseAGENTS.md. Codex uses the first non-empty file at that level. - Project files from the project root down to the working directory. In each directory,
AGENTS.override.mdwins overAGENTS.md, then any fallback names you configured. - Files are concatenated root-down, so a closer file overrides earlier guidance. The project files share a
project_doc_max_bytesbudget, 32 KiB by default; the filed report says the global file is outside it.
The global file is the main leak. A rule in the global AGENTS.md under CODEX_HOME applies in every repository. Use it for your own working agreements, not a client's release process. For separate identities, CODEX_HOME points Codex at a different home directory.
Codex also has local memories. They are off by default. The memories page describes a local store under the Codex home (by default ~/.codex/memories/), a setting for the selected machine in the desktop app, and /memories controls for the current chat. It says Codex redacts secrets from generated memory fields, and it tells you to review the files before sharing your Codex home directory.
The contrast with Claude Code is the useful part. Claude Code keys auto memory to the git repository: one directory per repository, shared by its worktrees. Codex keys its store to the Codex home, with no repository directory, so a session in any repository using that home can receive memories from the same store. A memory from a client repository can be injected into a side project. CODEX_HOME is the boundary: another directory means another config, history and memory store (and a separate auth.json if you use file-based credentials). That is a separate profile, not a per-repository scope. In the TUI or desktop app, a per-chat /memories switch can stop one chat reading the store; it does not divide the store.
The import path can create the same leak. The import page sends Claude Code project memories to Codex memories. Claude Code wrote them under a repository key; the Codex store is not divided that way, so an imported project memory can surface in a different project.
That is the practical rule: put required project guidance in AGENTS.md, not in memories. The memories page makes the same point: keep required team guidance in checked-in documentation, and treat memories as a helpful recall layer.
What to check#
- Read the global
AGENTS.mdunderCODEX_HOME(default~/.codex/AGENTS.md). Delete anything that belongs to one repository. - Check for an
AGENTS.override.mdhigher in the tree. It replaces the regular file in that directory. - Run
echo "${CODEX_HOME:-$HOME/.codex}". That is the home this run uses; the memory store is under itsmemories/directory. - List the memories directory under that home (default
~/.codex/memories/) and search for a fact from another project. If it is there, a session in any repository using this home can receive it; for strict separation, use a separateCODEX_HOME. - Run Codex from the project root and ask it to list the instruction files it loaded. The guide to Codex AGENTS.md loading shows this check.
- If the project is large, raise
project_doc_max_bytesor split guidance into nested files. Silent truncation is worse than a short file.
Who can read it#
A repository AGENTS.md is shared with the repository. The global AGENTS.md and the memories directory under CODEX_HOME stay on the machine and are readable by anyone with access to your user login, and by a Codex session in any repository that uses the same Codex home. The Codex docs do not describe per-project read permissions for local memories.
Gemini CLI: four tiers, and the model picks one#
Run gemini --version first; this section describes v0.65.0 (20 October 2026). Gemini CLI splits instructions and saved notes into four places. The system prompt names them and gives the model a routing rule for each kind of fact, but the model makes the call.
| Tier | Path | Who sees it |
|---|---|---|
| Global | ~/.gemini/GEMINI.md | Every project on this machine, for this user. |
| Project | GEMINI.md in the repository root or an ancestor | Everyone who can read that folder. |
| Subdirectory | ./src/GEMINI.md | Everyone with the repository; loaded at startup when it is on the path from the boundary marker to where you start, otherwise loaded when a tool touches that directory. |
| Private project | ~/.gemini/tmp/<project>/memory/MEMORY.md | This user, this machine, this project folder. Not committed. |
The private tier is not keyed to the git repository. In this version the CLI maps each working folder to a readable identifier derived from the folder name and records it in ~/.gemini/projects.json; the private memory is under ~/.gemini/tmp/<identifier>/memory/. Clone the same repository into two folders and you get two identifiers and two private stores. A note saved in one is not in the other. Sessions use the same identifier, under chats/.
Two leaks follow. A personal preference said inside a repository can be written to the global file, where it applies in every project on the machine. A project fact can be written to the private tier, where a fresh clone does not see it. The routing rule says to ask when a fact could belong to more than one tier, so a wrong tier is often the model deciding without asking.
The file name does not change the tier: if context.fileName adds AGENTS.md, a repository AGENTS.md sits in the project tier with GEMINI.md. The AGENTS.md loading guide covers the order.
What to check#
- Run
/memory list. It prints the path of every loaded file, so you can see which tiers are in play. - Run
/memory show. The--- Context from: <path>marker tells you which file supplied a line; theProjectlabel covers both the root and subdirectory files. - Search the private stores for a fact from another project:
grep -R "<fact>" ~/.gemini/tmp/*/memory/. - Keep client rules out of
~/.gemini/GEMINI.md. The CLI documents no per-project switch for the global file; the control is what the model writes there. - Check the project file is tracked, not ignored by git. A
GEMINI.mdthat exists only on your disk is not shared.
Who can read it#
A committed repository GEMINI.md and any committed subdirectory file are readable by everyone who can read the repository, and by anyone with access to its history. The global file and the private project memory are plain Markdown under your home directory, readable by anyone with access to your user login. The CLI does not encrypt them or scope them by key.
The per-project paths and the check are in what the memory command shows in Gemini CLI.
Qwen Code: per-worktree auto-memory and an opt-in team tier#
Qwen Code has more distinct scopes than the tools above, and the two that catch people are project auto-memory, which follows the worktree, and team memory, which is an opt-in committed tier. This is the 0.26 line; the npm latest is 0.26.2 (27 October 2026).
| Scope | Path | Who sees it |
|---|---|---|
| Global instructions | ~/.qwen/QWEN.md | Every project on this machine, for this user. |
| Project instructions | QWEN.md and AGENTS.md at the working directory and its ancestors up to the git root | Everyone who can read the repository. |
| Local instructions | .qwen/QWEN.local.md at the git root | You, this checkout; keep it out of git yourself. |
| User auto-memory | ~/.qwen/memories/ | This user; not committed, and cross-project. |
| Project auto-memory | ~/.qwen/projects/<project>/memory/ | This user, this machine, this worktree. |
| Team auto-memory | .qwen/team-memory/ | Everyone with repository access, once enabled and committed. |
The project instruction files are discovered by walking up from the working directory, not down into packages. A QWEN.md in packages/api is not loaded unless you start Qwen there, and context.fileName replaces the default names rather than adding to them. The instruction-loading guide has the details.
Auto-memory is the part that differs most from Claude Code. The docs say all branches of one checkout share the project memory folder, but each linked git worktree gets its own, because chats and session state are isolated per worktree too. A convention Qwen learns in one worktree is not in the next, and it is not in a colleague's checkout. Put repository-wide conventions in QWEN.md, or in team memory if you want the model to maintain them.
Team memory is off by default. memory.enableTeamMemory: true adds .qwen/team-memory/ inside the repository, with the same MEMORY.md layout as the private tiers. It is shared by ordinary git: pull to receive, commit and push to send. Two caveats: the directory must not be gitignored, and a directory-form .qwen/ ignore makes a re-include a no-op, so use the .qwen/* file-glob form. Writes are secret-scanned and rejected on a match, but the docs call that a backstop.
/remember does not let you pick the scope. It submits a prompt, and the model chooses the user or project tier by its type rules, or team when that tier is enabled, so the same command can land in ~/.qwen/memories/ or the project folder depending on how the fact is worded. Check both with ls after a save.
If you configure Mem0 under memory.mem0, the external service has its own scope: by default it is keyed to the user and repository, survives restarts and starting in a git subdirectory, but a moved repository or another checkout changes it. The docs say those scope identifiers are not provider-side access controls.
What to check#
- Run
/context detailand read Memory files. It lists the loadedQWEN.mdandAGENTS.mdfiles, plus anauto memoryrow. - Read
~/.qwen/QWEN.mdand the repositoryQWEN.mdandAGENTS.md. Remove anything in the global file that belongs to one client. - List the private auto-memory folders and search for a fact from another project:
grep -R "<fact>" ~/.qwen/projects/*/memory/ ~/.qwen/memories/- If team memory is on, run
git status --short.qwen/team-memoryandgit check-ignore -v.qwen/team-memory/MEMORY.md. The first shows tracked changes; the second prints the ignore rule if git is swallowing the folder. - Start Qwen from a package directory and check
/context detail. AQWEN.mdabove it loads; a nested one below it does not.
Who can read it#
A committed QWEN.md, AGENTS.md or .qwen/team-memory/ is readable by everyone with repository access and by anyone with its history. ~/.qwen/QWEN.md, .qwen/QWEN.local.md and both private auto-memory folders are plain Markdown under your home or checkout, readable by anyone with access to your user login. Qwen does not encrypt them or scope them by key.
GitHub Copilot CLI: one account, repository-scoped facts#
Copilot Memory is a GitHub-side store in public preview, and it draws a different boundary from the tools above. It is enabled per user, not per repository. Once it is on, it applies in every repository where you use a supported Copilot feature, and it saves both repository-level facts and user-level preferences. There is no local memory directory to review and no documented per-repository switch in the CLI.
The two stores have different audiences:
- Repository facts are keyed to the GitHub repository. The docs say they are available to all users with access to Copilot Memory in that repository, that only users with write access create them, and that they are used only in operations on the same repository. That is team-wide visibility by design. A joke or a personal preference stored as a repo fact reaches colleagues; a report describes that happening (#1719, open, 27 February 2026).
- User preferences stay with the user and appear only in that user's later interactions. On Business and Enterprise plans an administrator can view, export or delete them. Preferences are owned by the billing entity: if you have licenses from more than one organization or enterprise, you must select a default billing entity before user-level preferences are generated, and only memories owned by the active entity are retrieved.
The account switch is not a per-repository one. If memory is off in your personal settings, it is off everywhere for you. For organization- and enterprise-managed subscriptions the feature is off by default and an administrator has to enable it; users can then opt out. When a user has subscriptions from more than one organization, the most restrictive setting applies.
Where the identity goes wrong:
- The scope key is the GitHub repository, not the folder on disk. A report says a repository whose
originis not ongithub.comfails everystore_memorywrite, while reads of memories stored earlier can still work (#3060, open, 30 April 2026). - A similarly named repository can receive facts stored for another one, according to an open report (#3945, open, 26 June 2026). The docs say facts are scoped to the repository and validated against citations; the report is a reason to check the scope before trusting a fact in a new repository.
What to check#
- Run
/memory show. It reports the switch and documentation links./memoryarrived in 1.0.49;/memory showgained the documentation links in 1.0.51, and the current release is 1.0.104 (27 October 2026). - Open the repository's Settings → Copilot → Memory. Read the facts as team-visible statements, not as your notes.
- Open your personal Copilot settings → Copilot → Memory. Read the preferences as things that follow you into every repository you open.
- If you have more than one Copilot license, check the default billing entity in your account settings.
- There is no documented per-repository off switch. For work that must not accumulate shared facts, turn memory off for the account while you do it. A separate GitHub identity only separates user preferences; if it has write access and memory on, it can still add repository facts.
Who can read it#
A repository fact is readable by the people who can use Copilot Memory in that repository, and a repository owner can delete it. A user preference is readable by that user, and on Business or Enterprise by an administrator who can export or delete it. Neither is a local file: deleting ~/.copilot does not remove either.
OpenCode: global and repository files, no store#
Status, 27 October 2026: V2 does not read CLAUDE.md; V1 does. A rule that lives only in CLAUDE.md for V1 needs to be in AGENTS.md before the same setup works in V2.
OpenCode has no memory store, so the boundary is the instruction file. V2 loads the global ~/.config/opencode/AGENTS.md, then every AGENTS.md from the workspace toward the home directory, nearest first, then the paths in instructions. Outside the home directory it stops at the repository root. V1 loads the global file and the repository files it finds, and falls back to CLAUDE.md when no AGENTS.md is on the path. Neither version has a per-repository memory folder to leak.
The global file is the main leak. A rule there applies in every repository, so keep client-specific rules out of it. opencode debug paths prints the config directory; a custom config directory can give a run a different global file. The repository file is shared with everyone who can read the repository.
What to check#
- Read the global
AGENTS.mdunder the config directory, and in V1 also~/.claude/CLAUDE.mdwhen no configAGENTS.mdexists. Remove anything that belongs to one repository. - Check that the repository
AGENTS.mdis tracked, not gitignored. - Run
opencode debug pathsandopencode debug configin the repository you care about. The first shows the config directory. The second shows the config entries, including aninstructionslist; it does not show whichAGENTS.mdfiles were discovered. In V2, use the instruction record (the AGENTS page has the SQL); on V1, use a prompt log to see the file. - If a rule is in a nested
AGENTS.md, confirm the agent reads under that directory. Both lines load it when the agent reads a file under it; V2 also on a directory listing. - Keep task state in a handoff file. It has no scope of its own; the path and the pointer decide who reads it.
Who can read it#
A repository AGENTS.md is shared with the repository. The global file and the sessions stay on the machine, readable by anyone with access to your user login. There is no memory store with a separate scope to review.
ai-memory: workspace and project#
ai-memory namespaces every page by (workspace, project). MCP is the protocol an agent uses to call tools on another server; a static client loads its server list at startup and cannot attach a live session id. A lifecycle hook is a script the harness runs at an event such as session start, and some clients forward its session id. The marker file docs explain the defaults:
workspace = "default"unless you set another one.- A valid
upstreamororiginremote supplies the project name. Only a checkout without a valid remote uses the folder's basename. - A
.ai-memory.tomlfile in any allowed ancestor declares the workspace and project. The closest marker wins. - For static MCP clients, declare both
workspaceandproject, because those clients cannot attach the real lifecycle-hook session id. Without that pair, they fall back to the default route.
The marker is a scope declaration, not an access control. Two repositories can point at the same workspace and project, and then they share pages.
For a team, ai-memory has optional multi-user attribution and per-project access modes. The users docs describe the model:
openis the default. Every authenticated user reaches the project.restrictedlimits the project to the root operator, its creator, and users holding a grant.- Grants are
readorwrite, per project. - There is no per-page RBAC. RBAC means role-based access control: permissions attached to a person's role, rather than to each page. A page written in a project is readable by every operator in that project.
- Handoffs — short notes that tell the next session where the work stands — are owner-scoped by default. Use
shared: trueonly when the handoff is meant for anyone in the project. - Cross-project messages need
writeon the destination project. - The docs say separate servers are the way to get strict isolation. The project access modes are not a tenant boundary.
We did not test multi-user login, grants, or a remote server. Treat those rows as documentation, not as something we ran.
The scoping failure to watch for is a wrong-project write. The marker docs warn that unscoped calls use the current-project pointer. On a server whose startup line says active-project isolation mode... Single, unscoped writes from concurrent sessions share one last-write-wins slot. Upgrade to v1.39 or later for PerActor, or pass workspace and project explicitly on every write.
What to check#
- Open
.ai-memory.toml. Does it name the workspace and project you expect? - Run
ai-memory doctorandai-memory status. Confirm the scope it reports. - For a shared server, check the startup line for
PerActorisolation. If it saysSingle, upgrade to v1.39 or later, or passworkspaceandprojecton every write. - Review project access modes.
openis the default; nothing is restricted until an operator restricts it. - For strict separation between clients, use separate servers. The docs say so directly.
Where Noteheld fits#
Noteheld, one of the options, scopes access by workspace folder and key: a folder key reads or writes only inside its folder, while a master key — the owner key that opens the whole workspace — covers all of it. A leaked master key reaches the whole workspace until you revoke it or rotate it. A workspace can hold several repositories as folders. It fits one person or a small trusted group; it is a poor fit when you need per-person read permissions inside one workspace. Notes are stored as plaintext on the server, and version 1 does not encrypt note bodies end to end.
A scope review you can run#
| Question | What to write down |
|---|---|
| Which stores can this session read? | ~/.claude, $CODEX_HOME (default ~/.codex), ~/.gemini (the global file and per-project memory), ~/.qwen (global instructions, user auto-memory, per-worktree project memory) and .qwen/team-memory, the ai-memory data directory, the note store you use. |
| Which Copilot repositories can share this fact? | Repository Settings → Copilot → Memory. A repository fact reaches everyone with Copilot Memory access there; a preference stays with you. |
| What is the boundary? | Machine, project, or team. One answer per store. |
| Where does a new session write? | The default project or workspace, not the one you hoped for. |
| Can repository A read repository B? | Try it with a real query, not by reading the config. |
| Who holds the broad key? | Master key, root bearer, org policy file. Name the person. |
| Where is that written? | In the repository instruction file, so the next agent sees it. |
Put the boundary in CLAUDE.md or AGENTS.md, and put the shared record in the store you chose.