claudebox/.planning/phases/05-per-project-instance-isolation/05-CONTEXT.md
Christopher Mühl 4751161e0f chore: merge executor worktree (phase 05-01)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 09:58:14 +00:00

123 lines
6.6 KiB
Markdown

# 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
<domain>
## 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.
</domain>
<decisions>
## 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/<hash>/`.
- **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/<hash>/` 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
</decisions>
<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 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)
</canonical_refs>
<code_context>
## 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
</code_context>
<specifics>
## 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`
</specifics>
<deferred>
## 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
</deferred>
---
*Phase: 05-per-project-instance-isolation*
*Context gathered: 2026-04-10, updated 2026-04-13*