Imported Claude worktree sessions create standalone projects instead of grouping under the original repository
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Local paths and session identifiers in this report are synthetic.
Environment
- macOS 26.6.2
- ChatGPT desktop with Codex: 26.901.51231, build 8109
- Claude session import enabled, including automatic import at app startup
- Public source inspected at openai/codex commit
694b6319d3ad2399f6e435760a22d9b9357f0697; this is not asserted to be the exact source revision of the installed desktop build
Reproduction
- Save
/Users/example/code/repoas a Codex project. - Run a Claude session in
/Users/example/code/repo/.claude/worktrees/example-task. - Import Claude projects and chats into Codex.
- Observe the sidebar: the worktree is represented as another standalone project or the imported session is not assigned to the saved parent project.
- Manually repair existing imports, create another Claude worktree session, and restart with automatic import enabled. Newly imported sessions produce the same grouping problem.
Expected: group the imported session under the existing parent repository/workspace, while preserving the worktree as its runtime cwd. An explicit user project assignment or projectless choice should take precedence.
Actual local observation: a later import batch left 8 imported worktree sessions with a null database project ID and created 6 standalone worktree project entries. The local resolver can associate all 8 with desktop originCwd, independently corroborated by Git.
Evidence and distinction between cwd and project ownership
Claude CLI JSONL samples contain the worktree cwd; they do not contain a separate original-repository field. This is a valid execution directory, not by itself a Claude CLI bug. The parent association should be additional project metadata, not a replacement for cwd.
Claude desktop has a separate mapping:
{
"cliSessionId": "example-cli-session-id",
"cwd": "/Users/example/code/repo/.claude/worktrees/example-task",
"originCwd": "/Users/example/code/repo"
}
It lives in ~/Library/Application Support/Claude/claude-code-sessions/<org>/<user>/local_*.json. The filename stem of the CLI JSONL matches cliSessionId. In the observed local snapshot, 91 worktree desktop sessions had originCwd, but only 78 had cliSessionId; this source alone does not cover all sessions or terminal-only sessions.
~/Library/Application Support/Claude/git-worktrees.json provides path/baseRepo as a secondary, garbage-collected mapping. The encoded CLI project folder can be stale after a worktree is renamed/reused; it should not be decoded as the authoritative repository identity.
Public code pointers
- Claude session detection enumerates JSONL files and has no project-directory fallback.
- Session summary takes the first
cwdand includes it inExternalAgentSessionMigration. - App-server session importer preserves that execution cwd when creating the imported session. The precise desktop project-creation/grouping consumer still needs maintainer confirmation.
Suggested fix and regression coverage
Resolve a separate project-root hint without modifying the working directory:
- Respect
CLAUDE_CONFIG_DIRfirst; fall back to the default directory only if unavailable, without blindly merging sources. - Match desktop
cliSessionIdtooriginCwdwhere available. - Otherwise infer the base from
<repo>/.claude/worktrees/<name>using the JSONL cwd, including deleted worktrees. Do not require that the worktree still exists. - Use the desktop pool or Git common-directory/worktree metadata as additional evidence. Conflicting evidence should remain unresolved.
- Associate with the longest matching saved local project, preserving explicit user overrides. Apply this consistently to interactive and startup imports.
- Keep normalization idempotent. Do not delete a project referenced by archived/native sessions, another host, or explicit user state.
Tests should include desktop and CLI-only sessions, renamed encoded folders, deleted worktrees, custom Git worktree locations, a saved umbrella workspace, duplicate roots, configuration fallback, explicit projectless assignments, and repeated import.
The local workaround also found both SQLite project IDs and desktop legacy project mappings in use; changing one alone was insufficient in the observed build. An official fix should coordinate ownership through supported application APIs, not adopt direct offline database writes.
Related report
#40935 describes Desktop/App Server project-assignment divergence for a custom client. This report concerns the built-in Claude import flow and the missing worktree-to-original-project association, including the available originCwd mapping and recurrence on startup imports.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with codex-rs/external-agent-migration/src/detect/sessions/cla.rs, codex-rs/external-agent-migration/src/sessions/records_cla.rs, and codex-rs/app-server/src/external_agent_migration/session_importer.rs, then locate the desktop project-grouping consumer. Reproduce both interactive and startup imports using the listed worktree cases and add regression coverage. Done means imported sessions retain their worktree cwd, associate with the correct saved parent when evidence is sufficient, and preserve explicit assignments.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, rust, sqlite
- Domain
- backend, desktop
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100