Commit Graph
8080 Commits
Author SHA1 Message Date
Brennan Benson 0edc792c72 fix(skills): propagate discovery cancellation 2026-08-05 18:50:14 -07:00
Brennan Benson 9ab5c9b070 fix(skills): harden SSH discovery ownership 2026-08-05 18:50:14 -07:00
Brennan Benson 272bb1a8e6 fix(skills): trust SSH PTY pane mirrors 2026-08-05 18:50:14 -07:00
Brennan Benson 3e342302ac fix(skills): normalize mirrored pane paths 2026-08-05 18:50:14 -07:00
Brennan Benson 3d46367282 fix(skills): accept mirrored SSH pane session 2026-08-05 18:50:13 -07:00
Brennan Benson 1b5c7ec251 fix(skills): harden discovery ownership and timeouts 2026-08-05 18:50:13 -07:00
Brennan Benson 851ea077d5 fix(skills): fail closed during SSH owner hydration 2026-08-05 18:50:13 -07:00
Brennan Benson 5d7733ebe7 fix(skills): preserve SSH pane transport identity 2026-08-05 18:50:13 -07:00
Brennan Benson 414183111d fix(skills): resolve SSH transport collisions 2026-08-05 18:50:13 -07:00
Brennan Benson ae64fed822 fix(skills): reject ambiguous SSH runtime routes 2026-08-05 18:50:12 -07:00
Brennan Benson d2741dc203 fix(skills): preserve SSH owner hydration 2026-08-05 18:50:12 -07:00
Brennan Benson 17e2ea2d6a fix(skills): bound SSH discovery lifecycle 2026-08-05 18:50:12 -07:00
Brennan Benson 38b32a95e7 fix(skills): harden SSH pane discovery 2026-08-05 18:50:12 -07:00
Brennan Benson b388f6a0cf feat(skills): discover Native Chat skills on direct SSH hosts
Native Chat's / and $ pickers now list skills stored on the pane's SSH
execution host. Discovery runs as one skills.discover request inside the
deployed relay; the desktop never walks remote skill directories per-file.

- relay: new skills.discover handler runs the existing Node scanner on the
  remote host with an 8s abort budget; old relays answer -32601
- desktop: connection-scoped SshSkillDiscoveryProvider (9s timeout) validates
  relay frames against a bounded zod schema before renderer state
- runtime: new skills.discoverForPane({worktreeId, terminalTabId}) derives the
  scan cwd from persisted tab state; a renderer path can never reach a remote
  scan. Old paired runtimes answer method_not_found instead of silently
  scanning their own disk
- renderer: SSH panes use the pane method with a connection-generation cache
  key; old relay/runtime map to an upgrade-required picker state with Retry

Closes #11466
2026-08-05 18:50:12 -07:00
Brennan Benson 630f13bfe6 refactor(mobile): separate native chat controller contract (#12826) 2026-08-05 18:46:34 -07:00
Brennan Benson 79896cb9a6 fix(native chat): retain transcript while reconnecting (STA-3333) (#12495)
* fix(mobile): keep the cached transcript visible while reconnecting

A manual retry closes the client and opens a fresh one, so the chat session
hook saw a new client under an unchanged identity, dropped its settled read,
and handed out an empty list — the transcript collapsed to a full-screen
spinner until the swapped client's snapshot landed.

Hold the last settled list per identity (captured post-commit) and keep
rendering it while the re-read is in flight. `transcriptLoading` still gates
consumers that decide from an empty transcript, so the launch-draft seed is
unaffected. The held list is keyed by a new `sourceIdentity` (host/workspace)
in addition to agent/session/transcript, so it can never serve another
source's messages.

Refs STA-3333.

* test(mobile): assert the whole reconnect window, not just its first frame

The re-subscribe lands a commit after the first render of the swap, so a
regression that cleared the held list there left frame 0 green and still
blanked the transcript. Verified: clearing the cache in the subscribe
cleanup now fails this test, where before only the view-toggle test caught it.

* fix(mobile): don't derive a tappable ask card from the held transcript

The cache this PR adds keeps the previous list rendered while a swapped
client re-reads. useMobileNativeChatPrompts was the one consumer reading
`messages` without honouring `transcriptLoading`, so an ask answered on
the terminal resurrected as a live, tappable card during that window.

Gating on `transcriptLoading` is exactly base behaviour: `setRead` only
ever stores 'ready'/'error', so status==='loading' implied an empty list
before this PR. The live `askFromStatus` path is untouched.

* chore: keep merge formatting scoped
2026-08-05 18:06:36 -07:00
Jinwoo HongandJinwoo-H aa64ac9606 fix(runtime): degrade focus-requested terminal create on headless serve (#12791)
`orca serve` publishes a ready graph under HEADLESS_RUNTIME_WINDOW_ID with no
BrowserWindow behind it. `shouldCreateInBackground` only degraded when the
create was renderer-backed, so any focus-requested create fell through to
getAuthoritativeWindow() and threw "No renderer window available" — leaving
`terminal create --focus` with no workaround on a remote server (#10333).

With a worktree selector and no renderer window, a background spawn is the only
usable path, so collapse the renderer-backed window check into a plain
"no window" check. That is the existing rendererBacked clause plus exactly the
missing focus case, and it drops the confusing `rendererWindow === null`
indirection (rendererWindow is already gated on rendererBacked).

Focus is not lost by the degrade: the spawned pane is still published to the
session-tab model and revealed with `activate: true`, which is how a paired
client learns about it. Mirrors the in-tree precedent in
runCreateMobileSessionTerminal.

Headed hosts are unaffected — the clause only fires when no window exists.

Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
2026-08-05 17:49:04 -07:00
Brennan Benson 6df8997c3c fix(file-explorer): sort numbered file names naturally across every listing surface (#11576)
* fix(file-explorer): sort numbered file names naturally

The File Explorer compared names with bare localeCompare, so numbered
files listed 100, 200 before 99. Hoist the numeric collator Source
Control file rows already use (#10850) into src/shared and apply it to
the local and runtime directory listings, the name-filtered view, and
Source Control directory nodes, which were inconsistent with the file
rows one line below (#11426).

* fix(file-explorer): natural sort on SSH funnels, relay, and pickers

Adversarial-review round 1 rework:
- Both readDir funnels short-circuited to the SSH filesystem provider
  before the patched sort, so SSH workspaces kept lexicographic order;
  re-sort locally after the provider returns (the remote relay may be an
  older build), and fix the relay's own comparator for relay-native
  consumers.
- sortDirEntries (shared, unit-tested) owns the directories-first +
  natural-order listing contract used by every funnel.
- compareFileNames breaks numeric-collation ties ('2' vs '02') by code
  units so sibling order stays total instead of readdir order, and pins
  the collator locale to 'en' so every host produces one order.
- The SSH folder browser and runtime server dir picker now match the
  Explorer they browse into.
- Ordering pinned by tests at the relay, source-control tree, and shared
  helper.

* fix(mobile): natural sort in the mobile file explorer

Mobile re-sorted host readDir results with bare localeCompare, undoing
the host funnel's natural order (round-2 review). Reuse the shared
comparator and pin the order in the mobile suite.

* fix(file-explorer): natural sort at the renderer choke point and remaining ties

Round-3 review: the remote-runtime RPC and paired-web routes return the
host's order verbatim, so re-sort in readFileExplorerDirectory where
every desktop route converges; pin the SSH funnel with a handler-level
test; and route Source Control path compares through compareFileNames so
numeric-collation ties share one total order with the Explorer.

* docs(file-name-sort): state the real perf baseline in the hoist comment

* refactor(source-control): drop the dead collator export; pin the test oracle locale

* fix(file-listings): cover remaining natural-sort surfaces
2026-08-05 17:11:28 -07:00
Jinjing 9ce9ce9a2e Register PTY output processors in memory profile census (#12795)
Add a new census module that tracks pending and retained OSC sequences
across all active PTY output processors. Each processor registers a gauge
at creation and unregisters it on dispose, detach, or destroy — this
prevents retained gauges from inflating later heap high-water profiles
and allows the memory profiler to detect stalled processors as a sign of
leaks.
2026-08-05 17:04:22 -07:00
Brennan Benson 4774d38239 fix(sidebar): keep PR status visible with activity (#12793) 2026-08-05 16:58:55 -07:00
Jinjing 2ff2a1b268 Display SSH worktrees immediately using persisted metadata (#12646)
* Display SSH worktrees immediately using persisted metadata

Users can now see known worktrees for SSH hosts without waiting for the
provider connection to establish. Worktrees are fetched from local metadata
and displayed as non-authoritative, then merged without replacing richer
live data once the provider becomes available.

* Show SSH folder workspaces immediately via persisted metadata

Add safeguards for metadata fallback: track authoritatively removed
worktrees per host to prevent resurrection, position new rows within
the host block to avoid jumping on authoritative scan arrival, and
preserve co-owner detection status during merge. Coalesce concurrent
metadata fetches to dedupe overlapping queries.
2026-08-05 16:52:49 -07:00
Jinjing ae1ed5e886 Remove source control group order preference (#12785)
* Reorder source control to show staged changes first by default

Stages are closest to the commit action and most relevant to the
commit workflow. Merges untracked files into Changes visually while
preserving their Git area. Removes the untracked-first preset and
includes migration logic for existing user settings.

* Drop source control group order user preference

Remove the sourceControlGroupOrder setting and related UI, migrations, and persistence logic. The source control view now always displays sections in the order: staged changes, unstaged changes, untracked files.

* Reorder source control to show changes before staged

Aligns with the edit-stage-commit workflow by showing unstaged
changes (active edits) before staged changes (queued for commit).
2026-08-05 15:29:46 -07:00
Jinjing 2c2a3266a6 Change question card submit button label to 'Submit' (#12782)
- Replace 'Send answer' with 'Submit' for clarity and consistency
- Update all locale translations (en, es, ja, ko, zh)
- Remove fixed button width and add whitespace-nowrap for flexible sizing
- Update component and test references
2026-08-05 14:58:17 -07:00
Jinwoo Hong de152503b2 chore(mobile): prepare 0.0.41 releases (#12781) 2026-08-05 14:53:02 -07:00
github-actions[bot] 06b02e0d75 release: v1.4.174-rc.0 v1.4.174-rc.0 2026-08-05 21:50:31 +00:00
Jinjing debf4affe7 Display total lines of code change in branch header (#12771)
* Add branch line total chip to source control header

Display the total lines added and removed across a branch from its fork point, measured via `git diff <mergeBase>`. Only computed when the chip is visible (request gate on merge base OID), with 500ms soft deadline to protect status latency and 15s hard timeout. Deduplicated across concurrent pollers and cached alongside line stats. Omitted on failure — always shows exact or nothing, never a partial estimate. Updates throughout the stack: native git status, relay, renderer store/API, and UI components.

* Pin branch line total to app locale

Format line counts using the app's configured locale instead of the system
locale, ensuring consistent cross-platform display and test reliability.

* test: wait for coalescer joins instead of fixed sleep

Hold the diff until the second status pass actually takes the
branch-total coalescer lease instead of using a fixed 400ms sleep.
Fixes timing-dependent flakiness on slow machines.
2026-08-05 14:46:00 -07:00
Jinwoo Hong 73cd4c3f46 fix(mobile): bound live terminal input latency (#12763) 2026-08-05 13:52:14 -07:00
Jinwoo Hong be2f9eddd3 fix(mobile): abandon pairing journals that can no longer reconcile (#12773) 2026-08-05 13:45:39 -07:00
Brennan Benson 7f4570c9a6 fix(mobile): activate Source Control diff tabs on phones (#12770)
* fix(mobile): activate source-control diff tabs on phones

* fix(mobile): reveal legacy source control file tabs
2026-08-05 13:36:08 -07:00
Brennan Benson 885afb55a9 fix(mobile): open host editor from root navigation (#12766)
* fix(mobile): open host editor from root navigation

* test(mobile): update task navigation router contract
2026-08-05 13:32:58 -07:00
KyouandOrcaWin 74ac7049ec fix(windows): make managed grok-hook.cmd safe when GROK_HOME is unset (#11782)
* fix(windows): make managed grok-hook.cmd safe when GROK_HOME is unset

Fixes #9358 and #9941.

cmd.exe expands %VAR:~n,m% at parse time. When GROK_HOME is unset (default
outside Orca terminals), the generated length/trailing-backslash guards
became a syntax error and every Grok hook event failed with exit 255.

- Skip substring work when GROK_HOME is undefined (if defined + goto)
- Replace if "%x:~-1%"=="\" (itself a quote-parser bug) with findstr
- Extract Windows script builder; add template + spawn tests

* fix(windows): harden grok-hook GROK_HOME guards and tests

Address review on #11782:
- Inject grokHome via buildWindowsAgentHookPostCommand extra form lines
  (no fragile string replace of the shared payload line)
- Spawn tests delete GROK_HOME and keep PORT/TOKEN/PANE_KEY set so the
  GROK_HOME path actually runs before curl

* fix(windows): cover Grok hook home boundaries

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
2026-08-05 13:31:26 -07:00
Brennan Benson d4dfc35ac4 fix(mobile): preserve multi-image chat attachments (#12639)
* fix(mobile): preserve multi-image chat attachments

* fix(mobile): use preferred array syntax

* fix(mobile): harden multi-image attachment flow

* fix(mobile): retain first-send image previews
2026-08-05 13:23:29 -07:00
Brennan Benson 23238aee0b fix(mobile): stop native-chat send button flicker (#12764)
* fix(mobile): stop native-chat send button flicker

* fix(mobile): keep composer lock rendering pure
2026-08-05 13:13:05 -07:00
Brennan Benson 4c49989c2e refactor(codex): delete the unreachable managed shared-mirror lane (#12614)
PR 9501 shipped real-home routing for the host system default, and the
env override that could turn it back off was never a shipped control. The
managed-account half of the shared runtime mirror has been unreachable
since: every host account routes to its own self-contained CODEX_HOME
before that code runs.

Delete the flag module and its env plumbing plus the managed branch of
syncForCurrentSelection and the six helpers only it called. The three
lanes that still use the shared mirror -- Windows, a custom CODEX_HOME,
and a hook-lane gate that reports unusable -- are untouched, as are every
legacy migration and the WSL read-back helpers.
2026-08-05 12:57:02 -07:00
Brennan Benson 38ba22ecd1 fix(browser): align cookie import safeguards (#12607)
* fix(browser): align cookie import safeguards

* fix(browser): preserve sessions on failed cookie imports

* fix(browser): bound single-label cookie replacement

* fix(browser): preserve host-only parent cookies

* fix(build): bundle cookie scope parser
2026-08-05 12:51:05 -07:00
Maxon PhongandJinjing b1b291db08 feat(browser): add hard reload option and shortcut hints to reload button (#12483)
* feat(browser): add hard reload option and shortcut hints to reload button

Add a tooltip to the browser reload button showing the reload shortcut.
Add a right-click context menu with Reload and Hard Reload options.
Add localized labels for Hard Reload across EN, ZH, JA, KO, ES.

* fix(browser): add aria-labels to reload buttons

* feat(browser): make reload button contextual and extract action logic

- Button label now reflects actual action: Stop when loading, Retry on failure, Reload when idle
- Extract reload intent resolution into reusable browser-reload-action module with tests
- Add keyboard support (Enter/Space) for the reload button
- Simplify remote page reload to tooltip-only (no ignore-cache RPC for remote pages)
- Add "Stop" translations for all supported languages

* fix(browser): exhaust reload intent switch for type-aware lint

Replace the default branch with an explicit reload case so oxlint
switch-exhaustiveness-check accepts BrowserReloadIntent.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-08-05 12:25:21 -07:00
Jinjing fde816e4ee move folders (#12758) 2026-08-05 12:09:24 -07:00
Jinjing d021e1b711 Improve gh issue table (#9068)
* Refactor GitHub work-item mutations onto a shared optimistic coordinator

- Extract PR/issue status, assignee, reviewer, and merge/auto-merge mutations
  out of TaskPage cell components into a registry-backed
  begin/confirm/rollback pipeline (task-page-github-work-item-mutation-*),
  so soft-hide, sticky filter-membership, and quiet revalidation behave
  consistently across all mutation types instead of each cell re-implementing
  optimistic update/rollback/toast logic.
- Add quiet revalidation (no filter skeleton, no page blanking) and soft-hide
  handling so a row that exits the active filter (e.g. closing an issue under
  `is:open`) stays hidden without a jarring list reflow.
- Restyle the GitHub task table: opaque sticky ID/Title cells, distinct header
  fill, accent hover, and tighter row/toolbar chrome to fix background bleed
  and muddy contrast in the scrolled table.

* Fix quiet-revalidate cancellation and sticky-hide scoping in TaskPage

- Replace per-render `cancelled` flag with a ref that only flips on
  true unmount, so a nonce-triggered re-render no longer strands the
  shared quietState's trailing/backoff bookkeeping mid-flight.
- Fix backoff index to use max lag attempts instead of lagging-key
  count, matching processTaskPageQuietRevalidateSettle so several
  single-lag items can't jump the delay tier.
- Scope sticky-hide retention in materializeTaskPageItemList to the
  originating query key, preventing non-membership confirms (e.g.
  auto-merge) from lingering as stale rows across refetches.

* Fix is:draft filter to soft-hide non-draft PRs

Previously state was forced to 'open' for is:draft queries, so a PR
that stopped being a draft still passed the state check and stayed
visible. Add an explicit draft check to soft-hide it.

* Improve GitHub work-item mutations with scoped quiet revalidation

Prevent race conditions and stale data by tracking quiet run ownership,
validating scope changes with generations, and blocking overlapping mutations
with pre-flight checks. Extract quiet state management into a dedicated module
with improved authority clearing and network retry logic.
2026-08-05 11:48:50 -07:00
Jinjing 6c2d168f74 refactor(sidebar): restructure filters into consistent submenu layout (#12759)
Reorganize the host and project filters to share a unified single-row design
(label left, value right) with detailed selection moved to nested panels. Group
both filters under a "Show" section label to keep the parent menu flat. Extract
project-filter search logic into SidebarProjectFilterPanel with explicit focus
and keyboard-handling tests.
2026-08-05 11:36:14 -07:00
Brennan Benson 86b878cfd6 fix(mobile): parse classified PR lookup outcomes (#12659) 2026-08-05 11:31:11 -07:00
github-actions[bot] 2539889197 Update README downloads badge 2026-08-05 18:28:05 +00:00
Brennan Benson 5d2ad3597a fix(native-chat): add direct Codex model selection (#12657)
* fix(native-chat): select Codex models directly

* fix(native-chat): confirm agent exits before switching views

* fix(runtime): handle unavailable foreground probes
2026-08-05 11:27:12 -07:00
Brennan Benson de64337c26 fix(worktree-watcher): refresh status after external pushes (#12361)
* fix(worktree-watcher): surface external push -u through the git-common watch

An external-shell 'git push -u' writes only the common .git/config (plus
refs/remotes/<remote>/<branch>), both invisible to the git-common event
filter, so the Checks panel stayed on 'No upstream configured' until the
renderer safety poll. Classify the common config and remote-tracking refs
as status-tier signals, poll config alongside the other primary-checkout
metadata files, and keep FETCH_HEAD/reflog/ref-lock churn ignored.

* fix(worktree-watcher): refresh after subsequent pushes
2026-08-05 11:26:13 -07:00
6942871194 feat(editor): add Markdown table structure controls (#11985)
* feat(editor): add markdown table structure controls

* fix(editor): scope table context actions to cells

* Replace table toolbar with context-aware overlay controls

Replace the fixed table toolbar with context-sensitive overlay controls that position themselves around the active table, adding support for direct row/column insertion and full table deletion. This approach is less intrusive and supports click-targeted actions via coordinate-based cell resolution. Enhance structural safety by preventing header removal and ensuring tables never collapse below a single cell, deleting instead when the final row or column is removed. Harden the context-menu query with a 120ms timeout to keep the native menu responsive even if the renderer hangs.

* Replace markdown table context query with IPC coordination

Capture table cell targets on pointerdown and report via IPC channel
instead of executing JavaScript on context-menu events. Eliminates
120ms query timeout and unavailability race conditions. Header cells
now disable incompatible row-level actions.

* fix(editor): make table column rebalancing atomic with insertion

- Refactor rebalanceAddedColumn to mutate the caller's transaction, grouping
  insertion and rebalance into a single undo step
- Add validation for cached cell positions that may outlive the document
- Fix table detection to use isInTable() instead of isActive('table')
- Correct z-index layering to respect menu stacking context
- Fix cleanup of stale animation frames and pending pointer state

---------

Co-authored-by: rainL <WYK15@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-08-05 11:10:05 -07:00
Jinjing 7aba9b306b Validate auto-generated PR details; abort with clear errors (#12752)
* Validate auto-generated PR details; abort with clear errors

Add fail-closed validation for generated review fields to ensure empty
bodies and generation failures prevent unintended auto-submission, with
user-visible error messages. Preserves current base ref since intent
auto-submits without confirmation.

* Add translations for PR generation error messages
2026-08-05 11:09:46 -07:00
Jinjing d72daf8153 fix(release): admit PTY consumer in SSH watcher isolation gate (#12754)
#12746 only delivers pty.data after an authenticated openClient grant.
Update the macOS release harness to use a credentialed --connect client
and open a legacy session-owner so the gate still exercises watcher
isolation without timing out on the initial PTY echo.
2026-08-05 10:56:26 -07:00
Jinjing 9fb4dbe8eb fix(ssh): handle rejected PTY deliveries with targeted recovery (#12746)
Add targeted recovery for rejected PTY source frames instead of
terminating the relay channel. Classify rejection reasons (malformed,
generation mismatch, range invalid) and attempt recovery based on the
rejection type. Implement admission control at publication time to ensure
frames aren't delivered after ownership changes. Bound recovery attempts
and retry with backoff to prevent exhaustion. Diagnose and log rejection
reasons to aid debugging.
2026-08-05 10:30:04 -07:00
Jinwoo Hong eea0bb64db fix(ssh): make PTY owner admission explicit and non-destructive (#12673)
An owner-capable `pty.openClient` had two failure modes that presented as something else.

If the relay still held an owner record but the request carried no matching resume proof, admission fell through to a SUBSCRIBER grant — a success-shaped response the client cannot use, which it then rejected as "did not grant an authenticated PTY session owner". And if the relay had forgotten the record the client named, admission threw a stale-recovery error, which the client answered by deleting its own recovery row — `clientInstanceId` included — and reopening. Two round trips, and the identity that lets it resume that target at all went with the deletion.

Now every owner grant carries a required `resumed` flag, a forgotten record mints a fresh claim in one round trip, a held claim returns one of three coded refusals, duplicate opens on one connection are rejected even when identical, and an attached-holder refusal becomes a typed error routed through the terminal-relay-error callback instead of feeding redeploy backoff a link that is working fine.

Independent review caught two regressions in the first attempt, both now fixed and both with tests that fail without them:

**A backpressure teardown could take a live owner's session.** The safety argument was that a record only becomes `disconnected` from an observed peer close — but two of the six paths there are capacity paths, where the relay destroys the client's socket itself because its lane queue filled. That is the signature of a client that is ALIVE but not draining fast enough. Demonstrated: the real owner is torn down for backpressure, a rival is granted ownership 270ms into a nominal 30s grace, and the owner's later reconnect with a valid resume proof is refused permanently, backoff cleared, no retry. Closes now carry a cause (`peer-closed` | `local`, defaulting to `local`, which only ever widens a grace), and the floor applies only to closes the transport actually observed on the peer's side. Capacity teardowns, decode faults and sink failures keep the default.

**A client's own zombie connection blocked it permanently.** Only `SshRelaySession` ever requests owner, and every endpoint-credential client shares one principal — so in a normal single-app deployment an `active` incumbent refusing you is almost always your own half-open connection the relay never saw close. That was refused as terminal, where main recovered on bounded backoff once keepalive noticed. The refusal already held both client identities; a match is now a distinct transient refusal that falls through to relay-lost backoff, restoring that recovery. A genuinely different client is still blocked.

Also: each retry deadline now starts when its own phase begins, instead of both being computed at entry where a slow first phase could leave the second with zero attempts.

Fixes STA-3365.
2026-08-05 02:56:07 -07:00
Jinwoo Hong cc1859d61c fix(ssh): snapshot detached SSH leases on quit and bound the teardown (#12687)
Per-target SSH teardown awaited `removeAllForwards` BEFORE anything marked the lease detached, so a slow forward close let the final store flush snapshot while leases still said `attached` — and the later durable write was rejected because persistence had already finalized. On the next launch those leases described a state that never existed.

`beginSshShutdown()` now performs every in-memory transition synchronously before returning, and the quit path calls it immediately before `store.flushAsync()` with no await between. The whole drain shares one deadline that REPORTS unfinished `{targetId, phase}` rather than concluding anything about it, and `waitForSystemSshForwardStop` gained a post-SIGKILL bound.

Nothing here destroys a session. `detached` means this app let go of the lease, not that the shell died — the pre-pass exists precisely so still-running PTYs are recorded as detached-but-alive instead of being lost to an `attached` snapshot. Review confirmed every reader honors that: reattach enumeration and persistence restore filter only `terminated`/`expired`, lease normalization has no age-based expiry, and attempt exhaustion leaves a lease alone. The drain deadline's only consumers are a warning log and a join that discards the value — nothing reads it as "gone".

Review also caught a defect the refactor introduced, now fixed: making the pre-pass synchronous meant a throw from `beginShutdownDetach` — via `webContents.send` on a renderer that quit had already destroyed — escaped the non-async `will-quit` listener and skipped `killAllPty()`, the watchers, `store.flushAsync()`, the teardown barrier and `app.quit()`. That would have lost the exact snapshot this PR exists to make correct. Each call is now wrapped per session, collecting errors and continuing. Proven: the test throws from the first of two sessions and fails without the fix with "Object has been destroyed".

A second test could only fail via timeout rather than assertion; the ordering is corrected so removing the post-SIGKILL bound now fails in 5ms with a clean assertion instead of a 5s timeout.

Rebased onto main and verified independent of #12673 (zero references to its owner-admission changes), which is being reworked separately. Fixes STA-3366.
2026-08-05 02:54:29 -07:00
Jinjing ade4354ddd Open GitHub items in workspace composer with issue automation (#12653)
* Open TaskPage GitHub items directly in workspace composer

Remove background creation indirection. The composer prefills with
issue/pull-request metadata from the GitHub work item.

* Generalize workspace composer tests to support multiple sources

Rename test from task-page-github-composer-boundary to task-page-workspace-composer-boundary. Add test verifying Linear items open directly in the workspace composer, expanding beyond GitHub-specific routing.

* Support issue command automation in workspace composer

- Pass GitHub work items through composer for issue context
- Quick create resolves commands at workspace creation time
- Extract command building and trust logic to dedicated module

* Derive composer hooks by host context for React Doctor

Key loaded hooks and issue commands by execution-host context so
repo/host switches no longer reset derived state in effects (the
static-analysis gate). Also wire SSH-aware workspace targets and
cancel-safe submit settlement for workspace creation.

* Support duplicate repo IDs across hosts in workspace creation

When the same repo exists on multiple hosts (local and SSH), resolve the
workspace creation target to the ready setup on the preferred host instead of
failing closed. Also add resilience to hook checks by clearing the cache on
transient IPC failures.
2026-08-05 02:34:13 -07:00