From af9f1848ebdb669431403fe43b27e04871522c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=BChl?= Date: Fri, 10 Apr 2026 16:23:13 +0000 Subject: [PATCH] docs(05): capture phase context (assumptions mode) --- .../05-CONTEXT.md | 117 ++++++++++++++++++ .../05-DISCUSSION-LOG.md | 52 ++++++++ 2 files changed, 169 insertions(+) create mode 100644 .planning/phases/05-per-project-instance-isolation/05-CONTEXT.md create mode 100644 .planning/phases/05-per-project-instance-isolation/05-DISCUSSION-LOG.md diff --git a/.planning/phases/05-per-project-instance-isolation/05-CONTEXT.md b/.planning/phases/05-per-project-instance-isolation/05-CONTEXT.md new file mode 100644 index 0000000..ea29309 --- /dev/null +++ b/.planning/phases/05-per-project-instance-isolation/05-CONTEXT.md @@ -0,0 +1,117 @@ +# Phase 5: Per-Project Instance Isolation - Context + +**Gathered:** 2026-04-10 (assumptions mode) +**Status:** Ready for planning + + +## Phase Boundary + +Each 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 Decisions + +### Instance Isolation Mechanism + +- **D-01:** Use `CLAUDE_CONFIG_DIR` env var (via `--setenv CLAUDE_CONFIG_DIR "$HOME/.claudebox/instances/"`) to redirect Claude Code's data directory to a per-project path. The existing `~/.claudebox` bind mount stays intact — it is still needed for SANDBOX.md and CLAUDE.md management. +- **D-02:** Instance directories live at `~/.claudebox/instances//` 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 `$HOME` and is NOT redirected by `CLAUDE_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-dir` resolved to absolute path, falling back to `$CWD` for 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.json` are 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-root` plaintext file inside the instance dir containing the canonical project root path. Example: `echo "$CANONICAL_ROOT" > "$INSTANCE_DIR/project-root"`. +- **D-08:** `claudebox --gc` iterates `~/.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 `--gc` to the `case "$1" in` flag-parsing block (line 9 of claudebox.sh) alongside `--yes`, `--dry-run`, `--check`. + +### Concurrent Session Safety + +- **D-10:** No locking mechanism needed. `CLAUDE_CONFIG_DIR` gives 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 `--verbose` addition later) +- Handling of `~/.claudebox/instances/` directory creation (ensure it exists before writing) + + + + +## 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 include `coreutils` for `sha256sum`) + +### 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.json` NOT redirected (hardcoded to `$HOME`) +- Claude Code does NOT auto-resume on launch; `--continue` / `--resume` are explicit opt-in flags + + + + +## Existing Code Insights + +### Reusable Assets +- Flag parsing block (lines 1-18 of claudebox.sh): add `--gc` case here using the existing pattern +- `BWRAP_ARGS` array with `+=` appends: add `--setenv CLAUDE_CONFIG_DIR "..."` using the same pattern as existing `--setenv` calls +- Credential detection pattern (lines 104-122): same `[[ -f "$FILE" ]]` pattern for `~/.claude.json` optional mount +- `coreutils` is already in `runtimeInputs` (flake.nix) — provides `sha256sum` for hashing + +### Established Patterns +- All sandbox flags are built into `BWRAP_ARGS` array before `exec bwrap "${BWRAP_ARGS[@]}"` — new `--setenv CLAUDE_CONFIG_DIR` follows the same pattern +- Conditional mounts use `if [[ "$VAR" == true ]]; then BWRAP_ARGS+=(...)` — same pattern for `~/.claude.json` +- `--dry-run` block (lines 318-360) is a hardcoded parallel reproduction of `BWRAP_ARGS` — must also be updated to include `CLAUDE_CONFIG_DIR` and optional `~/.claude.json` mount +- `print_audit()` Mounts section must be updated to show the instance dir and `~/.claude.json` if mounted + +### Integration Points +- Line 383: `--bind "$HOME/.claudebox" "$HOME/.claudebox"` — stays unchanged; `CLAUDE_CONFIG_DIR` is additive +- Line 384: `--symlink "$HOME/.claudebox" "$HOME/.claude"` — stays unchanged +- Lines 366-368: env var `--setenv` block in `BWRAP_ARGS` — insert `CLAUDE_CONFIG_DIR` here +- Lines 390-392: credential bind conditional — add parallel block for `~/.claude.json` +- Lines 333-360: dry-run echo block — add `CLAUDE_CONFIG_DIR` setenv echo + `~/.claude.json` conditional echo + + + + +## 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) +- `--gc` exits after running GC, does not launch Claude (same pattern as `--check`) + + + + +## Deferred Ideas + +- Per-instance `settings.json` override (project-specific model selection) — Phase 7 (Named Profiles) +- `--gc --dry-run` to preview what would be removed — could be added but is Phase 5+ scope creep +- `claudebox --list-instances` to 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* diff --git a/.planning/phases/05-per-project-instance-isolation/05-DISCUSSION-LOG.md b/.planning/phases/05-per-project-instance-isolation/05-DISCUSSION-LOG.md new file mode 100644 index 0000000..8540fd7 --- /dev/null +++ b/.planning/phases/05-per-project-instance-isolation/05-DISCUSSION-LOG.md @@ -0,0 +1,52 @@ +# Phase 5: Per-Project Instance Isolation - Discussion Log (Assumptions Mode) + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions captured in CONTEXT.md — this log preserves the analysis. + +**Date:** 2026-04-10 +**Phase:** 05-per-project-instance-isolation +**Mode:** assumptions +**Areas analyzed:** Instance Isolation Mechanism, Project Root Identification, Shared vs Per-Instance Files, GC Mechanism + +## Assumptions Presented + +### Instance Isolation Mechanism +| Assumption | Confidence | Evidence | +|------------|-----------|----------| +| Use `CLAUDE_CONFIG_DIR` env var to redirect Claude state to per-project instance dir | Likely | Research confirmed env var exists in official Claude Code docs; current `~/.claudebox` mount stays intact | + +Alternatives presented: +- A (Env var `CLAUDE_CONFIG_DIR`): simpler, no mount changes, caveat `~/.claude.json` not redirectable +- B (Mount swap): replace `--bind "$HOME/.claudebox"` with per-instance path, more invasive + +### Project Root Identification +| Assumption | Confidence | Evidence | +|------------|-----------|----------| +| `git rev-parse --git-common-dir` as canonical root for worktree sharing | Likely | `--git-common-dir` returns shared `.git` for all worktrees; `--show-toplevel` would diverge per worktree | + +Alternatives presented: +- A (`--git-common-dir`): worktrees share instance — satisfies criterion 2 +- B (`--show-toplevel`): simpler but violates criterion 2 + +### Shared vs Per-Instance Files +| Assumption | Confidence | Evidence | +|------------|-----------|----------| +| Credentials stay shared across all instances, not per-instance | Confident | Phase 4 AUTH-01 locked; OAuth refresh requires single writable path; `~/.claude.json` not redirectable per research | + +### GC Mechanism +| Assumption | Confidence | Evidence | +|------------|-----------|----------| +| `project-root` plaintext file inside instance dir; `--gc` reads and checks if path exists | Likely | SHA-256 not reversible; `sessions/*.json` already uses `"cwd"` field establishing the pattern | + +Alternatives presented: +- A (plaintext `project-root` file): simple, debuggable +- B (`meta.json` with createdAt): more structure, allows future timestamp-based GC + +## Corrections Made + +No corrections — all assumptions confirmed by user. + +## External Research + +- **`CLAUDE_CONFIG_DIR`:** Confirmed in official Claude Code env-vars documentation. Redirects sessions/settings/todos/plugins. `~/.claude.json` hardcoded to `$HOME`, NOT redirected. Source: Claude Code docs + issues #3833, #1652. +- **Auto-resume behavior:** Claude Code does NOT auto-resume on launch. Sessions are fresh by default; `--continue`/`--resume` are explicit opt-in. Source: Claude Code common-workflows docs.