Commit Graph
10515 Commits
Author SHA1 Message Date
Jinjing 3b00cfcb2d fix(activity): keep badge in step with monitoring turns; drop dead cache branch (#19535)
Monitoring turns emit no working event (4b2e3dded0), so the titlebar
badge must not count them either, or it lights with no unread row to
clear. The build cache's cached-events ternary could never take its
cached path because the early return above already covers it.
2026-09-07 22:28:23 -07:00
OrcaWinandm4air 990674f7e3 perf: sum omitted workspace sizes without intermediate objects (#19491)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:52 -07:00
OrcaWinandm4air 5aaf146795 perf: cache matched and unmatched Claude usage cwd attribution (#19489)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:46 -07:00
OrcaWinandm4air 9039cd522e perf: index forgotten Codex turns for bounded ordinal retention (#19488)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:39 -07:00
OrcaWinandm4air d0969a4917 perf: parse Git history headers without splitting commit bodies (#19486)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:34 -07:00
OrcaWinandm4air 7dd6373e2c perf: skip sorting when all final automation runs fit (#19485)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:28 -07:00
OrcaWinandm4air 5d0a45bb92 perf: use counted membership for worker transcript roster twins (#19484)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:20 -07:00
OrcaWinandm4air d160c78921 perf: clone only changed work-item pages (#19478)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:14 -07:00
OrcaWinandm4air 505af34bb6 perf: stop clone URL discovery at the first usable source (#19477)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:21:08 -07:00
OrcaWinandm4air fb9d08f5f9 perf: index project table option and iteration order (#19476)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:20:57 -07:00
Jinjing 31b84cc71f Fix native PTY I/O failures disabling session termination (#19523)
* Fix native PTY I/O failures disabling session termination

I/O failures on write or resize were incorrectly treated as exit evidence,
which disabled further termination attempts and producer flow control.
Separate ioFailed state from dead state; I/O errors suppress operations
but keep kill, forceKill, and signal available. Publish physical exit
before notifying listeners to prevent reentrant cleanup attempts from
accessing the retired native process.

* Fix PTY I/O cleanup tests and config syntax error

- Fixed missing closing brace and comma in reliability-gates.jsonc
- Added clear() operation to PTY mock fixture and test coverage
- Enhanced assertions to verify operation suppression during I/O failures
- Improved kill operation error handling with better promise-chain assertions
- Updated test result summaries in reliability gate documentation
2026-09-07 22:20:35 -07:00
f0bfc945b4 fix: avoid duplicate repository groups during catalog refresh (#19170)
* fix: keep grouped repositories visible after creation race

* test: strengthen project group creation race verification

---------

Co-authored-by: Kien Le <122910950+kien-ship-it@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-09-07 22:19:52 -07:00
OrcaWinandm4air 253fa43256 perf: index editor ownership and restored workspace projections (#19444)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:10:00 -07:00
OrcaWinandm4air 919087897e perf: stop filesystem authorization at the first matching root (#19438)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:09:56 -07:00
OrcaWinandm4air 32510c9041 perf: skip Git-status indexing without eligible editors (#19437)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 22:09:52 -07:00
OrcaWinandm4air d3baad2527 perf: index selected team IDs and choose primary team in one pass (#19504)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 21:59:00 -07:00
Jinjing ba5f708290 Rank activity status groups by attention level (#19329)
* refactor(activity): rank status groups by attention level

Establishes consistent group ordering by introducing an attention-based ranking system, ensuring status groups maintain a fixed order regardless of thread recency. Consolidates thread status classification logic into `activityThreadStatusId` and simplifies group key naming.

* refactor(activity): emit working state for live agent turns

Activity events now emit working state for current turns,
enabling attention ranking above historical states.

* fix(activity): preserve working turns and count as unread

- Remove working-state events from cap logic so live turns stay visible
- Count fresh working/monitoring as unread in Activity badge
- Extract state-checking to activity-event-state module
- Use agentStatusEpoch for freshness-based invalidation

* fix(activity): subscribe only to epoch for unread count, not status map

The unread receipt is keyed on turn boundaries (stateStartedAt), not
heartbeats (updatedAt). Only the epoch matters; read the status map
directly via getState() to avoid wasteful re-renders on same-turn
heartbeats.

* fix(activity): prevent monitoring turns from emitting working events

Monitoring turns should surface only via the 'monitoring' snapshot in
the live state, not as separate working events that would contradict
the snapshot signal.
2026-09-07 21:50:14 -07:00
Jinjing 1a8640adb6 Stabilize scrollbar gutter to prevent message list layout shift (#19332)
* Stabilize scrollbar gutter to prevent message list layout shift

- Add `scrollbar-gutter:stable` to prevent reflow when scrollbar appears
- Adjust scroll container padding to properly accommodate the scrollbar
- Add 5px horizontal inset to content for alignment with composer field

* Simplify message list padding and update scrollbar-gutter
2026-09-07 20:24:59 -07:00
Jinjing 217125338e Log error details on session kill failure (#19381)
When a session kill operation fails, capture the error name and message
in the log to aid debugging and performance issue investigation.
2026-09-07 20:20:00 -07:00
Neil 7adb5b3dc4 fix(sidebar): label pinned rows with their host on a multi-host sidebar (#19351)
emitPinnedGroup was the one section emitter that appended worktree rows
without hostContextLabelByWorktreeIdentity, and the mixed-host map it
would have received was computed over naturalWorktrees, which under the
default pinned policy has the pinned worktrees filtered out. Under that
policy a pinned worktree renders only in the Pinned section, so a pinned
remote workspace had no host badge anywhere.

Compute the mixed-host map over the full worktree set and thread it into
the Pinned emitter. Single-host sidebars still draw no badge.

Fixes #18472
2026-09-07 19:54:30 -07:00
Neil 1e693edee4 fix(clipboard): route runtime-owned SSH image paste through the runtime (#17679) (#19352) 2026-09-07 19:54:16 -07:00
Neil fc78a7d9ca fix(runtime): stop a first status publication retiring in-flight worktree scans (#19357)
A paired runtime host's first status publication counted as a connection
change, advancing the connection generation. Worktree scans already in
flight against that same connection were then discarded, so the sidebar
showed a strict subset of the host's worktrees until an unrelated refresh.

Two independent defects, both fixed:

- `connectionChanged` conflated "no entry yet" with "recorded unreachable".
  Only the latter is a reconnect. The provider-session bump keeps the
  broader predicate, since a first publication is a real session start for
  integration-readiness caches.
- A stale-generation result was thrown away with no retry, so even a
  genuine mid-flight reconnect silently dropped completed work. The scan is
  now re-read once against the new generation.
2026-09-07 19:54:01 -07:00
Neil e182930670 test: cover input in five simultaneously flooding SSH panes (#19071)
* test: cover keyboard input in five simultaneously flooding SSH panes

* test: capture pane focus and buffers on flood input failure

* test: capture pane focus and buffers on flood input failure

* test: capture pane focus and buffers on flood input failure

* test: record replay input loss and application fix dependency

* test: record merged replay-input fix in the five-pane flood gate
2026-09-07 19:51:44 -07:00
Jinjing 66420537b7 fix e2e create menu races (#19448) 2026-09-07 19:45:54 -07:00
gatsby74andNeil a278d84a4e fix(pi): show input modals as waiting instead of working (#18836)
* fix(pi): show input modals as waiting instead of working

* test(pi): verify real input dialogs through Electron CDP

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-09-07 19:35:19 -07:00
aeddfa463d perf(renderer): avoid per-second spinner animation events (#19407)
* perf(renderer): avoid per-second spinner animation events

* fix(bench): ensure the Electron runtime before bench:spinners

The script launches Electron via Playwright but skipped ensure:electron-runtime,
which every other Electron-launching bench script runs first.

* docs(renderer): scope spinner pixel-tolerance claim to paused-animation checks

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-09-07 19:29:53 -07:00
Neil da836faeef fix: preserve terminal retirement proof across renderer publications (#19002)
* fix: preserve terminal retirement proof across renderer publications

* refactor: share the live-surface filter between retirement proof preservation and projection

The publication projection already dropped proofs whose surface is live;
reuse that as one helper instead of a second inline scan.

* fix: emit stored retirement proofs from host-authored snapshot writes

Three callers built a snapshot, stored it, then emitted the pre-store object. Storing grafts on the preserved proofs, so those frames carried the stored snapshotVersion without the proofs; subscribers dedupe on version and never saw them.

* fix: send terminal retirement proofs once per stream and fence them by occupant

Proofs are pinned per worktree for the host's lifetime, so every snapshot publication — including a 50ms title tick — re-shipped up to 64 proofs (~17 KB on realistic ids) to every paired client.

Negotiate session-tabs.retirement-proof-delta.v1: the host projects each session-tabs stream to send a proof only the first time that stream carries it, and a capable renderer keeps the union in a ledger keyed by (environment, worktree) with the same 64-entry bound and the same live-surface drop rule as the host, reset on removed frames and on a new connection generation. Legacy clients keep receiving the full list; CLI and mobile do not advertise the capability.

Also inherit worktreeInstanceId onto identity-less host writes so a host write between two renderer occupants can no longer launder one occupant's proofs into the next.

* fix: keep an empty proof delta distinguishable from a proof-less host

A negotiated stream now sends retiredTerminalSurfaces: [] when nothing is new instead of omitting the field. Absence is the host's "I hold no proofs" signal — which is also what a recreated worktree's fresh host entry publishes — so the client ledger forgets on absence and a successor occupant never inherits its predecessor's proofs, even when the removed frame was missed.

* test: pin ledger visibility against a legacy full-list host

An old host sends the full proof list whenever it holds any and omits the field when it holds none. Prove the new client ledger shows exactly what a legacy client would see across that sequence, so forgetting on absence is verified not to regress the mixed-version case.
2026-09-07 19:28:17 -07:00
Jinjing c056c6f9ac Unify sidebar create actions into single dropdown menu (#19375)
* Unify sidebar create actions into a single dropdown menu

- Combine "New workspace" and "Add project" under a unified "Create" button
- Remove layout logic that split these actions based on sidebar width
- Normalize "Add Project" to "Add project" (lowercase) throughout the UI

* Use null instead of 'Unassigned' for unassigned shortcut labels

Add formatOptionalPrimaryShortcutLabel that returns null when a
shortcut is unassigned, enabling simpler conditional rendering in
dropdown menus. Remove associated translation strings.
2026-09-07 19:24:34 -07:00
Neil 98b0c329ff fix: preserve user input during terminal scrollback replay (#19075)
* fix: preserve user input while terminal scrollback replays

* test: model multiple xterm user-input subscribers

* fix: keep mouse reports suppressed during replay and bind forwarders once

Real keystrokes now survive the replay guard, but xterm flags pointer
reports as user input too, and replayed bytes can leave mouse tracking
armed until the guarded mode reset lands. Keep those suppressed so a
click on restoring scrollback cannot print SGR fragments on the prompt.
Hoist the two provenance-bound forwarders out of the per-keystroke path.

* fix: keep wheel cursor keys off a replayed alt-screen frame

xterm turns a wheel notch into cursor up/down when the active buffer has
no scrollback, and flags it as user input. During a dead-TUI restore that
frame is replayed on the alt buffer and only leaves it when the guarded
?1049l lands, so forwarding those arrows would recall shell history at
the fresh prompt. Suppress them on the alt buffer only; the same bytes on
the normal buffer can only be a keyboard arrow and still survive replay.
Group the pointer-derived predicates in terminal-pointer-input-sequences.
2026-09-07 19:15:08 -07:00
OrcaWinandm4air de0a91b99f fix(deps): update Electron to reviewed 43.6 runtime (#19369)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 19:12:12 -07:00
Brennan BensonandMerge Sim c1e15c4008 feat(native-chat): read a tool batch as a group (#19372)
* feat(native-chat): read a tool batch as a group

A run of several tool calls collapsed to one joined string: names and
arguments run together, separated by a middle dot that also occurs inside
`browser.open` and `tools/read`, with the overflow cut mid-token. Opened,
the member rows sat flush with the header and with the message content
around them, so the batch had no visible end.

Two presentation changes, no new derivation:

- Each member gets its own bounded pill in the collapsed header, carrying
  its own category glyph, so the boundary between calls is a shape rather
  than a character. Pills wrap instead of truncating, and members past the
  summary cap are counted in `+N more` rather than dropped silently.
- Opened members are indented under the header, which is what marks where
  the run ends.

`toolRunSummaryMembers` keeps the run's leading calls apart instead of
pre-joining them; `summarizeToolRun` now derives its string from it, so
mobile's header is byte-identical and the two cannot disagree about which
calls speak for a run.

Two existing behaviours are pinned by test rather than changed, both being
naming decisions rather than layout ones: the header still prints the raw
`mcp__linear__list_issues` while the row beneath prints the split name, and
a call carrying only a `url` still falls through to a JSON preview clipped
at 28 characters.

* fix(native-chat): bundle hidden tool count copy

* fix(native-chat): drop the filled pill for a glyph-led member list

Rendered in the app, the filled chips were wrong twice over. `bg-accent` is
reserved for hover/active row backgrounds, and the only full-strength use of
it in native chat is on payload and diff surfaces — so each member read as a
shrunken content block, and a run became the loudest thing in the transcript.
Worse, `flex-wrap` degenerated: at a 297px pane each member is 274-288px, so
every one took its own line, the header grew 24px to 72px, and the `5x` count
centred against the block landed beside the second member as though it counted
that call alone.

The glyph already marks where a member starts, so the fill was carrying no
information the icon wasn't. Members are now inline, glyph-led, and separated
by spacing; the list stays one line and truncates as a whole, as it did before
this branch. `+N more` moves outside the truncating span so the count of what
is not shown survives a pane too narrow to print the list.

Members carry `data-tool-run-member` rather than being found by their fill.

* fix(native-chat): let the run summary size to its content

`flex-1` on the truncating member list made it claim the header's slack, so
`+N more` was pushed to the far right edge with a gap between it and the last
member it counts. Without it the span still shrinks and truncates — `min-w-0`
plus the default shrink is what drives the ellipsis, which is how the header
worked before this branch — and the count now sits directly after the list at
every width.

* fix(native-chat): separate run-header members with real whitespace

An `ml-3` margin marks the boundary on screen but is invisible to a copied
selection and to the button's accessible name, so the header read
`ls -latools/read`. Adds a space text node between members and trims the
margin to pay for its width. `+N more` also picks up the hover transition
every other header segment already had.

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-07 18:24:30 -07:00
BingZandNeil 5bd0247aaa fix(xterm): remove scrollback decorations by identity (#13178)
* fix(xterm): fire Marker dispose before clearing line (#10879)

Scrollback trim under search highlights was O(k²) because dispose set
marker.line to -1 before onDispose, collapsing SortedList keys. Fire
listeners first so delete still sees the real line, then clear the line.

Fixes #10879

* fix(xterm): remove scrollback decorations by identity

* perf(xterm): avoid index arrays for unique decorations

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-09-07 18:18:04 -07:00
Neil cbc7bb418c fix(ci): read the changed-path list past the first pipe buffer (#19409)
`pr-code-change-scope.mjs` read stdin with `readFileSync(0, 'utf8')`, a single
read of fd 0. Once the writer outgrows the 64 KB pipe buffer that read returns
early or throws EAGAIN, the script exits 0 having emitted no `name=value` pairs,
and `tee -a "$GITHUB_OUTPUT"` records nothing -- so every lane the classifier
gates is silently skipped rather than failing loudly.

A PR opened long ago carries a stale `pull_request.base.sha`, so the gate's
merge-base diff spans the whole base branch. PR #13178 diffed 13,294 files
(773 KB) against a base 1,592 commits behind main and lost typecheck, test,
static analysis, xterm patch sync, package and e2e to this.

Stream stdin instead, matching how the sibling `pr-e2e-source-routing.mjs`
already reads the same list in the same workflow.
2026-09-07 17:57:05 -07:00
OrcaWinandm4air 102402e41e fix(deps): update DOMPurify sanitizer hardening (#19377)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 17:43:28 -07:00
OrcaWinandm4air b8f6c7cabe fix(deps): update react-i18next for TypeScript 7 and parser fixes (#19378)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 17:43:18 -07:00
OrcaWinandm4air a4e5106e14 chore(docs): patch brace expansion resource exhaustion fixes (#19382)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 17:42:55 -07:00
OrcaWinandm4air 1dae024ab2 chore(mobile): patch xmldom security fixes in plist tooling (#19380)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 17:42:48 -07:00
OrcaWinandm4air 91fbc1529a fix(deps): harden cloud HTTP and WebSocket dependencies (#19362)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 17:42:37 -07:00
OrcaWinandm4air cfd59f2ca0 fix(deps): update desktop parser security dependencies (#19361)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-09-07 17:42:29 -07:00
Brennan BensonandMerge Sim 3b8128df04 fix(orchestration): index per-PTY mailbox reservation cleanup (#19390)
Co-authored-by: Merge Sim <sim@local>
2026-09-07 17:42:23 -07:00
Neil 588240043e fix(sidebar): reveal collapsed workspaces without clearing filters (#19398) 2026-09-07 17:14:54 -07:00
Brennan BensonandMerge Sim 9f044031fc fix(native-chat): render compaction notices, plan documents, and images (#19228)
* fix(native-chat): render compaction notices, plan documents, and images

* fix(native-chat): avoid repeating notice text in details

* fix(native-chat): journal canonical and legacy compaction events

* test: add digest to native chat notice payload fixture

* chore(native-chat): drop the planning doc from the PR

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-07 16:21:47 -07:00
Neil 2265fce591 chore(mobile): remove stale max-lines exceptions (#19366) 2026-09-07 14:54:18 -07:00
Brennan BensonandMerge Sim d0506bf5de feat(native-chat): add execution details and tool row identity (#19226)
* feat(native-chat): annotate tool rows with execution and source details

* fix(native-chat): require explicit MCP identity for tool annotations

* test: add required state to MCP projection fixture

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-07 13:51:47 -07:00
Brennan BensonandMerge Sim 0b3af9ddbc Show native chat message timestamps on hover and keyboard focus (#19218)
Co-authored-by: Merge Sim <sim@local>
2026-09-07 13:46:21 -07:00
Jinwoo Hong bcd4076dd1 fix(relay): never cache a region hint from a one-region catalog (#19349)
* fix(relay): never cache a region hint from a one-region catalog

The director lists only regions with a serving cell, so a roll wave shortens the
catalog to one entry. The resolver required only every *listed* region to be
measured, so that lone region won against nothing and was cached for 24 h: a US
desktop refreshing while US cells rolled published asia-east2 for a day, the
incident #19233 was written to end. Now fewer listed regions than the fleet serves
withholds the hint (1 h no-hint TTL), the same outcome as an unmeasurable peer.

* test(relay): give the unstable-probe case a two-region catalog so it has one cause
2026-09-07 16:44:33 -04:00
Jinwoo Hong d936d8da82 revert(mobile): pull the relay connect-speed mobile pass pending a smaller, verified re-land (#19348)
* Revert "feat(mobile): time relay dial stages so diagnostics say where a slow connect went (#19245)"

This reverts commit 83b1558ecc.

* Revert "perf(mobile): race the direct and relay dials from t=0 on every reconnect (#19308)"

This reverts commit ceafdcad2f.

* Revert "feat(mobile): draw the last known tab strip while a session reconnects (mobile pass) (#19281)"

This reverts commit 643571def6.

* Revert "perf(mobile): open a session with parallel startup RPCs and a pre-warmed terminal engine (#19260)"

This reverts commit c37413271e.

* Revert "perf(mobile): cut the relay reconnect critical path and admit dead sockets faster (mobile pass) (#19280)"

This reverts commit e628090ad4.

* chore: keep the react-doctor suppression for the startup timers

The pattern it covers (a variable number of timers cleared through one cleanup)
predates #19260 and is unchanged by the revert; dropping the entry only re-exposed
a pre-existing finding to the changed-code gate.
2026-09-07 16:44:26 -04:00
Brennan BensonandMerge Sim 6a47d2831f fix(native-chat): scope composer file drops to the pane that received them (#19328)
* fix(native-chat): scope composer file drops to the pane that received them

A native OS file drop resolving to `target: 'composer'` carried no pane
identity, so the window-wide payload was attached by every mounted composer.
Because inactive chat tabs stay mounted (hidden), one drop populated every
chat pane's attachment cache, and those chips replayed whenever the user
returned to a tab they never dropped into. The workspace-creation composer
and chat composers also leaked into each other, since neither could tell
which surface actually received the drop.

Composer drops now carry a `scopeKey` the way a terminal drop carries its
tab and pane leaf id: the composer publishes its pane key as
`data-composer-scope-key`, the preload harvests it during the composedPath
walk, and each composer attaches only its own. The workspace composer's
last-wins ownership stack now claims unscoped payloads only.

* test(native-chat): supersede the bug-asserting drop repro with the scoping test

The repro that landed on main asserts the pre-fix behavior (a drop reaching
every mounted composer), so it fails once drops are scoped to the pane that
received them. Its scoping cases now live in
native-chat-composer-drop-scope.test.tsx, which keeps its editor-target
control case verbatim and adds coverage for unscoped composers and a scope
key published inside the drop-target marker.

* test(native-chat): cover workspace composer drop isolation

* fix(native-chat): authorize external attachment paths before preview

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-07 13:34:23 -07:00
Brennan BensonandMerge Sim 5cefb440bf fix(native-chat): stop an unanswered host from reading as one that refuses structured chat (#19321)
* fix(native-chat): stop an unanswered host from reading as one that refuses structured chat

`readLocalRuntimeCapabilities()` returned `[]` both before the first status probe
landed and after one failed, so "not asked yet" and "host says no" were the same
value. Every structured-chat launch route consumed it, and an unprobed host was
routed to legacy chat exactly as a refusing one is.

Keep the two apart: the cache holds `null` until a probe succeeds, a failed probe
leaves it `null` rather than emptying it, and the launch route names the case with
its own blocker instead of borrowing `runtime-capability`.

No routing outcome changes — both cases still decline structured chat. The point is
that the reason is now truthful, which is what the routing work needs to build on:
once a launch can target a runtime peer, capabilities come from that host, and an
unanswered remote must not be indistinguishable from one that refuses.

`hostCapabilities` on the launch route stays local-only at every call site; a
per-target resolver replaces it when the route learns to reach a peer.

* test: cover unknown runtime capability lifecycle and launch fallback

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-07 13:25:40 -07:00
Jinjing 1d1b73c408 Defer inactive browser pages across worktree switches (#19326)
* Defer inactive browser tabs while retaining their viewport slots

Restore worktrees and tabs on demand instead of mounting the full tree.
Only render active pages and those required by automation, mobile drivers,
or remote viewers. Inactive panes stay deferred with persistent viewport
slots so their webview guests survive chrome unmounts, reducing memory
overhead when opening workspaces with many tabs.

* Defer browser pages until active and recover if evicted

Pages defer rendering until active, then retain state when inactive.
Add recovery logic to restore guests evicted by workspace memory
pressure when pages are reactivated.

* Stop retaining browser content when worktree is inactive

- Browser panes and pages now unmount when their worktree transitions to inactive, except for pages claimed by automation/mobile/viewer consumers
- Prevents unwanted restoration of all hidden browser tabs when switching between worktrees
- Tests verify proper cleanup at scale and correct page lifecycle across worktree switches

* Preserve document-preview guests when switching browser tab profiles

Document previews use a fixed partition and should not be recreated when
the profile changes. Only URL-based pages need their webviews destroyed
and rebuilt with the new profile. Includes test coverage.

* Create browser pages cold to defer guest initialization

Pages created in the background now start with loading: false, since they
don't own a guest until first shown. Only live guests can report loading
status, so background tabs sit idle until activation triggers navigation.

* Prevent document preview from swallowing pointer events during drag

Move webview registration to attachDocPreviewWebview before append,
ensuring it's enrolled in drag passthrough before becoming hittable.
When a document preview tab remounts mid-drag, the previous hook-based
enrollment landed too late. Also refactor mountEligible into
isBrowserPagePanePaintable for clarity.
2026-09-07 12:20:27 -07:00