Skip to content

Session continuation after plan mode exit not visible in sidebar #136

@justinchoo93

Description

@justinchoo93

Bug Description

When Claude Code exits plan mode (e.g., "clear context and implement"), it creates a new JSONL session file with a new filename UUID, but the internal sessionId in the file content still references the original pre-plan session. Freshell's session indexer resolves session IDs by preferring content IDs over filename IDs, causing both files to map to the same session key. The second file silently overwrites the first in sessionKeyToFilePath, and only one of the two sessions is ever visible in the sidebar.

The result: all conversation history after plan mode exit is invisible to the user in Freshell, even though the JSONL file exists on disk with valid content.

Steps to Reproduce

  1. Start a Claude Code session in a project directory (or worktree)
  2. Have a conversation (generates <session-A>.jsonl with internal sessionId A)
  3. Enter plan mode, write a plan
  4. Exit plan mode using one of the "clear context" options
  5. Continue chatting — Claude Code creates a new file <session-B>.jsonl, but the internal sessionId is still A
  6. Open Freshell sidebar — only the original session (pre-plan) is visible
  7. The continuation session (<session-B>.jsonl) is not shown, despite existing on disk

Root Cause

In server/coding-cli/session-indexer.ts, the processFile() method resolves the canonical session ID at line 418:

const sessionId = meta.sessionId || provider.extractSessionId(filePath, meta)

meta.sessionId comes from parsing the JSONL content (parseSessionContent() in server/coding-cli/providers/claude.ts), which extracts the sessionId field from message objects (lines 266-277). Since the continuation file's content still contains the original session's ID, both files resolve to the same sessionId.

At line 444, the session key is set without collision detection:

this.sessionKeyToFilePath.set(makeSessionKey(provider.name, sessionId), filePath)

Whichever file is processed last wins; the other becomes invisible.

Observed Example

  • Original session file: 136767ac-590b-416c-8cd4-7022e2cfb5df.jsonl — 980 lines, ends at plan mode exit
  • Continuation file: 361f4f46-af04-4c1c-8e2e-f94fee0fae2e.jsonl — 743 lines, contains post-plan implementation work
  • Both are in the same project directory (~/.claude/projects/...-worktrees-pane-extension-system/)
  • Content of the continuation file has sessionId: "136767ac-590b-416c-8cd4-7022e2cfb5df" (the original ID)
  • Only the original shows in the sidebar; the continuation is invisible

Possible Fix Approaches

  1. Prefer filename UUID as canonical ID — When the filename is a valid UUID and differs from the content sessionId, use the filename. Claude Code intentionally assigned a new UUID to the continuation file.

  2. Detect collisions and keep both — When sessionKeyToFilePath already has an entry for a session key pointing to a different file, treat them as separate sessions (e.g., use a composite key like provider:sessionId:filePath).

  3. Session chaining — Detect that the continuation file references a parent sessionId and link them in the UI as a continuation chain, showing both in the sidebar (possibly grouped).

Environment

  • Freshell v0.6.0
  • Claude Code v2.1.63
  • Commit: 940db3f

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions