Skip to content

feat(chat): realtime executor-subagent strip (executor-only, per-tab activity)#47

Merged
miguelrisero merged 12 commits into
mainfrom
chief/bc-subagent-tabs
Jul 23, 2026
Merged

feat(chat): realtime executor-subagent strip (executor-only, per-tab activity)#47
miguelrisero merged 12 commits into
mainfrom
chief/bc-subagent-tabs

Conversation

@miguelrisero

Copy link
Copy Markdown
Owner

Realtime executor-subagent strip (executor-only, per-tab activity) — DRAFT

Session-scoped strip at the top of the conversation pane that surfaces the executor's subagents live: active subagents render as tabs, each with its own working animation; finished ones collapse into a "N done" drawer. Pure render/projection over the task_create conversation rows the app already streams — no backend, no schema, no new ingestion, no new stream.

Owner decisions honored (these override the design spike where they conflict)

  • Executor / UI-driven agents ONLY — not CLI. A real claude in a terminal already surfaces its own subagents, so the CLI/native-sidechain path is out of scope. This removes the spike's riskiest piece (un-hiding native sidechain records).
  • Per-tab activity. Each subagent tab carries its own RunningDots animation, so you can see which subagent is busy vs done at a glance — not one session-wide state.

Behavior

  • Leading Main chip; then one tab per ACTIVE subagent (created/pending_approval), each animating its own RunningDots.
  • A subagent that finishes lingers ~5s (FINISHED_LINGER_MS = 5000) as a done-styled tab (check/x icon, no animation), then collapses into the trailing "N done ▾" button; the drawer lists finished subagents, each expandable to its result (reuses ChatSubagentEntry).
  • Bounded: at most MAX_ACTIVE_TABS = 4 tabs; overflow folds into the same button relabeled "N more ▾".
  • Mobile (useIsMobile, ≤767px): a single "N active · M done" summary line that expands to a floating overlay sheet (Radix Popover, does not resize the conversation) — no horizontal tab scroll. The global 7-tab nav is untouched. Existing inline ChatSubagentEntry rows remain the primary mobile reading surface.
  • Session-scoped (mounted inside EntriesProvider in both the standard and VS Code conversation panes); renders nothing when there are no subagents.

Design

  • All bounding / linger / overflow logic is in a pure partitionStrip() + selectSubagents() projection — both unit-tested (Node-env vitest, matching the repo's model-test convention; there's no jsdom/render-test setup). Timers + doneAt tracking live in a thin useSubagentStrip() hook with a single absolute-deadline timer.
  • Reuses existing primitives only: RunningDots (the feat(cli-mode): make CLI/tmux agent 'working right now' legible in web UI (animated, is_running-driven) #44-standardized working animation), ChatSubagentEntry, AgentIcon, Radix Popover, CpuIcon; status-icon language mirrored from ChatSubagentEntry. No new spinner.
  • a11y: decorative icons aria-hidden + sr-only status text; drawer entries announce status to screen readers; per-tab role="status" removed to avoid multiple live regions; reduced-motion respected. Honors the cn()-has-no-tailwind-merge footnote (§8): conditional classes are ternaries.

Spec-of-record

Brings the design doc (docs/superpowers/specs/2026-07-23-agent-activity-legibility-design.md, previously draft PR #43) into this branch and revises it to decided + as-built: facet 2 scoped to the executor path, §10 Q3→~5s linger, Q4→MAX_ACTIVE_TABS=4, Q5→per-tab yes, Q8→executor-only, Q9→generic "Subagent". The native/CLI path and facets 1/3 remain design-only. Please close #43 as superseded by this PR.

Verification

Gate (feature-only tree) GREEN: pnpm --filter @vibe/web-core run check, pnpm --filter @vibe/ui run check, pnpm run test (full suite: 27 files / 269 tests), pnpm run local-web:lint, pnpm run ui:lint, check-unused-i18n-keys.mjs, scripts/check-i18n.sh. New subagent-strip-model.test.ts + subagent-strip-partition.test.ts cover budget boundary, linger window (injected now), overflow labels, null subagent_type, phase mapping, ordering. No Rust/db/schema touched (clippy/cargo N/A). Frontend vitest is not in CI (tsconfig excludes *.test.ts) — run locally via pnpm run test.

Live visual verify (scratch Vite instance, never prod 4111; desktop + mobile, agent-browser/Chrome). Seeded EntriesProvider driving the REAL components (owner-approved seeded set in lieu of a live executor run). DOM assertions + screenshots:

  • (a) desktop: Main chip + 2 tabs, each with its OWN RunningDots (2 animated dot-sets) — desktop-1-two-active.png
  • (b) complete→linger: RunningDots 2→1 (one stops, the other keeps going), finished tab lingers — desktop-2-complete-linger.png
  • (c) overflow: 6 active → exactly 4 tabs + "2 more ▾", no horizontal overflow — desktop-3-overflow.png
  • (d) mobile 390px: "2 active · 0 done" summary, no tabs, no horizontal scroll; drawer opens as a floating Radix-portal overlay with conversation height 724px unchanged before/after (no squish) — mobile-1-summary.png, mobile-2-drawer-open.png
  • (e) self-hide/session-scope: clear → strip returns null — desktop-4-cleared-selfhide.png
  • global 7-tab nav unaffected (proven by diff — zero Navbar files changed).

Screenshots (viewable): /tmp/laneP-shots/*.png.

Review gauntlet + deviations (proportional, ship-it-light)

  • Independent read-only Codex review (gpt-5.6-sol) + a two-persona frontend-systems / mobile-touch-UX review. Findings fixed: mobile drawer → floating overlay (was squishing the conversation); sr-only drawer status; removed per-tab role="status" live-region noise; exhaustive status-mapping guard; reduced-motion on the surfaced spinner.
  • Deliberately NOT changed (owner decisions / brief): tabs use RunningDots not the entry's spinner (feat(cli-mode): make CLI/tmux agent 'working right now' legible in web UI (animated, is_running-driven) #44 mandate); the persistent "N done" affordance is the owner's explicit "small button to check inactive subagents" — the mobile "0 active · N done" line is left for the owner to decide at flip.
  • Known follow-ups, deferred (non-blocking): one-time ~40px layout shift when the strip first appears (real fix = coordinate ConversationList's bottom-lock, not small); per-stream-tick selector for subagent-free sessions; 44px touch target under reduced mobile font-scale; faint dark-mode tab tint; desktop popover expand-state reset on reopen.
  • CodeRabbit CLI unusable non-interactively → substituted the Codex sweep above (recorded deviation).

Risk: LOW–MEDIUM (frontend projection over existing data). Draft — boss verifies (screenshots above), flips, merges per the owner's standing "merge as you go".

…rk decided/as-built (Lane P)

Bring the design spike (PR #43) into this branch and record the two owner
decisions that override its recommendations: executor/UI-driven subagents only
(native/CLI sidechain out of scope) and per-tab activity. Add a §0 decision +
as-built section, mark §6.5 executor path implemented / native path deferred,
and resolve §10 Q2/Q3/Q4/Q5/Q8/Q9. Facets 1 & 3 and the native path remain
design-only.
@miguelrisero
miguelrisero marked this pull request as ready for review July 23, 2026 16:59
@miguelrisero
miguelrisero merged commit 4404e21 into main Jul 23, 2026
7 checks passed
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