Skip to content

Fix Claude sidebar resume config dir - #4116

Merged
austinywang merged 8 commits into
mainfrom
issue-4087-claude-config-dir-resume
May 18, 2026
Merged

austinywang merged 8 commits into
mainfrom
issue-4087-claude-config-dir-resume

Conversation

@austinywang

@austinywang austinywang commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Share the Claude configured-root predicate between session indexing and sidebar resume command construction.
  • Omit sidebar-derived CLAUDE_CONFIG_DIR unless the inferred transcript root contains populated Claude operational state.
  • Keep configured non-default Claude roots working and update existing command-string coverage to match the new invariant.

Reproduction

Local deterministic command-construction path for a canonical ~/.claude/projects/... transcript path.

Observed before fix: env CLAUDE_CONFIG_DIR=/Users/<user>/.claude ... claude --resume <id> redirects Claude operational state to ~/.claude/.claude.json and triggers first-run setup.

Expected after fix: canonical unconfigured ~/.claude transcript roots do not emit CLAUDE_CONFIG_DIR, so Claude falls back to ~/.claude.json in HOME. Non-default roots still emit CLAUDE_CONFIG_DIR when <root>/.claude.json contains auth state.

Cloud Mac/video: not used because this is a deterministic model/command-construction bug, not a visual or layout issue.

Fixes #4087


Note

Medium Risk
Changes Claude session indexing and resume command construction to conditionally set CLAUDE_CONFIG_DIR based on parsed on-disk auth state, which could affect whether existing sessions resume with the expected Claude account/config root.

Overview
Fixes Claude session resume to only export CLAUDE_CONFIG_DIR when the inferred config root appears configured (i.e., has non-empty auth state in .claude.json), preventing sidebar resumes from triggering first-run setup for canonical but unconfigured ~/.claude roots.

This threads a per-session configDirectoryForResume through AgentSpecifics.claude, propagates it from Claude session root discovery/candidate generation (including cache hits), removes the old file-URL-derived config-dir inference, and adds/updates tests covering configured vs unconfigured roots and null/empty auth fields.

Reviewed by Cursor Bugbot for commit 42ff59e. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Claude session resumption to properly incorporate configuration directory settings, ensuring sessions resume with correct environment configuration.
  • Tests

    • Added test coverage for Claude session resume command generation and configuration directory handling.

Review Change Stack

@vercel

vercel Bot commented May 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 18, 2026 0:13am
cmux-staging Building Building Preview, Comment May 18, 2026 0:13am

@coderabbitai

coderabbitai Bot commented May 13, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR threads an explicit configDirectoryForResume field through Claude session discovery and resume command generation, replacing path-based inference. The change adds configDirectoryForResume to AgentSpecifics.claude, introduces ClaudeConfigurationRoot validation, flows the value through the discovery pipeline, and uses it to conditionally set CLAUDE_CONFIG_DIR in generated resume commands.

Changes

Claude Resume Config Directory Threading

Layer / File(s) Summary
Data model expansion: AgentSpecifics and SessionEntry helpers
Sources/SessionIndexModels.swift
AgentSpecifics.claude now carries configDirectoryForResume, .codex adds effort, and new ClaudeConfigurationRoot utility validates config directories. SessionEntry.withClaudeConfigDirectoryForResume(_:) transformer allows updating a cached entry's config directory while leaving other session state intact.
Discovery pipeline: threading resumeConfigDirectory through roots and candidates
Sources/SessionIndexStore.swift
ClaudeSessionRoot and ClaudeSessionCandidate now carry resumeConfigDirectory, computed during root discovery via ClaudeConfigurationRoot.configuredResumeDirectory(...). Root discovery filters roots when requireConfigured is true. Each enumerated candidate includes the root's computed resume directory.
Entry loading and resumeCommand generation
Sources/SessionIndexStore.swift, Sources/SessionIndexModels.swift
Cached Claude entries are transformed via withClaudeConfigDirectoryForResume(candidate.resumeConfigDirectory). New entries include configDirectoryForResume in their specifics. Resume command builder destructures the field from specifics and uses it to populate CLAUDE_CONFIG_DIR in the shell environment, omitting it when the value is nil.
Test coverage: config directory pinning in resumeCommand
cmuxTests/ClaudeConfigDirectoryPathTests.swift, cmuxTests/SessionIndexViewTests.swift, cmuxTests/SessionPersistenceTests.swift
New tests verify resumeCommand omits CLAUDE_CONFIG_DIR when the canonical config root is unconfigured, and preserves it when configured. Refactored integration tests pass claudeConfigDirectoryForResume through session construction and verify full command output, removing tests that inferred config directory from file paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • manaflow-ai/cmux#4087: This PR directly addresses the reported issue where CLAUDE_CONFIG_DIR was being incorrectly set to ~/.claude for canonical installations, triggering first-run prompts. The fix adds explicit configDirectoryForResume to AgentSpecifics and only includes the env var when a non-canonical config root is actually configured.

Possibly related PRs

  • manaflow-ai/cmux#3816: Both PRs modify SessionEntry's Claude resume command generation in Sources/SessionIndexModels.swift, with overlap on the final resumeCommand string construction and cwd-wrapping logic.
  • manaflow-ai/cmux#3211: Both PRs modify Claude resume command generation to pin CLAUDE_CONFIG_DIR via explicit configuration, with this PR threading it through the discovery pipeline rather than per-session inference.

Poem

🐰 A hoppy fix for Claude's config woes,
No more false "first runs" when the config blows!
Resume to the right place, swift and true,
From root discovery through the whole shebang through,
Explicit paths now guide the CLI.

🚥 Pre-merge checks | ✅ 15 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix Claude sidebar resume config dir' directly addresses the main issue (#4087) by highlighting the core change: correcting how CLAUDE_CONFIG_DIR is handled in resume commands.
Linked Issues check ✅ Passed The PR fully addresses issue #4087 objectives: it prevents CLAUDE_CONFIG_DIR from being set for unconfigured canonical ~/.claude roots, derives it only when operational state exists via ClaudeConfigurationRoot, maintains non-default root behavior, and provides predicate-based checks.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the Claude resume config directory issue: SessionIndexModels additions (ClaudeConfigurationRoot, configDirectoryForResume), SessionIndexStore threading logic, and comprehensive test updates covering both configured and unconfigured roots.
Cmux Swift Actor Isolation ✅ Passed No new actor isolation issues introduced. ClaudeConfigurationRoot uses correct nonisolated static methods. All new fields (String?, String) are Sendable.
Cmux Swift Blocking Runtime ✅ Passed No blocking/timing constructs introduced in production code. Changes are deterministic: new ClaudeConfigurationRoot type, AgentSpecifics updates, and SessionIndexStore threading.
Cmux No Hacky Sleeps ✅ Passed Rule applies only to TypeScript, JavaScript, shell, and non-Swift runtime scripts. All PR changes are Swift files. Swift timing is covered by swift-blocking-runtime.md. Check not applicable.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns. Changes are synchronous value-type data model updates with no DispatchQueue, fire-and-forget Tasks, completion handlers, or new Combine patterns.
Cmux Swift @Concurrent ✅ Passed All new functions are synchronous, so no @concurrent needed. Async nonisolated functions properly hop off MainActor through call chain. No concurrency violations found.
Cmux Swift File And Package Boundaries ✅ Passed SessionIndexModels (484 lines) is coherent and not oversized. SessionIndexStore adds 32 lines, well within budget (1704/1826). No mixed responsibilities or inappropriate feature placement.
Cmux Swift Logging ✅ Passed No logging violations found. Production code (SessionIndexModels.swift, SessionIndexStore.swift) contains no print, NSLog, debugPrint, dump, or problematic Logger statements.
Cmux User-Facing Error Privacy ✅ Passed No NEW sensitive data exposed. PR reduces CLAUDE_CONFIG_DIR exposure by making it conditional instead of always including it. No new error messages or user-facing strings added.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI state layout violations. Changes are data structures and pure functions with no new property wrappers, ObservableObject, @Published, GeometryReader, or render-time mutations.
Cmux Architecture Rethink ✅ Passed Correctness fix adheres to rethink rules: single source of truth (AgentSpecifics), clear invariant (isLikelyConfigured), no timing/observer/split patterns, no duplicate ownership.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR does not add/modify NSWindow, NSPanel, NSWindowController, or SwiftUI Window/WindowGroup. Changes limited to Claude resume config logic. Check not applicable.
Description check ✅ Passed The PR description provides a clear summary of changes, includes a reproduction scenario, and references the related issue #4087.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4087-claude-config-dir-resume

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes the Claude sidebar resume command to stop emitting CLAUDE_CONFIG_DIR for unconfigured (default) transcript roots, preventing accidental first-run setup when resuming canonical ~/.claude sessions. Auth-presence checking is now shared via the new ClaudeConfigurationRoot enum, captured once at indexing time and threaded through ClaudeSessionRoot → ClaudeSessionCandidate → SessionEntry.

  • ClaudeConfigurationRoot.configuredResumeDirectory replaces the old per-entry claudeConfigDirectoryForResume computed property, consolidating the "is this root configured?" predicate so both session indexing and resume-command construction share the same logic.
  • AgentSpecifics.claude gains a configDirectoryForResume associated value that is populated at parse/cache-hit time and drives env-var injection; a withClaudeConfigDirectoryForResume mutator keeps cached entries up-to-date without invalidating the cache.
  • Existing tests are updated to use temp-dir fixtures with explicit configDirectoryForResume values, and new tests cover unconfigured canonical roots, null/empty auth fields, and non-default configured roots.

Confidence Score: 5/5

Safe to merge — the change is deterministic and narrowly scoped to resume command construction.

The fix correctly moves config-dir determination to indexing time, shares the predicate between root filtering and command generation, and handles cache-hit overrides on both code paths. No production data or auth state is mutated; the only observable change is whether CLAUDE_CONFIG_DIR appears in the sidebar-generated resume command. Tests cover the key invariants: unconfigured roots omit the env var, null/empty auth fields are treated as unconfigured, and configured non-default roots still emit it.

No files require special attention.

Important Files Changed

Filename Overview
Sources/SessionIndexModels.swift Adds ClaudeConfigurationRoot enum with shared auth-check predicate; moves config-dir logic from a computed property on SessionEntry to an explicit associated value on AgentSpecifics.claude; adds withClaudeConfigDirectoryForResume mutator. Logic and guard conditions are correct.
Sources/SessionIndexStore.swift Propagates resumeConfigDirectory from ClaudeSessionRoot through ClaudeSessionCandidate to both fresh-parse and cache-hit paths; deletes the old isLikelyConfiguredClaudeRoot in favour of the shared ClaudeConfigurationRoot predicate. Cache-hit override via withClaudeConfigDirectoryForResume is applied on both hit branches.
cmuxTests/ClaudeConfigDirectoryPathTests.swift Adds three new tests: unconfigured canonical root omits CLAUDE_CONFIG_DIR, null/empty auth fields return nil, and configured non-default root emits CLAUDE_CONFIG_DIR. Coverage is good; temp-dir fixtures avoid path-coupling.
cmuxTests/SessionIndexViewTests.swift Rewrites the two existing config-dir resume-command tests to use real temp-dir fixtures and explicit claudeConfigDirectoryForResume parameter; removes brittle full-string assertion in favour of structural checks.
cmuxTests/SessionPersistenceTests.swift Updates the cwd-change resume-command test to pass configDirectoryForResume: nil and match the new expected command (no CLAUDE_CONFIG_DIR for unconfigured canonical root); temp-dir fixture replaces the former hard-coded path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[claudeSessionRoots] --> B{appendRoot called for each root}
    B --> C[ClaudeConfigurationRoot.configuredResumeDirectory]
    C --> D[preferredPath: standardize dir]
    D --> E[isLikelyConfigured: read .claude.json]
    E --> F{has oauthAccount / primaryApiKey / apiKey?}
    F -- No/null/empty --> G[resumeConfigDirectory = nil]
    F -- Yes --> H[resumeConfigDirectory = standardized path]
    G --> I[ClaudeSessionCandidate resumeConfigDirectory = nil]
    H --> I
    I --> J{Cache hit?}
    J -- Yes --> K[cached.withClaudeConfigDirectoryForResume candidate.resumeConfigDirectory]
    J -- No --> L[Parse transcript, build SessionEntry with configDirectoryForResume from candidate]
    K --> M[SessionEntry with correct configDirectoryForResume]
    L --> M
    M --> N[resumeCommandWithoutWorkingDirectory]
    N --> O{configDirectoryForResume == nil?}
    O -- Yes --> P[claude --resume id, no env prefix]
    O -- No --> Q[env CLAUDE_CONFIG_DIR=dir ... claude --resume id]
Loading

Reviews (6): Last reviewed commit: "fix: ignore empty Claude auth state" | Re-trigger Greptile

Comment thread cmuxTests/ClaudeConfigDirectoryPathTests.swift Outdated
Comment thread cmuxTests/ClaudeConfigDirectoryPathTests.swift Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 71f5415. Configure here.

Comment thread cmuxTests/SessionPersistenceTests.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes May 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/SessionIndexModels.swift`:
- Around line 225-227: The current check treats mere presence of keys as
"configured"; update the condition that returns whether a session is configured
to verify each value is neither null/NSNull nor empty: for obj["oauthAccount"],
obj["primaryApiKey"], and obj["apiKey"] ensure the stored value is non-nil, not
an explicit null/NSNull, and for string values not an empty/whitespace string
(or for collections not empty) before considering it configured; replace the
simple != nil checks with these stricter validations so null/empty auth fields
are treated as unconfigured.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a5bf74bf-56b2-4743-b1bf-d107aa3d1130

📥 Commits

Reviewing files that changed from the base of the PR and between 50b985b and 5d31373.

📒 Files selected for processing (5)
  • Sources/SessionIndexModels.swift
  • Sources/SessionIndexStore.swift
  • cmuxTests/ClaudeConfigDirectoryPathTests.swift
  • cmuxTests/SessionIndexViewTests.swift
  • cmuxTests/SessionPersistenceTests.swift

Comment thread Sources/SessionIndexModels.swift Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

Re-trigger cubic

@lawrencecchen
lawrencecchen dismissed coderabbitai[bot]’s stale review May 18, 2026 07:01

Dismissed as stale: the requested null/empty Claude auth-state handling was addressed in 42ff59e via ClaudeConfigurationRoot.hasConfiguredAuthValue and covered by testConfiguredResumeDirectoryIgnoresNullAndEmptyAuthFields. CodeRabbit's latest check on HEAD is passing.

@austinywang
austinywang merged commit 98cd225 into main May 18, 2026
29 checks passed
@austinywang austinywang mentioned this pull request May 19, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 42ff59e5 Deployed May 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Right-sidebar resume (Copy + Resume in New Tab) sets CLAUDE_CONFIG_DIR=~/.claude and triggers first-run prompts

1 participant