diff --git a/DESIGN.md b/DESIGN.md
index 05d8865d46d..d11b7132122 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -118,9 +118,11 @@ recipient for a later draft must not change the response already starting.
Runtime discovery must not replace the turn with a generic spinner or a
"run started" receipt. Queued and running headers use the same spacing.
-A queued turn shows `Queued`, or `Waiting for maintenance` while the server holds
-admission. Reserve `Working` and `Thinking` for execution. A saved message that
-starts this queued turn stays in history, including when it reopens an older
+A queued turn shows the same `Working` header and shimmering `Thinking` activity
+as a running turn, starting at message submission. Keep specific server holds,
+such as `Waiting for maintenance` or agent setup, visible during admission.
+This is presentation only; queue and execution authority stay unchanged. A saved
+message that starts this queued turn stays in history, including when it reopens an older
task. This placement is not a consumption receipt. Later follow-ups remain in
the composer queue with their existing edit, order and discard controls.
diff --git a/ui/src/components/TaskChatThread.test.tsx b/ui/src/components/TaskChatThread.test.tsx
index 17d62058637..8c8e6041e26 100644
--- a/ui/src/components/TaskChatThread.test.tsx
+++ b/ui/src/components/TaskChatThread.test.tsx
@@ -573,7 +573,7 @@ it.each([true, false])("keeps an upload attached when a conversation draft becom
expect(onAdd.mock.calls[0]?.[3]).toEqual([id]);
});
-it("shows Queued during admission and clears it when the saved follow-up enters backoff", async () => {
+it("shows activity during admission and clears it when the saved follow-up enters backoff", async () => {
const pending = { id: "optimistic-recovery", companyId: "company", issueId: "issue", authorType: "user" as const,
authorAgentId: null, authorUserId: "board", body: "Confirm the existing result.", presentation: null, metadata: null,
createdAt: new Date(), updatedAt: new Date(), clientStatus: "pending" as const };
@@ -586,7 +586,9 @@ it("shows Queued during admission and clears it when the saved follow-up enters
agentMap={new Map([["agent", { id: "agent", name: "Worker", adapterType: "paperclip_runner" } as Agent]])} />);
show(false);
expect(container.textContent).toContain("Confirm the existing result.");
- expect(container.textContent).toContain("Queued");
+ expect(container.textContent).toContain("Working");
+ expect(container.textContent).toContain("Thinking");
+ expect(container.textContent).not.toContain("Queued");
show(true);
expect(container.textContent).not.toContain("Thinking");
expect(container.textContent).not.toContain("Working");
@@ -616,7 +618,9 @@ it.each((["task", "legacy"] as const).flatMap(surface =>
try {
await act(async () => { send!.click(); });
expect(onAdd).toHaveBeenCalledTimes(1);
- expect(container.textContent).toContain("Queued");
+ expect(container.textContent).toContain("Working");
+ expect(container.textContent).toContain("Thinking");
+ expect(container.textContent).not.toContain("Queued");
const identity = container.querySelector('[data-testid="task-chat-agent-identity"]');
render();
expect(container.querySelector('[data-testid="task-chat-agent-identity"]')).toBe(identity);
@@ -757,7 +761,9 @@ it.each([null, "previous-agent"].flatMap(previousAgentId => ["paperclip_runner",
render();
await act(async () => acknowledge());
expect(container.querySelector('[data-testid="task-chat-agent-identity"]')).toBe(identity);
- expect(container.textContent).toContain("Queued");
+ expect(container.textContent).toContain("Working");
+ expect(container.textContent).toContain("Thinking");
+ expect(container.textContent).not.toContain("Queued");
// Choosing a recipient for the next draft does not change the sent request.
await act(async () => container.querySelector('[data-testid="task-chat-composer-assignee"]')!.click());
await act(async () => document.querySelector('[data-assignee-identity="agent:previous-agent"]')!.closest("button")!.click());
@@ -1681,7 +1687,7 @@ describe("TaskChatThread runtime transcript selection", () => {
const activity = container.querySelector('[data-testid="task-chat-current-activity-label"]');
expect(identity?.textContent).toBe("Runner");
expect(identity?.querySelector("svg, img")).not.toBeNull();
- expect(activity?.textContent).toBe("Queued");
+ expect(activity?.textContent).toBe("Thinking");
renderThread(props);
expect(container.querySelector('[data-testid="task-chat-agent-identity"]')).toBe(identity);
@@ -1697,7 +1703,9 @@ describe("TaskChatThread runtime transcript selection", () => {
expect(container.querySelector('[data-testid="task-chat-agent-identity"]')).toBe(identity);
expect(container.querySelector('[data-testid="task-chat-runner-identity-row"]')).toBe(header);
expect(container.querySelector('[data-testid="task-chat-current-activity-label"]')).toBe(activity);
- expect(activity?.textContent).toBe(startup.status === "queued" ? "Queued" : "Thinking");
+ expect(activity?.textContent).toBe("Thinking");
+ expect(header?.textContent).toContain("Working");
+ expect(container.textContent).not.toContain("Queued");
expect(container.querySelector('[data-testid="task-chat-live-run-pill"]')).toBeNull();
expect(container.textContent).not.toContain("run started");
}
diff --git a/ui/src/components/task-chat/TaskChatRunnerTurn.test.tsx b/ui/src/components/task-chat/TaskChatRunnerTurn.test.tsx
index 5fd1cc50f38..1c050084bbb 100644
--- a/ui/src/components/task-chat/TaskChatRunnerTurn.test.tsx
+++ b/ui/src/components/task-chat/TaskChatRunnerTurn.test.tsx
@@ -57,7 +57,7 @@ describe("TaskChatRunnerTurn", () => {
status={status}
execution={execution}
preparationLabel={preparationLabel}
- startedAtMs={Date.now() - 2_000}
+ startedAtMs={runId ? Date.now() - 2_000 : null}
awaitingContinuation={awaitingContinuation}
continuedAfterSteering={continuedAfterSteering}
onRuntimeRequestDecision={onRuntimeRequestDecision}
@@ -68,35 +68,46 @@ describe("TaskChatRunnerTurn", () => {
),
);
- it("shows admission waiting instead of Thinking for a queued turn", () => {
- render([], "queued");
- expect(container.querySelector('[data-testid="task-chat-turn-status-header"]')?.textContent).toContain("Queued");
- expect(container.querySelector('[data-testid="task-chat-current-activity-label"]')?.textContent).toBe("Queued");
- expect(container.textContent).not.toContain("Working");
- expect(container.textContent).not.toContain("Thinking");
- });
-
- it("shows queued startup before a run ID or preparation label exists", () => {
- render([], "queued", null);
- expect(container.querySelector('[data-testid="task-chat-turn-status-header"]')?.textContent).toContain("Queued");
- expect(container.querySelector('[data-testid="task-chat-current-activity-label"]')?.textContent).toBe("Queued");
- expect(container.textContent).not.toContain("Thinking");
- });
+ it.each([null, "run-1"].flatMap(runId => [false, true].map(projected => ({ runId, projected }))))(
+ "shows Working and Thinking during ordinary admission (runId=$runId, projected=$projected)",
+ ({ runId, projected }) => {
+ render([], "queued", runId, undefined, false, false, projected ? {
+ phase: "queued", label: "Queued", cause: null,
+ lastConfirmedActivityAt: null, retryAt: null, attempt: 1, maxAttempts: 3,
+ recoveryOwner: null, nextAction: null, permittedActions: ["inspect_run"],
+ predecessorRunId: null, successorRunId: null,
+ } : undefined);
+ expect(container.querySelector('[data-testid="task-chat-turn-status-header"]')?.textContent).toContain("Working");
+ if (!runId) {
+ expect(container.querySelector('[data-testid="task-chat-turn-status-header"]')?.textContent).toBe("Working");
+ }
+ const activity = container.querySelector('[data-testid="task-chat-current-activity-label"]');
+ expect(activity?.textContent).toBe("Thinking");
+ expect(activity?.classList.contains("shimmer-text")).toBe(true);
+ expect(container.textContent).not.toContain("Queued");
+ },
+ );
it("preserves a specific preparation label before a run ID exists", () => {
render([], "queued", null, undefined, false, false, undefined, "Preparing workspace");
expect(container.querySelector('[data-testid="task-chat-current-activity-label"]')?.textContent).toBe("Preparing workspace");
});
- it("shows the server's maintenance wait while admission is held", () => {
+ it.each([
+ ["task_drain", "Waiting for maintenance"],
+ ["agent_setup_pending", "Waiting for agent setup"],
+ ["agent_setup_failed", "Agent setup needs attention"],
+ ["server_restarting", "Waiting for server restart"],
+ ["previous_run_settling", "Waiting for previous run to stop"],
+ ])("shows the server's %s wait while admission is held", (cause, label) => {
render([], "queued", "run-1", undefined, false, false, {
- phase: "queued", label: "Waiting for maintenance", cause: "task_drain",
+ phase: "queued", label, cause,
lastConfirmedActivityAt: null, retryAt: null, attempt: 1, maxAttempts: 3,
recoveryOwner: null, nextAction: "Starts automatically when maintenance finishes.",
permittedActions: ["inspect_run"], predecessorRunId: null, successorRunId: null,
});
- expect(container.querySelector('[data-testid="task-chat-turn-status-header"]')?.textContent).toContain("Waiting for maintenance");
- expect(container.querySelector('[data-testid="task-chat-current-activity-label"]')?.textContent).toBe("Waiting for maintenance");
+ expect(container.querySelector('[data-testid="task-chat-turn-status-header"]')?.textContent).toContain(label);
+ expect(container.querySelector('[data-testid="task-chat-current-activity-label"]')?.textContent).toBe(label);
expect(container.textContent).not.toContain("Thinking");
});
diff --git a/ui/src/components/task-chat/TaskChatRunnerTurn.tsx b/ui/src/components/task-chat/TaskChatRunnerTurn.tsx
index 3ad93e30966..dad7616ece8 100644
--- a/ui/src/components/task-chat/TaskChatRunnerTurn.tsx
+++ b/ui/src/components/task-chat/TaskChatRunnerTurn.tsx
@@ -94,7 +94,7 @@ function RunnerTurnStatus({
const label = terminal ? (failed ? "Stopped" : "Worked") : waitingLabel ?? "Working";
const suffix = terminal
? elapsed ? ` ${failed ? "after" : "for"} ${elapsed}` : ""
- : waitingLabel ? elapsed ? ` · ${elapsed}` : "" : ` for ${elapsed ?? "0s"}`;
+ : elapsed ? `${waitingLabel ? " · " : " for "}${elapsed}` : "";
return (