Skip to content

fix(sessions): filter sidechain (sub-agent) JSONL files from sidebar#726

Open
ofligit wants to merge 1 commit intositeboon:mainfrom
ofligit:fix/filter-sidechain-sessions
Open

fix(sessions): filter sidechain (sub-agent) JSONL files from sidebar#726
ofligit wants to merge 1 commit intositeboon:mainfrom
ofligit:fix/filter-sidechain-sessions

Conversation

@ofligit
Copy link
Copy Markdown

@ofligit ofligit commented Apr 29, 2026

fix(sessions): filter sidechain (sub-agent) JSONL files from sidebar

Summary

The session sidebar surfaces every *.jsonl file in
~/.claude/projects/<project-dir>/ as a resumable session. That includes
MCP-spawned sub-agent transcripts which the Agent SDK writes with normal UUID filenames —
indistinguishable from real sessions by name. Each one becomes a phantom
"tab" in the sidebar and resuming it does nothing useful, since each is a
finished non-interactive sub-conversation.

The existing agent-*.jsonl filename filter only catches Task-tool
transcripts; it doesn't catch MCP sub-agents.

Fix

Adds a content-based predicate isInteractiveClaudeSessionFile() next to
the existing filename filter in server/projects.js. It streams each
candidate file, finds the first user/assistant entry, and returns
entry.isSidechain !== true. Files with no user/assistant entry (e.g.
files that contain only file-history-snapshot records) are treated as
non-interactive.

Why isSidechain and not a heuristic:

  • It's Claude Code's own canonical field for "this record is part of a
    sub-conversation".
  • Every sub-agent path — Task tool, Agent SDK, future MCP servers — goes
    through the same SDK code that sets it, so the predicate is forward-
    compatible.
  • It's structural, not prompt-based.

The filename agent-* filter is kept as a cheap fast-path so we don't
have to open those files at all.

A small defensive change in parseJsonlSessions() also skips individual
entries with isSidechain === true. The file-level filter is the primary
gate; this prevents a stray sidechain entry inside a mixed-content file
from materialising a phantom session.

Files changed

  • server/projects.js
    • new helper: isInteractiveClaudeSessionFile(filePath)
    • getSessions(): applies the predicate after the agent-* filename
      filter, before iterating into parseJsonlSessions()
    • parseJsonlSessions(): per-entry defensive skip when
      entry.isSidechain === true

Verification

  • node --check server/projects.js passes.
  • Tested against local projects: every previously-listed interactive
    session is preserved; sidechain UUID files (when present) are dropped;
    file-history-snapshot-only stub files (no sessionId, didn't render
    before either) are also dropped — no behavioural change for those, just
    cleaner up-front filtering.
  • Sidebar smoke test: lightly-used projects show identical session lists;
    Taskmaster-heavy projects no longer show competing-tab phantoms.

Out of scope (follow-up)

isSidechain is Claude-specific. Other providers will hit the same UX
bug class as they grow MCP-style sub-agents. The architectural home for
that is a per-provider provider.isInteractiveSession(records) predicate
dispatched by a shared sidebar filter, which fits naturally into the
DB-driven session refactor in #715. Not changing the provider adapter
shape here to keep this fix small and back-portable.

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Session list now accurately displays only interactive Claude sessions, filtering out non-interactive files based on content analysis.
    • Sidechain entries within mixed-content session files are now properly excluded from session listings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

The pull request modifies server/projects.js to implement content-based filtering for Claude JSONL session files. It introduces a streaming helper to detect interactive sessions based on presence of user/assistant entries not marked as sidechain, enriches files with metadata, filters non-interactive entries before pagination, and adds defensive filtering to skip sidechain records during session reconstruction.

Changes

Cohort / File(s) Summary
Session Filtering Logic
server/projects.js
Added interactive session detection via new streaming helper isInteractiveClaudeSessionFile. Modified getSessions() to enrich files with mtime and interactive boolean, filter non-interactive files, and return empty results when none qualify. Updated parseJsonlSessions() to defensively skip records marked isSidechain: true. (+63/-7 lines)

Possibly related PRs

  • PR #488: Implements similar record-level visibility filtering in the same file, distinguishing interactive from non-interactive entries when reconstructing sessions for Claude-specific JSONL content.

Suggested reviewers

  • viper151

Poem

🐰 A filtering tale, so neat and tidy,
We separate the chats from the sideline's mighty,
Only interactive whispers shall remain,
While sidechain echoes fade like rain,
Sessions now sparkle, pure and bright!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main change: filtering sidechain JSONL files from the sidebar by content-based detection. It accurately summarizes the primary fix implemented in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server/projects.js (1)

2030-2031: Consider: searchConversations may surface sidechain entries.

The searchConversations function uses the same agent-* filename filter but doesn't apply isInteractiveClaudeSessionFile or skip entries with isSidechain === true. This means search results could include matches from sidechain content.

This may be acceptable since:

  1. Search is for content discovery, not session resumption
  2. Adding file-level checks would increase latency for search

If sidechain content is appearing in search results and causing noise, consider adding the isSidechain entry-level skip to the search loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/projects.js` around lines 2030 - 2031, searchConversations currently
filters JSONL files by filename (excluding agent-*) but does not check per-entry
flags, so sidechain entries can surface; update the search loop inside
searchConversations to skip any conversation entry where entry.isSidechain ===
true (and optionally reuse the existing isInteractiveClaudeSessionFile check) so
sidechain content is excluded from search results while keeping the
filename-level filter (refer to the searchConversations function and the
isInteractiveClaudeSessionFile / isSidechain entry properties to locate the
change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/projects.js`:
- Around line 2030-2031: searchConversations currently filters JSONL files by
filename (excluding agent-*) but does not check per-entry flags, so sidechain
entries can surface; update the search loop inside searchConversations to skip
any conversation entry where entry.isSidechain === true (and optionally reuse
the existing isInteractiveClaudeSessionFile check) so sidechain content is
excluded from search results while keeping the filename-level filter (refer to
the searchConversations function and the isInteractiveClaudeSessionFile /
isSidechain entry properties to locate the change).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a878869b-7989-4301-814d-f238b6ae97df

📥 Commits

Reviewing files that changed from the base of the PR and between f6200e3 and 4ede469.

📒 Files selected for processing (1)
  • server/projects.js

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.

1 participant