Agent pane: the todo list pins under the running turn's latest message and folds when the turn ends

Merged
TE
2d ago
+326-69

Leo, 2026-11-03, watching Claude Code desktop: "yo wth i want this kind of feature in cmux-next too" (one of four features he picked; this PR is the pinned task list).

What changes

  • Pinned while the turn runs, on by default. The agent's todo list (an ACP plan update: Claude Code's TodoWrite, Codex's update_plan) now sits under the running turn's latest message, with the live line under it, from its first update to the turn's end. Before, it stayed where the first update came in and scrolled away under later messages and calls; the pinned placement only happened with the Debug toggle agent.sessionStatus. That toggle now only swaps the live line for its activity line, as before.
  • Changes animate in place. A step's change fades its colours (the --agent-motion-in token, 120 ms, off under Reduce Motion). Nothing changes size, so nothing below it shifts. The list keeps one row per turn (direct.ts), so updates never add a second card.
  • Folds when the turn ends. An ended turn keeps its list where it stood pinned (under the answer, above the footer), so the end moves nothing above it. It folds to one muted line, "3 of 3 tasks done", with the transcript's chevron. A click opens the steps again; the open state survives the row scrolling out and back (planOpen.ts, bounded to 256 ids). A turn that stopped with steps left shows a dashed ring instead of the filled check.
  • The row height estimate (model.ts) follows the folded height.

No new strings: the folded line uses the existing plan.progress / plan.progress.one.

References and gallery

References: claude/web/october-live-checklist, claude/live/transcript-tool-rows-and-task-list (Claude desktop's list pinned over the live line), cursor/live/plan-completed-checklist (a finished list folded to its count line).

Gallery:

  • agent-pane.plan-list: pinned (Claude), pinned-codex, finished-folded, finished-stopped, and the existing variants.
  • agent-pane.plan-list-looks: the finished look as shipped (ended-line, ended-line-partial) beside ended-card (the folded line kept in its card) and ended-open (the whole list, the look before this PR). I picked ended-line: it reads like the turn's other summary lines and takes the least room in a finished chat.

Design spec: design/chat-transcript.md ("Answered question and checklist") is updated in leo-workspace for the folded finished list.

Coordination: the composer tray lane owns what sits above the composer; this stays in the transcript and touches no tray code.

Tests

  • New conversation/pinnedPlan.test.ts: the list sits under the latest message in all three live layouts with the toggle off; under a streaming answer with the live line under both; the toggle keeps its activity line; an ended turn (Claude and Codex families) keeps its list under the answer, ended, before the footer; a turn stopped without a summary keeps it last.
  • test/transcript-activity-line.test.ts: two expectations follow the pinned order.

Risk

Small, contained to the transcript's todo list: where its row is placed in a turn and how an ended one draws. Nothing in data, persistence or the daemon changes.

🤖 Generated with Claude Code

Agent session: agent session


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

The agent pane's todo list now pins under the running turn's latest message and folds to a single "3 of 3 tasks done" line when the turn ends.

  • Previously the list stayed where its first update came in and scrolled away under later messages; agent.sessionStatus was required to pin it — now pinning is always on, and that toggle only swaps the live line for its activity line.
  • A step's change fades in place (the --agent-motion-in token, 120 ms, off under Reduce Motion) with no size change, so nothing below shifts.
  • An ended turn keeps its list where it stood pinned and folds it to one bare line, its mark aligned with the turn's other summary lines and a chevron that re-opens the steps; a turn stopped with steps left shows a dashed ring instead of a filled check.
  • The open state survives the row scrolling out and back (planOpen.ts, bounded to 256 ids), and the row height estimate follows the folded height.