feat(instance): per-run instance HOME with credential copy/wipe #209
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!209
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/202"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #202
Every run now gets a private HOME at
<state>/instances/<runID>/home, spawned host-side withHOME=in the session env — the isolation seam the container runner (#205) will mount later, de-risked without podman.What shipped
Lifecycle (
internal/instancehome) — mirrors the vault credential materializer:Materializeat launch, idempotentWipeat stop/rollback,SweepAllkeep-set GC (onlyrun_<hex>-shaped dirs, 5-minute min-age guard protecting in-flight launches) at boot (StartupReconcile) and on the throttled runtime sweep.Provider seam —
AgentProvider.InjectCredentials(instanceHome) (env, err)is the per-provider credentialInjector: copies the master store's model credentials into the instance HOME and returns the env pins the spawn must carry. Exactly one call site (the launch path), so a server-side proxy can later replace the copy wholesale.SeedOpts.Home+ ahomeargument onLocateTranscript/Commands/CaptureDeepLink(ADR-0038 explicit-strings style). Empty home ⇒ miss/skip, never a master-store fallback — isolation by construction, and pre-upgrade active runs degrade gracefully (locate miss keeps the stored transcript path; deep-link miss falls back loudly as designed).claude — injector copies
.credentials.json(0600) and mirrorsoauthAccountinto the instance.claude.json; trust seeding (onboarding + folder trust) writes only underSeedOpts.Home; registry/projects/user-commands resolve under<home>/.claude. No env pin — HOME alone points the CLI (a service-wideCLAUDE_CONFIG_DIRwas never supported by lab's readers).codex — injector copies
auth.json(0600; parent dir forced 0700 regardless of seed/inject order — found and regression-tested during review) and always returnsCODEX_HOME=<home>/.codex: lab supports$CODEX_HOMEfor the master store, and a tmux-inherited value would otherwise point the instance back at it. Trustconfig.toml, theAGENTS.mdbridge, and the rollout tree resolve under<home>/.codex.Core wiring — launch materializes the home after the worktree add, seeds + injects into it, and spawns with
HOME=<home>+ injector env; the rollback closure wipes it. Manual Stop,StopAFK, and the reaper wipe beside their credential cleanup. Boot + runtime sweeps GC orphans against the live-run keep-set. Chat locate, the composer command catalog, and deep-link capture all read per-run paths.docs/ops.mddocumentsinstances/as ephemeral/no-backup.Conformance (Tier-1, hermetic, every adapter) — empty-home locate miss; empty-home injector error; missing-master tolerance with zero writes into the master tree; seed-home containment.
Acceptance criteria
~/.claude*/~/.codexno longer read or written by instance processes (live-verified, below)TestStart_spawnFailureWipesInstanceHome,TestStop_wipesInstanceHome,TestStopAFK_wipesInstanceHome,TestReap_wipesInstanceHome,TestStartupReconcile_instanceHomeKeepSet)seed-home-containmentconformance)Verification
go build ./..., fullgo test ./..., and golangci-lint 2.12.2 (CI's pin): all green;gofmtclean.<home>/.claude/projects/<slug>/<sessionId>.jsonlwhereLocateTranscriptresolved it via the per-run session registry, and left the master store byte-untouched (mtimes + project-slug set compared). Codex could not be live-driven in this sandbox; its behavior is pinned by unit tests + compat + conformance.Notes for the maintainer
~/.claude/commands) no longer reach instances — the catalog truthfully reports none (per-run home is fresh). Seeding them per-run would be a small follow-up if wanted./etc/claude-code/managed-mcp.jsonloads into every run regardless of HOME — a shared surface worth remembering for #205's container mount inventory.~/.claude/settings.json(operator prefs) similarly no longer reaches instances; per-run behavior is now default-config plus lab's own--settingshooks file.🤖 Generated with Claude Code
[autoland] verdict: pass
PASS with CONCERNS (non-blocking). Signal relied on: CI 'ci / native' = success (8m44s, run 229). Clean fast-forward from origin/main; conventional title + 'Closes #202' present; diff scoped to #202. Build + go vet clean locally.
CONCERN (non-blocking, conditional) — claude credential-isolation asymmetry vs codex:
Approving to record the finding; NOT auto-merging (a clean PASS is required to auto-merge). Human to decide whether to pin CLAUDE_CONFIG_DIR before merge.