## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - In task chat, a user can queue messages while an agent run is
active. The queued messages show in a strip above the composer, with
Steer, Interrupt, Discard and Edit actions.
> - An onboarding test on staging reported that the steering strip
clipped and covered nearby UI.
> - The strip adds one 44px row for each queued message and has no
height limit. With several queued messages it takes most of the thread.
In the Coordinator layout the dock is absolutely positioned at the
bottom of the thread, so the strip grows up over the conversation.
> - This pull request puts the rows in a bounded list that scrolls. The
status and error lines stay below the list.
> - The benefit is that the conversation stays readable at desktop and
narrow widths, and every queued message stays reachable.
## Linked Issues or Issue Description
**What happened?**
With several queued messages, the queued/steering strip above the task
composer grew without a limit. It covered the transcript and nearby
components. At a 1024x600 window, 8 queued messages made the strip 353px
tall in a 555px thread.
**Expected behavior**
The strip stays readable and inside the available space at normal and
narrow widths. Extra queued messages scroll inside the strip.
**Steps to reproduce**
1. Open a task with an active run in the Coordinator workspace.
2. Queue 6 or more messages.
3. See that the strip covers most of the conversation.
**Paperclip version or commit**
`master` at 03eb49053f. Also seen on a staging cloud deployment during
an onboarding test (BUG-04, P2).
## What Changed
- `TaskChatQueuedMessages`: wrap the sortable rows in a list with
`max-h-(--tc-queued-list-max-h) overflow-y-auto overscroll-contain`.
Status and error lines stay outside the list, so they are always
visible.
- `ui/src/index.css`: new token `--tc-queued-list-max-h:
min(calc(var(--spacing) * 40), 28dvh)`. This is about 3.5 rows, and less
on short windows.
- New Storybook story `coordinator-dock-queued-messages`: the production
`TaskChatComposerDock`, queued strip and composer inside the Coordinator
thread layout, with 8 long queued messages (light, dark, phone).
- New Playwright spec
`tests/storybook-visual/queued-messages-dock-layout.spec.ts`: measures
the dock and strip against the header, thread and viewport at desktop,
desktop dark, short desktop (1024x600), very short desktop (1024x420)
and phone sizes. It also checks that the last queued Steer action is
reachable by scrolling, and that the latest transcript message is not
under the dock. A separate test drags the first queued row below the
third and checks the new order inside the scrolling list.
## Verification
Red first (spec run on unchanged `master` code, Storybook dev server):
```
STORYBOOK_TEST_BASE_URL=http://localhost:6016 npx playwright test --config tests/storybook-visual/playwright.config.ts --project=stories tests/storybook-visual/queued-messages-dock-layout.spec.ts --retries=0 --workers=1
✘ 1 … queued messages stay bounded inside the composer dock (desktop)
✘ 2 … queued messages stay bounded inside the composer dock (desktop dark)
✘ 3 … queued messages stay bounded inside the composer dock (short desktop window)
✘ 4 … queued messages stay bounded inside the composer dock (narrow phone)
Error: queued strip covers the conversation
Expected: <= 286
Received: 353
…
Error: queued strip covers the conversation
Expected: <= 222
Received: 353
4 failed
```
Green after the fix (same command):
```
✓ 1 … queued messages stay bounded inside the composer dock (desktop) (14.8s)
✓ 2 … queued messages stay bounded inside the composer dock (desktop dark) (3.3s)
✓ 3 … queued messages stay bounded inside the composer dock (short desktop window) (2.1s)
✓ 4 … queued messages stay bounded inside the composer dock (narrow phone) (2.3s)
4 passed (25.7s)
```
Second round, after review feedback that a fixed 160px cap does not
shrink on very short windows. Red with the fixed `max-h-40` cap:
```
✓ 1 … (desktop)
✓ 2 … (desktop dark)
✓ 3 … (short desktop window)
✘ 4 … (very short desktop window)
✓ 5 … (narrow phone)
✓ 6 … queued messages still reorder by drag inside the scrolling list
Error: queued strip covers the conversation
Expected: <= 150
Received: 161
1 failed
5 passed (18.6s)
```
Green with the `--tc-queued-list-max-h` token:
```
✓ 1 … (desktop) (6.2s)
✓ 2 … (desktop dark) (1.9s)
✓ 3 … (short desktop window) (3.3s)
✓ 4 … (very short desktop window) (1.3s)
✓ 5 … (narrow phone) (1.4s)
✓ 6 … queued messages still reorder by drag inside the scrolling list (1.5s)
6 passed (16.1s)
```
Measured layout (strip height / dock top in a thread):
| Case | Before | After |
| --- | --- | --- |
| 1245x760 dark | strip 353px, dock top 272 | strip 161px, dock top 464
|
| 1024x600 | strip 353px, dock top 112 | strip 161px, dock top 304 |
| 390x700 phone | strip 353px, dock top 228 | strip 161px, dock top 420
|
Neighbouring suites and gates:
- `cd ui && npx vitest run src/components/task-chat/`: 55 files, 884
tests passed (includes `TaskChatQueuedMessages.test.tsx`).
- `pnpm check:token-gates`: All gates clean.
- `node scripts/storybook-catalog.mjs`: in sync.
Screenshots (1024x600, then 390x700; taken with the first-round 160px
cap, which is the same height at these sizes):
| Before | After |
| --- | --- |
| 
| 
|
| 
| 
|
## Risks
- Low risk. The change is one wrapper element with token-scale Tailwind
classes.
- Drag to reorder now happens inside a scroll container. The new spec
covers a drag reorder. dnd-kit auto-scrolls the container during a drag.
A dragged row can be clipped at the list edge while it moves, as before
at the strip edge.
- The original report mentioned a "black bar". The original screenshot
was not available. I reproduced the overlap and the unbounded growth. I
did not see a separate black bar in light or dark theme. The row action
menu uses a Radix portal, so it does not clip.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use
(shell, browser, Playwright).
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Storybook Visual Baselines
The visual suite compares built Storybook stories against PNG snapshots stored
outside git. The checked-in manifest at baseline-manifest.json pins the
immutable archive URL, SHA-256, byte size, snapshot count, and capture
environment.
Commands
pnpm storybook-visual:baseline download
pnpm storybook-visual:baseline verify
pnpm test:storybook-visual
pnpm test:storybook-visual:update
download fetches the archive, verifies its SHA-256 and byte size, unpacks it to
tests/storybook-visual/.snapshots/, and checks the PNG count. The same snapshot
directory can be overridden with STORYBOOK_VISUAL_SNAPSHOT_DIR.
Known Limitation: Linux Baselines
Storybook visual baselines are platform-locked. The checked-in manifest records
the capture environment as ubuntu-24.04, and Playwright compares screenshots
with maxDiffPixels: 0. Pixel-exact results are only meaningful when local runs
use the same Linux/Ubuntu capture platform as the baseline.
macOS, Windows, and other non-matching local environments can produce
false-positive diffs from font rasterization and subpixel rendering differences.
Use the Storybook Visual GitHub Actions workflow on ubuntu-latest as the
source of truth for cross-platform review, or run the suite locally in a matching
Linux environment before accepting or updating baselines.
CI and Review Artifacts
Storybook visual tests are opt-in while the suite stabilizes. Add the
storybook-visual label to a pull request, or run the Storybook Visual
workflow manually, to download the pinned baseline, build Storybook, and run the
Playwright visual suite on GitHub Actions.
The workflow uploads tests/storybook-visual/playwright-report/ and
tests/storybook-visual/test-results/ as a storybook-visual-report-* artifact
on every run. When screenshots differ, Playwright writes the actual, expected,
and diff PNGs into test-results, so reviewers can inspect the failure without
rerunning the suite locally.
Normal PR visual runs use repository read-only permissions and never upload or
modify baseline objects. To review intentional visual changes before updating
baseline-manifest.json, run the workflow manually with update_snapshots
enabled. That produces a storybook-visual-baseline-review-* artifact containing
the packed candidate snapshot archive for review. Publishing that bundle to the
baseline bucket still requires the explicit maintainer upload command below.
Updating Baselines
- Run
pnpm test:storybook-visual:updateafter reviewing intentional visual diffs. - Run
pnpm storybook-visual:baseline packto createtests/storybook-visual/baseline-review/snapshots.tgz. - Upload the archive from a trusted maintainer environment with
STORYBOOK_VISUAL_S3_URI=s3://bucket/baselines/storybook-visual/<sha>/snapshots.tgz pnpm storybook-visual:baseline upload. - Copy the printed
snapshotCountandarchivefields intobaseline-manifest.json.
Generated snapshots, review bundles, Playwright reports, and downloaded caches are ignored by git.