* fix(sidebar): close the workspace rename editor opened by the shortcut
The workspace.rename shortcut opened the editor with setEditing, while
every other transition went through setEditingMode, which also mirrors the
flag into editingRef and guards on it. After a shortcut-opened edit the ref
still read false, so Escape, commit, and blur all returned early and the
editor stayed on screen until the card unmounted. The borderless sidebar
editor gives no sign it is still live, so the row looked like a finished
rename.
The editing element also took its React key from the display name and the
unread flag. Neither is an edit, but both moved the key, so React remounted
the input and the ref callback re-ran focus and select over a half-typed
name. That half was never shortcut-specific: the double-click and hovercard
editors lost typed text the same way.
Remove editingRef so the editing state is the only source of truth, route
both entry points through a single openRenameEditor, and keep the key on
the rendered title where remounting is how truncation gets measured again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sidebar): guard rename editor transitions
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
Every snapshot in a batch scanned the whole openFiles array three times and did
a linear find per editor tab, so a reconnect paid for open files those snapshots
never touched. Bucket open files by worktree for the duration of a batch, skip
the global rebuild when a snapshot has nothing to drop or mirror, and retarget
the index at the array each snapshot produced.
Per-snapshot semantics are unchanged: openFiles still flows through each patch,
so the equality bail is still evaluated per snapshot against the current array.
An earlier draft deferred that bail to the end of the batch, which let the batch
keep pre-batch file objects that sequential reconciliation rebuilds; the two
regression tests here pin both cases.
1,200 workspaces, 3,000 unrelated open files: no workspace with editors
99.7ms -> 3.1ms, 5% with editors 105.3ms -> 7.7ms. Where every workspace has
editors the array genuinely changes each snapshot, so the rebuild remains
(113.9ms -> 84.2ms).
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): unmount closed setup guide content
* fix(renderer): keep setup-guide progress live through the close linger
Dropping shouldRefreshCoreState on close disabled skill discovery and
computer-use permission state while the dialog was still fading out, so
'Agent capabilities' visibly un-completed mid-animation. Mirrors the
paletteStatusInputsActive guard from the worktree palette.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): unmount closed workspace cleanup content
* fix(workspace-cleanup): restore unconditional delete-state clear on batch error
The session extraction swapped the batch-level onError handler from an
unconditional clearWorktreeDeleteState to the conditional queued-only
helper. When the batch driver dies, nothing settles those rows later, so
a hung 'deleting' row stayed stuck in the sidebar and was permanently
excluded from retry by filterWorkspaceCleanupRemovalCandidates.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* fix(ssh): release the relay-loss watcher before teardown mux writes
Cancelling the in-flight port scan emits rpc.cancel on the control lane.
If that lane is saturated, enqueue admission fails and the writer calls
fail() -> mux.dispose('connection_lost'), which fires the relay-loss
watcher during our own teardown and schedules a redundant relay redeploy.
teardownProviders already released the watcher first, but stopPortScanning
runs ahead of it and is what emits the frame. Hoist the release into a
named method and call it before stopPortScanning on all four teardown
paths.
Co-authored-by: Orca <help@stably.ai>
* fix(ssh): hoist the watcher release above abort on every teardown path
Review follow-up. Two nits from the readiness pass:
- The "stop scanning before teardownProviders" comment had drifted onto
releaseRelayLossWatcher(); move it back above the stopPortScanning()
call it describes.
- Release the watcher ahead of abortController.abort() too. That signal
reaches no mux request today, so this is not a live hole, but plumbing
it into one would have silently reopened the bug on three paths. Making
the release first keeps the invariant structural rather than incidental.
detachSshPtyConsumerRecovery stays first on the two detach paths, per the
existing synchronous-half-first rule.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): index GitHub resume lookups
* perf(renderer): skip the resume cache sweep publication when nothing changed
Every eviction helper already returns its input reference untouched when it
evicts nothing, so the opening sweep in refreshAllGitHub can compare identities
and return the previous state. Window resume then stops waking every store
subscriber on a no-op sweep.
Folds in the remaining unique optimization from #13711, which this PR
supersedes on the lookup-indexing side.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): preserve unchanged remote mirror resources
* test(renderer): restore layout-value coverage and record the workspaceId dependency
Notes why browser page identity reuse is safe: browserPageEqual must keep
comparing workspaceId, since the removed-workspace page-list cleanup gates on
it. Also re-asserts the effective active leaf that the patch-only assertion
stopped covering.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* perf(renderer): keep remote snapshot batches linear
* test(renderer): cover the prefix-colliding sibling worktree
The sibling-mapping test used a worktree id that shares no prefix with WT,
so it passed against the prefix-scan it was meant to pin. Use an id prefixed
by WT's and drain the new mapping index in the leak counters.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>