Skip to content

test(studio): keep the chat status pulses as status dots, not Skeletons - #2358

Merged
peppescg merged 1 commit into
mainfrom
refactor/studio-skeleton-2337
Oct 15, 2026
Merged

peppescg merged 1 commit into
mainfrom
refactor/studio-skeleton-2337

Conversation

@peppescg

@peppescg peppescg commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

#2337 asks for the Skeleton primitive (components/ui/skeleton.tsx) wherever animate-pulse marks content that is still loading. This PR reviews the three call sites that no other open PR touches. None of them is a loading placeholder. Each is a status dot that pulses while something runs, so no code changes. The PR records that decision and adds tests that pin it, so a later sweep can't turn these dots into Skeletons by mistake.

Part of #2337
Part of #2201
Part of #2210

Decisions

File Occurrence What it marks Decision
features/chat/delegation-card.tsx:54 StateGlyph: size-2 animate-pulse rounded-full bg-brand A subagent, parallel group, or branch is running. Once it settles, the dot becomes an alert icon or a branch icon. Status: keep.
features/chat/fleet-status-chip.tsx:132 size-1.5 animate-pulse rounded-full bg-brand in a chip segment That family (subagents, parallel, teams) has running work (segment.running). Status: keep.
features/chat/session-details-dialog.tsx:43 STATE_DOT.running: bg-brand animate-pulse The session's lifecycle state is running. The other states use static coloured dots. Status: keep.

Each dot is decorative (aria-hidden). The state is spoken as text beside it ("Running", the segment's aria-label, or "running"), and the dot disappears once the work settles. The prototype (stack-08) shows the same pulsing dots on all three surfaces; see the Prototype column below.

The details view's real loading state ("Loading details…") is a spinner (Loader2 animate-spin) in a role="status" paragraph, not an animate-pulse placeholder, so #2337 doesn't cover it. It stays unchanged, and is pinned too.

Later: these files from the issue are in open PRs. Each one gets its decision once that PR lands:

File Open PR
features/chat/chat-composer.tsx #2331
features/chat/delegation-panel.tsx #2334
features/chat/reasoning-disclosure.tsx #2352
features/chat/tool-call-list.tsx #2321

What changed

  • features/chat/status-pulse.pinning.test.tsx (new, 4 tests), against the unchanged code:

    • a delegation card pulses its dot only while the child runs; the dot is aria-hidden, and "Running" sits in the card's live region;
    • a fleet chip segment pulses only while its family has running work (data-running), and keeps its accessible name (Subagents · 1 running · 1 done) inside the Agents in this chat list;
    • the session details state dot pulses for running only; idle, completed, failed, awaiting and cancelled don't pulse;
    • the details loading state is a role="status" with "Loading details…" and an aria-hidden spinner; it has no pulse, no Skeleton, and no detail list.

    Every pulse check also asserts that the dot isn't the Skeleton primitive ([data-slot="skeleton"]).

No production file changes.

No regression

  • Mutation proof. I applied each mutation alone, reran the pin file, and reverted it. Each one fails exactly one test:

    Mutation Failing test
    Delegation card dot becomes a Skeleton (data-slot="skeleton") delegation card
    Delegation card dot loses aria-hidden delegation card
    Fleet chip pulse removed fleet chip
    Session details completed dot pulses too session details state dot
    Details loading spinner becomes animate-pulse details loading state
  • Gates (through the shared lock):

Screenshots

Before is origin/main (4aa238f3c), After is this branch (d49cc4714), and Prototype is stack-08. Every capture is pixel-identical to Before, and a second Before capture also matched (noise 0); pixel-report.txt in the asset folder has the counts. There are no red boxes, because nothing changed. Amber boxes mark the reviewed status dots instead. A separate capture pass hid only those dots and diffed the result to find them.

After matches the prototype: the prototype shows the same pulsing brand dots for running delegations, the fleet chip, and the running session state. The other differences in the Prototype column are layout and copy (for example, "Running" capitalised in its details dialog). This PR doesn't cover them.

Running delegation and fleet chip, desktop, light. The boxes are the fleet chip's Parallel segment dot and the dots of the running parallel group and branches 2 and 3. Branches 2 and 3 merge into one box.

delegation-running-desktop-light

Session details for a running session, desktop, light. The box beside "running" is the state dot. The dimmed chat behind the dialog shows the same delegation dots.

details-running-desktop-light

Desktop dark and 390px, light and dark

Running delegation, desktop, dark:

delegation-running-desktop-dark

Running delegation, 390px, light. The chat hides the fleet chip in a narrow header, so only the card dots are boxed:

delegation-running-mobile-light

Running delegation, 390px, dark:

delegation-running-mobile-dark

Session details, desktop, dark:

details-running-desktop-dark

Session details, 390px, light:

details-running-mobile-light

Session details, 390px, dark:

details-running-mobile-dark

🤖 Generated with Claude Code

#2337 reviewed every animate-pulse in delegation-card, fleet-status-chip and
session-details-dialog. All three are status dots for running work, not
loading placeholders, so none becomes a Skeleton. Pin that: each pulse shows
only while running, stays aria-hidden with the state spoken as text, and is
not the Skeleton primitive. Also pin the details view's real loading state, a
spinner beside "Loading details…" in a status region.

Refs #2337

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@kantord kantord left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, recording why these three dots stay dots is useful, and the decision table is clear. Approving, with a few non-blocking nits:

  • This is 188 lines of tests for no code change, so it is worth asking whether a code comment or the decision table alone would carry the decision more cheaply.
  • Only the [data-slot="skeleton"] absence check guards against a Skeleton swap. The .animate-pulse count would still pass, since Skeleton also pulses, and the rounded-full and bg-brand checks pin styling rather than the decision.
  • It covers 3 of the animate-pulse sites, so other pulse sites remain unpinned.

Fully or partially written by an AI agent.

@peppescg
peppescg merged commit 91bb1ef into main Oct 15, 2026
31 checks passed
@peppescg
peppescg deleted the refactor/studio-skeleton-2337 branch October 15, 2026 17:02
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.

2 participants