Skip to content

test(studio): keep the reasoning and activity roster pulses as status, not Skeletons - #2374

Merged
peppescg merged 1 commit into
mainfrom
test/studio-status-pulse-2337b
Oct 16, 2026
Merged

peppescg merged 1 commit into
mainfrom
test/studio-status-pulse-2337b

Conversation

@peppescg

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. #2358 reviewed three call sites. This PR reviews the last two, in reasoning-disclosure.tsx and delegation-panel.tsx. Neither is a loading placeholder. Both pulse while something runs, so there are no code changes. The PR records the decision and pins it, which finishes the issue.

Closes #2337
Part of #2201
Part of #2210

Decisions

File Occurrence What it marks Decision
features/chat/reasoning-disclosure.tsx:46 shrink-0 animate-pulse font-medium on the "Reasoning…" label The model's reasoning is streaming. The label is real text, and it sits beside the latest reasoning line as a live preview. Once the turn settles, the label is replaced by the "Reasoning summary · N lines" toggle. Status: keep.
features/chat/delegation-panel.tsx:216 RosterGlyph: size-2 animate-pulse rounded-full bg-brand A roster row in Session activity is running. When the row settles, the dot becomes a check, an alert, or a help icon. Status: keep.

The roster dot is decorative (aria-hidden), and the row's text names the state ("Parallel group d2 · Running"). The reasoning label isn't hidden, because it's the spoken text itself. Neither one stands in for content that hasn't arrived yet: the preview line beside the label and the row text are the content. The prototype (stack-08) pulses the same "Reasoning…" label and the same brand dot on running agent rows.

Why delegation-panel isn't stacked on #2334. delegation-panel.tsx is also changed in #2334. That PR moves the roster rows onto the Toggle primitive, but it leaves RosterGlyph alone. Because the decision is "keep" and this PR changes only the new test file, it doesn't conflict with #2334. The new test passes on main (8cc7c2347) and on #2334's head (acfbf3382), so I folded it in here and didn't open a stacked PR. It selects roster rows by button[aria-pressed], which matches both the current <button> and #2334's Toggle.

What changed

features/chat/status-pulse.pinning.test.tsx (from #2358) gets 2 new tests, written against the unchanged code. The file now has 6 tests, and its header comment covers the two new call sites:

  • Activity roster: a running row pulses its dot. The dot is aria-hidden, round, bg-brand, has no text, and isn't a Skeleton. A finished row in the same panel doesn't pulse, so the panel has exactly one pulse, and an all-finished roster has none.
  • Reasoning: while reasoning streams, there is exactly one pulse. It's the "Reasoning…" label: not aria-hidden, not a Skeleton, and followed by the preview line. The settled summary has no pulse, and empty reasoning renders nothing.

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
    Roster running dot becomes a Skeleton (data-slot="skeleton") activity roster
    Roster running dot loses aria-hidden activity roster
    Roster finished glyph (Check) pulses too activity roster
    "Reasoning…" label replaced by a Skeleton bar reasoning
    "Reasoning…" label becomes aria-hidden reasoning
    Settled "Reasoning summary" toggle pulses reasoning
    Reasoning pulse removed reasoning
  • Cross-branch check. The same pin file passes on refactor(studio): use the Tabs and Toggle primitives in two hand-rolled controls #2334's head (acfbf3382), with 6 of 6 tests.

  • Gates (through the shared lock):

    • task studio:check: lint, typecheck and generated-check pass. Web: 188 files, 1618 of 1618 tests pass. Server: 27 files, 252 of 252 tests pass.
    • task studio:browser: 136 passed, 38 skipped.

Screenshots

Before is origin/main (8cc7c2347), After is this branch (e83afc10a), and Prototype is stack-08. Every capture is pixel-identical to Before. 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 pulses instead. To find them, a separate capture pass hid only those pulses and diffed the result.

Reasoning running, desktop, light. The box is the "Reasoning…" label. After matches the prototype: both show the same pulsing label and the same one-line preview. The other differences are outside this PR: the indicator under the turn reads "Thinking" here and "Reasoning" in the prototype, and the layout and composer copy differ. The prototype's harness streamed the same reasoning text.

reasoning-running-desktop-light

Running activity in the Session activity panel, desktop, light. The box is the running parallel group's roster dot. After matches the prototype's running-row dot. The prototype's Agents panel opens on Subagents and lists branches as rows, where Studio lists the parallel group. That's a layout difference this PR doesn't cover.

delegation-running-desktop-light

Desktop dark and 390px, light and dark

Reasoning running, desktop, dark:

reasoning-running-desktop-dark

Reasoning running, 390px, light:

reasoning-running-mobile-light

Reasoning running, 390px, dark:

reasoning-running-mobile-dark

Session activity, desktop, dark:

delegation-running-desktop-dark

Session activity, 390px, light:

delegation-running-mobile-light

Session activity, 390px, dark:

delegation-running-mobile-dark

🤖 Generated with Claude Code

…, not Skeletons

#2337 asks for the Skeleton primitive wherever animate-pulse marks
content that is still loading. The last two call sites are status
indicators, not loading placeholders:

- reasoning-disclosure.tsx: the "Reasoning…" label pulses while the
  model's reasoning streams, beside the live preview it labels.
- delegation-panel.tsx: the activity roster's brand dot pulses for a
  running row; settled rows show an icon instead.

Pin both in status-pulse.pinning.test.tsx against the unchanged code.

Closes #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 finishing the animate-pulse audit, and for checking the roster test against #2334's Toggle rows (button[aria-pressed] was a good choice of selector). Approving, with one non-blocking nit:

  • Same as on #2358: this pins a "keep it a status dot" decision with tests, and some assertions pin incidental details (font-medium, nextElementSibling) rather than the decision. The data-slot="skeleton" absence check and the textContent check are the ones that would actually catch a Skeleton swap. A code comment next to the decision might carry it more cheaply than more class-level assertions.

Fully or partially written by an AI agent.

@peppescg
peppescg merged commit ca9283a into main Oct 16, 2026
31 checks passed
@peppescg
peppescg deleted the test/studio-status-pulse-2337b branch October 16, 2026 08:49
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.

Studio: use Skeleton for loading placeholders instead of animate-pulse

2 participants