perf(panel,host): a task opens through one skeleton, its agent's transcript followed at once

Merged
CH
4d ago
+835-115

Opening a task now shows one placeholder shaped like the finished page and then the page. Before, it went through a chain of placeholders: the "Loading task" spinner, an empty column, "Loading agent", and "Loading transcript". The agent's transcript also starts loading before the task's agent list answers.

What changed

Host: fewer pushes per open (compose-tasks.ts)

  • TASKS_OPEN and TASKS_REFRESH start the document, board, code, and pages reads together (readOpened).
  • The document is published together with every part that has already answered, so an open is usually 2 pushes (reading, landed) instead of about 6. The code's "reading" state is part of the switch's own view.
  • A part that answers after the document never holds the document back. Parts still out land afterwards in one more view.
  • The board, code, and pages reads that run on their own (the live follow loops) share the same withCodeRead and withPagesRead helpers.

Choosing the agent tab from the task row (use-main-tabs.ts, use-tasks-tab.ts)

  • useMainTabs also receives what the task's sidebar row names of its agents (NamedAgents, built from TaskAgentLines). While the agent list is unread, shownIn picks the tab the list would pick, but only when the row is enough to know that:

    • the last-viewed agent, if the row names it;
    • or the task's only agent, when it has exactly one and none was viewed. The row can't reproduce the list's order for agents started at the same moment, so with more agents and none viewed it waits.
  • If the row can't tell, the column waits as before, so it never shows one agent and then switches to another.

  • Status and title from transcript pages that arrive before the agent list are kept, and laid over the list's answer when it lands (useCodingAgents).

AgentTab split (agent-tab.tsx, main-tabs.tsx)

  • AgentTab takes agentId and an optional agent.
    • The outer part needs only the id. It runs useAgentTranscript and useAgentCalls, plus the held, plan-edit, list-call, and sidebar-call effects.
    • AgentTabBody runs once the CodingAgentSummary arrives: composer, model, servers, pull request, message reveal, continue.
  • MainTabContent draws AgentTab from the tab's agent id, keyed by that id, so the follow keeps running when the summary lands. No more "Loading agent" spinners in the column.

One layout-shaped skeleton (new agent-tab-skeleton.tsx, desktop-tasks.tsx)

  • AgentTabSkeleton draws the last lines of the log above a composer-shaped card, built with the composer's own classes so it is the same size. TranscriptSkeleton replaces the "Loading transcript" spinner.
    • #4465's holding uses it too: holding || reading.
  • While the document is read, the task page draws TaskPageSkeleton instead of "Loading task":
    • a toolbar with a tab placeholder and action placeholders;
    • the column as an AgentTabSkeleton;
    • the summary's room, when the summary will show;
    • the side panel at its width, when it is open.
  • After the document lands, the column shows the same skeleton until a tab is chosen, rather than drawing nothing.
  • The tab strip shows a tab placeholder while the agents are being read.
  • The summary rail's room is kept from the moment the task opens when the task's row says it has agents, so the agent list arriving doesn't reflow the column.

Tests

  • Host, compose-tasks.test.ts:
    • opening a task tells the window "reading", then document, board, code, and pages in one view;
    • a read that never answers holds back neither the document nor the parts that have answered.
  • use-coding-agents.test.ts: status and title heard before the list stand over the list's answer.
  • use-tasks-tab.test.ts: the row settles the tab before the list is read (last viewed and named; the only agent); it waits when the row can't tell (an unnamed last-viewed agent among more agents; two agents with none viewed).
  • New task-open.test.ts, through DesktopTasks with a fake follower:
    • the transcript is followed before the list answers, and the tab shows its skeleton, never an empty column or "Loading agent";
    • from the document read to the transcript, the column passes through exactly one placeholder (same blocks at each step), the toolbar tab placeholder stays put, and the composer takes its card's place;
    • the summary's room holds from the open through the list landing.
  • Each of these fails on main.
  • agent-tab-virtual.test.ts (#4465): the "loading" check now looks for the transcript skeleton instead of .conversation-empty.

Notes

  • The follow still starts after the document read. The agent tab mounts inside the task page's providers, which need the document. The host now reads the document in parallel with the rest, so this costs one round trip. Starting the follow at the select act would be the next step (it fits beside the host transcript cache).
  • The rail is reserved only when the row says the task has agents. On a task with no agents, comments or Inbox items can still bring the rail in late.
  • Not checked on a Mac. The skeleton's sizes come from the real components' classes. The tests check structure, not pixels, so it's worth opening a task once to look.

Caching tasks or transcripts across opens, the AppWindow redraw, and server changes are out of scope.

Made with Luke


Devin Review