# Phase 5: Per-Project Instance Isolation - Context **Gathered:** 2026-04-10 (assumptions mode) **Updated:** 2026-04-13 (PLUGIN_MOUNT_FIX.md integration) **Status:** Ready for planning ## Phase Boundary Two changes in one phase: 1. **Plugin mount fix** — Replace `~/.claudebox` symlink approach with direct `~/.claude` mount + overlays. This fixes all plugins, skills, hooks, MCP configs, commands, agents, keybindings, settings being invisible inside the sandbox. 2. **Per-project isolation** — Each project directory gets its own conversation history and project-scoped memory. 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 Decisions ### Mount Architecture Change (from PLUGIN_MOUNT_FIX.md) - **D-01:** Replace `--bind ~/.claudebox ~/.claudebox` + `--symlink ~/.claudebox ~/.claude` with `--bind ~/.claude ~/.claude`. This gives the sandbox direct access to all Claude Code config: skills/, commands/, hooks/, plugins/, agents/, mcp.json, settings.json, keybindings.json, get-shit-done/, CLAUDE.md, statusline.sh — no maintenance when new files are added. - **D-02:** Overlay `--bind ~/.claudebox/projects ~/.claude/projects` AFTER the `~/.claude` bind mount (bwrap last-mount-wins). This isolates per-project memory while keeping everything else from real `~/.claude`. - **D-03:** Overlay `--bind ~/.claudebox/history.jsonl ~/.claude/history.jsonl` AFTER the `~/.claude` bind mount. This isolates session history. - **D-04:** Ensure `~/.claudebox/projects/` directory and `~/.claudebox/history.jsonl` file exist at startup (before bwrap). - **D-05:** `.credentials.json` handling stays as-is — already separate in current code. - **D-06:** SANDBOX.md and CLAUDE.md management moves from `~/.claudebox/` to `~/.claude/` (or is dropped if real `~/.claude/CLAUDE.md` already has what's needed). Must not overwrite user's real `~/.claude/CLAUDE.md` destructively. ### Per-Project Scoping of projects/ Directory - **D-07:** `~/.claudebox/projects/` contains per-project subdirs keyed by hash: `~/.claudebox/projects/<16-char-hash>/`. Inside sandbox, these appear at `~/.claude/projects//`. - **D-08:** Canonical project root = `git rev-parse --git-common-dir` resolved to absolute path, falling back to `$CWD` for non-git directories. All worktrees of the same repo get the same hash. - **D-09:** Hash = SHA-256 of canonical root path, truncated to 16 hex chars. - **D-10:** On instance creation, write `project-root` plaintext file inside `~/.claudebox/projects//` containing the canonical root path. ### GC Mechanism - **D-11:** `claudebox --gc` iterates `~/.claudebox/projects/*/project-root`, reads each path, removes any dir whose recorded path no longer exists on disk. Prints removed paths to stderr. - **D-12:** Add `--gc` to flag-parsing block alongside `--yes`, `--dry-run`, `--check`. ### Concurrent Session Safety - **D-13:** No locking needed. Claude Code manages file-level concurrency within its own data dir. Same instance dir shared by concurrent sessions in same project is fine. ### CLAUDE_CONFIG_DIR — NOT USED - **D-14:** Previous approach using `CLAUDE_CONFIG_DIR` env var is abandoned. Direct mount + overlay is simpler, preserves all plugins/skills/hooks, and requires no per-instance directory tree duplication. ### Claude's Discretion - Exact hash truncation length (16 chars recommended) - Whether to print instance path at launch (verbose addition later) - SANDBOX.md strategy: whether to keep writing it to `~/.claudebox/` and bind-mount it, or write to `~/.claude/` directly - Whether `history.jsonl` needs per-project hashing too or one shared sandbox history is fine ## 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 364-401), credential mount logic (lines 104-122), SANDBOX.md/CLAUDE.md management (lines 124-175), dry-run echo block (lines 318-361) - `flake.nix` — Nix packaging; runtimeInputs list - `PLUGIN_MOUNT_FIX.md` — the architectural change driving this update ### Requirements - `.planning/REQUIREMENTS.md` — INST-01 through INST-04 need to be added during planning - `.planning/ROADMAP.md` — Phase 5 success criteria (lines 44-53) ## Existing Code Insights ### What Must Change - Lines 383-384: `--bind "$HOME/.claudebox" "$HOME/.claudebox"` + `--symlink "$HOME/.claudebox" "$HOME/.claude"` → `--bind "$HOME/.claude" "$HOME/.claude"` + overlay mounts - Lines 348-349 (dry-run): Same change mirrored in dry-run output - Lines 104-112: Credential file path may need adjusting (`~/.claudebox/.credentials.json` stays but mount target changes) - Lines 124-175: SANDBOX.md/CLAUDE.md generation — rethink since `~/.claude` is now real - Lines 276-283 (print_audit): Update mount display to reflect new architecture - Line 102: `mkdir -p "$HOME/.claudebox"` — also ensure projects/ and history.jsonl exist ### Reusable Assets - Flag parsing block (lines 1-18): add `--gc` case - `BWRAP_ARGS` array with `+=` appends: add overlay mounts after `~/.claude` bind - Credential detection pattern (lines 104-122): same pattern - `coreutils` already in `runtimeInputs` — provides `sha256sum` ### Integration Points - Mount order critical: `--bind ~/.claude ~/.claude` first, then overlays for projects/ and history.jsonl - Dry-run block must mirror all changes - print_audit() must reflect new mount layout ## Specific Ideas - Per-project dirs at `~/.claudebox/projects/<16-char-hash>/` — human-debuggable length - GC prints removed paths to stderr (consistent with claudebox's stderr-only output convention) - `--gc` exits after running, does not launch Claude (same pattern as `--check`) - SANDBOX.md could be bind-mounted as single file from `~/.claudebox/SANDBOX.md` → `~/.claude/SANDBOX.md` to avoid touching user's real `~/.claude` ## Deferred Ideas - Per-instance `settings.json` override (project-specific model selection) — Phase 7 (Named Profiles) - `--gc --dry-run` to preview what would be removed — scope creep - `claudebox --list-instances` to show all instances with their project roots — nice-to-have --- *Phase: 05-per-project-instance-isolation* *Context gathered: 2026-04-10, updated 2026-04-13*