Files
buzz/desktop
ss-dev-02 eeb2f702ac fix(desktop): guard the work-block pulse and stop a finished block going live
Two reader-visible bugs in the focus-mode work block, both found reviewing
this PR rather than by a test.

**The running step's pulse ignored reduced motion.** `WorkBlockRailGlyph`
applied bare `animate-pulse`. `useWorkBlockMotionEnabled` skips the fold's
height animation, but it cannot reach a keyframe animation applied by a
utility class, and none of the app's 20 `prefers-reduced-motion: reduce`
blocks matches `.animate-pulse` — every one is scoped to a
`buzz-*`/`motion-*`/`t-skel-*` class. So a reader who asked for no motion got
an indefinite pulse. Now `motion-safe:animate-pulse`, which this repo's
Tailwind (4.3.0) compiles to the same declaration wrapped in
`@media (prefers-reduced-motion: no-preference)` — and which matches every
other pulse in this feature (`AgentStatusBadge`, `ManagedAgentRow`).

That swap also repaired three assertions it would otherwise have disarmed.
`motion-safe:animate-pulse` is a different class TOKEN, not `animate-pulse`
plus a modifier, so the suite's `.animate-pulse` selectors would have stopped
matching anything and passed unconditionally. Worse, they were already
vacuous: all three ran after `settleToStepCount(0)`, i.e. on a folded rail
with no rows, so "nothing pulses" held against a build where abandoned steps
pulse forever — the exact Codex finding this PR answers. They now assert on
rows that exist (expanded, or mid-settle) and match the exact class token, so
neither a substring nor a missing element can fake a pass.

**A finished block went live again between turns.** `lastTurnId` walked the
display blocks for the newest `turn` block, but a turn that has only emitted
setup lifecycle rows (`turn_started`, `session_resolved`) classifies to zero
segments and so produces no block at all. For the whole gap between
`turn_started` and the next turn's first renderable item, "newest turn with a
block" was therefore the turn that had already ENDED, and its own trailing
item became the streaming item: a settled 6-step block re-opened, dropped to
its last three steps behind a previous-steps disclosure, then folded back.
`turn_started` fires on every turn and the gap is real observer-stream
latency, so this was every turn, not an edge case.

Liveness now comes from the newest turn id in the item stream, which is why
`buildConversationTurnMeta` takes `items`. A trailing item is also only
reported as streaming when the live turn owns it — the same ownership rule
`toolEntryState` already applies to an `executing` status, for the same
reason: without it the newest block's tail holds a finished turn's block open
regardless of whose turn it is.

Tests, each mutation-checked in isolation so none of them is vacuous:

- pulse guarded, asserted under BOTH preference values (the class must not
  depend on it) — reverting to the bare class fails exactly that test;
  dropping the `running` guard fails the three retargeted negatives.
- the meta gap contract over the real 4-frame sequence built from a raw item
  stream through the real grouping, since the bug is precisely which turns do
  and do not produce a block — reverting either half of the fix fails it.
- the rendered consequence with a 6-step block (above the live window, where
  the symptom is loudest): folded summary and no rail across the gap.

Also fixed a fixture that named the wrong thing: "no turn at all" carried the
shared helper's default `turnId: "turn-1"`, so it was asserting that a turn
with no block reports no live turn — the opposite of the rule.

Full desktop suite 5491 passing / 0 failing, 81 suites; `tsc --noEmit` clean.

Co-authored-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
2026-08-24 21:50:44 -07:00
..
…

Buzz

Desktop chat shell with:

  • Tauri + React + TypeScript + Vite
  • Tailwind CSS
  • shadcn/ui-ready shared components
  • Biome (lint/format/check)
  • Feature-driven frontend structure

Scripts

  • pnpm dev - run the web frontend
  • pnpm tauri dev - run the desktop app
  • pnpm build - typecheck and build frontend
  • pnpm typecheck - TypeScript checks
  • pnpm lint - Biome lint
  • pnpm format - Biome format (write)
  • pnpm check - Biome check

Structure

  • src/shared - reusable app-wide code (ui, lib, styles)
  • src/features - feature modules (vertical slices)
  • src/app - top-level app composition