fix: dedupe Claude sessions imported by Codex Desktop#220
Merged
vakovalskii merged 1 commit intoMay 25, 2026
Merged
Conversation
Codex Desktop >= 0.133.0-alpha.1 ingests Claude Code sessions from ~/.claude/projects/**/*.jsonl into ~/.codex/sessions/ and registers them in session_index.jsonl with a fresh UUIDv7 thread id. The original Claude file stays in place, so the same conversation appeared twice in codbash — once correctly as a Claude session and once incorrectly as a Codex session. Read ~/.codex/external_agent_session_imports.json and skip imported rollouts whose source path still exists on disk. When the source has been deleted, retain the Codex copy so no history is lost. Also watch the ledger file in the rescan-change detector to close a race where Codex writes the rollout/index slightly before the ledger and a poll fires in between.
vakovalskii
approved these changes
May 25, 2026
Owner
vakovalskii
left a comment
There was a problem hiding this comment.
LGTM ✅ Targeted dedup fix with the right defensive posture.
Verified:
node -c src/data.jsclean- Full test suite still 127/0/2 pass — nothing regresses
- CI green 6/6
Spot-checks:
parseCodexExternalImportsis defensive at every step: missing file → empty Set, unreadable → empty Set, malformed JSON → empty Set, non-arrayrecords→ empty Set- Conservative no-data-loss policy: only skip Codex copy when the source Claude file still exists. If user deleted the original, the Codex import becomes the sole surviving copy
- Skip applied at both Codex code paths (
history.jsonlloop and rollout files walk) — full coverage - Cache invalidation correctly watches the ledger mtime+size + handles the file-disappeared case (
else if (_codexImportsLedgerMtime !== 0 || _codexImportsLedgerSize !== 0)) - Uses the ledger as authoritative — avoids parsing every rollout to detect the
Codex Desktoporiginator +external-import-turn-1markers 👍
Real-world impact reported in the PR (56 → 6 Codex sessions, 605 → 557 total) suggests this will be felt immediately by anyone running both Codex Desktop and Claude Code.
This was referenced May 25, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Codex Desktop
>= 0.133.0-alpha.1introduced an External Agent Session Imports feature that ingests Claude Code sessions from~/.claude/projects/**/*.jsonlinto~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonland registers them in~/.codex/session_index.jsonlwith a fresh UUIDv7 thread id. The original Claude file stays in place.Codbash was loading both copies, so every imported conversation appeared twice in the dashboard — once correctly attributed to Claude (original file, UUIDv4 id) and once incorrectly attributed to Codex (the import, UUIDv7 id). On my machine that produced 50 duplicates (56 "Codex" sessions of which only 6 were genuinely Codex-native).
Fix
parseCodexExternalImports(codexDir)reads~/.codex/external_agent_session_imports.jsonand returns aSetofimported_thread_ids whosesource_pathstill exists on disk.scanCodexSessions()skips those ids in both thehistory.jsonlloop and the rollout-files walk._sessionsNeedRescan/_updateScanMarkersso a two-phase Codex write (rollout/index first, ledger a tick later) cannot leave a stale dedup view between polls.Identifying an import on disk
Imported rollouts have a distinctive meta header:
But the ledger is the authoritative source — using it avoids parsing every rollout to detect imports.
Before / after
019e55cf-3089-...ee956bf6-...is shown instead)Test plan
node -c src/data.js— syntaxnode bin/cli.js run,/api/sessionscounts before/after match expected dedup019e55cf-3089-7031-bc64-fd29a650def8no longer appears under Codex; originalee956bf6-b5f6-4571-b1d0-019ce2874122still appears under Claude~/.codex/external_agent_session_imports.json— fallback (helper returns empty set, all Codex sessions shown as before)