Skip to content

Agent chat: one bottom stack, so the queue never covers the PR dock - #20521

Merged
teamleaderleo merged 37 commits into
feat-cmux-nextfrom
cg-lane-adopt/bottom-stack
Oct 14, 2026
Merged

teamleaderleo merged 37 commits into
feat-cmux-nextfrom
cg-lane-adopt/bottom-stack

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Leo saw the queued prompts drawn over the pull request dock and clipped. The queue was laid over the transcript's foot with absolute positioning, at a fixed offset above the card, so anything else above the composer collided with it.

Now everything under the transcript is one bottom stack in the layout flow (bottomStack.css). From top to bottom:

  1. the question waiting for an answer
  2. notices
  3. the PR dock
  4. then, inside the composer form: the queued prompts, the location strip and the card.

How it behaves:

  • Every part takes its own height, so none draws over another.
  • When the pane gets short, the transcript gives up height first, down to 96px. After that, the dock and the queue shrink to one row and scroll inside it.
  • The transcript already keeps a followed view at its end when the stack changes height, so queuing a prompt doesn't jump it.

The queue's published height (--acpmux-queue-space, its ResizeObserver and its JSDOM test) is removed. The flow itself now keeps the question above the queue clear (hq #1893). The new browser test covers that case.

This is the slot op-next-steering's queue/steer panel (#20391) plugs into, and the PR + subagent strip will join it next.

#20454 landed (e2a6fac). This branch takes base's strip and keeps only the bottom stack: the red test (07e8841) and the fix (f4675cf), replayed on 590d535.

Testing

  • agent-pane-bottom-stack.test.ts, run with CMUX_BROWSER_TESTS=1 bun test. Fixture: a running chat with a permission question, three PRs and six queued prompts. It checks for no overlap, no clipping and nothing over the transcript at 900×760; at 900×480 the transcript must also stay ≥96px and the queue must scroll.
    • Red at 07e8841: dock/queue overlap; in the short pane, the question was over the transcript.
    • Green at f4675cf.
  • 15 neighbouring browser suites pass (queue, steer, PR dock, permission, follow/jump, composer strip/edges/row).
  • bun run check passes.
  • After the rebuild on 590d535: typecheck passes, and 19 browser suites (128 tests) that touch the composer, the queue, the dock, the question stack or the permission card pass. The one red there, subtle-elevation on .lg-sheet, is base's (e111f3f in home-channels/ledger.css).

Recording

Before/after, recorded headless in Chromium from the bottom-stack test's fixture (the pane's dev entry and a stand-in acpmux daemon): 900×760, then the pane shrinks to 480. The native app recording is blocked on this host (the window is occluded). I watched both recordings frame by frame.

  • Before (base 590d535): the queued prompts draw over the PR dock at every height. In the short pane the question rides over the transcript.
  • After: the question, the dock, the queue, the strip and the card stack in order at every height. At 480 the transcript keeps its room and the queue scrolls in a one-row box.

Files on big-red:/tmp/cg-lane-adopt-dogfood/bottom-stack/: bottom-stack-before-after.mp4 (side by side) and {before,after}-{tall-760,short-480}.png.

Changelog

  • Fixed: Queued prompts in an agent chat no longer draw over the pull request dock or get clipped in a short pane.

🤖 Generated with Claude Code


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


Summary by cubic

Makes the agent chat's bottom area under the transcript one flow stack, so queued prompts no longer draw over the pull request dock or clip in short panes.

The stack runs: question waiting for an answer, notices, PR dock, then inside the composer the queued prompts, location strip and card. Every part takes its own height; in a short pane the transcript shrinks first (down to 96 px), then the dock and queue collapse to one scrolling row. The queue's published-height mechanism (--acpmux-queue-space) and its JSDOM test are gone, replaced by a browser test that checks nothing overlaps or clips.

Written for commit 2ce21bd. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 30 commits October 13, 2026 21:55
…r inset

hq#1957 composer controls row (item 15). A gallery entry (agent-pane.composer-row) draws
Leo's row (Claude Opus 5.5, ring, Medium, Auto), a long model name, the chat-wide
allowance chip and a queued running turn at 420, 720 and 1100 px. The browser test
measures it: one contiguous left cluster, free space only before mic/Send, no cut label
while the row has room, permission labels cut first and the model last, and the footer
inset from the card's bottom edge.

Red: CMUX_BROWSER_TESTS=1 bun test test/composer-row-layout.test.ts -> 0 pass, 12 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hq#1957 composer controls row (item 15). The permission chip was a fixed 180 px wide
behind a flex spacer, so the row kept a large empty gap while the model and effort chips
truncated, and below 480 px the model went icon-only before anything else.

- The chips hug their controls (+, model, ring, effort, permission mode, then the granted
  status); mic and Send/Stop keep margin-left: auto. Free space sits only between the two
  clusters. The permission chip stays in the left cluster with the other session settings;
  the right cluster stays the input's own actions.
- A short row drops the permission labels to their lock first (composerRowFit.ts measures
  the row and sets data-fit=icons; hidden labels stay laid out so the decision never
  toggles to measure), then effort shortens, the model last (flex-shrink weights 1e5:1
  with min widths). The 480 px icon-only model and fixed access widths are gone.
- The footer grows with its content (min-height, no fixed height), never shrinks, and keeps
  the bar's 12 px side inset and a 10 px bottom inset inside the card.

The composer-menu-width case that pinned the 180 px chip now checks the contract that
mattered: a mode change moves no other control.

Green: CMUX_BROWSER_TESTS=1 bun test test/composer-row-layout.test.ts -> 12 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hq#1957 composer controls row (item 15), layout-shift follow-up. A gallery entry
(agent-pane.composer-turn-cycle) opens a chat's first turn idle, before any context usage,
and its cycle play delivers running, one then three subagents, a subagent's permission
ask, a queued prompt and finished through the pane bridge. The browser test checks that
the prompt, controls row, footer and every control keep their boxes within 0.5 px at 420,
720 and 1100 px, and that typing during the turn (Stop -> Steer + Send) moves neither the
mic nor Send.

Red: CMUX_BROWSER_TESTS=1 bun test test/composer-turn-cycle.test.ts -> 0 pass, 6 fail
(the context ring mounts when the turn starts and shifts effort and the permission chip;
Steer lands between the mic and Send and pushes the mic left).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hq#1957 composer controls row (item 15), layout-shift follow-up.

- The context ring holds its slot from the session's start (inert until the first usage
  report, as ContextRing already draws it) instead of mounting when the turn starts, which
  slid effort and the permission chip 34 px right on every chat's first turn.
- Steer comes in at the leading edge of the right cluster, so when typing during a turn
  turns Stop into Steer and Send, the mic and Send keep their places (it used to land
  between them and push the mic left).

The queued-prompt stack, a subagent's permission ask and the PR dock already grow upward
over the transcript; Send and Stop share one 32 px circle.

Green: CMUX_BROWSER_TESTS=1 bun test test/composer-turn-cycle.test.ts -> 6 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xel band (hq#1957 item 15)

Red: bun test test/composer-row-fit.test.ts -> 1 pass, 2 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1957 item 15)

Hysteresis on the labels/icons switch so a sub-pixel squeeze cannot flip it every
frame; drops a leftover spacer from the dictation gallery.
Green: bun test test/composer-row-fit.test.ts -> 3 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…q#1957 composer location strip)

Unit test for the disambiguation rule: projects sharing a name each take the
fewest trailing folders of their path that no other of that name shares;
~ for the home folder; the parent folder when nothing else tells it apart.

Red: `bun test test/project-suffix.test.ts` -> 0 pass, 1 fail, 1 error
(projectSuffix.ts does not exist yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…composer location strip)

projectSuffixes(): projects sharing a name each take the fewest trailing
folders of their path that no other of that name shares at the same depth
(the name's own folder left out), the home folder as ~, and the parent
folder when nothing else tells them apart.

Green: `bun test test/project-suffix.test.ts` -> 8 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct row)

hq#1957 item 15 compact row. Leo's nightly row reads as toy blocks: a context ring that looks
like a spinner, uneven gaps, two locks (Auto and the "Trusted this chat's..." status chip) and a
large white Stop disc. The row-layout test now checks, at 420, 720 and 1100 px in every
variant: the left cluster's controls sit 8-12 px apart and within 1 px of each other; no ring
or spinner in the row; one lock at most; no granted-status chip; Stop's glyph is a square of
at most 12 px; and Stop takes Send's box (within 0.5 px) and its resting fill. The turn-cycle
test checks no ring or spinner, one lock and no status chip in every phase, and no longer
tracks the ring's slot.

Red: CMUX_BROWSER_TESTS=1 bun test test/composer-row-layout.test.ts test/composer-turn-cycle.test.ts
-> 3 pass, 30 fail (model-effort 36 px apart around the ring, effort-access 2 px; ring in the
row in every phase; 2 locks and the status chip in "granted"; Stop's fill rgb(255,255,255)
against Send's grey).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…omposer location strip)

Chromium layout tests on new gallery entries (agent-pane.composer-strip:
project, no-project, host-loading, queued, awaiting, reported; and
agent-pane.composer-context duplicate-worktrees / duplicate-search) plus the
New Tab's `empty` page:
- the strip's bottom edge is the card's top edge (within 1 px), chat and
  New Tab, at 420 and 720 px; the card holds one controls row and no
  location row;
- the strip is as tall with no project, a project and while the host loads;
- queued prompts sit above the strip;
- no skeleton/placeholder while effort and access load, and the controls
  keep their x positions when the values arrive;
- the folder popover suffixes duplicate names, keeps the path for hover,
  checks the current project, filters as typed, ends with Choose folder….

Red: `CMUX_BROWSER_TESTS=1 bun test test/composer-location-strip.test.ts`
-> 0 pass, 10 fail (no strip; the folder menu has no search or suffixes;
effort moves 34 px when the session reports, as the context ring arrives).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t row)

hq#1957 item 15 compact row. The row is "+ · model · effort · access" on the left and
mic, then Send/Stop on the right, with free space only between the two.

- Gaps: the chips sit the bar's 8 px apart (they were 2 px, plus the ring's 36 px slot), so
  the left cluster's gaps are all 8 px. Model, effort and access are the bar's regular 28 px
  (were 30) with tighter padding.
- Hover: the shared quiet wash (ui.css --ui-hover's fallback, the control's ink at 6% over
  the card; 12% on press) tight to the control, in place of the opaque composer-hover block.
  Mic, Plan and Steer use it too.
- No context ring in the row. This undoes the earlier fixed ring slot. The chat's usage is
  in the model chip's tooltip ("Opus 5.5 · 42% of context used", the existing picker.context
  string), shown while showContextUsage is on. The row's right-click no longer offers
  Hide/Show Context Usage, which has no control to act on now. Compact stays reachable as
  /compact. ContextRing itself and its gallery are kept.
- One lock: the granted-permission status chip (PermissionStatus) is no longer placed in the
  row. The component and its gallery stay. Trust undo and chat-allowance revoke keep their
  other entry points in App.tsx.
- Stop is Send's 28 px circle at its resting fill (the accent at 34% over the base), with a
  7 px square in the text color, not a white disc. Its hover and press step the circle.
- composerRowFit.ts no longer watches the status chip. Galleries drop the ring plays
  (composer context-usage-menu, picker context-details/context-focus).

Green: CMUX_BROWSER_TESTS=1 bun test test/composer-*.test.ts -> 53 pass, 0 fail
(composer-row-layout + composer-turn-cycle: 33 pass, 0 fail). bun run check -> pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…popover with search (hq#1957 composer location strip)

- The folder · computer row (and the branch) leaves the card's bottom:
  ComposerContext always draws `.acpmux-composer-strip`, a tab on the
  card's top edge with the card's fill and edge, a 10 px top radius, inset
  by the card's corner radius and a fixed 30 px height. The card keeps one
  controls row. Composer drops `contextFirst`; App stops passing it.
- New Tab: the strip sits on `.nt-box`'s top edge inside `.nt-composer`.
- Queued prompts stack above the strip at its insets.
- FolderPopover (new): the shared Combobox in a Popover. Rows show the
  project name plus a suffix only for duplicate names (projectSuffix.ts),
  the path on hover, a check on the current folder; the search filters by
  name, suffix and path; arrows reach New Worktree, Add project and Choose
  folder… after the folders; Escape returns to the strip. The started
  chat's folder combobox uses the same suffixes and tooltip.
- Hooks shared by the location menus move to locationPopover.ts.
- Tests and plays that read the old footer or menu roles follow the strip
  and the popover (composer-row-layout, new-tab-composer-layout,
  new-tab-add-project-layer, chat-new-worktree,
  started-chat-folder-new-chat, start-folder and composer-context plays).

Green: `CMUX_BROWSER_TESTS=1 bun test test/composer-location-strip.test.ts`
-> 9 pass, 1 fail. The remaining red is the effort/access x-position
check: the context ring joins the row when the session reports and moves
effort 34 px; the updated base (cg-lane-adopt/composer-row) takes the ring
out of the row, merged next.
`bun run check` passes; composer-row-layout and composer-turn-cycle pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…7 composer location strip)

The base's compact row (no context ring or status chip in the row, even
gaps, Stop as Send) merges cleanly but for composer-row-layout.test.ts,
where both sides changed Row: keep the base's new compact-row fields and
the strip's checks, drop the old footer-inset fields.

After the merge:
`CMUX_BROWSER_TESTS=1 bun test test/composer-location-strip.test.ts` -> 10 pass, 0 fail
(the effort/access x-position check is green now that the ring left the row);
23 related files (composer-row-layout, composer-turn-cycle, location side,
menus, New Tab, gallery coverage/play, ui-rules, ...) -> 116 pass, 0 fail.
`bun run check` passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lass (hq#1957 border hunt)

Red: CMUX_BROWSER_TESTS=1 bun test test/composer-edges.test.ts -> 0 pass, 5 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…asured at rest (hq#1957 border hunt)

Red (New Tab box): CMUX_BROWSER_TESTS=1 bun test test/composer-edges.test.ts -> New Tab box: no drop shadow fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ass shadow and rim (hq#1957 border hunt)

The composer card keeps only its --acpmux-composer-edge hairline (clear under
appearance.borders = none); permission cards take the same hairline and no shadow; the New
Tab box loses its drop shadow at rest and on focus. Focus still steps the edge toward the accent.
Green: CMUX_BROWSER_TESTS=1 bun test test/composer-edges.test.ts -> 6 pass.
Related composer/New Tab/permission suites: 132 pass, 1 fail (new-tab-chat-title-width,
fails identically on the base).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1957 border hunt)

The permission panel's gallery entry loaded only styles.css and sat outside .acpmux-shell, so
the card's edge rules (composerLocation.css, --acpmux-composer-edge) never applied and its edge
checks passed vacuously. The entry now loads the pane's stylesheets inside the shell, and a guard
keeps the card's one .5px hairline (Leo: a thin Liquid Glass rim is good, don't strip it to zero).
Red on the pre-fix CSS (1bebce0): 1 pass, 6 fail. Green: 7 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38521546851
…-location-strip

# Conflicts:
#	webviews/src/agent-session/acpmux/composerLocation.css
…(hq#1957 composer location strip)

Red: CMUX_BROWSER_TESTS=1 bun test test/composer-location-strip.test.ts -t seam -> 0 pass, 2 fail
(the chat card's top hairline 24 levels off the fills around it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o seam hairline (hq#1957 composer location strip)

The card's rim (resting, focused, shell, Increase Contrast) is --acpmux-card-ring; under a strip the
card draws it on ::before with the strip's stretch masked out, so the card's edge meets the
strip's sides. The strip takes the card's edge color (clear under appearance.borders = none),
and the strip inset lives on the composer so the card knows it. Chat composer and New Tab.
Green: CMUX_BROWSER_TESTS=1 bun test test/composer-location-strip.test.ts test/composer-edges.test.ts
-> 19 pass. Composer/New Tab/permission suites: 154 pass, 1 fail (new-tab-chat-title-width, fails
on the base too). bun run check passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pt/composer-location-strip

# Conflicts:
#	webviews/src/agent-session/acpmux/modelPickerLayout.ts
- vp fmt webviews/src/agent-session/acpmux/App.tsx
- vp fmt webviews/src/agent-session/acpmux/subagents/subagents.gallery.ts
- vp fmt webviews/src/agent-session/acpmux/summary/SubagentStatusList.tsx
- vp fmt webviews/src/agent-session/acpmux/summary/cardState.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38524642314
…pt/composer-location-strip

# Conflicts:
#	webviews/src/agent-session/acpmux/newtab/screen.css
…-next merge (hq#1957 item 15)

The merge took feat-cmux-next's modelPickerLayout.ts and dropped ModelPickerProps.usage, which
the compact row passes for the context-usage tooltip (TS2339 in web / react-apps-check).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated schemas/defaults/cmux-next.json
- vp fmt webviews/src/pages/home-channels/ConversationList.tsx
- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx
- vp fmt webviews/src/pages/home-channels/ThreadSheet.tsx
- vp fmt webviews/src/pages/home-channels/daemonHome.ts
- vp fmt webviews/src/pages/home-channels/model.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38535029393
The summary card's raw z-index goes into the layers baseline (op-next-toolbar is
reworking that card), and its auto-open checkbox gets the label the lint wants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…strip' into cg-lane-adopt/composer-location-strip
Red: the queued prompts are laid over the transcript's foot, so with pull
requests docked above the composer the queue draws over the dock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
teamleaderleo added a commit that referenced this pull request Oct 14, 2026
Base's one bottom stack (#20521) already puts the queue in the page's
flow above the composer, which this branch did with its own queue slot.
Kept base's structure (no queue slot, no floating queue) and this
branch's content: the Queued header, Steering vs Queued, the steer
landed state and the readable two-line rows. Catalog rebuilt from base
text plus this branch's six keys; strings regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 14, 2026
Since #20521 the queue sits in the composer form, under the pull request
bar and over the location strip; the tests still wanted it above the bar.
The steer test waits for the composer to empty after each Enter: the
prompt stays until the host takes it, which can land after its row draws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant