Commit Graph
7755 Commits
Author SHA1 Message Date
Neil 01e08ea43d fix(crash-reporting): stop the sibling sweep re-admitting excluded crashes
`markRendererCrashesAutoRecovered` rules on each pending renderer-process
report against `notBeforeMs`, then sweeps burst siblings. The sweep did not
honour the first pass's decision: `isRelatedCrashEvent` ignores processType
and reaches 5s either side of any anchor, and the guard only excluded
react-render. A renderer crash the cutoff had just excluded was therefore
dismissed anyway as a "burst sibling" of a crash from the same process,
pushing effective reach to cutoff + 5s and dropping the
`rendererAutoRecovered` flag that records why it was resolved.

Collapse the guard to `status !== 'pending' || source === 'renderer'`. Every
renderer-sourced report is either an anchor the first pass already ruled on,
or a react-render boundary report the reload did not cause; neither is ever a
burst sibling, which is what the sweep exists to catch. This subsumes the
previous react-render exclusion rather than replacing it — deleting the whole
guard reds three tests, the two pre-existing boundary ones plus the new
cutoff one, so both protections still ride on it.

Scope, so a reader does not overrate this: the newest anchor normally sits
250ms before the arm (the recovery timer delay), so the naturally exploitable
band is about 250ms wide. It only widens toward the full 5s when the arm
lands late relative to its crash, or through a chain of anchors. The fix is
worth making on structural grounds regardless — the sweep should not be able
to override the cutoff the anchor pass just applied.
2026-08-01 06:53:17 -07:00
Neil d43712f658 fix(crash-reporting): close three auto-recovery mis-attribution paths
Adversarial round 6 found round 5 had covered two of four manual
main-window reload paths, and that the outcome check's lookback was
wider than its own arming delay.

- `app:reload` (the renderer's own retry button) never disarmed. The
  round-5 wiring test could not see it: it anchors on `onBeforeReload`
  and this site is spelled `onBeforeRendererReload`, so even its
  "exactly two occurrences" assertion still passed.
- A replacement main window consumed a live arm. The arm is module-
  global rather than keyed to a webContents, so a fresh window opened
  after the old one died (dock re-activation, tray reopen, second
  instance) bootstrapped and claimed the dead window's arm, dismissing
  every renderer crash in the lookback as auto-recovered.
- The lookback reused RENDERER_RECOVERY_OUTCOME_WINDOW_MS for two
  different jobs. That constant bounds how long an arm stays valid; it
  must not also bound how far back the arm may credit crashes. The
  reload is armed 250ms after its crash, so only that crash's own kill
  burst is ever in scope. At 30s an unrelated crash 25s older — with a
  different reason the reload never healed — was dismissed and stamped
  rendererAutoRecovered. Split out a burst-sized lookback.

Rather than add a third and fourth call-site disarm, the disarm now
lives inside markExpectedRendererReload. Every caller is a manual
main-window reload, so this covers all of them at once and a future
site cannot forget it. The wiring test pins the invariant at the
definition instead of at three call-site spellings.

Known gap, deliberately not fixed here: browser-guest-ui's forceReload
shortcut reloads the host renderer directly, bypassing this path. That
site is also missing the pre-existing markExpectedRendererReload
marking, so both halves belong in one browser-scoped change.

Mutation-verified: deleting any of the three fixes turns exactly one
test red. Gates: 3152 passed | 11 skipped, tsc 0, oxlint/oxfmt clean.
2026-08-01 06:13:58 -07:00
Neil 1e6e9fbef2 fix(crash): disarm the recovery arm on a manual reload, and pin the wiring
Two gaps found by adversarial round 5.

The arm could be consumed by the user's own reload. A recovery reload
that stalls short of renderer_bootstrap_rendered without tripping the
breaker leaves the arm set; a Cmd+R or View > Reload within 30s then
boots, consumes it, and stamps details.rendererAutoRecovered on a crash
the USER healed - dismissing its burst siblings too. The disarm goes in
both onBeforeReload bodies (createMainWindow's shortcut and the app menu
wire them separately) and inside the existing main-window guard, since
the menu path reloads whichever window has focus.

The arm/disarm call sites had no coverage at all: deleting them left the
full src/main suite green, so the earlier regression tests pinned the
module functions and not the wiring. index.ts cannot be imported by a
test (app-level side effects at module scope), so this follows the
repo's existing answer - a bounded, code-anchored source assertion,
mutation-verified to kill each of the four call sites individually.
2026-08-01 05:38:40 -07:00
Neil 50e1b50b09 fix(crash-reporting): disarm the recovery flag when auto-recovery gives up
The arm was set in one place and cancelled in none. When the renderer
recovery circuit breaker opens, the arm from the last allowed attempt
stays live for 30s, so the user's own reload from the "Orca keeps
failing to load" dialog consumes it — and the whole crash-on-load loop
is written dismissed with rendererAutoRecovered: true.

That flag is a direct lie there: the same code path had just recorded
renderer_recovery_circuit_breaker_open. It silences the highest-signal
crash class we have, repeated launch-failed before bootstrap.

Clearing on exhaustion leaves the manual retry unarmed, so the crash
prompts on its own evidence at the next launch.
2026-08-01 05:21:07 -07:00
NeilandOrca d85576e50b fix(crash-reporting): keep error-boundary reports out of the sibling sweep
Relation matching ignores processType, so a react-render report was swept the
moment it shared reason and exitCode with the anchor. Unreachable today only
because the boundary reason literal never collides with an Electron exit
reason — an invariant held 200 lines away with nothing pinning it.

Restates the anchor pass's own exclusion rather than reusing
isReactErrorBoundaryReport(), which tests that same reason literal. Both
regression tests now use a crash-shaped reason/exitCode so they fail without
the guard instead of passing on the mismatch.

Co-authored-by: Orca <help@stably.ai>
2026-08-01 05:09:25 -07:00
NeilandOrca 77705fba8c fix(crash-reporting): sweep sibling crashes when recovery resolves the renderer report
One kill burst (kernel OOM-killer, macOS jetsam) can emit a Utility or GPU
exit alongside the renderer's. dismiss() already sweeps those siblings via
isRelatedCrashEvent; markRendererCrashesAutoRecovered did not, so the healed
crash still reached the user wearing the sibling's report.

Reuses the exact predicate dismiss() uses, anchored on a snapshot of the
resolved set so a swept sibling cannot anchor a further sweep.

Co-authored-by: Orca <help@stably.ai>
2026-08-01 04:57:00 -07:00
NeilandOrca b8e597bc20 fix(crash-reporting): scope auto-recovery resolution to renderer process crashes
React error-boundary reports share source 'renderer' but are produced by the
renderer itself, not by a dead process, so a recovery reload must not dismiss
them.

Co-authored-by: Orca <help@stably.ai>
2026-08-01 04:40:57 -07:00
NeilandOrca e928bed289 fix(crash-reporting): stop prompting for renderer crashes auto-recovery healed
A renderer killed by the OS is recorded as a pending crash report, then the
recovery reload brings a new renderer up ~600ms later. That renderer's
CrashReportDialog immediately queries getLatestPending and opens the crash
prompt for a crash the app already healed (10/10 of the cluster).

Key the suppression on observed recovery, not on reason/exit code: arm an
outcome check when the recovery reload is issued, and resolve the pending
renderer report only once the new renderer emits renderer_bootstrap_rendered.
A reload that never brings React back leaves the report pending, so the
blank-window case still prompts. The record stays on disk, keeps its
breadcrumbs, stays sendable from Help > Report Crash, and is stamped
rendererAutoRecovered.

Co-authored-by: Orca <help@stably.ai>
2026-08-01 04:20:44 -07:00
Neil 16c5526dfd fix(daemon): cover in-flight sleep in PAM watch (#11921)
* fix(daemon): rebaseline in-flight PAM suspension

* test(activity): await portal readiness commits
2026-08-01 03:35:34 -07:00
Neil 33c14bc716 fix(ssh): fall back to OpenSSH for FIDO2 keys (#11913)
Closes #11645
2026-08-01 03:27:42 -07:00
Neil 1f307afa6d fix(terminal): preserve follow output through streaming refocus (#11915) 2026-08-01 03:26:30 -07:00
Neil 76a2317bc0 fix(persistence): stop blocking backup rotation (#11916)
Use async profile probes and serialize the current-time due decision plus mutations under one async owner so sync flushes and detached writers cannot double-shift or miss the recovery interval. Add syscall, timer-liveness, parity, and held-I/O interleaving coverage.
2026-08-01 03:22:36 -07:00
Neil c79b859758 fix(browser): prevent window.close guest crashes (#11910)
* fix(browser): prevent window.close guest crashes

* fix(browser): guard close before inline scripts

* fix(browser): preserve explicit window close policy
2026-08-01 03:08:11 -07:00
Neil e531e796b2 fix(relay): chunk fs.changed notifications to fit the client frame capacity (#11917)
* fix(relay): chunk fs.changed notifications to fit the client frame capacity

emitRelayWatcherEvents published an entire watcher batch as one
fs.changed notification on the ordinary lane. A batch runs routinely
tens of times over the per-frame producer capacity (12,288 bytes against
a Node <=21 socket, 49,152 against Node >=22), and an over-capacity
frame there is not queued or trimmed -- notify() fails admission and
closes the client, tearing down every in-flight request on that
connection. Regressed in 5f7807497e (#11005).

The emitter now sizes chunks against the tightest capacity across
attached clients, measured in encoded bytes through the same envelope
admission measures, and preserves event order. A single event too large
for an empty envelope cannot be chunked, so the ordinary-lane close
remains its backstop: a silent drop on this lane has no resync contract.

* chore(relay): shorten watcher chunking comments
2026-08-01 02:56:52 -07:00
Neil 3a70078ab9 fix(daemon): prevent PAM rejection restart cascades (#11911)
* fix(daemon): back off transient PAM rejection retirement

* fix(daemon): rebaseline PAM evidence after sleep
2026-08-01 02:36:49 -07:00
Neil edb5607e28 ci: block new root-level entries (#11903)
* ci: guard repository root additions

* fix: clear existing type-aware lint warnings
2026-08-01 01:48:24 -07:00
Brennan Benson 169ec8f08d fix(mobile): refresh folder workspace catalog (#11767) 2026-08-01 01:42:27 -07:00
Brennan Benson c2e3d13efe fix(mobile): focus Kimi terminal input after touch (#11865)
* fix(mobile): focus terminal input after TUI touch

* fix(mobile): defer terminal focus after WebView taps

* fix(mobile): reset deferred terminal focus on route blur
2026-08-01 01:40:55 -07:00
Neil 340faaa839 fix(workspaces): use the emojibase shortcode preset for emoji suggestions (#11888)
Swap the worktree-name emoji picker from emojibase-data's `github` shortcode preset to `emojibase`, which carries both `flag_kr` and `south_korea` style flag names, and drop the hand-maintained `kr` entry that patched around the gap. Filter skin-tone aliases so they neither crowd the suggestion list nor clobber base-emoji branch names.

Search now matches anywhere in the shortcode, ranked exact > prefix > word-start > substring, so `:korea` surfaces both Koreas.

Emoji-derived branch names now prefer spelled-out aliases: flags use country names (japan, germany, south-korea) and cryptic stubs are skipped (thumbsdown over no, victory over v).
2026-08-01 00:40:47 -07:00
Jinjing 96c954f3be chore: remove force-added design docs from docs/ (#11891)
Keep only the durable docs already allowlisted for tracking
(STYLEGUIDE, assets, localized readme, and reference compatibility
guides). Drop feature design notes, plans, and repro artifacts that
were force-added past the existing docs ignore rules.
2026-08-01 00:33:20 -07:00
Jinjing ad1e58d966 chore: declutter top-level repo layout (#11890)
Remove one-off incident docs and committed test-results noise, move
dev/repro/bench tools under tests/tools, and relocate i18next config
into config/ so the GitHub root scrolls to the description faster.
2026-08-01 00:25:35 -07:00
Neil 278a4b28c8 fix(terminal): close async capability review gaps (#11887) 2026-08-01 00:00:12 -07:00
OrcaWinandNeil c8a22ad0a6 fix(terminal): make snapshot capability lookup async (#11881)
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-07-31 23:38:26 -07:00
NeilandBrennan Benson 6e7ceafd07 perf(mobile): avoid unchanged worktree catalog payloads (#11735)
* perf(mobile): avoid unchanged worktree catalog payloads

* fix(mobile): isolate catalog snapshots by limit

* review: reassert host truth on unchanged polls; content-address snapshots

Client — the `changed` gate meant an unchanged poll skipped setWorktrees /
setLastKnownWorktrees / setCachedWorktrees, so optimistic local edits
(togglePin, handleDeleteWorktree's failure re-add) and the #8498 cache guard
were no longer repaired while the host catalog was stable. The gate bought
nothing: setCachedWorktrees is an in-memory Map write and areWorktreeListsEqual
already ran every poll, so the steady state still short-circuits on array
identity. All wire savings are unaffected. admit() now just returns the
confirmed rows and HostScreen applies them exactly as it did pre-PR.

Also on the client:
- a stale response from a superseded client/host no longer clears the token the
  current client/host just established
- discriminate on `worktrees` rather than on `'unchanged' in response`, so a
  future catalog field named `unchanged` can't reclassify a full response
- useRef over useMemo for the snapshot client; React may discard memoized values
- hoist WORKTREE_PS_FULL_LIMIT so the truncates-at-200 rationale travels with it

Host — replace the per-limit snapshot cache with a content-addressed id (ETag
semantics). Ownership lives in the id, so concurrent clients, differing limits,
and runtime restarts are correct by construction; this drops the LRU, the
eviction policy, the per-runtime WeakMap, and the retention of up to 8 full
catalogs. The remaining cache is a pure memo: because ids derive from content,
dropping or thrashing it costs CPU and nothing else. Keeping the memo also
keeps the measured steady-state cost — hashing every poll instead measured
2.24ms vs 0.75ms for the compare on a 310KB catalog.

Verified: mobile 2784 passed / 3 skipped, src/main/runtime/rpc 1064 passed,
node + mobile typechecks, oxlint, oxfmt, max-lines ratchet.

* fix(runtime): isolate catalog snapshot memo

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-31 23:12:13 -07:00
Brennan Benson 4c03cdff72 fix(mobile): mount host before opening tasks (#11853) 2026-07-31 20:46:51 -07:00
Brennan Benson ed00ab0f34 fix(ssh): restore relay ownership after app restart (#11860) 2026-07-31 20:45:52 -07:00
Jinwoo HongandOrcaWin c09a2ee251 fix(mobile): open resume workspace route reliably (#11876)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 20:42:05 -07:00
Brennan Benson 402e49203d fix(codex): keep persistent panes logged in after home routing (#11720) 2026-07-31 18:33:26 -07:00
072133fb27 fix(preflight): route landing banner through the runtime-aware preflight slice (#11390)
* fix(preflight): route landing banner through the runtime-aware preflight slice

Landing called window.api.preflight.check directly, which always probes the
local client. The preflight slice is the only caller that consults
getActiveRuntimeTarget and forwards to preflight.check on the active runtime
environment, so while connected to a remote runtime the landing banner
reported the client machine's git/gh state instead of the server's.

Delegate to refreshPreflightStatus and derive the issue list from
state.preflightStatus. This also drops Landing's duplicate probe: the slice
dedupes concurrent and forced checks, so the mount/focus/poll paths now share
one in-flight request with the rest of the app.

* fix(preflight): refresh landing status across runtime sessions

* test(preflight): cover paired runtime session races

* test: make landing preflight oracle behavioral

* fix(preflight): scope runtime session invalidation

* test(preflight): cover headed runtime switching

* test(preflight): isolate runtime status toast

---------

Co-authored-by: Marty <marty@localhost>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 18:01:24 -07:00
OrcaWin 33ad64b1c8 fix(runtime): bound persisted graph hydration (#11832) 2026-07-31 17:56:40 -07:00
Brennan Benson 377b580bab Restyle voice dictation feature tip (#11842)
* Restyle voice dictation feature tip

* Remove workspace name from dictation preview

* Refine voice dictation tip instructions

* Clarify voice dictation setup action

* Fix voice dictation tip keyboard behavior

* Use neutral voice dictation demo prompt
2026-07-31 17:51:03 -07:00
Neil 5738d61fe0 fix(workspaces): guarantee Korean flag kr shortcode (#11858) 2026-07-31 17:33:35 -07:00
Brennan Benson a53c5d3fb0 fix(mobile): stop serving a pre-write host-list snapshot to loads issued after the write (#11458)
* fix(mobile): stop serving a pre-write host-list snapshot to loads issued after the write

removeHost/persistHost await hostListMutation, but the in-flight loadHosts()
de-dupe handed back a pass that started BEFORE the write committed, so a load
issued after removal repainted the removed host card (#8791). Every durable
write now drops the shared pass via host-list-load-sharing.ts so the next
caller reads fresh; concurrent loads with no write between them still share
one Keychain pass.

Also extracts the host action sheet into host-list-action-sheet-actions.ts to
pin closeBeforePress on Edit host + Remove (the freeze half of #8791, already
fixed by #8536).

* fix(mobile): invalidate host loads after token writes

* fix(mobile): protect host token cache from stale reads
2026-07-31 17:26:42 -07:00
fsdwen 1a0a1ce4d0 fix(i18n): correct mistranslation of Grace/grace period in Chinese (#11505) (#11507) 2026-07-31 17:18:36 -07:00
Neil e79304ccca feat(workspaces): add Korean flag shortcode (#11845) 2026-07-31 17:16:49 -07:00
Neil 3c05e03c6f Revert "feat(workspaces): add Slack emoji aliases (#11837)" (#11843)
This reverts commit adc56a71f2.
2026-07-31 17:09:41 -07:00
Neil adc56a71f2 feat(workspaces): add Slack emoji aliases (#11837) 2026-07-31 16:39:38 -07:00
Brennan Benson 9bf05b0a9c Prevent Agent sleep while orchestration dispatch is active (#11808)
* fix(agent-sleep): keep active dispatch workers awake

* fix(agent-sleep): harden background work detection
2026-07-31 16:35:33 -07:00
Neil 676964b099 ci: run only changed e2e specs on pull requests (#11834) 2026-07-31 16:25:13 -07:00
Rod BoevandOrcaWin f56e6ade80 fix(ssh): recover orphaned relay install locks (#9828) (#10207)
* fix(ssh): recover orphaned relay install locks (#9828)

* test(ssh): split staged upload relay specs (#9828)

* fix(ssh): verify staged relay upload namespace

* fix(ssh): bound stale relay stage cleanup

* fix(ssh): complete bounded stage recovery

* fix(ssh): generate valid PowerShell stage scripts

* fix(ssh): make staged upload cancellation safe

* fix(ssh): fence staged relay recovery

* test(ssh): align deploy timeout oracle

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 16:17:37 -07:00
OrcaWin 139f756064 Add recursive sleep actions for workspace descendants (#11810) 2026-07-31 16:15:20 -07:00
Brennan Benson e4937956b3 Improve disconnected server recovery copy (#11827) 2026-07-31 16:03:59 -07:00
475f63ea1b fix(remote): scope renderer throttling to paired terminal publication (#11581)
* fix(remote): unthrottle host renderer while serving a paired client

A paired desktop host left in the background could not open or close
agent sessions for its remote/relay client: the action stalled and
eventually failed with the host-side "Timed out waiting for terminal
surface after creation" (10s) error, while an already-live terminal's
keystrokes stayed fast.

Root cause: creating/closing a session routes through the host
renderer's setTimeout-coalesced graph sync to publish the terminal
surface, but the host window runs with Electron background throttling
(the hidden-window default, reaffirmed on macOS). When the window is
backgrounded/occluded, those renderer timers are throttled to a crawl
and the surface publication misses the 10s deadline. Live keystrokes are
unaffected because PTY I/O flows through the main process, never the
renderer.

Keep the authoritative renderer unthrottled while at least one remote
client is connected and restore the throttled power-saving default once
the last one disconnects. Connect/disconnect are driven from the shared
MobileSocketWiring onReady/onClose, so both direct-WS and cloud-relay
clients are covered; headless serve has no window and is a safe no-op.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor(remote): tidy renderer-throttle comment and test per review

Address automated review nits on #11581:
- Trim the module-level rationale comment to the non-obvious contract,
  matching the repo's concise-comment guideline.
- Drop the dead `detachedThrottle` variable from the reapply test; the
  detached-target scenario is already covered by the lazy-resolution
  test, so the case now asserts only what it exercises.

No behavior change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(remote): scope paired terminal publication throttling

Keep headed paired terminal creation and close renderer-owned so host inventory, input routing, ACK recovery, and cleanup retain the established lifecycle. Hold a reference-counted background-throttle lease only while the renderer publishes a paired operation, and epoch-fence async resolution so renderer reloads reject before any request or PTY spawn. Preserve headless main ownership and prevent paired clients from falling back to a local terminal.

* test(e2e): verify minimized host terminal repaint

* fix(remote): preserve paired terminal inventory through graph gaps

---------

Co-authored-by: fanyunqian.1 <fanyunqian.1@bytedance.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-31 16:01:44 -07:00
Brennan Benson fcb23e19cd fix(sidebar): stabilize cross-host project grouping (#11805)
* fix(sidebar): stabilize cross-host project grouping

* fix(runtime): suppress outage toast on disconnect

* fix(runtime): dismiss intentional outage toast

* fix(runtime): keep offline retry toast visible
2026-07-31 15:38:08 -07:00
Brennan Benson c6d2180417 fix(mobile): keep source-control layout steady while Create PR eligibility loads (#11467)
* fix(mobile): keep source-control layout steady while Create PR eligibility loads

The Create PR entry unmounted until the first hostedReview.getCreationEligibility
answer arrived, so on a cold open the changed-files list painted first and then
shifted down 54pt (createPrBlock marginTop 12 + createPrButton minHeight 42)
when the button appeared — while the user was already tapping (#8411).

- buildMobileCreatePrAction: cold loading now reserves the row with a disabled
  placeholder instead of unmounting it.
- useMobileHostedReviewEligibility: a fetch-imminent idle frame renders as an
  in-flight load, so the reservation is present on the first painted frame.
- New per-worktree+branch memory of the last resolved eligibility seeds cold
  loads, so branches whose answer is hidden (existing review, unsupported
  provider) do not get a placeholder that collapses on every reopen.

Fixes #8411

* fix(mobile): harden source-control layout reservation

* fix(mobile): keep review status row footprint fixed

* fix(mobile): derive eligibility state from keyed snapshots
2026-07-31 15:35:56 -07:00
github-actions[bot] 5e258a9447 release: v1.4.163 v1.4.163 2026-07-31 22:15:11 +00:00
Brennan Benson 4205d5f31f Refine macOS access prompt guidance (#11822)
* Refine macOS access prompt guidance

* Capitalize MacOS in access prompt

* Use official macOS styling in prompt
2026-07-31 15:13:41 -07:00
Brennan Benson 751b6b119b fix(scm): keep git-status pacing across scheduler rebuilds (#11820)
* fix(scm): keep git-status pacing across scheduler rebuilds

* fix(scm): order shared refresh pacing updates
2026-07-31 15:13:04 -07:00
Brennan Benson 4d044c47dc Revert "Clarify macOS access prompt guidance (#11807)" (#11821)
This reverts commit 7d24dad48a.
2026-07-31 14:43:30 -07:00
NeilandBrennan Benson 9f30a780f5 fix(codex): prevent transient managed-auth onboarding (#11731)
* fix(codex): gate terminal spawn on managed auth readiness

* fix(codex): recover unavailable managed auth safely

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-07-31 14:40:46 -07:00