6.9 KiB
Phase 5: Per-Project Instance Isolation - Context
Gathered: 2026-04-10 (assumptions mode) Status: Ready for planning
## Phase BoundaryEach project directory gets its own isolated Claude state so conversation history, todos, and settings do not bleed between projects. Launching from a git worktree shares state with the main worktree of the same repo. Two concurrent sessions in the same project do not corrupt each other. claudebox --gc removes instance directories whose project root no longer exists on disk.
Credentials (OAuth tokens, API keys) are intentionally shared across all instances — not isolated per project.
## Implementation DecisionsInstance Isolation Mechanism
- D-01: Use
CLAUDE_CONFIG_DIRenv var (via--setenv CLAUDE_CONFIG_DIR "$HOME/.claudebox/instances/<hash>") to redirect Claude Code's data directory to a per-project path. The existing~/.claudeboxbind mount stays intact — it is still needed for SANDBOX.md and CLAUDE.md management. - D-02: Instance directories live at
~/.claudebox/instances/<hash>/on the host. The hash is derived from the canonical project root (see D-03). - D-03:
~/.claude.json(top-level auth) is hardcoded by Claude Code to$HOMEand is NOT redirected byCLAUDE_CONFIG_DIR. If it exists on the host at$HOME/.claude.json, bind-mount it read-write into the sandbox alongside the existing credential mounts. If absent, skip silently (same pattern as Phase 4 credentials).
Project Root Identification
- D-04: Canonical project root =
git rev-parse --git-common-dirresolved to absolute path, falling back to$CWDfor non-git directories. This gives all worktrees of the same repo the same instance key, satisfying the worktree-sharing requirement. - D-05: Hash the canonical root path with SHA-256 (or
sha256sum, available via coreutils in the sandbox PATH) to produce the instance directory name. The hash is truncated to 16 hex chars for readability:~/.claudebox/instances/a3f9c2b1d4e8f701/.
Credentials Stay Shared
- D-06:
~/.claudebox/.credentials.json(OAuth tokens, Phase 4) and~/.claude.jsonare bind-mounted from the host top-level into every sandbox — not per-instance. OAuth token refreshes must write to a single file shared across all sessions; per-instance copies would cause divergence and re-auth failures.
GC Mechanism
- D-07: On instance creation, write a
project-rootplaintext file inside the instance dir containing the canonical project root path. Example:echo "$CANONICAL_ROOT" > "$INSTANCE_DIR/project-root". - D-08:
claudebox --gciterates~/.claudebox/instances/*/project-root, reads each path, and removes any instance dir whose recorded path no longer exists on disk ([[ ! -d "$root" ]]). Print removed paths to stderr. - D-09: Add
--gcto thecase "$1" inflag-parsing block (line 9 of claudebox.sh) alongside--yes,--dry-run,--check.
Concurrent Session Safety
- D-10: No locking mechanism needed.
CLAUDE_CONFIG_DIRgives each session its own directory tree; concurrent sessions in the same project share the same instance dir but Claude Code itself manages file-level concurrency within its own data dir (same as it does without claudebox). If Claude Code corrupts its own state under concurrency, that is Claude Code's problem, not claudebox's.
Claude's Discretion
- Exact hash truncation length (16 chars recommended above, can adjust)
- Whether to print instance path at launch (could be a
--verboseaddition later) - Handling of
~/.claudebox/instances/directory creation (ensure it exists before writing)
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Codebase
claudebox.sh— full script; flag parsing (lines 1-18), BWRAP_ARGS construction (lines 365-401), credential mount logic (lines 104-122), SANDBOX.md/CLAUDE.md management (lines 124-175)flake.nix— Nix packaging; runtimeInputs list (must includecoreutilsforsha256sum)
Requirements
.planning/REQUIREMENTS.md— v2 requirements section; INST-01 through INST-04 are NOT yet defined here (they need to be added during planning).planning/ROADMAP.md— Phase 5 success criteria (lines 44-53)
External (researched)
- Claude Code env var
CLAUDE_CONFIG_DIR: official docs confirm it redirects sessions/settings/todos;~/.claude.jsonNOT redirected (hardcoded to$HOME) - Claude Code does NOT auto-resume on launch;
--continue/--resumeare explicit opt-in flags
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
- Flag parsing block (lines 1-18 of claudebox.sh): add
--gccase here using the existing pattern BWRAP_ARGSarray with+=appends: add--setenv CLAUDE_CONFIG_DIR "..."using the same pattern as existing--setenvcalls- Credential detection pattern (lines 104-122): same
[[ -f "$FILE" ]]pattern for~/.claude.jsonoptional mount coreutilsis already inruntimeInputs(flake.nix) — providessha256sumfor hashing
Established Patterns
- All sandbox flags are built into
BWRAP_ARGSarray beforeexec bwrap "${BWRAP_ARGS[@]}"— new--setenv CLAUDE_CONFIG_DIRfollows the same pattern - Conditional mounts use
if [[ "$VAR" == true ]]; then BWRAP_ARGS+=(...)— same pattern for~/.claude.json --dry-runblock (lines 318-360) is a hardcoded parallel reproduction ofBWRAP_ARGS— must also be updated to includeCLAUDE_CONFIG_DIRand optional~/.claude.jsonmountprint_audit()Mounts section must be updated to show the instance dir and~/.claude.jsonif mounted
Integration Points
- Line 383:
--bind "$HOME/.claudebox" "$HOME/.claudebox"— stays unchanged;CLAUDE_CONFIG_DIRis additive - Line 384:
--symlink "$HOME/.claudebox" "$HOME/.claude"— stays unchanged - Lines 366-368: env var
--setenvblock inBWRAP_ARGS— insertCLAUDE_CONFIG_DIRhere - Lines 390-392: credential bind conditional — add parallel block for
~/.claude.json - Lines 333-360: dry-run echo block — add
CLAUDE_CONFIG_DIRsetenv echo +~/.claude.jsonconditional echo
</code_context>
## Specific Ideas- Instance dirs at
~/.claudebox/instances/<16-char-hash>/— human-debuggable length, not full 64-char SHA-256 - GC prints removed paths to stderr (consistent with claudebox's stderr-only output convention)
--gcexits after running GC, does not launch Claude (same pattern as--check)
- Per-instance
settings.jsonoverride (project-specific model selection) — Phase 7 (Named Profiles) --gc --dry-runto preview what would be removed — could be added but is Phase 5+ scope creepclaudebox --list-instancesto show all instances with their project roots — nice-to-have, not required
None — analysis stayed within phase scope.
Phase: 05-per-project-instance-isolation Context gathered: 2026-04-10