Agent chat: readable queue, Steering vs Queued, steer landed state
Leo saw the agent chat's queued prompts as a numbered strip, cut to one line and drawn under the pull request bar. You couldn't tell what was queued from what was sent as steering, and nothing showed when a steer landed. A live run on a tagged DEV build with a real Claude session (queue mid-turn, steer with ⌘Return, send after the turn) found one more: a prompt queued with Return drew as a transcript bubble for about 0.3 s before it moved into the queue.
What changes
- Queue placement: the queue sits above the pull request bar, under a "Queued · Sends after this turn" header.
- Full text: a queued prompt opens to its whole text on click. acpmux lists only a 200-character preview; the pane keeps the text from the
queuedrecord. - Steering vs queued: a prompt sent now to an agent that cannot steer reads "Steering" in the queue, apart from the numbered queued prompts.
- Steer marks: a steer's bubble reads "Steering" until the agent reads it, then "Steered · the agent read it". A prompt sent now reads "Sent now · stopped the turn". Two steers in one step each wait for the agent.
- No transcript flash: a prompt sent behind a running turn shows in the queue from the send on. If acpmux runs it at once instead, its bubble draws and the queue row goes. A refused one gets its failed bubble back, only in the chat it was sent in.
Scroll pinning and the transcript's room for the queue belong to the lane that owns chat scrolling. I sent it the live evidence and left both alone here.
Leo's two asks (2026-10-14)
- No fade behind a queued row's actions. The gradient behind
.acpmux-queued-actionsis gone. The actions sit in the row's own third grid column, so they never cover the text. This was already true on this branch since e9bb5cb. A WebKit test now guards it: no background image or backdrop blur on the actions or the row, and the actions don't overlap the text. - A long queue folds. With more than three queued prompts, the queue shows the next two, then one quiet row reading "N more queued". The string is plural-aware in all 21 locales. That row opens the whole list in place and changes to "Show less", which closes it again.
- The row is a button with
aria-expanded. Return and Space toggle it, and ↑ from the empty prompt reaches it first. ↑ and ↓ move between it and the rows, and ↓ or Esc goes back to the prompt. - Numbering and order stay the same, and Steering prompts always show.
- Moving a row into the folded part (⌥↓) opens the list. When a row leaves a folded queue, focus goes to the next prompt.
- Open or closed is remembered per chat for the life of the page, not across reloads.
- There's no motion and no new default keys.
- The
queued-longgallery variant now shows the folded queue. The newqueued-long-openvariant has a play step that opens it.
- The row is a button with
Red, then green, on the same focused command, CMUX_BROWSER_TESTS=1 heavy --mem 6G bun test test/agent-pane-queue-collapse.test.ts (WebKit):
- Red at 2c53dc3: 1 pass (the no-fade guard), 2 fail (no
.acpmux-queue-fold). - Green at 90721dc: 3 pass. I also corrected the test's prompt-focus check to read the focused field through
closest(".acpmux-composer-prompt").
test/queue-row-actions.test.tsx was failing on this branch, and the failure was ours: this PR made the row text a button, and the test counted it as an action. It now reads only .acpmux-queued-action, and it passes.
Also run on 90721dc: typecheck, vp fmt --check, bun run check (lint and ratchets), test/ui-rules.test.ts, gen-strings --check, check-l10n.sh (0 errors), and one at a time agent-pane-queue (6 pass), agent-pane-queue-steer (7 pass) and agent-pane-steer (5 pass).
Research credit: lane cg-next-reds (queued-messages research covering T3 Code, Codex and Claude Code) and lane cg-queue (its collapsed-queue draft). Their fold-at-two "Queued" header, Cancel-on-edit, drag grip and new default keys (⌘⇧↩, ⌥↑) aren't in this PR. Leo asked for the next two rows plus "N more", and default key changes need a design call.
Follow-up from the real-app capture at 90721dc. With 5 queued, the opened list stayed capped at 132px. Row 4 was cut through its text, and row 5 and the focused "Show less" were below the visible edge.
The fix:
- When open, the list drops the cap and takes the full height of its rows; the transcript gives way first, down to its minimum.
- The fold row sticks to the list's bottom edge, over anything that scrolls under it.
scroll-padding-bottomkeeps a focused row clear of the fold.
Red, then green, on the same command, CMUX_BROWSER_TESTS=1 heavy --mem 6G bun test test/agent-pane-queue-collapse.test.ts. The new test opens 8 queued prompts and checks:
- "Show less" sits inside the list and the page;
- the focused element is in sight;
- no visible row is cut;
- the same holds at every ↑ and ↓ step through all the rows.
Results:
Also green at 935dada, each run with pipefail and its exit status checked: bun run check, vp fmt --check, typecheck, lint, ui-rules, queue-row-actions, agent-pane-queue (6), agent-pane-queue-steer (7) and agent-pane-steer (13).
Limitation: in a pane too short for every row, the rows scroll under the pinned fold, and a row can be cut at the list's top edge. Nothing snaps the scroll to whole rows.
Second follow-up, from the capture at 935dada. Two problems showed up:
- In a folded queue, a row opened to its full text ran under the pinned fold.
- Opening 8 queued prompts shrank the transcript from 312 to 156 px in one step.
The rework:
- The fold ("N more queued" / "Show less") is now its own line below the list, outside the scrolling area, so no row can run under it.
- When opened, the list stays close to its folded height: a pinned "Queued" header over three whole rows. It scrolls with mandatory snapping to whole rows and
scroll-padding, so a focused row always shows in full. Opening the fold moves the chat above by one row at most. - This replaces the earlier full-height growth and the pinned fold row inside the list.
Red, then green, on CMUX_BROWSER_TESTS=1 heavy --mem 6G bun test test/agent-pane-queue-collapse.test.ts:
- Red at 648f33a: 3 pass, 3 fail. The fold sat inside the list, it was not clear of an opened row, and the composer grew 156 px (the limit is 40).
- Green at 436917b: 6 pass.
Also green at 436917b, each with pipefail and its exit code checked: typecheck, vp fmt --check, vp lint, bun run check, ui-rules, queue-row-actions, gen-strings --check, agent-pane-queue (6), agent-pane-queue-steer (7) and agent-pane-steer (13).
Superseded limitation: the short-pane note above (one follow-up up) no longer applies. The opened list now has a fixed height and snaps to whole rows.
Third follow-up, from the capture at 436917b. It showed three bugs:
- After ↑↑↑ in the opened queue, row 5 drew over the pinned "Queued" header.
- Opened with a two-line first row, the list showed a sliver of row 3.
- In the folded queue with a long row opened, a sliver of row 2 showed above the fold.
The fix:
- The list now sets its own height to the bottom of the last row that fits under its cap. Folded, that cap is the CSS cap. Opened, it's the bottom of the third row, measured from the rows' real heights.
- While the list scrolls, a row its edges cut is not drawn.
- A row that takes focus scrolls in whole, starting at a row's top; the first row brings its header with it.
- The header no longer pins.
- Rows keep their own height in the capped grid (
grid-auto-rows: max-content). WebKit had shrunk an opened row's track, so its text ran over the next row.
Red, then green, on CMUX_BROWSER_TESTS=1 heavy --mem 6G bun test test/agent-pane-queue-collapse.test.ts:
- Red at 7f07af3: 6 pass, 2 fail, both on rows cut or spilling.
- Green at f1a737b: 8 pass.
- The tests treat a row the list does not draw (opacity 0) as hidden, not cut.
Also green at f1a737b, each run with pipefail and its exit code checked: typecheck, vp fmt --check, vp lint, bun run check, ui-rules, queue-row-actions, gen-strings --check, agent-pane-queue (6), agent-pane-queue-steer (7) and agent-pane-steer (13).
Fourth follow-up, from the capture at f1a737b. It showed two problems:
- The first frame after opening showed rows 2–5, with the header and row 1 scrolled away.
- Rows the list had hidden because they were cut showed as blank slots.
The fix:
- Opening now sets the list's
scrollTopto 0. - The snap inset equals the list's padding, so the header's top is the snap point.
- Only an opened queue snaps. Folded, a row opened to its full text had re-snapped the list off its header.
- Cut rows are no longer hidden. A sliver while scrolling is normal; a blank slot isn't.
- The list ends at a row's bottom, never in its own padding, where a 4 px strip of the next row showed.
- The header and the next prompt always show whole, even when that prompt is opened past the cap.
- The tests ask for whole rows only when the list rests at its top. While scrolled, they check that the focused row is whole and nothing covers the header.
Red, then green, on CMUX_BROWSER_TESTS=1 heavy --mem 6G bun test test/agent-pane-queue-collapse.test.ts:
- Red at 62b85da: 8 pass, 1 fail. After opening,
scrollTopwas 68, the header and row 1 were out of sight, and 5 rows were at opacity 0. - Green on the merged head caecb81, which includes a base merge another agent pushed: 9 pass.
Also green on caecb81, each run with pipefail and its exit code checked: typecheck, vp fmt --check, vp lint, bun run check, ui-rules, queue-row-actions, gen-strings --check, agent-pane-queue (6), agent-pane-queue-steer (7) and agent-pane-steer (13).
Verification
Each behavior has a replay test committed red, then green:
test/agent-pane-queue-steer.test.ts: queue above the PR bar, full text, Steering label, steer marks, two steers in one step.test/agent-pane-steer.test.ts: no transcript bubble before the queue row.
Run green on the head, one file at a time:
agent-pane-queue-steer,agent-pane-steer,agent-pane-queueandagent-pane-steer-split;bun run check,test/ui-rules.test.tsand the pane's unit tests.
Fixes found in live runs, red then green:
- Red on
133ed42, green on9331b7c:CMUX_BROWSER_TESTS=1 bun test test/agent-pane-queue-steer.test.ts -t "WebKit a queued prompt"(closed height 90 px, expected ≤ 37) and-t "sent now to an agent that cannot steer"(no "Steering" row in 10 s). - Red on
a38bc98, green ond312d58:CMUX_BROWSER_TESTS=1 bun test test/agent-pane-queue.test.ts -t "Return sends a queued prompt now"(focus never reached the next row).
Localization: 6 new strings, translated in all 21 locales, with gen-strings rerun.
Left indent: root cause
Each block inserted above the composer card added its own horizontal inset on top of the chat column. The location strip and the queue tray used --acpmux-strip-inset (18px, 20px on New Tab), the queue added its own 16px margin, and the notes had a hard-coded 18px margin. Everything else (transcript, permission and trust cards, PR dock, the card) already sat on the shared --cv-inset column edge. Now every inserted block shares that edge: the strip is the card's full-width head with the card's top corners, and the queue, trust/remote note, dictation notice and import error start on the column's left edge. test/agent-pane-column-edges.test.ts measures the key blocks against the column's left edge in real Chromium and WebKit.
Steering fix
Send now went through the queue's leaving path, which hid the row until the request failed, and it never failed. So no Steering row showed, and nothing appeared until the stopped turn ended. A Steering label in the mark column also pushed every row's text to 71px; it now leads its prompt's text.
Captures
After at caecb81 (real app)
cmux NEXT DEV build of caecb81 on a capture mini, real Claude through the lane's routing, and acpmux's test agent slowed to stop for the sent-now rows. The current head 77017ea only adds a base merge. Stills are cropped to the queue or the pane; there's no host name in any of them. The earlier captures below still hold the before images.
Measured in the run: opening 8 queued shrinks the transcript by 22 px (444 → 422 CSS px); the first frame and the 0.5 s frame match. After ↑↑↑, a sliver of the row above can show at the list's top edge while scrolled; the focused rows are whole.
| light | dark | |
|---|---|---|
| 8 queued, folded | ![]() | ![]() |
| ↑ focuses the fold | ![]() | ![]() |
| Return opens: header, row 1, rows 2–3 whole, Show less | ![]() | ![]() |
| Open, after send-now on row 2: still at the top | ![]() | ![]() |
| Open, after Delete: still at the top | ![]() | ![]() |
| ↑↑↑ into the rows | ![]() | ![]() |
| Show less: folded again | ![]() | ![]() |
| Folded, long row opened (clear of the fold) | ![]() | ![]() |
| Row hover | ![]() | ![]() |
| Keyboard Delete, focus stays on a row | ![]() | ![]() |
| Opening, first frame | 0.5 s later |
|---|---|
![]() | ![]() |
| light | dark | |
|---|---|---|
| Steer with ⌘Return: Steering | ![]() | ![]() |
| Both steers: Steered | ![]() | ![]() |
| Send now, agent can't steer: Steering row | ![]() | ![]() |
| Turn stopped: Sent now · stopped the turn | ![]() | ![]() |
Clips (dark):
| open and scroll | long row in a folded queue | send now |
|---|---|---|
| | |
Earlier captures (before → after)
Real cmux NEXT DEV builds on a capture mini, with acpmux's test agent, which cannot steer. Before: base ba0ea854. After: this PR at 2eae4edc (New Tab, new chat and queued focus at 776f7090, which has the same composer look). The window is cropped to the agent pane and the host name chip is blanked.
Send now to an agent that can't steer, 0.4 s after the click. Before, the prompt just leaves the queue. After, it shows as Steering above the queue.
| before → after | |
|---|---|
| narrow, light | ![]() |
| narrow, dark | ![]() |
| wide, light | ![]() |
After the stopped turn ends, only the head marks the bubble "Sent now · stopped the turn".
| before → after | |
|---|---|
| narrow, light | ![]() |
| narrow, dark | ![]() |
Clip: queue two prompts, Send now the second, then the stopped turn ends.
| before | after |
|---|---|
| |
The join. In a sent chat the queue is the card's head; in a new chat the location strip is.
| before → after | |
|---|---|
| queue, at rest | ![]() |
| queue, focused | ![]() |
| new chat, at rest | ![]() |
| new chat, focused | ![]() |
| new chat, wide | ![]() |
| New Tab 420px, dark | ![]() |
| New Tab 420px, light | ![]() |
Known: rest and focus barely differ on both base and these builds; that's the composer's quiet focus and out of scope. Head 819b2989 merges base's focus change, which makes the focused rim visible.
Gallery: agent-pane.composer queued-paused, 1100 and 420, light and dark
Rendered at 776f7090 (before: ba0ea854). The builder lane was out of disk for a re-render at 2eae4edc, so queued-steering is left out here: the 776f7090 render predates the Steering label fix. The in-app stills above show it.
| before → after | |
|---|---|
| queued-paused, 1100, light | ![]() |
| queued-paused, 1100, dark | ![]() |
| queued-paused, 420, light | ![]() |
| queued-paused, 420, dark | ![]() |
Changelog
- Fixed: Queued prompts in agent chat show above the pull request bar and open to their full text. Steering prompts are labeled apart from queued ones, a steer's bubble shows whether the agent has read it, and a queued prompt no longer flashes into the transcript. A long queue shows its next two prompts and folds the rest behind an "N more queued" row that expands in place.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Makes agent chat's queued prompts readable and separates steering from queued prompts, showing when a steer lands. A prompt sent behind a running turn now appears in the queue from the send on instead of flashing into the transcript first.
Queue behavior
- Queued prompts sit above the pull request bar under a "Queued" header, show two lines, and open to their full text on click.
- A steer's bubble reads "Steering" until the agent reads it, then "Steered"; a prompt sent now to an agent that cannot steer reads "Steering" in the queue and "Sent now · stopped the turn" once it runs.
- A long queue shows the next two prompts and folds the rest behind an "N more queued" row that expands in place; the fold sits on its own line below the list, so no row runs under it.
Every block in the chat column now starts on the column's left edge, with the location strip as the composer card's full-width head sharing a single rim. Attach asks acpmux for queue_order and queue_updated by kind so sent-now marks read live; the new strings are translated in all 21 locales and the behaviors are guarded by real-browser tests in Chromium and WebKit.













































