Skip to content

CPLAT-9497: preserve conv preview selection + fix LIVE/HERE markers#38

Merged
gavin-jeong merged 2 commits into
masterfrom
CPLAT-9497-preview-and-live-marker-fixes
May 12, 2026
Merged

CPLAT-9497: preserve conv preview selection + fix LIVE/HERE markers#38
gavin-jeong merged 2 commits into
masterfrom
CPLAT-9497-preview-and-live-marker-fixes

Conversation

@gavin-jeong
Copy link
Copy Markdown
Collaborator

JIRA: https://sendbird.atlassian.net/browse/CPLAT-9497

Summary

  • Conv preview rework — unify compact / standard / verbose under a single previewBuild abstraction with per-block source-entry tracking, so the selected block (or its nearest text equivalent) survives mode toggles and parent-pane drilldown pops in both flat and tree views. Tree-mode task/agent/bg-job previews now honour the right-pane detail level. TaskCreate-only convTask items get their task.ID resolved against the session task list, so previews/enter-drilldowns target the right entries instead of accidentally landing on an unrelated TaskCreate burst.
  • ESC behaviour in conv view — ESC in a drilldown pops one level back and re-opens the preview so a second ESC closes that preview cleanly, instead of skipping past the parent into the session list. In a plain conv view with the preview already closed, ESC is a no-op; remains the explicit exit.
  • LIVE / HERE marker fix — claudes wrapped by ccproxy (or any non-pane intermediary) used to land in the orphan bucket and inherit [HERE] from any matching cwd, falsely marking sessions in other windows as LIVE in the current one. The new classifyClaudeProcsByAncestry walks the PPID chain past wrappers, attributing each claude to its true owning pane shell. True orphans keep LIVE via cwd but no longer auto-inherit [HERE]. Subagents (claude under another claude) are dropped from the attribution pool. IsCurrentWindow is now reset alongside IsLive on refresh.

Test plan

  • go test ./... — all green
  • TestModeCyclePreservesOriginalSelection — compact ↔ standard ↔ verbose round-trip selection
  • TestStandardToVerbosePreservesToolOnlyTurn — `[[TaskUpdate]]` summary → tool_use mapping
  • TestVerboseToolToCompactFallsBackToPrecedingText — tool/result → nearest plain-text fallback
  • TestTreeTaskCompactModeFiltersToTextOnly + TestTreeBgJobPreviewShowsCommandAndOutput — tree-mode mode transforms
  • TestPopNavFrameRestoresParentConvPosition, TestEscFromTaskDrilldownReturnsToParentConv, TestEscClosesPreview — ESC navigation rules
  • TestClassifyClaudeProcsByAncestry_* (5 cases including the user-reported rehome scenario) + nested subagent / cycle defence
  • Manual verification: ccproxy + claude in two tmux windows under the same cwd, confirmed only the current-window claude shows [HERE]
  • Manual verification: enter task drilldown → ESC twice lands on parent conv list with preview re-opened; cursor restored to original task item

JIRA: https://sendbird.atlassian.net/browse/CPLAT-9497

Conversation preview was losing user state across detail-mode switches
and surfacing the wrong content for tasks built from TaskCreate-only
tool calls. This rework unifies the preview pipeline behind a single
abstraction so the three modes apply consistently and the user's
position survives mode toggles and parent-pane drilldowns.

Selection preservation across compact / standard / verbose:
- Introduce previewBuild { Header, Sources, Fallback } and three
  uniform transformers (compactPreview / standardPreview /
  verbosePreview). All preview kinds — convMsg, convTask, convAgent,
  convBgJob — emit one of these, so per-kind branching downstream is
  gone.
- Track per-block source-entry index on FoldState.BlockSourceIdx;
  the anchor capture/restore now matches by sourceIdx first (with
  tool-name ordinal + preview-core-text + raw-text fallbacks), so a
  selected block keeps its position when switching modes.
- Tree-mode task / agent / bg-job previews now honour the right-pane
  detail level, matching the flat conversation view. Verbose retains
  the rich synthetic entry.

Tasks built from TaskCreate-only tool inputs were missing a task.ID,
causing extractTaskEntries to fall back to subject-less JSON matching
and surface unrelated entries. Resolve the ID against the session task
list at buildConvItems time; when no ID can be found, the Enter
handler opens the parent message in msgFull instead of drilling into
the wrong task.

Drilldown ESC behaviour:
- popNavFrame now restores leftPaneMode + treeItems via
  rebuildConversationList, so the cursor lands on the correct slice
  regardless of flat/tree mode.
- ESC in a drilldown pops to the parent and re-opens the preview so a
  second ESC closes that preview cleanly, never skipping past the
  parent into the session list. ESC in plain conv view with the
  preview already closed is now a no-op — use 'left' for the explicit
  session-list exit.

Tests:
- TestModeCyclePreservesOriginalSelection covers compact ↔ standard
  ↔ verbose round-trips with a deliberately mis-aligned source layout.
- TestStandardToVerbosePreservesToolOnlyTurn covers the
  "[[TaskUpdate]]" summary -> tool_use mapping.
- TestVerboseToolToCompactFallsBackToPrecedingText covers the
  tool/result -> nearest plain-text fallback.
- TestTreeTaskCompactModeFiltersToTextOnly +
  TestTreeBgJobPreviewShowsCommandAndOutput cover tree-mode mode
  transforms.
- TestPopNavFrameRestoresParentConvPosition,
  TestEscFromTaskDrilldownReturnsToParentConv,
  TestEscClosesPreview cover the ESC navigation rules.
JIRA: https://sendbird.atlassian.net/browse/CPLAT-9497

Sessions sharing a project path with the user's active claude were
spuriously marked LIVE and HERE in the session list. Two causes:

1. Claude processes wrapped by ccproxy (or any non-pane intermediary)
   have their parent PID pointing at the wrapper, not the tmux pane
   shell, so the previous direct-PPID classification dropped them into
   the orphan bucket. The orphan path then attributed them to the
   current window by cwd alone, which collided whenever another tmux
   window had a claude running in the same directory.
2. orphan claudes whose cwd happened to match a path in the current
   tmux window inherited the [HERE] badge even though they belonged to
   another window entirely.

Fix:
- classifyClaudeProcsByAncestry walks the PPID chain (via a
  ps -e -o pid=,ppid= snapshot) past wrappers like ccproxy / sudo /
  tee, attributing each claude to the first tmux pane shell it
  reaches. Subagents — claudes whose chain crosses another claude
  before any pane — are dropped entirely so the most-recent-session
  fallback can't promote unrelated past sessions.
- True orphans (chain ends at init without ever hitting a pane) keep
  their LIVE marking via cwd but no longer auto-inherit HERE just
  because their cwd matches a current-window pane.
- IsCurrentWindow is now reset alongside IsLive/IsResponding before
  re-running MarkLiveSessions on refresh, so HERE doesn't leak across
  refresh cycles after pane closes.

Tests in internal/tmux/classify_test.go cover:
- direct pane child + standalone orphan classification
- single-level and nested subagent drop
- ccproxy-wrapped claude attribution via ancestry walk
- subagent under wrapped claude
- corrupt-cycle defence
- the user-reported rehome scenario (1 wrapped claude + 2 leftover
  orphans on the same cwd) marks only the wrapped one as direct
Copy link
Copy Markdown

@jinsekim jinsekim left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@jinsekim jinsekim left a comment

Choose a reason for hiding this comment

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

LGTM!

@gavin-jeong gavin-jeong merged commit a82e416 into master May 12, 2026
3 checks passed
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.

3 participants