Skip to content

feat: thread docks — background tasks, global placement, and composer bubbles - #675

Merged
SDSLeon merged 5 commits into
masterfrom
poracode/noble-kestrel-1e6ec041
Sep 2, 2026
Merged

feat: thread docks — background tasks, global placement, and composer bubbles#675
SDSLeon merged 5 commits into
masterfrom
poracode/noble-kestrel-1e6ec041

Conversation

@SDSLeon

@SDSLeon SDSLeon commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two connected pieces of work, reviewed and fixed in the same pass:

Background tasks. A new provider-agnostic background_tasks.changed runtime event carries the provider's live background work (backgrounded shell commands, watchers, detached jobs) as REPLACE semantics. The Claude provider folds its CLI's background_tasks_changed level signal into it, filtering sub-agent runs (they keep their own dock) while their ids still hold turn completion open — so a backgrounded pnpm test keeps the thread "working" until the level drains. The list surfaces in a new read-only dock, dies with the session (session exit / thread exit / supervisor restart all drain it), never resurrects from persistence, and rides the remote snapshot protocol (backgroundTasks field) to mobile, where it appears as an info chip.

Thread docks placement. Goal, plan, agents, and background tasks now share one global mode (Settings > Appearance > Thread docks, plus a toggle on the topmost composer dock): stacked above the composer as today, or the right panel's new Docks tab — flush, drag-reorderable sections with a compact glass bubble per dock above the composer standing in for it. Clicking a bubble opens the tab scrolled to that section; clicking the active one closes it. Panel visibility and the auxiliary panel share one has-dock-content hook; the per-thread plan placement (and its store) is retired in favor of the global mode.

Review

Deep review ran as four routed lanes (lifecycle/protocol, UX/a11y, versioning/compat/proof, slop/duplication) over the full diff, followed by a targeted re-review of the fix delta. Confirmed findings fixed include:

  • Session death without a draining level (CLI crash, close, unload) left phantom pulsing dock rows — markThreadExited now drains the list on desktop and remote clients, and both remote hosts clear their cached levels on supervisor restart.
  • A remote history snapshot built before an already-applied WS drain event could REPLACE the fresher level with a stale one and stick on an idle thread — the snapshot applier now refuses snapshots older than the client's last-seen event seq.
  • A focusless Docks-tab open left every bubble announcing "Hide X" / aria-pressed=true while the first click opened instead of hid — bubble pressed state now strictly tracks the focused dock.
  • Proof gaps closed: reducer REPLACE/drain semantics, batched event envelopes, legacy settings-file shape, and the session.exited behavior change all carry regression tests (23 touched test files, 739 tests green).
  • Dedup: one shared docks-content hook, one visible-plan selector, flush right-panel chrome owned by ThreadDockSection, and the background-task hooks living beside their summary consumers.

Gates: typecheck, oxlint, oxfmt, and i18n:extract (0 missing across all 13 catalogs) clean.

🤖 Generated with Claude Code

SDSLeon and others added 5 commits September 2, 2026 03:07
…ks.changed

Fold the Claude CLI's `background_tasks_changed` level signal into a new
provider-agnostic runtime event carrying the renderer-facing task list
(REPLACE semantics; sub-agent runs stay on their own dock and are filtered
out). `liveBackgroundTaskIds` now holds turn completion open together with
the sub-agent registry, so a backgrounded Bash keeps the thread "working"
until the level drains, and the task list dies with the session via
`getBackgroundTasks` on the structured session handle plus a new
`readThreadBackgroundTasks` supervisor procedure.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Add the `threadDocksPlacement` (composer | right) and `threadDocksOrder`
shared settings with tolerant normalization for older settings files, and
the renderer state behind them: `background_tasks.changed` REPLACE handling
in the runtime event reducer (empty level drops the key; `session.exited`
and `markThreadExited` drain the list so it cannot outlive the agent
process), per-thread goal dismissal and background-task dismissal stores,
and the plan dock store migrated to v3 now that placement is one global
mode instead of per-thread.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Add the optional `backgroundTasks` field to the remote thread snapshot,
backed by a replayable per-thread level on the RemoteAccessServer that is
updated synchronously with broadcast events (single, batched, and multi
envelopes) and prefers the map over the racy live supervisor read. The map
clears when the supervisor restarts on both the desktop and headless hosts,
and the renderer's snapshot applier skips a history response built before
an already-applied WS event (per-host last-seen seq) so a stale level can
never REPLACE a fresher drain.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Unify the informational docks (goal, plan, agents, background tasks) under
one global placement: stacked above the composer, or the right panel's new
Docks tab with compact glass bubbles standing in above the composer — one
bubble per dock with content, opening the tab scrolled to its section and
closing it on repeat (pressed state always describes the click). The Docks
tab stacks the docks as flush sections with keyboard-accessible drag
reorder, per-dock dismissal, and the reverse placement toggle in its
header; panel visibility and the auxiliary panel share one
has-dock-content hook. A Settings > Appearance row exposes the mode, and
the mobile surface gains a background-tasks info chip fed from snapshots.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Extract the new dock, bubble, placement-toggle, and settings strings and
fill every msgstr across the 12 non-English catalogs (0 missing).

Co-Authored-By: Claude Code <noreply@anthropic.com>
@SDSLeon
SDSLeon merged commit c53fc0a into master Sep 2, 2026
8 checks passed
@SDSLeon
SDSLeon deleted the poracode/noble-kestrel-1e6ec041 branch September 2, 2026 10:15
SDSLeon added a commit that referenced this pull request Sep 2, 2026
node-pty has no Linux prebuild, so every test shard compiles it from
source and downloads Node headers from nodejs.org. A flaky transfer
crashes node-gyp's downloader (undici assert(!this.paused)) instead of
failing cleanly, which red-mastered #675's merge run. Retry the rebuild
up to three times, matching the Sentry upload retry loop in _build.yml.

Co-Authored-By: Claude Code <noreply@anthropic.com>
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