* ci: propose downloads badge updates through pull requests
* fix(i18n): align Portuguese CLI version message with current catalog
* test: provide locale in queued editor translation mock
* fix(editor): list unsaved editor files before a workspace removal closes them
Removing a workspace closed its editor tabs and threw away unsaved text
without a word, including with "Don't ask again" on.
Removal confirms now list the files whose unsaved edits the removal
discards: the tabs the purge itself would close (host twins included),
kept only when no view sharing their model or record stays open. Editable
unstaged diff tabs count; delayed rich Markdown, CSV and notebook text is
flushed first.
- Delete Workspace dialog: the list sits with the git-change warnings.
While it is non-empty the dialog no longer starts focus on Delete, and
"Don't ask again" no longer skips the dialog.
- The same list in Remove Project, SSH workspace forget/reconnect-and-
delete, SSH host removal with its workspaces, nested-worktree deletion
(rows matched by path containment under the parent) and project-group
deletion with its contained projects.
- Removals with no dialog whose files stay on disk refuse instead, with
"Save or close N unsaved files in <name> first": folder-workspace
delete (card action, context menu, hovered shortcut, through one shared
helper) and Settings > Remove Project.
Force retries with no list of their own (the failure toast's Force
Delete / Delete Anyway, Space's Force button) re-check at that moment and
refuse the same way. A folder-workspace delete that leaves a same-id
folder on another host closes no tabs, so it is not refused. Removal
confirms touched here render on the security surface.
Removals with no prompt (CLI, automations, paired clients) behave as
before; a follow-up offers their text back.
Known limitation: the list is decided by the tabs a removal closes, not
by paths; a symlink into a deleted folder is not resolved on the host.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): offer back unsaved text a removal with no prompt closed
A workspace removed with no dialog (CLI, automations, paired clients,
removal outside Orca, listing and runtime-host purges) still closed its
editor tabs and dropped their unsaved text.
Those removals now flush delayed rich Markdown, CSV and notebook text,
then show one toast, "Closed N files with unsaved edits from <workspace>",
whose Reopen action reopens each file as an unsaved tab: in its own
workspace if it is still open, else the active one, only when that
resolves to the same host. The next save is guarded against the disk
version the edits came from. Dismissing the toast drops the text and
logs it. The captured entries belong to that removal alone.
Removals confirmed in a dialog that listed their unsaved files pass
unsavedEditsConfirmed through removeWorktree, deleteFolderWorkspace,
removeProject, project-group deletion and the purge builders, so they
discard without a toast. Folder-workspace and Settings project removals
refuse while files are unsaved, so they never reach the toast.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): keep reopened drafts editable when their file is missing
Preserve recovered text through a missing-file read, retaining previous content and rename state. Cover empty drafts and keep unrelated failures on the existing error path.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): recover removed workspace edits into new files
Create collision-safe recovered files in the active workspace using the existing untitled flow. Capture delayed edits across host twins as one removal, match stale-host flushes to purge selection, honor host-qualified confirmations during announcements, and release retained toast text.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): snapshot confirmed discards and simplify removed-workspace recovery
A removal dialog now records the exact text it listed when the user
confirms. Every purge path (worktrees:changed, announcements, removal
teardown, runtime host retirement, repo removal, nested children) skips a
closed file only while its text still equals that snapshot, so edits made
after confirming are offered back instead of dropped.
- One flushAllPendingEditorBuffers() before each purge replaces per-id
flushing and the getState threading; idle CSV cells stay open.
- Captures merge into one toast per microtask, deduped by file id; the
per-call options, scope ref-counting and announcement wrapper are gone.
- Capture reads a shared model's live text, else any sharing tab's draft.
- Recovery writes through a guarded write that only fills the empty file
it created, moves to the next name on conflict, and never deletes it.
- Reopen resolves the destination path from the provenance owner host.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): scope removal discards to the confirmed teardown and flush only closing panes
- No lingering allowlist: a dialog's confirm returns a snapshot that rides
the confirmed removal (removeWorktree, removeProject, project groups, SSH
host clear) to its own teardown capture. Every other purge path offers
unsaved text back, even if it races a confirmed delete.
- The pre-purge flush covers only the closing files and views sharing their
records, and flushes every pane registered for them, so split rich and
notebook panes all commit while other workspaces' CSV cells keep editing.
- Listing announcements flush the files they will remove before the purge.
- Capture reads a model's text only while it is attached to an editor;
otherwise it prefers the drafts.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(editor): keep a CSV cell that can't commit as draft text when its tab closes
A pending cell over the table size limit fails commit(), so the closing
flush now falls back to the view's preserve hook, which writes the cell
through the same limit-free serialization the unmount path uses. Recovery
then captures the typed text instead of an older draft.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Classify substantially delayed connect/handshake deadlines as interrupted
instead of blaming endpoint reachability, retaining the existing close/retry
path and ordinary timeout errors. Limit sleep-clock compensation to observed
background segments and let fresh failures replace delayed evidence.
Remove the stale host-version fallback and discard captured versions when
report hydration crosses an observed reconnect. Independently reviewed;
128 focused tests, adjacent transport contracts and changed quality pass.
Adapted from community PR #24425.
Fixes#18829.
Co-authored-by: Wooseong Kim <innocarpe@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(browser): bring the client-hosted page pane back under the 400-line limit
Move the client-hosted guest's six event-listener add/remove calls into
bindBrowserClientPageGuestEvents beside the guest load-failure handler,
so ClientHostedBrowserPagePane.tsx drops from 404 to 395 counted lines.
No behavior change.
* test(browser): pin the client page guest event binder's bind and unbind contract
* fix(native-chat): bring the question module back under the 300-line limit
Move answer encoding (pasted answer text and the Claude and Codex selector
keystrokes) into native-chat-ask-answer.ts and re-export it, so importers are
unchanged.
Capture Android terminal keyboard controls in the OTA live input before native web input edits or moves focus. Preserve composition handling, send ordering, and stale-target protection.
Fixes#25496
Co-authored-by: Sangrak Choi <kars@kargn.as>
Co-authored-by: zenghaolun02 <zenghaolun02@baidu.com>
* fix(native-chat): agents need only a minimum version for chat; newer releases and prereleases run
OpenCode chats accepted only the 1.x and 2.x lines and Pi only 0.x and 1.x,
every prerelease was refused, and a slow or failed --version run counted as
unsupported. A new major would open as a terminal and existing chats said
"Orca can't run this agent in a chat here."
Each agent now lists ascending minimum versions; a version must reach the
latest one on or below its own major line, compared by semver. The check
answers ready, too old, unknown (slow, failed or wordless run) or missing
(did not start). Only too old refuses a launch, as Codex already did, with
"<Agent> X is too old for chats. Update to Y or newer." carried in a new
optional refusal fact agentInstallation; Codex keeps sending
codexInstallation for older clients. Codex's reader and error fold into the
same check.
* refactor(native-chat): a launch check declares its minimum versions; create support and the agent health snapshot run the one shared check
* fix(native-chat): read an agent's version from stderr too, so Pi 0.73 is known too old instead of unreadable
Switching desktops retains another host history and can scan its filesystem path on the new host. Reset content by host while preserving same-host reconnect identity.
Concurrent close replies filter captured terminal arrays and resurrect an already closed terminal. Reconcile the current collection synchronously and respect newer selection and metadata.
* fix(mobile): own commit generation by workspace and admit once
AI commit generation crosses workspace transitions and admits duplicate calls before a render. Retire the active run on scope changes and use synchronous admission.
* fix(mobile): retire commit generations only after scope commits
Pass the selected repository to New Workspace agent detection through the host-authoritative runtime resolver, including WSL paths and Windows project preferences. Preserve old-client request shape, SSH routing, and strict ownership/refusal behavior.
This merge credits @welldoingk’s original WSL agent-detection investigation and community PR #20101, adapted to the current host-resolved contract.
Co-authored-by: welldoingk <thkwon96@gmail.com>
Fixes#20060
* fix(mobile): show commit comparison failures with retry
Comparison refusals, transport errors and malformed payloads appear as No file changes. Decode accepted data, preserve loaded files on failed refresh, and offer Retry.
* test(mobile): record commit comparison errors and retry affordance
* refactor(browser): simplify address bar callbacks within lint limit
* style(browser): keep client-hosted pane within line limit
* fix(i18n): retire obsolete Portuguese default zoom entries
* fix(mobile): invalidate chat file search on authority migration
Modern search caches and late replies survive authority migration on the same client object. Own cache, capability decision and inventory by logical generation.
* test(mobile): record retired file-search requests after reset
* refactor(browser): simplify address bar callbacks within lint limit
* style(browser): keep client-hosted pane within line limit
* fix(i18n): retire obsolete Portuguese default zoom entries
* fix(mobile): the home screen's New Workspace opens the create sheet again
The native host-list route dropped the home screen's action=newWorktree when it built
the page's route, so the page opened as a plain list. The route now forwards it, and
the page names it back once applied so a reload does not reopen the sheet.
Reaches phones only with the next native build.
* test(mobile): drop the New Workspace tests that only re-check types and a null guard
* fix(mobile): Home's New Workspace opens the create sheet over Home
Replaces the action=newWorktree route hand-off. The quick action picks a
host (as Tasks does), then mounts the host's create sheet on Home; a
create opens the new workspace's session marked as just created.
The sheet controller becomes a plain visible/onClose component, and the
host screen's route-action state, the action route param and its builder
are deleted.
* fix(mobile): Home's create sheet seeds only proven worktree rows
Home's cold-start seed is a pathless resume row that the name suggester
cannot read, so the sheet now seeds from proven rows and is keyed by host.
The host screen gets one setter for the sheet's visibility and its ref.
* test(mobile): name the Home new-workspace test after the screen it renders
* proto(mobile): Home's New Workspace opens a native drawer hosting the page's create form
Home pushes /new-workspace/<host>, a react-native-screens formSheet over Home whose content is
MobileWebShellScreen for the page route /h/<host>/new-workspace. The page renders the shared
Create Workspace form with its drawers inline, so the form and its pickers fill the native
drawer. The shell's loading, download and failure states render inside the drawer. Prototype only.
* proto(mobile): the New Workspace drawer is the host screen's own sheet over a transparent modal
The native route is a transparentModal hosting the shell over Home; the page route renders the
host screen's NewWorktreeModal on a transparent page, so the drawer is the same component. While
the page loads, the shell shows a placeholder of that sheet (scrim, grabber, spinner or download
bar), and the page's sheet opens in place without its slide. A host that cannot serve the page
(old desktop, blocked, failed) opens the host list instead of a wall in a drawer.
* feat(mobile): Home's New Workspace opens the page's create drawer over a dimmed Home
A transparent native overlay route hosts the host picker and then the page's
/h/[hostId]/new-workspace route, which renders the same NewWorktreeModal as the
host screen's +. The native scrim matches the drawer backdrop and the page's
first drawer opens already dimmed, so Home stays dimmed throughout. Old desktops
without the route fall back to the host list.
* fix(mobile): one loading layer for Home's New Workspace overlay
The overlay route owns a single tappable loading layer (spinner, or the
download on a small card) that stays mounted from the pick until the page
paints, so the fade and spinner no longer restart per shell state. The shell
reports its load and shows the host list for anything it cannot open; the
picker hands the pick over at once and keeps sliding away. The page's first
drawer takes the dimmed backdrop once, and a gate's overlay pending view no
longer stacks a second scrim over a mounted stack. Native /h/[hostId]/new-workspace
redirects to the overlay. Drops the unused session-route params, the
clipboard grant, and pass-through tests.
* feat(mobile): New Workspace loads in a short drawer over an undimmed Home
While the page loads, the overlay shows one short native drawer in the shared
drawer chrome with a spinner or the download, and nothing dims Home: a tap
above it, a drag down or Back cancels. The page's real drawer then slides up
with its own backdrop exactly as on the host screen's +. Drops the native
scrim, the loading card and the already-dimmed backdrop handoff; the overlay's
WebView stays invisible until the page paints, since a fresh one draws opaque.
* feat(mobile): the New Workspace loading drawer dims Home like any drawer
The short loading drawer opens with the shared drawer backdrop and show
duration, and a tap on that backdrop, a drag down or Back cancels. It stays
in the screen's own window (a native Modal over the loading WebView stalls
the page) and goes in the frame the page paints: the shell reports its load
from a layout effect so the WebView shows and the drawer leaves together.
* fix(mobile): the page's first paint waits for a drawer opening with it
A drawer opening as its page screen mounts holds that screen's first-paint
report until its open animation ends, through a hold on the existing report
rather than a new bridge message. The New Workspace overlay's loading drawer
now stays until the create drawer is up, so a cold open no longer shows Home
undimmed between the two.
* refactor(mobile): the New Workspace overlay owns its exits; the loading drawer fades out
The shell no longer imports the feature: an overlay is a shell with
onOverlayLoad, which reports `unavailable` for what it cannot show, and the
overlay route opens the host list for every exit. HostProtocolGate takes one
transparentWhilePending flag. The loading drawer's drag handler follows a
stable cancel, and on paint the drawer fades out eased in, as the shell's page
cover does, because the WebView takes a frame or two to show the page after it
is revealed; removing it outright left Home undimmed for those frames.
* Revert "fix(mobile): the page's first paint waits for a drawer opening with it"
This reverts commit be0913b9d5.
* fix(mobile): Home's host picker leaves when there is no desktop to pick
An unreadable host catalog, or offered desktops that are all gone, left the
picker invisible inside the transparent overlay, holding a screen over Home
with nothing to dismiss it. The catalog read now resolves to no hosts on
failure, and an empty pick list goes back to Home.
* fix(mobile): Home's create drawer names SSH hosts as the host screen does
The page drawer opened from Home passed no host labels, so a project on an SSH
host showed its run target by raw host id. Reading the SSH target summaries
and display overrides moves into one function that both the host screen's
metadata fetch and the page drawer call.
#31656 left BrowserAddressBar.tsx one counted line over max-lines, which fails
Lint for every PR. The six copies of clear-a-timeout-ref become one helper.
* Use one model catalog per execution scope
* Accept a missing session record in host scope
* Merge duplicate launch-resolution import
* Fix catalog typecheck errors
* Align catalog tests with the one-list design
* Type the integration fixture's send without a cast
* Give the discovery test complete runtime dependencies
* Fix model catalog completion and scoped configured display
* Preserve model catalog authority across released clients
* Fix mobile first-frame catalog fixture narrowing
* Keep startup catalog persistence with one completion owner
* fix: preserve chat selections across CLI model updates
A newer CLI can omit a model that an existing chat still runs. Keep that chat's selected model and effort instead of replacing them from the available list.
Discover one model list per host, account, launch command and folder, refreshing after two minutes. Keep current values and the running model's exact effort choices with its chat. Show only the known effort when Claude supplies no matching menu.
Preserve older paired-client delivery and configured launch arguments. Make Codex use independent discovery and retain the established ACP and Pi paths.
Tracked in STA-10947.
* fix: keep the Pi options read within its file limit after main's growth
Tracked in STA-10947.
* fix: import the option snapshot's shared module once
Tracked in STA-10947.
* fix: keep Grok and Pi model menus and fast controls across restarts
Restore the established ACP and Pi catalog route: newer listed models and qualified startup defaults stay visible, and genuine retained effort menus survive later reads. Keep exact-child Fast and Speed controls. Update the phone option contracts and the remaining stale expectations to the selection rules.
Tracked in STA-10947.
* fix: share each workspace's model list and keep live Grok and Pi menus
New chats read their workspace's own model list through the one shared request for that workspace, so a second chat in the same folder starts on the model and effort the agent actually runs there, and a slow first answer can no longer overwrite a newer list. A running Grok, OpenCode or Pi chat keeps its live model menu when an older shared list arrives later. Update the tests that still expected the replaced behavior.
Tracked in STA-10947.
* fix: keep separate agent and chat model lists for Grok, OpenCode and Pi
A brand-new Claude chat now offers the selected model's effort levels before the first message, as it did before. Grok, OpenCode, omp and Pi again keep the list read from the installed agent apart from the list a running chat reports, and each newer list replaces only its own source. So a chat shows the model the agent is actually running with that model's own effort menu, and an effort change can no longer act on an older model's menu. Saved model lists on WSL are readable again without starting the agent.
Tracked in STA-10947.
* fix: keep desktop catalog storage out of the phone's type graph and show Grok's late model answer
The phone app's type check no longer pulls in desktop-only catalog storage. The model-list code no longer reaches the saved-catalog file through a type import. When a Grok model change times out and Grok later confirms the model, the chat shows the model Grok actually runs.
Tracked in STA-10947.
* Remove unrelated merge formatter and package manager churn
* Clarify independent catalog ownership and refresh contracts
* Declare picker demand callback as an effect dependency
* Complete mobile catalog surface and catalog merge types
* Complete catalog contract fixtures for current host types
* fix: preserve catalog source contracts and account fallback
* test: inject independent Codex discovery transport
* test: remove accidental client fixture assertion
* test: qualify the saved account fallback read
* fix(chat): preserve catalog evidence and current child controls
* Fix catalog CI types and mobile option readiness
* Reconcile session controls after inventory changes
* Fix catalog import and deferred test resolver types
* Keep model capability observations separate from picker views
* Use installed model choices for chat options
* Fix model catalog test fixture contracts
* Restore model option compatibility and stable settlement
* Return a catalog reply in provisional launch fixture
* Compose unreported live model defaults in the host
* Preserve explicit replacement model on recovery send
* fix(chat): use stable scoped catalogs on local desktop
* test(chat): preserve generic catalog runner type
* fix: scope catalogs to configured environment and keep saved reads passive
* fix: satisfy catalog regression and controller CI checks
Saving an endpoint after the user leaves can pop a second screen. Retire navigation ownership while allowing the endpoint save and client refresh to finish.
A reused file reader carries a note composer into another document, and a late submission closes the new composer. Give document state and callbacks a keyed lifetime.
Normalize bounded Base64 input consistently across desktop, browser, and mobile pairing paths while preserving lossless legacy padding and trailing-bit spellings. Retain offer validation and authentication behavior.
Adapted from #12027. Compatibility verified across 378 spellings with no previously accepted inputs lost; full PR workflow and aggregate gate passed for a84dfaee35.
Co-authored-by: Henry Su <henrysu4707@gmail.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)
The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.
- Main stops counting a closed tab's terminal once it was asked to stop
and no pane shows it; a failed or refused stop takes the request back,
and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
feed stands in while a fresh read is out; it highlights its own
last-opened workspace and a fold never scrolls; projects file under
their project group with the desktop's indent; the collapse spinner
shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.
* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises
- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.
* feat(sidebar): main publishes cached snapshots and announces its own stores; detected, view and live-content domains (inert)
* fix(sidebar): keep links confirmed only by a new git run, no rescan on metadata writes, no phone wait on detected checkouts
* fix(sidebar): rescan the workspace rows when the Windows runtime default changes
* feat(sidebar): one lifecycle queue per workspace in main, with in-progress hints (switch-gated)
Create, delete, terminal.closeAll, terminal.sleep and worktree.sleep now run through one
first-in, first-out queue per host-qualified workspace: strict order, no merging, delete
cancels what has not started and waits for what has, an unconfirmed outcome pauses until
the host's own answer. Hints publish in a new inProgress feed domain (no reader yet); the
creations domain is its projection. A finished create or delete keeps its hint until a scan
started after it applies, in the same revision. Adds the paired-server metadata serializer
(saving domain, no caller yet). Replaces the creation registry.
* fix(sidebar): a create never queues behind the previous occupant's delete
* fix(sidebar): resume a paused workspace by re-observing, never replaying
- A command issued while paused waits behind the pause; when the owner answers again main
re-reads terminal liveness and the listing, then carries on. A forced delete runs past it.
- Commands keep their arrival order across a workspace lookup; deletes by name key by the
resolved id, so they wait for that workspace's running sleep.
- A second delete with different options is refused as already in progress.
- Close and delete read lost contact as sleep does.
- A create always frees its own queue entry, even after a retry took over its id.
- Drops the unused opId, and the paired-server write serializer and its feed section,
which land with their first caller.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: restore main's final P2 test fixes a merge reverted
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)
The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.
- Main stops counting a closed tab's terminal once it was asked to stop
and no pane shows it; a failed or refused stop takes the request back,
and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
feed stands in while a fresh read is out; it highlights its own
last-opened workspace and a fold never scrolls; projects file under
their project group with the desktop's indent; the collapse spinner
shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.
* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises
- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.
* refactor(sidebar): read the sidebar feed through one shared client library on desktop and phone
The window and the phone each had their own feed reader. Both now use one reader under
src/shared/sidebar-feed-client: paging, notices, retries and the snapshot it installs. A
snapshot reuses each unchanged entity's parsed object, so one row's change re-parses one row
instead of its whole domain. Adds the gesture and view-report pieces later cuts wire up.
Lists are unchanged on both sides.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(sidebar): drop the feed client's unused pieces until their callers land
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sidebar): key the snapshot test's rows by host identity, as main's feed types now do
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(wsl-host): apply the SSH update rules to a WSL connect
A WSL connect deployed this app's orcad whenever the active build differed,
so a distro another, newer Orca had activated ended in an unconnectable
deferral, and a build the host had rolled back from was re-activated on
every connect. It now runs the same planManagedOrcadAutoUpdate decision SSH
uses and serves the active build when that plan skips.
* fix(wsl-host): serve the skipped-update incumbent only if it is still active
* fix(orcad): one update decision for SSH and WSL, applied again under the fence
The connect's update plan moves to its own module, and the fenced admission
every first deploy and WSL connect uses now applies it, so a transient
readiness failure or a race cannot reactivate a rolled-back build.
* refactor(wsl-host): adopt the serving build through the SSH first-deploy adoption
* fix(wsl-host): do not retry a candidate rejected earlier this launch
A rejected candidate is restored without recording rolledBackFrom, so WSL
retried it on every connect. SSH's launch-scoped failure memory moves to a
shared helper keyed per host, and WSL feeds it to the shared update decision.
A null adoption now re-reads the record before the deploy, as SSH does.
* fix(wsl-host): keep Force offered when a connect skips this app's build
The WSL connect's update admission moves to its own module, which returns a
forceable hint when it serves the incumbent over a rolled-back or failed build.
* fix(orcad): connect, Update and Force are the three update intents on SSH and WSL
Only a connect applies the skips; an explicit Update retries a skipped or failed
build (clearing the failure memory) behind the terminal census; only Force passes
the census. WSL's Update button now sends the update intent instead of a plain
connect. A connect that records a rejection serves the restored incumbent at once.
* fix(orcad): an explicit Update keeps the newer-host skip on SSH and WSL
Update lifts only a host's own skips (rolled back, failed before); Force lifts
all. SSH's explicit update now applies the same fenced admission, so stable
cannot replace a host a newer Orca activated without Force. A rejection retries
adoption only once the incumbent is back and no journal awaits recovery.
* fix(wsl-host): keep Update and Force offered on a deferred cold connect
A connect the host deferred with nothing serving now publishes its deferral, so
a host stopped after a rollback can still be retried from the UI. SSH's explicit
Update forgets a remembered failure only once it lands, and a restore that could
not be read keeps the rejection as reported.
* fix(wsl-host): offer only Force over a host a newer Orca runs, and keep failure memory until an Update lands
A host-newer refusal gets its own deferral code so the WSL row hides Update,
which can never pass it. An explicit Update no longer clears the launch failure
memory up front; only a landed deploy does, as on SSH.
* refactor(wsl-host): merge the intent type into the existing shared imports
* fix(profile-state): repair only the damaged sections instead of rolling back the whole profile
One profile-state row whose checksum no longer matches made Orca refuse to open the
whole profile, and the only recovery swapped in a whole backup, losing every newer
change in every section.
Startup now checks each section on its own. When only some sections are damaged, the
dialog offers "Repair and Restart": Orca copies the damaged database aside, then replaces
just those sections with the newest backup copy that passes its checks, keeping every
intact section's current value, in one checked write. Headless starts name the damaged
sections and `orca profile state rollback --repair`. Whole-file damage keeps the existing
backup rollback.
Also turns on checkpoint_fullfsync for the profile database: on macOS plain fsync leaves
the drive cache unflushed, which can leave a long row's pages from two versions after a
power loss or forced shutdown.
Tracked in STA-10893.
* test(profile-state): pin the repair dialog's text and buttons
* fix(profile-state): list the repair module for the CLI typecheck; keep section labels out of the layout-writer count
* fix: validate section repair admission before decoding payloads
* fix(profile-state): share backup handles during section repair
The phone and the desktop each parsed the status row's
{ approval: { tool, summary } } envelope with identical validation.
Move that validation and the answer keys into
src/shared/native-chat-approval-envelope.ts; each caller still builds
its own card (phone English labels, desktop translated labels) and
keeps its own precedence and heuristics. No behaviour change.
Co-authored-by: E2E Test <e2e@test.local>
Co-authored-by: Claude <noreply@anthropic.com>
* fix(sidebar): land flip-a's switch-on fixes on main, still dark (no flip)
The fixes found while the sidebar ran from main's feed, without turning
that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's
stop-gaps come along.
- Main stops counting a closed tab's terminal once it was asked to stop
and no pane shows it; a failed or refused stop takes the request back,
and a host out of contact stays unverifiable.
- Main fetches a paired server's rows with the transport's normal bound,
so a server slower than 5 s still lists.
- A deferred unread raise flips the folder row it was decided for, and a
read while the owner check is out cancels it.
- Phone: no worktree.ps flash before the feed; a host's last installed
feed stands in while a fresh read is out; it highlights its own
last-opened workspace and a fold never scrolls; projects file under
their project group with the desktop's indent; the collapse spinner
shows only after 500 ms; "Reopen to update" keeps the rows on screen.
- e2e helpers and journeys for the switch-on suite.
* fix(sidebar): keep an owed relay stop marked, clear a thrown stop, and prune pending unread raises
- A stop-and-wait that throws takes its stop request back, like one that fails.
- A final stop the legacy relay still owes stays marked, since the relay retries it.
- Pending unread raises are tracked only while one waits, so the map no longer grows.
- The paired-server fallback poll test gives its third fetch a reply and asserts live.
* fix(sidebar): key activity, review, retention and repo lookups by host, not bare id
The feed published activity and PR facts by bare workspace id, so a same-id
workspace on two hosts (a paired server and this desktop, or two servers)
shared one host's facts. Activity and review are now keyed by row identity,
retention sweeps per listed row, and the builder resolves each row's repo by
its own host's registration. Bumps the feed semantics version to 3.
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
* test(e2e): read a review fact by its row key
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
* test(mobile): key fixture rows by row identity
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
* fix(sidebar): count and label rows by their own repo registration; keep activity keyed through a failed listing
Host header counts and mixed-host labels read the row's own registration, as
the host filter and row now do, so one render cannot file a row under one host
and count it under another. A failed listing keys activity by the last rows.
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
* fix(sidebar): a slower older listing never overwrites a newer one's rows
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
* test(sidebar): read the quiet-desktop PR fact by its row key
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
* refactor(repos): the renderer re-exports the shared getRepoHostIdentity
Claude-Session: e1fdb3d9-6985-4693-84aa-9a9fad732103
Recover workspace capabilities after transient status failures without treating an unknown answer as unsupported. Share probes, invalidate across reconnects, and bound active-sheet retries with backoff and cleanup fencing.
Fixes#16347. Replaces the earlier recovery work in #16850 (already closed).
Credit Brennan Benson (@brennanb2025) for the original report and recovery implementation. Verification includes 105 focused tests, deterministic retry/leak counts, native iOS evidence with disclosed fixture limits, and green CI at a1cf7b54d2.
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>