Commit Graph
414 Commits
Author SHA1 Message Date
Samuel VilaandNeil 3fd928373c fix(source-control): flag failed git status reads instead of showing a clean workspace (#28079)
When git status failed (e.g. a corrupted index), the read returned an empty
result and Source Control said the workspace was clean. getStatus now carries
Git's error as an optional readError; the store keeps the last known entries
and conflicts while it is set, and the panel shows a status-read banner with
Git's message and Retry instead of the clean empty state. The Retry logic is
shared with the too-many-changes banner, and the per-worktree error is cleaned
up on teardown and migrated on worktree rename.

Fixes #28033

Co-authored-by: Neil <neil@stably.ai>
2026-10-28 06:56:38 -07:00
f231d1cde6 feat(files): play remote audio and video through one byte-range preview path (#35038)
* feat(media): read remote media ranges through the paired runtime

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(media): stream remote media through orca-media grants

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(editor): play video and audio from remote runtime workspaces

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(media): surface update-required for remote media and cancel in-flight chunks

Probe files.readChunk once per environment and pairing revision before minting a
grant so older servers show the update message. Closing a media stream now aborts
the in-flight remote chunk. Also validate the IPC address, fix extension-only
names, and tighten the preview return type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(files): one classifier decides which viewer opens a file

Local reads, files.readPreview on hosts, git blob previews and the desktop's
remote binary handling each kept their own image/PDF extension table, and
media had another. file-preview-kind.ts is now the one table: image, PDF,
audio or video, with the MIME type. AVIF, which only local reads knew,
previews on hosts and in git diffs too.

* perf(runtime): give byte-range reads their own small call lane

files.readChunk (media streams, paged files, downloads) now runs in a
two-call lane per environment, so a playing video or a download never holds
the slots terminal input and workspace calls use.

* feat(files): stream previews from paired runtimes by byte range

Generalises the remote media grant into one byte-range preview path: an
orca-media:// grant keyed by environment, worktree, path and pairing
revision, streamed over files.stat/readChunk. Audio and video use it now;
the shared classifier decides which viewers do.

- One files.stat at mint replaces the readChunk probe: it proves the host
  serves range reads and that the path is a file, so an old host or a
  missing file fails in the tab, not as a silent player error.
- A burst of range requests shares one stat (reused for 1s), so seeking
  costs one round trip less on a slow link; later requests re-stat, so a
  growing recording keeps playing.
- Removing a paired runtime revokes its grants; a re-paired one answers
  410 and drops the grant.
- Only readers that render bytes mint a stream; baseline checks don't.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

* fix(files): prove chunk reads at mint, and let a reload report the new size

Hosts from v1.4.5 answer files.stat but only v1.4.111 and later answer
files.readChunk, and no capability names it, so minting reads one byte,
cached per pairing, and an older host fails in the tab with the update
message. A re-mint seeds the grant with its fresh stat, so a reload never
reuses an older size. A renderer gone mid-mint mints nothing. Preview
chunks time out after 20s so a stalled one frees its lane sooner.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

* fix(files): resolve preview hosts in the pairing registry's own directory

The remote preview reader looked environments up under the canonical
user-data path, while the pairing IPC writes the registry under
app.getPath('userData'), which can differ in casing on case-sensitive
installs. Both now use one getter. A chunk-read verdict is cached only
when a pairing revision pins it, so it can't outlive a re-pair.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

* fix(files): read the pairing registry from one directory everywhere

Doc previews resolved paired servers under the canonical user-data path,
the same casing mismatch the media reader had. They and every other main
caller that names the registry directory inline now use
getRuntimeEnvironmentRegistryRoot(), so it has one source.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

* fix(files): pin an unpinned preview grant to the pairing it was minted on

A renderer that hadn't loaded the environment's pairing revision yet sent
none, and every later chunk read went unguarded, so a re-pair under the
same environment id would let the old URL read from the new host. Main now
pins the registered revision at mint.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

* refactor(files): route the remaining pairing-registry readers through the one getter

AI-vault host scans, the settings runtime-environment preference, skill
capability checks and the mobile relay host list named the registry
directory inline. They now use getRuntimeEnvironmentRegistryRoot() too;
callers that also use the directory for other storage keep their own path.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

* refactor(files): write new pairings through the same registry root

The ephemeral VM pairing writer named the registry directory inline; it now
uses getRuntimeEnvironmentRegistryRoot() like every reader.

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>

---------

Co-authored-by: Sahil Sharma <128581847+SahilSharma0810@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 05:08:55 -07:00
Neil d6918c7a1d fix(remote-files): cap search replies, say when a diff is too large, and measure a 100 MB read on WSL (#34882)
Follow-ups from the large-file audit (E7):

- files.search had no byte budget: a result past the remote reply cap failed outright with
  reply_too_large. It now keeps the whole files that fit and marks the result truncated, as
  files.listAll and searchPaths already do. Local callers stay uncapped.
- A diff side past the 10 MiB read cap came back as binary, so the diff said "Binary file
  changed" for a large text file. Blob reads mark it `tooLarge`, the binary diff result carries an
  optional `tooLarge`, and both diff views say "File too large to diff". Older clients ignore the
  field and keep the binary view. buildDiffResult builds its binary sides without a cast.
- The real-WSL lane pages a 100 MB file over the plain distro's shared control socket in the
  editor's 384 KiB chunks and records the time, chunk count and orcad's VmHWM. Record-only: it
  asserts nothing, and a failure is recorded in the receipt.
2026-10-28 02:01:16 -07:00
780ab3d71d fix(bitbucket): canonicalize ssh.bitbucket.org remotes in review links and Gitea detection (#30808)
Fold every forge SSH host alias (ssh.github.com, altssh.gitlab.com,
ssh.bitbucket.org) through one shared normalizeForgeWebHost so main-process
provider detection and renderer manual review links agree. Remotes on
ssh.bitbucket.org now open bitbucket.org pull request forms instead of
ssh.bitbucket.org URLs, and Gitea no longer claims the host.

Co-authored-by: Eric Junker <4758+ejunker@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-28 01:57:10 -07:00
OrcaWinandOrcaWin 0866cb1b08 fix: bound local iteration checks and show live progress (#34700)
* fix: bound local iteration checks and show live progress

* fix: harden check lifecycles and cached test evidence

* fix: preserve process instrumentation and fixture lifecycles

* fix: give fixture unref spy a portable function type

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-27 23:04:23 -07:00
Žan MlakarandNeil 4dca163bb7 fix(github): count gh pr and issue commands against the GraphQL rate limit (#28778)
* fix(github): count gh pr and issue commands against the GraphQL rate limit

gh serves pr/issue/repo view-style subcommands over GraphQL, but the breaker
filed every non-api command under core and only matched REST's
"API rate limit exceeded" wording, so GraphQL exhaustion never tripped it.
Map those subcommands to the graphql bucket, match GraphQL's
"API rate limit already exceeded", and guard project-view issue close/reopen
against the GraphQL quota.

Fixes #28767

* test(github): drop redundant Date.now() args in graphql breaker test

Keeps the flaky-test-pattern ratchet at its baseline.

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-27 22:57:10 -07:00
Neil 3929371709 fix(wsl): background repo probes, PR refresh and worktree scans never start a stopped distro (E9) (#34632)
* fix(wsl): background repo probes, PR refresh and worktree scans never start a stopped distro (E9)

Any Windows touch of a \\wsl.localhost path, or Git/gh run for it, boots a
stopped distro. These background paths ran without asking the stopped-distro
gate: the worktree scan refresh (Git-admin fingerprint reads plus git worktree
list), the remote-identity and username enrichment sweeps, the startup
upstream backfill, the quiet-period ref maintenance, and queued PR refreshes.
Each now asks blockedWslDistroForPath first and skips, keeping known data,
until the distro runs; a host path never asks the gate.

* fix(wsl): a user act's wake allowance ends with the act

The allowance lived in an AsyncLocalStorage store, so any timer, listener or
queue created during a user act (opening a workspace, a wake read, the first
git status) kept it forever and could start the distro later, after a
terminate. The scope now carries an active flag cleared when the act settles.

* fix(wsl): skill discovery and automatic refreshPRNow never start a stopped distro (E9)

- Skill discovery (mounted by passive UI) leaves out roots on a stopped or
  unverifiable distro, and skips plugin discovery for such a cwd, instead of
  reading \\wsl.localhost; they are not reported missing, so the next scan
  picks them up.
- refreshPRNow skips automatic reasons (visible, active, post-push) on a
  blocked distro like the drainer does; only a manual refresh may start it.
- Ref maintenance's opt-out probe takes the same sync wslDistroForPath
  early-out as the other paths.
- Tests: stopped-with-no-cache worktree scans answer from stored rows, and
  'unverifiable' is treated as blocked.

* fix(wsl): skill discovery for a WSL target never starts a stopped distro (E9)

Passive UI discovers skills for projects whose agents run in WSL. The target
resolution probed the distro home (wsl.exe) and the scan ran in the distro,
so either booted a stopped one. Discovery now refuses a stopped or
unverifiable WSL target before the home probe and answers no skills and no
sources, so nothing reads as missing and the next scan retries. Delete and
share keep resolving without the gate.

* fix(wsl): a settled user act marks its distro woken; host skill scans never await the gate

- runAsWslUserAct (the gate's wrapper) notes the distro woken inside the act
  when it settles, success or throw, so a follow-up is never answered by a
  'stopped' read taken before the act.
- Skill discovery takes sync early-outs: a host-only root set and a host cwd
  never await the WSL gate.

* fix(wsl): Share discovers as a user act; a stopped distro's skills read as unknown, not absent

- skills:prepareShare (and the skills.share RPC) discover as a user act on the
  target's distro, so an explicit Share on a stopped distro finds its skills.
- A blocked WSL target answers an 'unavailable' source per requested kind, and
  roots (or a workspace plugin folder) on a stopped distro are reported
  'unavailable' instead of dropped, so the installed-skill verdict says the
  status may be incomplete rather than offering Install.

* test(wsl): use a fixed fetchedAt in the stopped-distro PR refresh test

* fix(wsl): Share scans stopped distros' skill folders, with or without a target

The desktop Share dialog sends no target, so the user-act scope had no distro
and the gated scan reported a stopped distro's repo folders unavailable; Share
then failed with skill-share-source-not-found. discoverSkills takes
gateStoppedWslDistros: false for both share paths (IPC and RPC), skipping the
stopped-root split and the plugin-cwd check; passive discovery keeps the gate.
Gated and ungated scans of one target no longer share a coalesced answer.
2026-10-27 18:56:02 -07:00
Neil 5784e146de feat(wsl): internal distro allowlist that keeps WSL execs out of other distros on shared test boxes (#34539)
* feat(wsl): internal distro allowlist for WSL execs on shared test boxes

ORCA_INTERNAL_WSL_DISTRO_ALLOWLIST (comma-separated distro names) keeps every
wsl.exe call out of distros not on the list: process-host's resolveSpawn,
the main and daemon node-pty spawns, and the two codex-accounts calls that
bypass process-host. Pure list/status/version calls stay allowed; any other
form must name an allowlisted distro, and --shutdown or a default-distro exec
is refused. A malformed value refuses every distro and logs once. Unset, the
check reads the variable and returns, so behavior is identical to today.

* fix(wsl): cover every direct wsl.exe exec with the internal distro allowlist

- Restore #34525's worktree-create payload and captured-host test, reverted by
  accident in the first commit.
- Check the allowlist in execFileCapture, the choke point for WSL Git, gh and
  worker-thread captures, and before the WSL git-read probe, the Codex hook
  path canonicalization and the CLI login spawn. The WSL watcher now spawns
  through process-host.
- Stop scanning wsl.exe argv at the first positional argument, which wsl.exe
  runs in the default distro.
- check-process-host-imports fails on any direct wsl.exe exec outside
  process-host that does not call assertWslDistroAllowed.

* fix(wsl): check the distro allowlist where resolveCommand produces the wsl.exe form

Every Git, gh and glab path into a distro resolves through resolveCommand, so
gitExecFileSync, gitSpawn, git stream reads and wslAwareSpawn are covered too;
the execFileCapture check is now redundant and removed. The import check also
treats buildWslExecArgs as a wsl.exe exec and accepts resolveCommand as covered.
2026-10-27 16:19:13 -07:00
Sojiro.OandBrennan Benson 9802866ed5 fix(editor): Copy Remote URL links to the code the workspace is on, with the selected line range (#33597)
* fix(editor): Copy Remote URL links to the code the workspace is on, with the selected line range

The gutter menu's Copy Remote URL always built the link on the default
branch and dropped any multi-line selection. Prefer the HEAD commit when
origin already has it, then the origin branch the current branch tracks,
then the default branch; carry an optional end line through IPC, RPC and
the git provider so a selection becomes a line range.

* fix: pair remote file links with published repository refs

* fix: prepare exact remote file fallback before publication scan

* test: consolidate remote file URL integration coverage

* test: document real-clock cancellation deadline proof

* test: handle optional cancellation signal in assertion

* test: reuse spawned child fixture across runtimes

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-27 13:00:23 -07:00
Brennan BensonandOrca Worker f0eb0949d0 fix(windows): delete again succeeds after a held file is released (#34058)
* fix(windows): stop recursive deletes from retry-storming when a file is held open

Node's async recursive rm retries every nested entry separately and orphans
sibling walkers when it rejects, so deleting a workspace holding a live
browser profile multiplied filesystem work per directory level until the
main process hung. Delete Windows host trees with a walker that retries
whole passes from one shared budget and leaves nothing running on failure.

Also bound and log detached worktree removal, and fail a file transaction
whose proper-lockfile lock is compromised instead of throwing from its timer.

* fix(windows): retry a locked delete as whole passes instead of a custom walker

Replace the hand-rolled tree walker with Node's own recursive rm and no
per-entry retries. Async rm gives each nested entry its own retry counter and
never stops siblings, which is what multiplied the work; with retries off each
pass is one bounded walk, and a transient Windows lock repeats the whole pass
from the existing 250/500/1000/2000 ms budget, checking the job's signal
between passes.

A failed delete now names the file another program holds open instead of
Node's EBUSY/EPERM text. Drop the 30-minute background deadline. The lost-lock
error no longer embeds the library's message and says the write may or may not
have been saved.

* test: cover bounded deletion failure and recovery contracts

* test: prune unchanged cleanup coverage

* fix(worktrees): give truthful failed-delete cleanup advice

* test: document fake-clock retry adapter

* fix(windows): preserve checkout ownership until files are removed

* fix: preserve worktree ownership checks across metadata aliases

* test: make checkout retry clock completion explicit

* Keep failed Windows workspace deletions retryable

* test: correct removal fixture imports and path formatting

* fix: retain failed removal until a retry is accepted

* test: identify the mocked backoff clock

* test: preserve failed removal records and recreate marker fixtures

* test(worktrees): preserve exact checkout bytes in refusal contract

* test(worktrees): exercise current native deletion contracts

* test(windows): prove long-path deletion through native checkout removal

* test(windows): preserve real Git runner exports in removal fixture

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
2026-10-27 12:31:10 -07:00
Neil 80c9278b89 fix(wsl): say why a silent WSL guest command failed (#34304)
* fix(wsl): say why a silent WSL guest command failed

A guest command that exits non-zero with empty stderr was reported as
"WSL <distro>: command failed". When wsl.exe itself fails it writes its
error to stdout, so the real cause was dropped. Report stderr, else
wsl.exe's own error, else the exit code, for the Claude account pointer,
OpenCode reader, WSL CLI install and WSL file operations.

Refs STA-12262 (#33917)

* refactor(wsl): share the wsl.exe host-failure exit check

* test(wsl): type the guest failure fixture instead of asserting it
2026-10-27 00:58:25 -07:00
OrcaWinandOrcaWin 84494ce394 test: remove 338 low-value cases and 28 suites (#34234)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-26 22:58:55 -07:00
Neil 362104ca18 fix(wsl): background Git and the roots catalog never start a stopped WSL distro (E9) (#34231)
* fix(wsl): background Git and the roots catalog never start a stopped WSL distro

git status ran on the UNC project root 0.45 s after a terminate, next to the
explorer read; either boots the distro. Background Git (status, conflict
operation, upstream status, branch compare, the upstream-ref watch) now asks the
shared running gate first and refuses for a stopped distro, including host
paths whose Git runs through wsl.exe. The authorized-roots rebuild skips a
stopped distro's repo and leaves it dirty, and the detected-worktree scan fails
as unavailable, so rows are kept and the next scan after the distro runs lists it.

* fix(wsl): the active workspace's first status and a wake read's roots rebuild may start WSL

- Drive-path projects whose Git runs through WSL had no wake path: the active
  workspace's first git:status after it becomes active now passes
  wakeWslDistro, so Source Control starts the distro; later polls stay gated.
- A user act runs in an async scope naming its distro; gated work inside it
  (the authorized-roots rebuild an authorization miss triggers, the detected
  scan, the Git) may start that distro and no other. External worktrees on a
  stopped distro no longer fail a wake read with access denied.
- After a user act wakes a distro, background reads stop trusting the older
  'stopped' read and probe again.

* fix(wsl): explicit WSL launches refresh the running read; wake only on tab change; gate the home probe

- Any wsl.exe Git/CLI run (commit, push, fetch) or WSL terminal spawn for a
  distro last read as stopped records that it started, so background reads probe
  again instead of refusing for up to 60 s.
- The active editor tab wakes only when the active tab changes; effect re-runs
  driven by git status or overview views read without waking.
- The authorization roots' WSL home probe no longer runs wsl.exe in a stopped
  distro when the home is uncached.
- The settle window no longer imports the orcad liveness module (it pulled main
  code into the web typecheck); a test pins it to EXIT_SETTLE_MS.

* fix(wsl): deferred work and running-list listeners never inherit a user act

A user act's async scope could leak into work it triggers indirectly: a drain of
deferred tasks it caused, or listeners of a running-list probe started inside
it. Both now run outside the scope, so only the act's own work may start its
distro.

* fix(wsl): keep shared modules free of main-only gate imports

worktree-logic.ts and wsl-running-distro-cache.ts are also compiled by the web
project, which cannot see main-only modules (AsyncLocalStorage, wsl.exe).
getAllowedRoots now injects the gated home lookup into computeWorkspaceRootWithin,
and the background gate's own cache subscriptions run outside the user-act scope
instead of the cache knowing about it.
2026-10-26 22:23:07 -07:00
ad2156c358 test: remove 140 low-value cases and 49 suites (#34062)
* test: remove 140 low-value cases and redundant browser probes

* fix: remove unused upstream chat menu import

* test: pin the host-split layout fixture branch

---------

Co-authored-by: Orca Test Audit <test-audit@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-26 15:16:27 -07:00
c2ceb5204f fix(windows): stop recursive deletes from retry-storming when a file is held open (#33722)
* fix(windows): stop recursive deletes from retry-storming when a file is held open

Node's async recursive rm retries every nested entry separately and orphans
sibling walkers when it rejects, so deleting a workspace holding a live
browser profile multiplied filesystem work per directory level until the
main process hung. Delete Windows host trees with a walker that retries
whole passes from one shared budget and leaves nothing running on failure.

Also bound and log detached worktree removal, and fail a file transaction
whose proper-lockfile lock is compromised instead of throwing from its timer.

* fix(windows): retry a locked delete as whole passes instead of a custom walker

Replace the hand-rolled tree walker with Node's own recursive rm and no
per-entry retries. Async rm gives each nested entry its own retry counter and
never stops siblings, which is what multiplied the work; with retries off each
pass is one bounded walk, and a transient Windows lock repeats the whole pass
from the existing 250/500/1000/2000 ms budget, checking the job's signal
between passes.

A failed delete now names the file another program holds open instead of
Node's EBUSY/EPERM text. Drop the 30-minute background deadline. The lost-lock
error no longer embeds the library's message and says the write may or may not
have been saved.

* test: cover bounded deletion failure and recovery contracts

* test: prune unchanged cleanup coverage

* fix(worktrees): give truthful failed-delete cleanup advice

* test: document fake-clock retry adapter

---------

Co-authored-by: Orca Worker <orca-worker@localhost>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-26 13:50:11 -07:00
Pr1pandBrennan Benson fdcf193cd4 fix(worktree): track explicit remote-only branch (#31645)
* fix(worktree): track explicit remote branch

* test(worktree): cover remote branch path retry

* fix(worktree): resolve cached remote branches canonically

* fix(git): retain commit validation in bounded ref probes

* fix(worktree): preserve selected remote identity during creation

* test(worktree): count cached resolver Git work during path retries

* Fix branch intent state in composer source fixture

* Make Git and daemon regression fixtures portable

* Use the selected base for existing branch creation

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-26 13:13:56 -07:00
OrcaWinandOrcaWin f34c13ca1e test: remove low-value main-process suites and browser probes (#33889)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-26 12:48:28 -07:00
Wooseong KimandNeil 6ad0a42425 fix(github): send issue bodies to gh over stdin instead of argv (#25215)
Issue create (including the empty-body fallback and follow-up PATCH),
issue body edits, and issue comments passed the body as
`--raw-field body=...` on argv. Windows caps the command line at
32,767 chars, so bodies with pasted inline images failed with
`spawn ENAMETOOLONG` before reaching GitHub.

These calls now use `gh api -X <METHOD> <endpoint> --input -` with a
JSON payload on stdin. ghExecFileWithScopeAsync also now forwards
`options.stdin` to the child process; it was previously dropped.

Fixes #15832

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-10-26 03:26:35 -07:00
OrcaWinandOrcaWin de93f326ea test: remove low-value inventories, passthroughs and redundant cases (#33796)
* test: remove low-value inventories, passthroughs and redundant cases

* style: format test suites after pruning redundant cases

* test: remove obsolete workflow and flaky-test references

* test: prune deleted builder test from Windows shim allowlist

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-25 23:41:49 -07:00
Neil d7cec1c3f8 fix(wsl): keep a UNC drive-folder repo's worktrees in its own spelling (#33706)
A repo saved as \\wsl.localhost\<distro>\mnt\c\... runs Git in WSL, which
lists its worktrees as /mnt/c/.... The listing mapped those to C:\..., so the
primary worktree got the id <repo>::C:\... instead of the saved
<repo>::\\wsl.localhost\...\mnt\c\.... Its tabs, agents and terminal history
are keyed by the saved id: the renderer dropped them as belonging to no
worktree, and the C:\ row showed up as a hidden discovered worktree.

Map drvfs output to the distro's UNC view when the repo itself is saved
under it. Repos saved as drive paths (Git routed to WSL) and in-distro repos
keep today's drive mapping.
2026-10-25 22:05:53 -07:00
OrcaWinandOrcaWin 7d6603aa17 [comment audit] Complete repository-wide comment cleanup (#33249)
* [comment audit] Align legacy failure-read counts with cached display

* [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci]

* [comment audit] Condense shared generation, path and preview comments [skip ci]

* [comment audit] Condense host, file and Git contract rationale [skip ci]

* [comment audit] Tighten Git lifecycle and provider contract comments [skip ci]

* [comment audit] Clarify shared host authority and launch isolation [skip ci]

* [comment audit] simplify local access mobile and native chat contracts [skip ci]

* [comment audit] simplify native chat presentation and state contracts [skip ci]

* [comment audit] simplify native runtime and provider contracts [skip ci]

* [comment audit] simplify migration and orchestration contracts [skip ci]

* [comment audit] simplify plugin trust and process capture rationale [skip ci]

* [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci]

* [comment audit] clarify remote recovery and launch wire rationale [skip ci]

* [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci]

* [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci]

* [comment audit] clarify sidebar, skill and SSH invariants [skip ci]

* [comment audit] condense structured chat delivery and timing rationale [skip ci]

* [comment audit] condense terminal replay and telemetry invariants [skip ci]

* [comment audit] clarify launch permission and terminal delivery rationale [skip ci]

* [comment audit] clarify workspace ownership and layout contracts [skip ci]

* [comment audit] wait for fitted and painted SSH terminal links

* [comment audit] clarify layout persistence and session ownership [skip ci]

* [comment audit] clarify host routing and workspace lifecycle contracts [skip ci]

* [comment audit] clarify startup persistence and app lifetime comments [skip ci]

* [comment audit] clarify recovery and activity boundaries [skip ci]

* [comment audit] condense activity and artifact invariants [skip ci]

* [comment audit] condense automation authority contracts [skip ci]

* [comment audit] simplify automation scheduling and history contracts [skip ci]

* [comment audit] clarify browser chrome and annotation lifecycles [skip ci]

* [comment audit] clarify browser routing and retained guest ownership [skip ci]

* [comment audit] clarify browser recovery and document ownership comments [skip ci]

* [comment audit] restore hosted runners required by native IME fixtures

* [comment audit] shorten preview, palette and dashboard invariants [skip ci]

* [comment audit] clarify terminal preview, dashboard and retention comments [skip ci]

* [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci]

* [comment audit] shorten rich-text and combined-diff ownership comments [skip ci]

* [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci]

* [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci]

* [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci]

* [comment audit] clarify rich Markdown source and selection safeguards [skip ci]

* [comment audit] clarify editor loading and emulator stream lifetimes [skip ci]

* [comment audit] trim emulator and feature tour narration [skip ci]

* [comment audit] clarify floating workspace and GitHub cache ownership [skip ci]

* [comment audit] trim provider controls and Linear filter explanations [skip ci]

* [comment audit] clarify pairing and chat resume lifetimes [skip ci]

* [comment audit] clarify chat delivery and transcript lifetimes [skip ci]

* [comment audit] clarify session options and host-owned status [skip ci]

* [comment audit] clarify draft durability and live-status precedence [skip ci]

* [comment audit] Trim chat send and model ownership explanations [skip ci]

* [comment audit] Shorten transcript ownership and send recovery comments [skip ci]

* [comment audit] Simplify native chat composer and transcript hook comments [skip ci]

* [comment audit] Shorten native chat scroll and structured session explanations [skip ci]

* [comment audit] Trim workspace picker and onboarding commentary [skip ci]

* [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci]

* [comment audit] Trim sidebar and session history explanations [skip ci]

* [comment audit] Condense session resume and review checks comments [skip ci]

* [comment audit] Shorten checks state and file explorer refresh comments [skip ci]

* [comment audit] Trim plugin bridge and source-control action rationale [skip ci]

* [comment audit] Trim source-control listing and review rationale [skip ci]

* [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci]

* [comment audit] Condense Settings account and appearance rationale [skip ci]

* [comment audit] Trim pairing and repository Settings explanations [skip ci]

* [comment audit] Condense Settings search SSH and terminal safeguards [skip ci]

* [comment audit] Shorten Settings voice and provider ownership comments [skip ci]

* [comment audit] Condense Settings draft persistence and host-state rationale [skip ci]

* [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci]

* [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci]

* [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci]

* [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci]

* [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci]

* [comment audit] Prune focus-read baseline after comment cleanup

* [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci]

* [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci]

* [comment audit] Clarify viewport geometry and skill update safeguards [skip ci]

* [comment audit] Tighten skill inventory, usage and host status comments [skip ci]

* [comment audit] Clarify usage ownership and resource kill safeguards [skip ci]

* [comment audit] Tighten tab selection, rename and search contracts [skip ci]

* [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci]

* [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci]

* [comment audit] Tighten provider loading and terminal recovery comments [skip ci]

* [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci]

* [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci]

* [comment audit] Tighten PTY startup and replay explanations [skip ci]

* [comment audit] Clarify PTY identity and delivery ownership [skip ci]

* [comment audit] Shorten remote reconnect and input replay rationale [skip ci]

* [comment audit] Condense terminal parking clipboard and file link comments [skip ci]

* [comment audit] Clarify IME ownership and trim trace history [skip ci]

* [comment audit] Shorten pane recovery and parked watcher explanations [skip ci]

* [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci]

* [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci]

* [comment audit] Trim renderer lifecycle and menu rationale [skip ci]

* [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci]

* [comment audit] Clarify renderer hook evidence and source ownership [skip ci]

* [comment audit] Tighten IPC ownership and reconnect explanations [skip ci]

* [comment audit] Trim renderer focus, paste and attention comments [skip ci]

* [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci]

* [comment audit] Trim agent launch, resume and ownership commentary [skip ci]

* [comment audit] Shorten account, browser and editor comments [skip ci]

* [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci]

* [comment audit] Condense launch recovery and editor tokenization comments [skip ci]

* [comment audit] Condense workspace selection and terminal pane rationale [skip ci]

* [comment audit] Condense terminal fitting and rendering recovery comments [skip ci]

* [comment audit] Condense terminal input and host routing explanations [skip ci]

* [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci]

* [comment audit] Condense structured launch and terminal link explanations [skip ci]

* [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci]

* [comment audit] Condense session persistence and workspace activation rationale [skip ci]

* [comment audit] Condense workspace routing and browser ownership rationale [skip ci]

* [comment audit] Condense mirror recovery and layout authority rationale [skip ci]

* [comment audit] Condense runtime transport and file safety rationale [skip ci]

* [comment audit] Condense runtime graph and structured session rationale [skip ci]

* [comment audit] Condense browser creation and remote tab recovery rationale [skip ci]

* [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci]

* [comment audit] Condense frame, startup and provider cache rationale [skip ci]

* [comment audit] Condense host twin and agent lifecycle rationale [skip ci]

* [comment audit] Condense browser authority and editor cleanup rationale [skip ci]

* [comment audit] Condense editor buffer identity and review cache rationale [skip ci]

* [comment audit] Condense provider lifetime and repository ownership rationale [skip ci]

* [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci]

* [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci]

* [comment audit] Condense cleanup ownership and listing admission rationale [skip ci]

* [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci]

* [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci]

* [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci]

* [comment audit] Condense cloud rollout and assignment safety rationale [skip ci]

* [comment audit] Condense relay session and database recovery rationale [skip ci]

* [comment audit] Condense relay placement and protocol compatibility rationale [skip ci]

* [comment audit] Condense build and packaging constraints [skip ci]

* [comment audit] Condense CI gates and validation isolation rationale [skip ci]

* [comment audit] Condense catalog identity and browser validation rationale [skip ci]

* [comment audit] Condense mobile browser and frame-budget rationale [skip ci]

* [comment audit] Shorten mobile route and browser test explanations [skip ci]

* [comment audit] Condense editor and session seam test rationale [skip ci]

* [comment audit] Trim session and terminal closure history [skip ci]

* [comment audit] Shorten terminal browser and census explanations [skip ci]

* [comment audit] Preserve root-only E2E diagnostic baseline

* [comment audit] Condense mobile seam and HTML preview test rationale [skip ci]

* [comment audit] Condense preview instrumentation and render harness rationale [skip ci]

* [comment audit] Condense bundle contracts and native-build constraints [skip ci]

* [comment audit] Shorten native packaging and CI routing rationale [skip ci]

* [comment audit] Condense release tooling and benchmark constraints [skip ci]

* [comment audit] Condense packaging and platform gate rationale [skip ci]

* [comment audit] Shorten mobile route and history rationale [skip ci]

* [comment audit] Condense mobile browser and component rationale [skip ci]

* [comment audit] Shorten drawer and workspace-form rationale [skip ci]

* [comment audit] Trim mobile review and editor-document explanations [skip ci]

* [comment audit] Condense editor lifecycle and Markdown explanations [skip ci]

* [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci]

* [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci]

* [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci]

* [comment audit] Condense bridge stream, native request and audio rationale [skip ci]

* [comment audit] Reduce bridge schema and page-client commentary [skip ci]

* [comment audit] Tighten mobile page bridge contracts [skip ci]

* [comment audit] Clarify mobile cache and session constraints [skip ci]

* [comment audit] Condense mobile route and lifecycle rationale [skip ci]

* [comment audit] Tighten mobile shell hook timing rationale [skip ci]

* [comment audit] Clarify route handoff and notification compatibility [skip ci]

* [comment audit] Clarify push registration and dictation cleanup constraints [skip ci]

* [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci]

* [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci]

* [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci]

* [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci]

* [comment audit] Tighten stream replay and queued session ownership rationale [skip ci]

* [comment audit] Condense mobile composer and reply-schema constraints [skip ci]

* [comment audit] Clarify mobile transcript recovery and session ownership [skip ci]

* [comment audit] Clarify mobile queue and source-control compatibility [skip ci]

* [comment audit] Condense storage and task launch constraints [skip ci]

* [comment audit] Clarify task schemas and terminal document ownership [skip ci]

* [comment audit] Tighten terminal input and viewport rationale [skip ci]

* [comment audit] Shorten terminal and RPC recorder contracts [skip ci]

* [comment audit] Condense recorder engine and connection contracts [skip ci]

* [comment audit] Shorten host credential and relay recovery comments [skip ci]

* [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci]

* [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci]

* [comment audit] Condense catalog and relay entry contracts [skip ci]

* [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci]

* [comment audit] Condense WSL relay and E2E regression rationale [skip ci]

* [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci]

* [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci]

* [comment audit] Shorten layout oracle and regression explanations [skip ci]

* [comment audit] Condense paired recovery and ownership regression contracts [skip ci]

* [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci]

* [comment audit] Condense terminal rendering and IME regression rationale [skip ci]

* [comment audit] Shorten terminal recovery and benchmark contracts [skip ci]

* [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci]

* [comment audit] Condense workflow routing and release rationale [skip ci]

* [comment audit] Condense mobile security and installer contracts [skip ci]

* [comment audit] Shorten native rationale and stylesheet workarounds [skip ci]

* [comment audit] Shorten Windows visibility probe rationale [skip ci]

* [comment audit] Condense embedded release and signing rationale [skip ci]

* [comment audit] Retain current main test behavior in final reconciliation [skip ci]

* [comment audit] Complete final comment review across current source inventory

* [comment audit] Handle consolidated audit reports before final CI

* [comment audit] Preserve credential storage census detail [skip ci]

* [comment audit] Scale broad changed E2E selections with verified timing shards

* [comment audit] Align E2E assertions with browser close and settled session metadata

* [comment audit] Match existing E2Es to current resource and windowing contracts

* [comment audit] Observe the shared server outage notice in reconnect coverage

* [comment audit] Make existing renderer probes measure settled tab and PTY work

* [comment audit] Align palette E2E selector with task search

* [comment audit] Refresh remaining palette search selectors

* [comment audit] Keep selector correction within selected audit specs

* [comment audit] Await emoji fixture settings inside renderer evaluation

* [comment audit] Cancel fixture fit retries between terminal tests

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-25 04:05:46 -07:00
Neil d81958f0e8 refactor: group local and WSL filesystem watchers (#33407)
* refactor: group local and WSL filesystem watchers

* test: isolate startup ordering from watcher initialization

* test: collect the startup preflight subject once
2026-10-25 00:13:36 -07:00
OrcaWinandOrcaWin e7a567b178 fix(worktrees): explain the shallow-clone local base warning and let users stop the refresh (#33260)
* fix(worktrees): explain the shallow-clone local base warning and let users stop the refresh

When a shallow clone hides the shared history, the local base fast-forward
refusal now says so and shows the git fetch --unshallow command, instead of
blaming local-only commits. Every refresh-failure toast also gets a
"Stop updating local main" action that turns off Keep Local Main Up to Date.

* feat(worktrees): add an Unshallow button that runs the fetch in a new terminal tab

* feat(worktrees): rename the opt-out to Disable Check and explain it in a tooltip

* fix(worktrees): harden the Unshallow launch

Reuse createNewTerminalTab (owner-routed, no focus read) instead of a
parallel launcher, report real host-launch outcomes before dismissing,
only offer the button for shell-inert remote names, route activation by
the creation host, ignore double clicks, and treat only a silent exit 1
from merge-base as a missing merge base.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 22:45:21 -07:00
0302e0a5fd fix(git): preserve unmerged branches with no net file changes (#32181)
* fix(git): preserve unmerged branches with no net file changes

* fix(workspace): show launch actions in empty tab groups (#32182)

* fix(workspace): show launch actions in empty tab groups

* fix(workspace): resolve empty-pane browser policy by owner

* Scope daemon inventories for safe worktree teardown (#32171)

* Fix scoped daemon inventories without changing terminal input routes

* Serialize daemon owner inventory refreshes safely

* Serialize daemon owner inventory refreshes safely

* test(daemon): protect baseline inventory budgets and recovery

* fix(daemon): clear route proof during synthetic exits (#32196)

* docs: condense test anti-pattern guidance (#32201)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* refactor: colocate repository draft input and tests (#32155)

* test: consolidate discovery coverage and replace copied oracles (#32211)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* test: retire copied oracles and implementation inventories (#32206)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* test: remove classification inventories and verify settled boundaries (#32216)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* test: keep explicit regressions without legacy algorithm copies (#32218)

* test: keep explicit regressions without legacy algorithm copies

* test: use Number.NaN in latency fixtures

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* ci: avoid slow Ubuntu mirrors during WebKit setup (#32205)

* ci: reuse bounded Ubuntu mirror setup for WebKit

* ci: preserve Ubuntu setup for dispatched older refs

* perf(renderer): scope runtime connection and parking subscriptions (#32199)

* fix(chat): allow visuals to declare open without breaking scripts (#32226)

* fix(native-chat): keep queued messages in the chat's own record, so a refused one can't be skipped (#30418)

* fix(native-chat): a refused queued message still blocks the queue when its card write fails

Every reader of a queued card goes through one derived view of the journal's dispatch facts,
so a card whose message was refused reads as returned even when its card-table update and heal
both failed. One decision and one writer store the derived state for every action.

* refactor(native-chat): keep each queued message in the chat's journal, not a second table

A queued message lived in its own SQL table, and a hook that may be skipped copied
each refusal from the journal into it. When that write failed, a refused card
vanished: the cards behind it sent, and its Send and Delete did nothing.

Each queued message is now a `queue` row in the chat's journal beside the
submission that hands it off and the dispatch row that answers it. The reducer
folds the card from those rows, so a refusal returns the card and holds the
cards behind it with nothing written, and replay reads the same. Every action
writes its own rows in one transaction. Cards an earlier build stored are read
through the same classification until they move: each action moves its own
card, and a bulk import runs once per open. A new epoch restates every card
still owed an answer in the transaction that deletes the old rows.

The settlement hook and its savepoint, the open-time heal, the drain's heal and
the derived-card view are gone. The published card list is unchanged.

* test(native-chat): adapt the queue tests to cards kept in the journal

Cards are written through the journal, so tests queue them with their body
alone and hand them off under their real fingerprint. Tests of the removed
settlement hook, heal and SQL store are deleted; the text-update spec now
covers the edit decision alone, and a test of an earlier build's stored holds
seeds the legacy table and reads the imported cards. Rolling over an epoch now
keeps a card whose hand-off was never handed over, so that test checks the
retried id never sends rather than that nothing sends.

* test(native-chat): a refused card holds the queue through rewind, a new epoch and a restart

One whole-flow test through the host: the refusal writes no card row, a rewind
that retires the refused hand-off and a rolled-over epoch keep the card
returned, a restart and Resume send nothing behind it, its own Send refused
again still holds the queue, and its Delete releases the next card in order.

* test(native-chat): an earlier build's queued cards move into the journal once, each read the same

One mixed-import test through the host. The first open's import fails and
moves nothing, yet every card reads as it will; Delete and Send each move only
their own card. The next open moves the rest in one transaction, writing a
refusal only where its hand-off is gone, nothing for answered cards, and leaving
an unreadable row on disk. A later command turn spends a moved command card, a
third open writes nothing, an old Delete replays from its receipt, and a chat
with only answered legacy rows clears them in a delete-only transaction.

* test(native-chat): adapt typechecked tests to cards kept in the journal

The row-kind table lists a queue row, the clear and legacy-import tests
match the current signatures, and the downgrade test keeps only the
leftover-send case: a queued card is now a newer-version journal row an
older build keeps read-only, and users don't downgrade.

* refactor(native-chat): drop the journal row writer's unused transaction hook

No caller passes one since the queue's savepoint bookkeeping left.

* test(native-chat): downgrade probes write rows at the version older builds read

The highest version this build reads is now 4, the queue row's. A newer
build's row in these probes is written at version 3 so the older build
meets its kind, not a newer version, and the Stop-event probe hands the
older build the rows as stored, since a read raises each row's version.

* test(native-chat): unit tests follow the queue row's version 4

The legacy-import test opens real SQLite, so it runs in the Node runtime
project. The newest version this build reads is now 4, and a read raises a
row's version to it, so the turn-lifecycle test no longer pins the
in-memory version (the written one stays pinned by journalRowSchemaVersion).

* fix(native-chat): one batch echoing two refused queued messages spends both

The per-row delivery check returned only the first waiting card an echo
proved delivered, so when one settlement carried echoes for two cards the
second stayed waiting and was sent again. It now returns every card the
row proves delivered, and the fold withdraws each; replay folds the same.

* fix(native-chat): a rewind moves an earlier build's cards into the journal first

While the open's import had failed, an earlier build's card that read as
refused through its hand-off lost that reading when an in-place rewind
retired the hand-off: it disappeared and the card behind it sent. The
rewind now adopts every legacy card ahead of its tombstones and deletes
their rows in its own transaction, as an epoch replacement already does,
so the fold latches each card's reading before its hand-off is gone.

* fix(native-chat): keep an earlier build's card whose hand-off is still unconfirmed

The import dropped a legacy card handed off as a send whose delivery was
unknown, though the fold keeps such a card linked and a later refusal
returns it. The import now keeps every card whose hand-off a later row can
still answer, through the same check the reducer folds dispatch rows by.

* fix(native-chat): one delivery echo withdraws at most one queued card

One provider echo proves one delivery, yet it withdrew every waiting card
with the same text whose hand-off was refused, so a second identical card
was lost. The fold now pairs each echo with one card: on its own row the
earliest-positioned match no other echo of that row took, and for a card
that waits again the earliest applied echo no card has taken. Paired echoes
live in the fold's state, so live and replay pair the same; readers of
cards not yet imported work on a copy.

* fix(native-chat): a queued message handed to the agent is sent again only on proof it never got it

Pressing Stop after Orca handed a queued or steered message to the agent,
before the agent confirmed it, put the card back in the queue, and Resume
or the next send sent it again. A check matching the agent's echo of the
text was the only guard against a double send, and it kept trading a
resend for a lost card. Whether the card waited again was decided by the
rejection's category, not by proof the agent never got the message.

A refused hand-off now waits again only where Orca proves the agent never
received it: the host still held it, or whoever settled it recorded that.
A failure still returns the card and holds the cards behind it. Anything
else may have reached the agent, so the card is spent and its message
reads as sent, derived on every fold from the stored rejection: with the
turn it was answered into, as that turn's unconfirmed message where it was
handed over; withdrawn before any turn opened, as a person's own send a
Stop took back. The reopen mark ignores a recovered doubt, and the restart
reconciler no longer judges a queued hand-off unsent from what history
lacks. The echo text matching and its pairing are deleted.

A close or quit while the agent starts settles sends with the unrun hold.
The host hands nothing to a child before its start answers
(structured-agent-session-delivery-loop.ts: a handover needs phase
ready), so such a send went to an earlier child and may have run: it now
reads as sent instead of waiting again.

* fix(native-chat): Claude's report that it removed a queued message unrun lets the card wait again

Claude says when it removed a send without running it: the interrupt
receipt's cancelled list, or a command_lifecycle cancelled, discarded or
refused frame for a command that never started. Every one settles through
rejectClaudeDispatchWaiters, which now marks the settlement unrun. For a
queued card's hand-off the host records that as proof the agent never got
it, so the card waits again under the queue's pause. A direct send keeps
the plain refusal: there the proof would owe it as stopped history.

* fix(native-chat): an earlier build's card stopped after hand-over follows the same rule on upgrade

Earlier builds put a card back to waiting after any refusal without a
verdict and cleared its link, so a card the agent may already have would
be sent again after the upgrade. The card's own hand-offs in the journal
decide now: unchanged and last sent as one that may have reached the
agent, it is spent on that hand-off and its message reads as sent; edited
or sent again since, it is held for its own Send under a host-only marker
that publishes as a plain hold; with no hand-off in doubt, including one
that never left Orca, it waits as before. The import writes a card spent
on a hand-off in doubt, so its message shows.

* fix(native-chat): read a queued row's hand-off before the lookup that uses it

The lookup's callback lost the narrowing of the row's change, which the
mobile test typecheck rejects.

* test(native-chat): a card steered into a turn a Stop ends stays sent in the Stop tests

Three tests pinned the old rule, that a card handed into a running turn
comes back to waiting when a Stop ends it. Under the new rule nothing
proves the agent never got it, so the card is spent: with the turn named,
its message reads as that turn's unconfirmed one; without, as a send the
Stop took back. Neither is ever sent again.

* fix(native-chat): a queued message stopped before Orca handed it to the agent waits again

A Stop that aborts a hand-off after its row is written but before the agent call now records
the withdrawal as never reaching the agent, for queued hand-offs only, so plan D's rule returns
the card to waiting under the Stop's pause instead of spending it. Direct sends keep the plain
withdrawal (the flag would owe them as stopped history). The agent's own refusal while aborted
is kept rather than overwritten, and an abort thrown during the call stays in doubt.

* fix(native-chat): a queued message stopped while the agent prepares it waits again

The abort signal reaches only an adapter's preparation (its options, the previous turn opening),
never its write, so the signal's own reason thrown back is the adapter saying nothing was
written. The handover now reads that, for queued hand-offs only, as never reaching the agent,
so the card waits again under the Stop's pause. Claude no longer wraps that abort as an option
refusal, which had returned a stopped card as a refused one blocking the queue. A real refusal
keeps its cause and its barrier; any other throw stays in doubt and is never resent.

* test(native-chat): the usage-limit Continue backlog case writes its card through the journal queue

Main's #30396 wrote its backlog card with the stored fingerprint and host-instance stamp the
journal queue derives instead; the card the Continue must yield to is the same waiting card.

* test(claude): the real-CLI queued Stop expects the unrun mark Claude's cancelled-unrun receipt carries

* fix(native-chat): retain spent legacy messages when import fails

* Bound per-write chat selector work and guard ownership scans (#32109)

* Guard per-write chat selector work with shared ownership indexes

* Use checked sender session identities in the scale fixture

* Read each indexed tab row once during snapshot rebuilds

* Fix lexical binding and cache matching in selector lint rules

* Keep behavioral selector guards and resolve imported bindings

* Require selector binding definitions to declare functions

* Key performance subscribers by saved chat scope

* Show activity status with worktree-style counts (#26443)

* Show prioritized agent counts on the activity bell

* Move activity badge into the corner and shrink its count

* Use a fixed side column for activity status and counts

* Fix activity count spacing and status alignment

* Use worktree-style superscript counts for activity

* Keep activity count positions and update only number styling

* Keep idle layout fixtures out of active agent status updates

* Show child workspace statuses in the children toggle (#32224)

* Show child workspace status counts in lineage toggle

* Give child status summaries room and wrap narrow layouts

* Use solid working dots and keep child summaries on one line

* Restore children icon and clarify worktree tooltip

* Keep child totals clear of status counts in narrow layouts

* fix: preserve workspace review references when clearing active reviews (#32263)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* Saving a file, then closing that tab mid-save, no longer leaves its other tabs looking clean (STA-11786) (#32259)

* fix(editor): settle a save's shared record when the saving tab closed mid-write

With one file open in two workspaces, closing the saving tab while its write was in
flight left the other tab's disk baseline, dirty flag, draft and loaded text on the
pre-save version: every completion step looked the closed tab up by id and found
nothing, and the saved event listed only the closed tab as the record's views. An undo
back to the old text then read as clean and closed without asking.

The write now captures its record id with no await before the write, and completion
(rebaseline, dirty/draft settle, conflict clear or refusal banner, saved event's view
list) goes through a live view of that record, preferring a writable one. The save
runner and the write-lock wait do the same, so a save queued by a tab that closes
still writes through the record's remaining view. The chat visual's saved-event
listener also falls back to the record's views.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(editor): follow a closed saver's record through re-keys, kept records and listeners

Review follow-ups to the closed-saving-tab fix:

- Completion now finds the record through the view ids captured before the write,
  not the record id: a re-key mid-write (an unpinned runtime tab resolving its
  pairing) keeps views but changes the id, which brought the original bug back.
- A fallback writer (asking view closed while queued or waiting on the file lock) is
  chosen like autosave/restart's writer: writable and on a current connection, so a
  stale-connection sibling no longer throws when a current view exists.
- With no view of the record left, a kept record (no-prompt removal) is dropped when
  the write saved its text, else rebaselined; other open views of the file (a
  detached peer) take the written baseline, keeping their edits.
- A save may mark a record clean through a read-only or CSV-preview view (markFileDirty
  `written`); such views still never become dirty.
- Markdown preview refresh and the CSV pending-cell listener match the saved event's
  record views, not only the saving view's id.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(editor): name the closed-view save harness as a test fixture

The focus-read ratchet scans non-test sources; the `.test-fixture.ts` suffix marks this
harness as test-only like the repo's other fixtures.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(ci): list the closed-view save rig as a unit support bridge

The rig builds on the autosave test fixture, which takes the editor slice harness, so
the routing check saw it as a product consumer of that harness. Bridges are an explicit
list, like the browser drawn-from-main fixture.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* Show task names for Codex subagents (#32243)

* fix: name Codex subagents from rollout metadata

* Hide generic default role in child agent rows

* Reject non-regular Codex identity transcripts without blocking

* [comment audit] Shorten preload comments (#32257)

* chore: audit preload root comments and track repository coverage

* docs: track repository comment audit coverage

* chore: keep comment audit tracker local

* chore: shorten preload API contract comments

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix: offload macOS deletion spawns and reduce branch cleanup (#32227)

* fix: reduce worktree deletion branch cleanup overhead

Refresh only the remote base branches needed to prove a branch merged, with automatic maintenance disabled. Skip merge simulation when no common ancestor is available and preserve the branch when its remote base cannot be refreshed.

* fix: preserve fetch mappings and live branch cleanup fallbacks

* fix: spawn macOS worktree deletion commands off the main thread

* test: remove redundant cases and start comprehensive value tracker (#32269)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix(web): the browser client advertises the desktop's chat capabilities, so its host opens chats (#32253)

* fix(web): the browser client advertises the desktop's chat capabilities, so its host opens chats

The browser client's handshake never named agent-session.structured.v1, so a paired
host refused every agentSession call with structured_agent_session_unsupported and
withheld chat tabs. The desktop window, the Electron remote handshake and the browser
client now build their lists from one shared array of what the renderer's chat implements.

Fixes #32145

* refactor(web): keep the browser client's capability list beside the desktop's in shared

The host-side test parses both handshakes with the host's own parser, which the node
typecheck project can only import from src/shared.

* test(web): assert close-intent support in the auth frame, not the whole capability list

* fix(terminal): a timed-out CLI split names its pending pane, and a retry can never re-run its command (#32278)

* fix(terminal): a timed-out CLI split names its pending pane and can be retried without splitting again

Fixes #32169

* fix(terminal): a blank retry id or a closed pane never re-runs a split's command

* refactor(terminal): a split retry only waits for its pane and never asks the window again

* fix(terminal): a split missing from the graph is reported pending, never closed

* fix(ssh): never block or over-read on an SSH identity file (#32274)

Every place Orca reads an SSH key or identity file now goes through one
reader that refuses directories, FIFOs and devices (including through
symlinks) with a nonblocking open, and caps the read at 1 MB. An auth
failure now names any identity file that was skipped and why.

Fixes #31965. Reported by @Huge, who also proposed the first fix in #31966.

Co-authored-by: Martin Vlach <mr.huge@seznam.cz>

* test: finish fixture work before unit-test cleanup (#32285)

* fix(mobile): keep a terminal tab's chat identity while its handle is unchanged (#32251)

* fix(mobile): keep a terminal tab's chat identity while its handle is unchanged

A status frame that omits agentType or providerSession flipped chat back to the
terminal for tabs with no launchAgent (#31977), and left a launched tab with no
session to read (#32134). Mobile now remembers the last resolved agent and
session per tab and handle, so a missing field is treated as missing evidence.

* fix(mobile): keep remembered chat read-only and drop it on relaunch

A chat resolved only from this phone's memory may sit on a bare shell, so its
writes stay locked until live status confirms the agent. A live agent frame
without a session no longer inherits the previous conversation, and the
latest remembered agent outranks an older launch hint.

* fix(mobile): gate native-chat image sends on a confirmed live agent

An image send from a chat only this phone remembers could paste into a bare
shell; it now shares the confirmed-agent gate with text sends.

* fix(mobile): type remembered chat identity structurally

Importing MobileSessionTab into the eligibility module pulled React Native
globals into the e2e program through nested-agent-idle-session-owner.spec.ts,
retyping setTimeout as number there.

* fix(terminal): never present a half-built synchronized-output frame on reveal or recovery (#32281)

Settled reveal and every other atlas recovery forced the current xterm buffer
onto the screen even while a TUI held DEC 2026 synchronized output. Codex
erases rows inside each held redraw, so a reveal mid-redraw showed a blank
pane for a frame (#32198).

Viewport presents now have one mode: they keep the last coherent canvas and
let xterm present when the TUI closes the frame, bounded by xterm's 1s
synchronized-output safety timeout. Structural replay keeps its retained frame
up until an open synchronized frame closes, bounded by its existing hold cap.

Co-authored-by: YEONGUK LEE <contact@abruption.dev>

* refactor(renderer): colocate task page pagination rules (#32297)

* perf(test): bundle Zod once per isolated Vitest invocation (#32298)

* Use incremental tsrs locally and update Bun to 1.4.4 (#32276)

* Use incremental tsrs for local typechecks

* Update pinned Bun to 1.4.4

* fix(deps): bump dompurify to 3.4.17 and docs next to 16.4.1 for security advisories

dompurify 3.4.17 patches GHSA-cg2g-xh64-v4q7, GHSA-mrv2-3pqr-j5jr and
GHSA-c6gr-qvv4-5v3f in the root and mobile lockfiles. next 16.4.1 patches
GHSA-2464-fhr4-w9xw, GHSA-673q-mjm6-r5xr and GHSA-p3m5-59mp-jv5r in the
docs site.

* Prepare mobile bundle for smoke benchmarks

* Give native compiler benchmarks a larger ARM runner

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* test: trim redundant coverage and fix vacuous assertions (#32305)

* test: remove redundant cases and repair behavioral assertions

* docs: reconcile audit records with current main

* fix(deps): apply security updates from #32101

Apply the six-file DOMPurify 3.4.17 and Next.js 16.4.1 patch from PR #32101 to resolve the dependency security gate for this test audit. Original patch by Brennan Benson.

* test: satisfy changed-line quality gates

Add required braces and replace filter indexing with find. Rerun both CI quality gates and the 27 affected relay tests; refresh audit hashes and record the corrections.

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* fix(ci): retain ambiguous unit-test import consumers (#32366)

* test: exercise real boundaries and trim redundant cases (#32353)

* test: replace redundant checks with observed behavior

* docs: reconcile completed audit batches and upstream review

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>

* fix(git): retain zero-net history with current merge-base guards

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Martin Vlach <mr.huge@seznam.cz>
Co-authored-by: YEONGUK LEE <contact@abruption.dev>
2026-10-24 13:26:44 -07:00
Neil 4e7c3538e5 fix(wsl): stop the worktree watcher killing a cold distro's boot (root cause of #33145) (#33175)
* fix(wsl): the worktree watcher no longer probes a WSL distro's home synchronously at startup

For a project inside WSL the watcher resolved the workspace root before seeing the
repo is a UNC path it skips. That ran wsl.exe 'echo $HOME' synchronously with a 5 s
timeout: on a cold distro it blocked main for 5 s and killed the distro's first
boot, after which every wsl.exe call failed with Wsl/Service/E_UNEXPECTED. The
watcher now skips WSL repos first and resolves other roots asynchronously.

* revert(wsl): drop the transient E_UNEXPECTED retry from #33145

The error came from the watcher's synchronous home probe killing the distro's first
boot (fixed in the previous commit). Once that happens the distro answers every
wsl.exe call with E_UNEXPECTED for 14+ s, so a 2.5 s retry never recovered it; on
the measuring box it failed every attempt. With the trigger gone the retry is dead
weight.

* fix(wsl): skip mirrored WSL roots without probing; keep the direct-git guard

Review follow-ups: a Windows repo whose workspace root mirrors into WSL also probed
the distro's home (async, still killed at 5 s) only to skip it, so the watcher now
recognizes the mirror and keeps just the Git-common watch. The revert keeps one
piece of #33145: a WSL service failure no longer falls back to the login shell and
switches the direct git read path off.
2026-10-24 08:05:08 -07:00
Neil 9a8ede3be7 fix(wsl): retry a git read when wsl.exe hits a transient service error on a booting distro (#33145)
* fix(wsl): retry a git read when wsl.exe hits a transient service error on a booting distro

On a cold distro, concurrent first wsl.exe launches intermittently fail with
Wsl/Service/E_UNEXPECTED before the guest runs. The bridge's worktree listing gave
up on the first one, so a WSL project showed 0 workspaces until the user acted.
Reads now retry that one error up to three times (0.25, 0.75, 1.5 s).

* fix(wsl): bound transient-error retries per distro, honour aborts, keep the direct git path

Review follow-ups: after one read spends its retries the distro fails fast for 5 s
instead of every queued read sleeping in its admission slot; an abort or a disposed
caller ends the backoff at once; a transient service error no longer falls back to
the login shell and turns the direct read path off.

* test(wsl): drop type assertions from the transient-retry test
2026-10-24 05:13:46 -07:00
NeilandClaude 0b4028ae99 fix(worktrees): don't prune worktrees the other Git (Windows or WSL) created (#33103)
* fix(worktrees): don't prune worktrees the other Git (Windows or WSL) created

On Windows one repo can hold linked worktrees written by both Git for Windows
(link C:/...) and WSL Git (/mnt/c/..., /home/...), e.g. after the project runtime
toggle switched it. Each Git reads the other's links as missing, and the
`git worktree prune` Orca runs after removing a worktree (and before deleting a branch
held by one) deleted those worktrees' metadata, leaving checkouts git no longer knows.
All five prune sites now go through pruneGitWorktrees, which reads the link files first
and refuses (through each site's existing failure path) when any link is foreign to the
Git that would run.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(worktrees): lock the other Git's entries for the prune instead of skipping it

Review: refusing the whole prune broke stale-registration recovery and left branches
undeletable in mixed repos, and reading <cwd>/.git/worktrees missed bare and linked
checkouts. Now the other Git's entries get a marked lock file for the prune (git never
prunes a locked entry; a user's existing lock is kept), the rest prune as before, and
the admin dir comes from rev-parse --git-common-dir once routing is settled.
//server/share links read as Windows Git's.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(worktrees): drop a type assertion from the prune guard test

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(worktrees): serialize guarded prunes per repo and reclaim stale guard locks

Re-review: two prunes in one repo could interleave so the first's unlock let the
second prune the other Git's entry, and a crash between lock and unlock left a lock
read as the user's forever. Prunes now chain per admin dir, and a lock carrying the
guard's own reason is reclaimed.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(worktrees): assert the overlapping prune waits, not only its outcome

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-24 02:26:50 -07:00
8604a4d570 fix(bitbucket): recognize ssh.bitbucket.org remotes (#31002)
* fix(bitbucket): recognize the SSH host alias

* fix(bitbucket): recognize the SSH host alias on current main

Apply Chen's original SSH alias fix to current main, preserving canonical
Bitbucket browser links and adding case/port and lookalike-host coverage.

Co-authored-by: Chen <zwq19980411@gmail.com>

---------

Co-authored-by: Neil <neil@stably.ai>
2026-10-23 20:43:39 -07:00
df97aa3a6f fix(gitlab): recognize SSH-over-443 remotes (#30998)
* fix(gitlab): recognize SSH-over-443 remotes

* fix(gitlab): canonicalize SSH-over-443 project and review URLs

Reuse the existing GitLab transport-host normalizer in project identity,
hosted links, and manual merge-request links. Cover alias variants,
lookalikes, nested groups, and canonical encoded links.

Fixes #30876

Co-authored-by: lorenzozanee <wyz0707@proton.me>

* test(gitlab): cover alias identity consumers

---------

Co-authored-by: Neil <neil@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-10-23 20:30:43 -07:00
Neil 4975e2451e fix(git): preserve binary output through termination barriers (#32633) 2026-10-23 19:21:53 -07:00
fancivezandNeil 2fb3de3405 fix(git): close subprocess stdin for SSH-signed commits (#30961)
Co-authored-by: Neil <neil@stably.ai>
2026-10-23 19:20:58 -07:00
OrcaWinandOrcaWin 4687e90a16 [comment audit] Continue main-process comment cleanup (#32946)
* [comment audit] Shorten daemon, hook, and emulator comments [skip ci]

* [comment audit] Shorten VM, conversation, SQLite, and theme-import comments [skip ci]

* [comment audit] Tighten Git execution and source-control comment contracts [skip ci]

* [comment audit] Clarify repository probes and maintenance comments [skip ci]

* [comment audit] Shorten status, diff-cache, and checkout comments [skip ci]

* [comment audit] Clarify worktree lifecycle and WSL routing comments [skip ci]

* [comment audit] Shorten forge identity and review-refresh comments [skip ci]

* [comment audit] Tighten hosted-review client comment contracts [skip ci]

* [comment audit] Clarify GitLab compatibility and review comments [skip ci]

* [comment audit] Tighten hook ownership and watchdog rationale [skip ci]

* [comment audit] Condense host and integration compatibility comments [skip ci]

* [comment audit] Clarify IPC authority and remote-routing rationale [skip ci]

* [comment audit] Finalize file-import and containment comment cleanup

* [comment audit] Fix stale Jira type imports blocking full CI

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 16:27:37 -07:00
Neil c32d46850b fix: offload macOS deletion spawns and reduce branch cleanup (#32227)
* fix: reduce worktree deletion branch cleanup overhead

Refresh only the remote base branches needed to prove a branch merged, with automatic maintenance disabled. Skip merge simulation when no common ancestor is available and preserve the branch when its remote base cannot be refreshed.

* fix: preserve fetch mappings and live branch cleanup fallbacks

* fix: spawn macOS worktree deletion commands off the main thread
2026-10-23 02:24:07 -07:00
Jinjing f1189be850 Offer Resolve with AI for paused merge, rebase, and cherry-pick (#28569)
* Offer Resolve with AI for paused merge, rebase, and cherry-pick

A paused operation can need recovery after every conflict is staged, but the Resolve with AI action only appeared when unmerged paths remained. The operation banner now offers it whenever AI actions are visible and an operation is in progress.

Starting an AI conflict-resolution agent is also blocked while a Git operation is running. The composer, the banner and card buttons, and the agent launch itself check the running-operation state, so a launch that began during agent detection cannot start while Git is still running.

* Block paused-operation recovery only on Orca's own Git work

The previous gate scanned the host's process table and refused "Resolve with AI" whenever it could not prove no Git process was running. That refused recovery on older paired hosts, on Windows whenever any Git process ran anywhere on the machine, and while a paged `git reflog` or `git grep` was open in a terminal.

- The host tracks Orca's own mutating Git commands per worktree (including ones still queued) and reports them through `git.mutationInFlight`. Reads and fetch/push/ls-remote are not tracked.
- Recovery is refused only when the host reports that work. Older hosts without the method keep today's behavior.
- The gate applies only to launches from the paused-operation banner and conflict card, not every resolveConflicts launch, so the Checks panel's PR-conflict flow is unchanged.
- The agent prompt now tells the agent to stop if index.lock exists or another git command is running, which covers Git started outside Orca on every host. With no unmerged files, the prompt asks the agent to find why Git paused instead of listing zero conflicts.

* Preserve Git mutation state until terminated commands exit

- Cancel stale recovery checks and agent launches when their dialog or scope closes
- Guide rebase recovery agents to amend fixes after failed exec steps

* Cover failed rebase exec recovery with real Git

* Recognize global Git options in worktree mutation tracking
2026-10-22 12:08:38 -07:00
Brennan Benson 1a0e1e9414 PR conflict previews fetch the base branch through the shared fetch controller (#30654)
* fix(github): PR conflict previews fetch the base branch through the fetch controller

The conflict summary on a PR card spawned its own `git fetch origin <base>`,
outside RuntimeRemoteFetchController: it refetched a base a worktree create
had just refreshed (and vice versa), ran git's auto-maintenance in the
background, and never armed the idle loose-ref sweep.

It now asks the runtime's controller for the exact-base refresh creates
already use (same argv, per-remote queue, dedupe and freshness), waiting at
most the old 10 s before falling back to the local ref. The runtime registers
its controller at construction, since the IPC and poller paths have no
runtime handle.

Also corrects the controller comment that claimed every fetch skips
auto-maintenance: full fetches keep it on purpose (#8039).

* test(github): read the mocked fetch's WSL option without a type assertion

* test(github): give the conflicting fallback-lookup test a git mock for its conflict summary

* test(github): check WSL routing without duplicating fetch policy

* fix(git): bound preview fetch ownership without cancelling create

* Preserve repository identity after preview cancellation
2026-10-20 17:14:25 -07:00
Neil b03ac56c00 fix(git): forward stdin on buffered Git reads (#30634)
gitExecFileAsyncBuffer never passed stdin to the child, so a buffered read
that needs it (cat-file --batch, hash-object --stdin) waited for EOF until
its timeout. Add the option and forward it, with a real-Git regression
test.
2026-10-19 14:18:03 -07:00
zzr 95f544aedf fix(git): derive clone folder from scp-style remote path (#30763) 2026-10-19 10:58:05 -07:00
Stefan StefanovandClaude Sonnet 5.5 346f6028a3 fix(git): stage and discard files in WSL repos opened by UNC path (#30693)
The Git runner sends a command into WSL when the working directory is a
\wsl.localhost UNC path, even if no WSL distro is set in the options.
literalPathspec only looked at the options, so Windows-style backslashes
in file paths reached Git inside WSL unconverted and stage, unstage and
discard failed to match the file.

literalPathspec now also checks the worktree path, and every caller
passes it.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 04:43:08 -07:00
Neil f6e9e649f4 fix(projects): a project added from the CLI, web or a paired desktop through a symlink or subfolder shows its workspaces (#30657)
* fix(projects): a project added from a paired client or the CLI through a symlink or subfolder shows its workspaces

* fix(git): keep a literal backslash in a POSIX checkout root
2026-10-19 01:54:59 -07:00
Brennan Benson 05688e7856 fix(git): stop starting Git in deleted project folders and for removed projects (#28822)
* fix(worktrees): skip Git sweeps for missing repo folders

* fix(worktrees): skip missing roots in headless scans

* fix(worktrees): check missing roots at Git listing boundary

* test(git): treat branch removal fixture as existing repo

* test(git): keep mocked worktree roots present for Git cases

* test(git): await host preflight in scan sharing fixtures

* revert(worktrees): drop the listing pre-check for missing repo folders

Reverts 82ccf05a4d, 13854fd3dc, c19604012a, 3f1b7ef64c, a868225608
and 962d9218c2. The reader-level stat ran in the wrong WSL distro, added
a wsl.exe spawn per listing, and only covered git worktree list. The next
commits replace it with a gate at the shared Git runner.

* fix(git): stop spawning Git in deleted native project folders

A registered project whose folder was deleted spawned a failing Git
process (spawn git ENOENT) on every sweep: git worktree list and the
git remote probes. The shared async Git runner now remembers, in memory,
a native cwd once a spawn ENOENT is followed by a definitive lstat
absence. Later calls for that cwd re-check with lstat before admission
and, while it is still absent, reject with the same spawn ENOENT shape
without starting a process. The entry is dropped as soon as the folder
exists again. WSL routes and UNC paths are never recorded, so their
behavior is unchanged.

The fenced-output readers move unchanged to captured-git-output.ts so
git-exec-file.ts stays under the file-length limit.

* fix(github): skip queued PR refreshes for removed projects

A PR-status refresh queued before its project was removed still ran
and probed Git (git remote) against the removed path. The drainer now
re-checks registration for each entry before any Git or GitHub call,
using the same predicate the refresh IPC entry points use, so every
removal path (sidebar, CLI, phone, closed window) and the rate-limit
re-queue are covered. The skip mirrors the existing not-visible skip,
so there is no new skip reason on the wire.

* fix(git,github): skip missing folders without a span; pure refresh registration

Round-2 review fixes.

- The known-missing-folder check now runs before withGitSpan in both
  async runners, so a skipped call starts no process and writes no
  git.exec span. The command is resolved lazily, only for a cwd already
  recorded, and the git.known_missing_cwd attribute is gone.
- The gate uses stat, which follows symlinks the way spawn's chdir does,
  so a symlinked project root whose target is gone is recorded too.
- The Set is re-checked after the stat, so concurrent first failures
  record and warn once.
- The PR-refresh drain check is now a pure predicate: is a project still
  registered at the candidate's path on its execution host. It reads the
  store each time and no longer resolves runtime Git options, which could
  throw inside the drain loop. Matching by path and host, not repo id,
  keeps a re-added project's coalesced refresh from being dropped.

* refactor(git,github): one wrapper per Git runner; drainer holds its check

Tripwire re-plan cuts, no behavior change except one Windows edge.

- The missing-folder gate is a single wrapper around each exported async
  runner (gitExecFileAsync outside the fetch-head lock, and
  gitExecFileAsyncBuffer). It decides "native" from the call's inputs
  (cwd set, not UNC, and no WSL distro on Windows) for both recording and
  checking, so the early command resolution and the second filter are
  gone. The span bodies are back to main's code.
- A Windows WSL project's linked worktree that runs host Git on a drive
  path is no longer recorded; before, it was recorded but never gated.
- The synthesized error is built from constants (spawn git ENOENT, empty
  stdout and stderr). Nothing reads spawnargs, and the buffer runner's
  only caller reads code and message.
- The drainer holds the registration check the way the event publisher
  holds its outcome observer, and the registration skip shares the
  existing not-visible skip.
2026-10-18 22:25:27 -07:00
NeilandClaude 6328a104bd refactor(ssh): split ssh-connection-utils into concretely named modules (#30488)
* refactor(ssh): split ssh-connection-utils into concretely named modules

- classifiers -> ssh-reconnect-error-classification.ts
- retry/timeout constants and sleep -> connection/ssh-connection-timing.ts
- buildConnectConfig -> connection/ssh-connect-config.ts
- credential callbacks -> connection/ssh-connection-callbacks.ts
- exec options and abort error -> connection/ssh-exec-options.ts
- wrapRemoteCommandForPosixShell -> ssh-posix-command-wrapper.ts
- shellEscape and shared/wsl-login-shell-command quotePosixShell (identical) -> shared/shell-argument-quoting.ts quotePosixShell

No re-exports are left behind.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: point the WSL git benchmark and the WSL lane route at shell-argument-quoting

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-18 20:48:05 -07:00
Jinjing 5ee118a908 fix(source-control): clear unavailable branch line totals (#30475) 2026-10-18 20:44:00 -07:00
Neil 58ce3f5eb4 refactor(git): the common-dir check uses areWorktreePathsEqual (#30263)
Its private POSIX copy predates areWorktreePathsEqual's POSIX guard and WSL UNC fold, which now give the same answers; the one difference is macOS treating /tmp and /private/tmp as one directory, which they are. Also reuse looksLikePosixAbsolutePath in the frozen comparison key.
2026-10-18 01:04:34 -07:00
Neil f47337d852 refactor(worktree): one worktree path equality; the ipc copy folded WSL ext4 tails (#30227)
src/main/ipc/worktree-path-comparison.ts kept its own areWorktreePathsEqual and
dedupeWorktreesByPath. Its Windows branch lowercased a WSL UNC path's ext4 tail, so two
case-twin WSL checkouts merged into one row in removal safety, folder-upgrade dedupe and
the SSH listing fallback. Every caller now uses git/worktree-path-comparison, which gains
the macOS /private/tmp fold the ipc copy had. worktreePathComparisonKey keeps its exact
bytes (retirement namespace keys persist it) in worktree-path-comparison-key.ts, and the
worktree-logic re-export goes.
2026-10-18 00:02:29 -07:00
Neil 0ed0caa663 fix(wsl): run wsl.exe probes from the system folder, never the profile (#29853)
* fix(wsl): run wsl.exe probes from the system root, never the profile

WSL holds wsl.exe's working directory busy for about its idle timeout
after the probe exits. Since #29209 the orcad startup probe ran in the
profile, so a temporary orca serve profile could not be deleted for about
a minute (diagnosed in #29845).

* test(orcad): drop the WSL profile-hold wait from serve parity

Reverts #29845's extended EBUSY retry: wsl.exe no longer runs in the
profile, so nothing outside the test's process tree holds it.

* build(cli): list the WSL spawn-directory module in the CLI typecheck project
2026-10-16 17:12:44 -07:00
Brennan Benson 23b60a5d07 fix(worktree): offer Force Delete with a plain warning when a checkout has submodules (#28878)
* fix(worktree): remove a clean checkout that contains submodules

Git refuses to remove any worktree with an initialized submodule unless --force is set, even when nothing is uncommitted. After that specific refusal, re-check the checkout including files inside the submodule and pass --force once. A dirty submodule still blocks the delete. A second --force is not added, because that would also clear a Git lock.

* fix(worktree): offer Force Delete for a checkout Git refuses over submodules

Drop the automatic --force retry. A forced removal deletes the submodule's
repository under .git/worktrees/<id>/modules, including commits, local
branches and stashes that were never pushed, and the parent's dirty check
cannot see them (the retry #24602 removed for the same reason).

Instead classify Git's "working trees containing submodules cannot be moved
or removed" refusal as a new Force Delete reason, 'submodules', after the
lock check and the force guard. The renderer classifies the error string,
so older SSH and paired hosts get it too with no wire change. The toast,
the delete dialog and the Space manager row show one plain warning naming
what Force Delete would lose instead of Git's text.

Keep --ignore-submodules=none in the pre-teardown dirty check so a user's
ignore setting cannot hide submodule edits from it.

Replace the retry test with one real-Git test: a clean checkout whose
submodule holds a local-only commit is refused without --force, stays
registered, keeps the commit, and its refusal classifies as 'submodules'.

* fix(worktree): clarify the submodule Force Delete warning and hide View for it

The warning now says Git deletes the workspace only with Force Delete, the submodule
refusal no longer offers View (there are no changes to show), the real-Git test pins
that Force Delete then succeeds, and a comment no longer describes the removed retry.

* fix(worktree): name submodules when a changed submodule blocks the delete, and clear a stale delete toast

With a commit inside a submodule that the parent has not recorded, the parent sees `M vendor`, so
Orca's dirty check refused first with the generic "It has changed files". Force Delete then destroyed
the never-pushed submodule commit without warning. On a dirty refusal the check now asks
`git status --porcelain=v2` whether a changed entry is a submodule, and if so reports a refusal the
app shows with the submodule Force Delete warning. The detail keeps the dirty sentence, so older
apps still offer Force Delete.

After the Space manager deleted a workspace (its row Force, or a later delete), the failure toast for
that workspace stayed up and still offered Force Delete. A Space delete now dismisses it.

* fix(worktree): warn about submodules on any refused delete of a checkout that has them

Review found the warning still vanished when a submodule commit was already recorded and some other
file was dirty: the parent's status shows no submodule change, yet Force Delete destroys the commit.
The changed-files check now asks whether the checkout has a populated submodule (a .gitmodules path
holding a .git, as Git's own refusal checks) instead of whether one changed. The refusal keeps the
changed paths after the detail, and View stays available since there are changes to view.

* test(worktree): mock the .gitmodules probe as missing in the preflight unit tests

* refactor(worktree): attach the dirty-check status without a type assertion

* fix(worktree): key the delete failure toast by host and worktree id

A Space delete on one host must not dismiss another host's failure toast for a worktree with the same id.

* fix(worktree): dismiss the failure toast by the store row's host after a Space row Force
2026-10-16 15:23:13 -07:00
Neil 088fefbb5a fix(wsl): bound the line-ending sample's index scan, not just its matches (#29552)
* fix(wsl): bound the line-ending sample's index scan, not just its matches

The WSL-on-Windows-drive line-ending sample piped ls-files --eol through grep
then head, so when no entry was eligible (* text=auto) head never closed the
pipe and every file was read over drvfs until the 15 s timeout, which stage,
discard and commit waited on after each backoff. Cap the raw ls-files output
before the grep.

Follow-up to #27778.

* docs(wsl): describe the line-ending sample as the first index entries
2026-10-16 02:27:24 -07:00
NeilandClaude 99064a5e11 fix(worktree): a worktree created from a remote branch tracks it (#26344) (#29275)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-15 18:19:03 -07:00
Brennan Benson 131986aa16 feat(worktree): show checkout progress while a worktree is created (#24093)
* feat(worktree): show checkout progress while a worktree is created

A local create spends most of its time in the checkout `git worktree add`
performs, and the card only said "Creating worktree…" for that whole time.
Git already writes its "Updating files: N%" meter into our stderr pipe, so
the add call reads it passively (same command line and environment) and the
create card shows "Checking out files… N%" with a thin bar. All six create
labels are translated so a create never switches language mid-flight.

Rebuilt on main from the version stacked on the create-gating branch. On
main a create can also claim a prepared checkout; that path never runs
`git worktree add`, so it keeps today's "Creating worktree…" label with no
bar, and a missed claim falls back to the plain add, which shows the bar.

* test(worktree): pin that finalizing a prepared checkout reports no checkout progress

Claiming a prepared checkout never runs `git worktree add`, so the create
card keeps its label with no bar; this pins that at the finalize step,
including the retarget `reset --hard`.

* test(worktree): keep only the checkout-progress tests that pin a contract

Drops unit tests that mirror the implementation; the real-git flow, the parser
captures, the cross-version Git contract and the create card test remain.

* test(worktree): pin the checkout meter's path from git to the create card

* test(worktree): merge the narrow-terminal capture's pipe chunks
2026-10-15 18:14:56 -07:00
Brennan BensonandClaude 0fea878680 fix(git): keep merge-tree's throwaway trees out of the real object store (#22356)
* fix(git): keep merge-tree's throwaway trees out of the real object store

The PR conflict summary and the squash-merged branch cleanup both run
`git merge-tree --write-tree` only to read its output, but every divergent
run left unreachable loose objects behind. Run it with a per-call scratch
object directory (inside objects/, alternates pointing at the real store)
that is deleted afterwards, on native, WSL and SSH relay hosts alike.

* fix(git): keep partial-clone downloads and clean up stranded scratch stores

- A partial clone fetches missing blobs during merge-tree; those packs landed in the
  scratch store and were deleted, so every check re-downloaded them. Move fetched packs
  (index last) into the real store before removing the scratch dir.
- Sweep this app's own scratch dirs older than an hour, once per objects dir per process,
  so a crash or failed delete cannot strand one forever in repos where gc never runs.
- Remove with Windows transient-lock retries; the shared core now owns all filesystem
  work and host adapters only resolve the objects dir.

* fix(git): quote Windows alternates paths containing ';' and log scratch cleanup failures

* fix(git): sweep a merge-tree scratch dir only when its owner is provably gone

Another Orca process on the same repo (a second instance, dev and release
builds) could delete a scratch object dir still in use once its mtime passed
an hour. Each scratch dir now records its owner (pid, hostname, platform,
Linux pid namespace); the sweep removes it only when that owner shares this
pid space and has exited, or, with no checkable owner, at Git's own two-week
tmp_ expiry.

Also:
- A fetched pack whose later rename fails is rolled back, so the real store
  never keeps an index-less pack; a pack already installed is left alone.
- WSL Git gets the /mnt/<drive> spelling of a drive-path common dir (Git
  < 2.31 prints it relative, resolved against the C:\ repo path).
- conflict-summary tests no longer create scratch dirs in the cwd.

* fix(git): cap every merge-tree scratch dir at two weeks, live-looking owner included

A reused pid held by a long-lived process could keep a leftover scratch dir
forever in a repo where git gc never runs. The two-week age (git gc's own
tmp_ expiry) now removes any scratch dir; a younger one is still swept only
when its owner shares this pid space and has exited.

* fix(git): sweep merge-tree scratch dirs by age alone, off the check's path

- The stale sweep now removes any scratch dir at least two weeks old, Git's
  own expiry for its objects/tmp_* dirs; no merge-tree runs that long. The
  owner file and pid-space probe are gone, and with them the cases where a
  sweeper misjudged another process's pid.
- The sweep no longer delays the user's check: it starts without being
  awaited and can only touch dirs far older than the current run's.
- A fetched pack's .keep never moves into the real store, so a transient one
  from an aborted lazy fetch cannot stop gc from repacking that pack.
- Tests: the legacy merge-tree fallback gets the scratch env; the local
  quarantine's WSL decisions (distro from a UNC path, drvfs spelling,
  host-Git linked-worktree skip, WSLENV hand-off).

* fix(git): let quarantined merge-tree see exactly the object stores Git sees without it

The scratch object store must not change what merge-tree can read, or the
conflict list and the squash-merged branch cleanup differ from main:

- An inherited GIT_ALTERNATE_OBJECT_DIRECTORIES (from Orca's or the SSH
  relay's environment) was replaced; it is now kept and the real store
  appended after it, as Git's own temporary object stores do.
- An inherited GIT_OBJECT_DIRECTORY is the store Git reads and writes, so the
  command now runs unquarantined. On the WSL route a Windows value only
  counts if WSLENV forwards it, and then the quarantine is skipped too.
- The real store's own info/alternates entries are listed before it, so an
  alternates chain is linked at the same depth as without the quarantine
  (a 6-deep chain no longer hits Git's nesting limit). An alternates file
  that cannot be read or holds a quoted entry runs unquarantined.

* fix(git): run merge-tree unquarantined when the repo's store has its own alternates

Mirroring Git's alternates resolution could not match it in every shape: a
path back to the real store (mutual alternates, or an inherited alternates
value naming the repo itself) still linked its chain one level deeper, and a
NUL byte in info/alternates broke the spawn. When objects/info/alternates
exists, or cannot be checked, the command now runs exactly as before. Repos
cloned with --reference or --shared keep writing throwaway objects, as they
always have. Inherited alternates are still appended to, and an inherited
GIT_OBJECT_DIRECTORY still skips the quarantine.

* refactor(git): drop the stale-folder sweep and pack-move rollback from merge-tree's scratch store

A scratch folder left by a crash is a tmp_* dir inside objects/, which
Git's own gc prunes after gc.pruneExpire, as it does Git's own leftovers,
so Orca's 14-day sweep and its test hooks go.

Packs a partial clone fetched into the scratch store are moved in Git's
migration order: pack- files only, index last, existing names left in
place, stopping at the first failed rename with no rollback. A failed
move is logged and never stops the scratch dir's removal or the result.

Adds a real-Git test that a partial clone lists its conflicts offline
once an earlier check downloaded the contents.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(git): quarantine merge-tree only once the host is known to support --write-tree

Git before 2.35 cannot prune a leftover scratch folder under objects/tmp_*, so
a crash in the moment one exists makes every later `git gc` warn and stops
automatic cleanup. On Git before 2.38 merge-tree --write-tree is rejected, yet
the first check per host per session still made and removed a folder around it.

Branch cleanup and the PR conflict list now use the scratch object store only
when that host's capability cache already knows merge-tree --write-tree works.
Until then the command runs exactly as it does on main: on a Git that
rejects it no folder is ever created, and every Git that does get one is 2.38+,
whose gc prunes it. Also reword two comments in the quarantine module.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(git): learn merge-tree support from a conflicting branch-cleanup check

Branch cleanup quarantines merge-tree only once the host's capability cache
knows --write-tree works. A conflicting merge-tree exits 1, so the check threw
and support was never recorded; on a host whose only checks conflict (likely
over SSH, where the relay has no conflict summary to teach its cache) every
check kept writing loose objects into the real store.

A failure whose stdout starts with a tree ID proves --write-tree ran (old Git
that rejects the option prints nothing there), so it now resolves to the same
"not merged" result and lets the cache record support. Every other failure is
rethrown unchanged. Also shorten two comments and soften a third.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(git): put WSL Git's scratch object store on its command line

A WSL login profile that set GIT_ALTERNATE_OBJECT_DIRECTORIES replaced the
WSLENV-forwarded value, so a quarantined merge-tree lost the real store and
failed where main succeeds. The WSL runner now passes both variables as
command-line assignments after the profile, appending to its alternates.

* test(git): drop quarantine tests the real-Git and compatibility tests already pin

The cold-capability gating, scratch-folder lifecycle and WSL path spelling
are covered end to end by merge-tree-object-quarantine-real-git.test.ts, the
real-binary compatibility contract (Git 2.25/2.38/2.49) and the
localGitObjectsDirectory tests, so the unit copies only mirrored them.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-15 15:23:18 -07:00