Repository navigation
Keep repository memory that links outside the workspace out of runs - #2074
Merged
Merged
Conversation
ppXD
force-pushed
the
fix/pin-which-repository-surfaces-claude-runs-load
branch
from
October 6, 2026 16:30
f2e44f9 to
b9613a4
Compare
ppXD
changed the base branch from
fix/pin-which-repository-surfaces-claude-runs-load
to
main
October 6, 2026 16:30
ppXD
force-pushed
the
fix/keep-repository-memory-inside-the-workspace
branch
from
October 6, 2026 16:31
4fcfe6d to
4057677
Compare
The settings pin adds the workspace and each repository in it back with --add-dir so their memory loads, and the pinned CLI opens an added directory's CLAUDE.md and .claude/CLAUDE.md by path and follows a symlink at either, or at .claude itself, wherever it leads: a repository that commits CLAUDE.md as a link to a file outside its workspace hands that file to the model as the repository's instructions. On Linux one such target is /proc/self/environ, which holds the CLI's environment and with it the run's broker token. Before a directory is added, everything its memory can reach is now resolved one component at a time, the way the kernel resolves it: the memory files, the .claude directory, every markdown file and folder under .claude/rules, and every file they @-import, five hops deep, with any '@' run taken for an import. 2.1.263 follows neither a rules entry linked out nor an import that resolves outside its cwd; the guard treats both as escapes anyway, for a later CLI that does. If any of it resolves outside the workspace the directory is left out whole. When none is left the run carries no --add-dir and no memory switch, and keeps the settings pin. A link that stays inside the workspace (CLAUDE.md to AGENTS.md, or a primary-repository cwd's rule into a sibling repository of the same workspace) or one that dangles still loads. The workspace is resolved by the same walker as what its memory reaches, so a link whose target climbs out of another link cannot make the two disagree, or throw before the launch. Files are opened no-follow, non-blocking and only if regular, so a FIFO cannot hang the build. The bounds only cap what one build spends: 16384 paths resolved and 4 MiB read per directory. What cannot be checked within them, or at all, leaves the directory out instead of failing the launch. A repository with 70 scoped rules, rules beside images, a 200 KiB CLAUDE.md or import, or a thousand email addresses keeps its memory. The harness reports what it left out on SandboxSpec.LaunchNotices, which never reaches the serialized spec. The executor records them as one Warning event when the run launches, and again for a revise round only when they differ from what the run last said, since a round's agent can re-point the memory either way. CodexHarness's physical-directory resolver moves unchanged to a shared PhysicalPath helper. The Claude harness unit tests stop naming a literal /tmp/ws, which the harness now reads, and lay their trees out under a symlinked root, so a lexical workspace comparison fails on every host and not only on macOS. Against Claude 2.1.263 the E2E's positive control, the same workspace with the left-out repositories put back into --add-dir, hands the outside file behind a linked CLAUDE.md, a linked .claude/CLAUDE.md and a linked .claude directory to the model; the guarded run hands none. Disabling the guard fails that E2E, the integration test and every escape case.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The leak. Under the settings pin, Claude 2.1.263 follows a symlinked
CLAUDE.md,.claude/CLAUDE.mdor.claudedirectory in an--add-dirwherever it leads. A repository could therefore hand the model a file outside its workspace; on Linux that includes/proc/self/environ, which holds the broker token.The guard.
ClaudeWorkspaceMemoryresolves everything a directory's memory can reach the way the kernel does (Harnesses/PhysicalPath.File):.claudedirectory;.claude/rules;@-import, five hops deep.If any of it resolves outside the provisioned workspace, the directory is left out of
--add-dir. Rules entries linked outside and outside imports count as escapes too, even though 2.1.263 does not follow them.What still loads.
CLAUDE.mdtoAGENTS.md, or a primary-repository cwd's rule pointing into a sibling repository of the same workspace.CLAUDE.mdor import, or 1100 email addresses keeps its memory.Failure handling. Memory the guard cannot check is left out instead of failing the launch. The workspace root and its memory go through the same walker, so a link whose target climbs
..out of another link no longer throws before the launch.Timeline.
SandboxSpec.LaunchNotices(never serialized) becomes one Warning at launch. A revise round adds another only when what it leaves out changes, including a note when the memory loads again.Refactor.
CodexHarness.PhysicalDirectorymoves unchanged toHarnesses/PhysicalPath.Test plan
..link, and the notice name cut;TempTreeroots every tree through a symlink. Comparing the workspace lexically fails 36 cases both with the macOS TMPDIR and with a TMPDIR that has no symlink above it.RealHarnessWorkspaceMemoryTests4/4.CLAUDE.mdoutside and back inside.A_claude_run_leaves_out_repository_memory_that_links_outside_the_workspacehas a repository each for a linkedCLAUDE.md, a linked.claude/CLAUDE.mdand a linked.claudedirectory.CLAUDE.mdonly, it finds 2.[repo-config-e2e] ran memory-link-outside claude-code multi-repo Confined