mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 08:02:02 +00:00
3fc2ed8b238e451fa183540d0a4448bbabb6933e
9743
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3fc2ed8b23 |
fix(windows): log the port-scan fall-through on the relay diagnostic stream
Checked where this code actually runs before trusting the log. `console.warn` did reach a file, but relayLogLine is the right call and the reasoning is worth recording. `scanWindowsListeningPorts` runs only in the detached relay daemon: relay.ts returns early for --connect and --orca-cli, so PortScanHandler is reached only through runRelayDaemon, and both launchers start it detached with a log file (POSIX `> relay.log 2>&1`, Windows `1>relay.log 2>relay.err.log` via Win32_Process.Create). installRelayLogRotation then wraps both streams into relay.log, which is the file the documented diagnostics tail reads. Verified by installing the real rotation over a temp path and reading the file back. So the line surfaced -- but untimestamped, in a log whose format exists so reconnect flaps can be correlated with the events around them (#7773). relayLogLine is that format and the relay idiom in 41 other places, and "since when has this host been stuck on PowerShell" is most of what this line is for. The test spies on process.stderr to pin the stream and the ISO stamp rather than just asserting something was called, since a fall-through logged somewhere unread is the failure being guarded against. Also fixes a comment that ended its own block early: `relay-*/relay.log` in a doc comment contains `*/`. |
||
|
|
77940e54cc |
fix(windows): read netstat state by shape and refuse a truncated table
Review of the port-scan inversion found two ways the new primary path could be silently wrong, both of which would have kept the flagged PowerShell payload running on exactly the hosts this change targets. `LISTENING` is not in netstat.exe. It lives in System32\<locale>\netstat.exe.mui and MUI selection follows the UI language, so the pinned-locale env in relay-command-env.ts cannot reach it -- a German host prints `ABHOEREN` and the word test parsed zero rows. The zero-listeners guard then read that as a blocked reader and ran `Get-NetTCPConnection` every 12-30s forever, or returned nothing at all where PowerShell is also restricted. Keep the word as the fast path and, when it finds nothing over output that did contain TCP rows, re-read by shape: only a listening socket has no peer. Measured on this host across all four states present (LISTENING 47, ESTABLISHED 49, CLOSE_WAIT 29, TIME_WAIT 213): zero non-listening rows with a zero peer, zero listening rows without one, and the same 47 rows parse after substituting the German state words. Shape stays the fallback because `BOUND` also prints a zero peer. Truncation was invisible: createOutputSink discards overflow, ProcessResult carries no flag, so a capped read still exits 0 and its head still parses. netstat orders IPv4 TCP, then IPv6 TCP, then UDP, so a host with tens of thousands of TIME_WAIT rows would have lost every `[::]` listener -- the exact loss dropping `-p tcp` exists to prevent, and one the zero-listeners guard cannot see. Refuse the read instead. A `truncated` flag on the shared sink would be cleaner and is left as a follow-up rather than widened into this PR. Also: decline to wait on the shared process table once the request is aborted (it takes no signal and must not be cancelled for other callers); note the name lookup as best-effort, since a TTL-cached snapshot can hand a recycled PID its previous owner name; log once on either fall-through, because both are permanent and invisible when wrong; and drop a stderr assertion that any PowerShell autoload banner would redden. Correcting the cost claim in the previous commit: the aggregate win holds with the native addon (netstat 21ms vs the retired payload 860ms at 532 processes), not without it. The addon is optional, the snapshot TTL is 500ms and the scan cadence is 12-30s, so a relay with no active agent pane never warms its own cache and pays ~1.4s cold on the CIM path -- slower than what it replaced. |
||
|
|
28163ce419 |
fix(windows): scan ports natively instead of encoded PowerShell
Microsoft Defender for Endpoint scored the relay's Windows port scan as suspicious PowerShell plus network discovery (T1049). The command line was `-ExecutionPolicy Bypass -EncodedCommand <base64>` around a Get-NetTCPConnection/Get-Process join -- base64 next to a policy override is the highest-weighted token pair on a PowerShell command line, and netstat only ever ran as its fallback. Invert the chain. `netstat.exe -ano` is now the primary reader and the owning process name comes from the shared native process table, which exists to keep PID lookups off PowerShell. The payload survives only as a last resort, and without the override: execution policy gates script files, never `-Command`, so nothing needed it (verified: `-ExecutionPolicy Restricted -Command` runs). Drop `-p tcp` while inverting: on Windows that protocol name means IPv4 only, so as a primary reader it would have hidden every `[::]` listener the payload used to report. Names arrive as `sshd.exe` from the table and are published as `sshd`, keeping the sshd filter and old clients' rendering intact. Routes both spawns through runProcess, removing the file from the child_process and windowsHide ratchets. |
||
|
|
f088872554 |
fix(mobile): keep split session hooks render-pure
Documents the pre-existing render-time refs the split relocated onto changed lines, and drops a ref assignment the split added that the monolith never had. |
||
|
|
451002ba1a |
fix(mobile): honor host-follow tab snapshots
(cherry picked from commit a4a9c4da19d6967eceb8025dd01480a56261039f) |
||
|
|
a8d92e5df9 |
chore(mobile): prune stale max-lines overrides
(cherry picked from commit be954c3663409116f34ba08658876262733b3f67) |
||
|
|
cb94265cf4 |
fix(mobile): restore session parity after extraction
(cherry picked from commit
|
||
|
|
7caf7ab1e1 |
refactor(mobile): split session and terminal surfaces
(cherry picked from commit
|
||
|
|
d4db524ba7 |
fix(native-chat): stop unjournaled provider frames from killing the session (#17813)
A frame the classifier declines (status-chrome, suppressed-benign, stream-into-item) is deliberately not journaled. #17720 turned that null translation into `{accepted: false, reason: 'untranslated'}`, which is not `backpressure`, so the notification retry queue treated it as unreplayable and escalated through fail() -> forceCloseUnexpected -> connection.close(). The app-server latched `closing` and the create path's next model/list rejected with "codex app-server connection is closed (model/list)". The provider emits `remoteControl/status/changed` right after initialize, so every structured Codex session died on its first chrome frame. Admit the null translation instead, before any bookkeeping or publish. Also restore the error-frame exemption from the generic row cap, dropped by the same PR: the cap now runs after the classification check, so a noisy turn can no longer reduce provider errors to a suppression count. The test that pinned the capped behavior is inverted to assert the exemption. Co-authored-by: Merge Sim <sim@local> |
||
|
|
51bc2ec343 |
fix(native-chat): keep the attachments on a Claude turn that pasted images (#17801)
* fix(native-chat): keep the attachments on a Claude turn that pasted images
A Claude turn carrying pasted images reached native chat with no images at all —
no thumbnails on mobile, and not even an attachment chip on desktop. Nothing
showed that the message had any.
Both carriers were being dropped:
- Claude records the paths in a companion turn marked `isMeta`, holding one
`[Image: source: <path>]` text block per image. The decoder treats an `isMeta`
user row as injected, filters it down to tool-result blocks, and returns null
when none remain — so the whole row went away.
- The prompt row's own `image` blocks are `{source: {type: 'base64'}}`, which
carry no url or path, so `imageRefBlock` drops them too.
With the companion gone, `isImageSourceUserTurn` could never fire and the fold in
`normalizeImageTranscriptMessages` was unreachable on the Claude path.
Surveying every transcript under `~/.claude/projects`: 238 of 241 image-source
rows are `isMeta`, across every versioned release (2.1.220 through 2.1.237); the
3 that are not carry no version field at all. 38 of those rows hold more than one
content block, which also defeated the single-block rule in
`isImageSourceUserTurn`.
Let image-source text survive the injected-turn filter, and recognize a turn
whose blocks are *all* markers rather than only a lone one. An ordinary injected
turn (a skill preamble, a compact summary) is still dropped, and a turn that
mixes prose with a marker is still not an image-source turn.
Carrying the paths keeps the payload small; decoding the base64 instead would put
hundreds of KB per image on the wire to mobile.
* fix(native-chat): preserve image companion ordering
* fix(native-chat): keep image companions turn-local
---------
Co-authored-by: Merge Sim <sim@local>
|
||
|
|
a9e6fb7eff |
fix(native-chat): stop rendering tool output as the agent's streaming reply (#17782)
* fix(native-chat): stop rendering tool output as the agent's streaming reply A tool result could appear in native chat as a raw, un-collapsed "assistant" bubble that never went away for the rest of the turn — on mobile it showed up as a wall of a source file's contents, prefixed by "Exit code 1". Providers publish a tool's stdout/error as `lastAssistantMessage` so status cards and dashboard rows can preview what the agent just did. Native chat reuses that same field as its live streaming bubble, so the preview rendered as prose. For Claude the preview is *only ever* tool output mid-turn: claude-tool-fields writes real prose exclusively at Stop, so the bubble could never contain an actual streaming reply. It also could not be retired. The bubble hides once a transcript assistant block leads with the streamed text, and tool output never lands in one — so the only remaining exit was the turn ending, which is why a long tool-heavy turn pinned it on screen. Carry provenance instead of changing what the status surfaces show: mark the writes that come from a tool result/error, keep the flag in lockstep with the value it describes through the listener merge, and have both native-chat streaming paths ignore a flagged preview. Status cards, dashboard rows and automation capture are untouched. The wire field is optional, so an older host that never sends it keeps today's behavior rather than silently suppressing previews. * fix(native-chat): preserve tool output provenance through renderer sync * fix(native-chat): retain preview provenance in Claude roster state * test(native-chat): cover restored tool preview provenance --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
9bc564c2aa |
fix(cleanup): follow WSL-written gitdir pointers on a Windows host (#17806)
`readLocalWorktreeGitDir` resolved a linked worktree's `.git` gitfile
pointer by hand, translating a POSIX-rooted pointer only when the
worktree path was itself `\\wsl.localhost\...`. On a Windows host
`path.isAbsolute('/mnt/c/repo/.git/worktrees/wt')` is true, so a
drive-path worktree (`C:\Users\me\wt`) whose gitfile was written by git
running inside WSL kept the guest spelling and was joined to
`\mnt\c\repo\.git\worktrees\wt\HEAD`. All four probes (HEAD,
COMMIT_EDITMSG, ORIG_HEAD, tail of logs/HEAD) missed, so the row's
lastActivityAt fell back to the worktree directory mtime and a worktree
with recent commits could read as stale in the cleanup browser.
Delegate to `resolveGitMetadataPath` (src/shared/git-metadata-path.ts,
landed in
|
||
|
|
d7d3114716 |
perf(wsl): single-flight the async WSL distro list (#17805)
On Windows, seven production call sites reach listWslDistrosAsync and on a cold cache each spawned its own `wsl.exe --list --quiet` (5s timeout each): the wsl:listDistros IPC behind the renderer capability read, the host.wsl.listDistros RPC, the skill-install IPC, CLI registration reconciliation, the hook relay deps, the kimi runtime home, plus relay preflight in the relay process. Concurrent callers in one process now share one spawn. Joining happens ahead of the negative cache, which also fixes a stranding bug: a synchronous listWslDistros() landing an empty result mid-probe arms the 15s retry window, and later async callers read that [] even though the pending probe is about to see a distro that just finished provisioning. The non-empty-cache short-circuit sits ahead of the join so a list already found synchronously is still returned without waiting; that is main's existing behaviour preserved, not a new fast path. The shared promise cannot reject -- `catch` sits ahead of the stored promise, so joiners get the same fail-safe [] the old per-caller catch returned -- and the slot is cleared on settle, by the owning probe only. wsl-directory-probe-command.ts is a verbatim move of the guest directory-probe marker protocol and its parser out of wsl.ts, for oxlint max-lines headroom: inlining it back makes wsl.ts 306 effective lines against a cap of 300. It takes WslUncPathInfo from ../shared/wsl-paths -- the actual type of every value passed at both call sites -- so it does not import from wsl.ts. _resetWslCachesForTests and _setWslCachesForTests now share one resetWslDistroListState() instead of repeating the same six assignments. Per-platform delta: - WSL on Windows: fewer wsl.exe spawns under startup fan-out, and a distro provisioned while a probe is pending is no longer hidden for the retry window. - Native Windows without WSL: no behavioural change. The empty/failure retry windows, their backoff and the cache sequence guard are unchanged; N concurrent callers now cost one failed spawn instead of N. - macOS, Linux, folder workspaces: no change. Both new early returns are unreachable off win32. - SSH remote: no change for macOS/Linux hosts; a remote Windows host gets the Windows behaviour in its own process. No wire change -- host.wsl.listDistros keeps its string[] shape and its [] failure value. - Relay: same single-flight inside the relay process. It stays per-process; the relay and main process still probe independently, as before. Costs: a never-settling execFileUtf8 now pins the shared slot for the process lifetime rather than only its own callers -- transient-to-permanent, not identical exposure. And a joiner inherits the first probe's failure instead of making an independent attempt. |
||
|
|
a9babde9a3 |
refactor(git): accept a caller-named WSL distro on the metadata path resolver (#17804)
Two small changes to the Git metadata read path. Neither has a user-visible
effect on any platform except for a malformed `.git` gitfile, described below.
1. resolveGitMetadataPath's third parameter becomes an options object
`{ platform?, wslDistro? }`. A caller that knows which distro wrote a pointer
can now say so, where previously only a WSL UNC base path could. The distro
encoded in the base path still outranks the caller's, and translation only
happens when the reading host is win32, so a caller-named distro cannot make
a POSIX host fabricate a Windows path. The UNC-base branch is exempt from
that gate because that spelling only exists on Windows. Main's other
contracts are verbatim: never null for a non-empty pointer, and a drvfs
pointer keeps its drive spelling even when a distro is named. Both production
call sites (repo-git-marker-scan.ts) pass no options, so they are unchanged.
2. The `.git` gitfile marker parse moves into one shared function,
parseGitdirMarkerPayload: `gitdir:` at the start of the file, payload
trimmed, empty payload rejected — git's own read_gitfile_gently rule.
resolve-git-dir.ts and repo-git-marker-scan.ts both call it; the latter had a
near-identical private copy and is behaviorally identical after the swap
(verified across twelve marker spellings; the only divergence, a
whitespace-only payload, already resolved to null one call further down).
Main's `/^gitdir:\s*(.+)\s*$/m` in resolve-git-dir captured trailing padding
into the path and honored a `gitdir:` line anywhere in the file.
Per-platform delta: none on macOS, Linux, native Windows, WSL, SSH, relay, or
folder workspaces. The wslDistro option is inert; this change adds no caller.
For a malformed `.git` gitfile, padding is now stripped (strict improvement), a
whitespace-only payload falls back to `<worktree>/.git`, and a `gitdir:` line
that is not the first line is no longer honored — a narrowing, since main could
return a working gitdir there. All four resolveGitDir consumers already degrade
through a catch, so that case reports no sparse state / conflict operation /
diff stamp rather than failing.
Six other hand-rolled `gitdir:` parsers remain, including the relay's SSH copy;
converging them is its own change.
|
||
|
|
bf62abc3c9 | fix(docs): align OSS docs presentation and SEO (#17809) | ||
|
|
2e30187560 |
feat(dev): sweep the backlog of idle dev Electron bundles (#17803)
* fix(dev): make reclaim report real sizes on Windows and keep setuid intact Two bugs found by running the reclaim script on real Linux and Windows hosts. The size report shelled out to `du`, which does not exist on Windows, so every worktree measured 0 bytes and the script reported nothing reclaimable on the platform with the largest dist (374MB). Walk the tree in Node instead. makeTreeReadOnly chmod'd files to a flat 0o555, which clears setuid. On Linux that would silently strip the bit from chrome-sandbox if a developer had run the usual `sudo chown root && chmod 4755` workaround -- and under hardlink sharing it would strip it from every worktree and the cache at once. Clear the write bits and nothing else. Measured after the fix: 7.30 GiB across 23 worktrees on one Windows host and 18.31 GiB across 56 on another, both previously reported as 0. * feat(dev): sweep the backlog of idle dev Electron bundles out/electron-dev holds one ~275MB patched Electron.app per branch title x Electron version. The dev runner already prunes them, but only inside the worktree it is starting and only when that worktree holds more than one bundle -- and a worktree almost always holds exactly one, so the sweep returns early every time and nothing ever reclaims another worktree's bundle. pnpm reclaim:dev-bundles sweeps across every worktree of the repo. Bundles are pure build output that pnpm dev rebuilds on demand, and rebuilding is cheap now that the Electron dist is shared. Reuses the runner's own staleness rules, so a bundle a live process is running from, or one whose build is still in flight, is never removed. Refuses to run at all if the process table cannot be read, rather than guessing. Measured: 120 bundles, 32.2 GiB, on one machine. Also guards both reclaim scripts behind a direct-invocation check; importing one for tests previously ran a full sweep at import time. |
||
|
|
e2f326cad7 | ci(release): prevent signing on workflow reruns (#17802) | ||
|
|
05b31d6e92 |
fix(i18n): correct zh-CN translation for editor view toggle buttons (#13723)
* fix(i18n): correct zh-CN translation for editor view toggle buttons - "Rich Editor" (aff15f94f5): 丰富的编辑器 → 富文本编辑器 - "Source" (4d6ccb7ba6): 来源 → 源码 - Settings description (f80603d293): 丰富的编辑器 → 富文本编辑器 "Source" in the Markdown editor context means source-code view, not data source. "丰富的编辑器" is an awkward literal translation; the standard term is "富文本编辑器", already used inconsistently in nearby keys (5f02e6fb21, 8090:694613d47f). * fix translation --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> |
||
|
|
28373fcea7 |
fix(windows): repair the install-dir package ACL that blanks the window (#17740)
* fix(windows): repair the install-dir package ACL that blanks the window An install tree carrying an orphan AppContainer ACE (S-1-15-2-<x>) with no ALL RESTRICTED APPLICATION PACKAGES grant denies Chromium's LPAC children read on the shipped modules; they die at init with 0x80000003 and the window stays blank forever (electron/electron#51761). - Tighten the probe verdict to require the S-1-15-2-2 grant specifically: an ALL APPLICATION PACKAGES (S-1-15-2-1) ACE, the Program Files default, does not appear in an LPAC token and cannot satisfy the orphan. - Drop BUILTIN from the English-locale heuristic (fr-FR/es-ES print it verbatim) so a localized icacls is correctly reported as un-name-checkable. - Add an additive, marker-guarded icacls self-repair: an inheritable root grant plus a flagless (RX) /T pass, never /grant:r. - Route the crash-loop dialog through a testable prompt module that names the permission cause, offers Copy Commands without dismissing itself, and keeps the graphics-driver hint. The repair only runs on win32, off serve mode, and only on the exact probe verdict that reproduced the crash. * docs(windows): correct the install-tree ACL walk cost model * fix(windows): keep the install-ACL poison gate at the reproduced shape An orphan package ACE alongside the Program Files ALL APPLICATION PACKAGES default launches clean on win32 10.0.26200 / Electron 43.4.1, so requiring S-1-15-2-2 specifically declared poison on healthy installs - and this branch acts on that verdict with a tree-wide icacls write and the crash-recovery dialog's primary cause. hasRestrictedPackageGrant stays reported for triage; only the verdict reverts. |
||
|
|
abe1d30881 | fix(dev): make reclaim report real sizes on Windows and keep setuid intact (#17800) | ||
|
|
e6257b6e32 |
perf(worktree): resolve the WSL workspace root off the main thread when preparing (#17792)
`computeWorkspaceRoot` resolves a WSL repo's mirror root through `getWslHome`,
which is a synchronous `execFileSync('wsl.exe', ...)` with a 5s timeout. Two
worktree preparation paths ran it on the Electron main thread:
`prepareLocalWorktreeRootForRepo` (repo registration, clone completion, repo
update, project host setup, folder->git upgrade) and `prepareWorktreeCreateForRepo`
(the speculative checkout started while the create composer is open). On a stopped
or cold distro that froze every window for up to 5s. Being fire-and-forget did not
help: only 3 of the 16 `prepareLocalWorktreeRootForRepo` call sites are `void`-ed,
the other 13 are awaited inside IPC handlers, and the sync probe blocks the main
thread either way. `prepareLocalWorktreeRootsForRepos` runs the same probe for
every repo from the settings-save handler.
Adopt the existing `computeWorkspaceRootAsync` (now exported) at those two call
sites, and give the two resolvers a shared mirror-distro decision and shared
root-from-home layout so they cannot drift apart.
Also thread the mirror distro into the prepare-side path settings.
`createLocalWorktree` passes `getWorktreeMirrorDistro(store, repo)` and
`prepareWorktreeCreateForRepo` did not, so a `C:\` repo on a WSL project runtime
prepared under `C:\workspaces` while the create click looked under the mirrored
WSL root: the keys never matched and every prepared checkout was discarded, after
paying for a full checkout that sat until the 5min TTL. Pre-existing on main;
included because it is the same line and the same resolver.
Scope of the win, stated precisely: only those two preparation paths stop
blocking. On a reachable distro `getWslHome` caches on success, so before this
change the first repo paid one blocking probe and the rest were cache hits -- the
change makes that one probe non-blocking, it does not remove N probes. Failed
probes are never cached, so on a stopped distro N repos did pay N sequential 5s
blocking probes and now share one in-flight async probe.
Costs: five sync `computeWorkspaceRoot` callers remain (allowed-roots resolution,
the create click in worktree-remote, CLI create, watch targets, worktree trash),
and `getWslHome` reads only `wslHomeCache` -- it cannot join an in-flight async
probe. The guaranteed synchronous cache warm-up therefore becomes a window in
which one of those callers can still block and can spawn a second concurrent
`wsl.exe`. Concretely: opening the create composer and clicking Create within a
few hundred ms on a cold distro now pays the freeze on the click instead of on the
background prep. Separately, the mirror-distro fix makes prepare spawn an async
`wsl.exe` home probe for `C:\` repos on a WSL runtime, where it previously spawned
none.
No race added: `prepareWorktreeCreateForRepo` computes the preparation key and
inserts the registry entry in one synchronous run after the await, so two
concurrent creates still dedupe to a single prepared checkout.
`worktree-create-preparation-wsl-root.test.ts` runs the real resolver through
prepare and then claims the entry with the production consume-side call shape
(including the mirror distro), so a divergence between the two resolvers fails a
test instead of silently discarding every prepared checkout.
|
||
|
|
5c831c1846 |
fix(crash-reporting): record Linux MemAvailable at process-gone (#17733)
Linux process-gone crash reports emitted only systemMemoryFreeMB, which is /proc/meminfo MemFree — it excludes page cache and other reclaimable memory, so an OOM-killed renderer could report gigabytes "free" and hide the pressure that caused the kill. Electron 43 exposes MemAvailable as `available` on Linux, and getSystemMemoryAtGoneDetails already had the getSystemMemoryInfo() result in hand, so emit it as systemMemoryAvailableMB through the existing field table. The field is omitted on macOS/Windows (where Electron does not report it) and on a non-finite reading, matching every other bucket. |
||
|
|
ed496317f0 |
perf(renderer): fetch host lineage once per connect, narrow sidebar ack subscriptions (#17761)
* perf(renderer): bound runtime refresh and sidebar subscriptions * perf(renderer): widen interactive connect to 15 concurrent probes The 5-wide bound came from the coalesced background event lane, where repo events repeat and can storm. Connect is one-shot and the user is waiting on it, so it gets its own wider lane while still capping fan-out on the remote, which runs a worktree-detection RPC per repo. |
||
|
|
fe0f2f9be7 |
perf(dev): share one Electron dist per repo instead of per worktree (#17664)
* perf(dev): clone one Electron dist per repo instead of per worktree Every worktree extracted its own ~295MB node_modules/electron/dist, measured at 69GB across 241 worktrees on one machine. Extract once per repository into <git-common-dir>/orca-cache/electron, then APFS-clone it into each worktree: copy-on-write, so the second worktree allocates ~0 bytes and still gets a real, private, writable directory. Hangs off install-electron-package-binary.mjs, inside the transaction it already uses to swap dist. Every cache path returns a boolean and false means "install normally", so non-APFS, cross-volume, corrupt entry, no Git, folder workspace and CI all keep today's behavior. No symlinks, no lifecycle changes. out/electron-dev's per-branch Electron.app copy clones too, via the same helper. Refs #13709 * perf(dev): share the Electron dist on Linux and Windows too Extends the shared dist cache beyond macOS APFS. Three mechanisms, strongest isolation first: macOS APFS cp -c private copy-on-write Linux btrfs cp --reflink private copy-on-write ext4 / NTFS hardlink + 0555 shared inodes, forced read-only Reflinks cover btrfs/XFS/bcachefs/ZFS but not ext4, and Windows block cloning is ReFS-only, so most Linux and effectively all Windows developers need hardlinks to get any saving at all. Extracted dist is 327MB on linux-x64 and 374MB on win32-x64, both larger than macOS. Hardlinks share inodes, so a write through one worktree would rewrite every sibling and the cache. Nothing in this repo writes inside dist -- every mutation replaces the directory via rename -- but Electron's own install.js extracts over an existing dist with O_TRUNC, and is reachable through `pnpm rebuild electron`. Publishing the entry read-only turns that from silent cross-worktree corruption into EPERM. Directories stay writable so the install transaction's renames and unlinks still work. out/electron-dev's per-branch Electron.app is patched and codesigned after it is copied, so it uses copyPrivateTree, which never hardlinks. Refs #13709 * test(dev): keep shared-dist tests honest across ext4 and NTFS Verified on real hardware: Ubuntu 24.04/ext4 (no reflink support, so the hardlink tier is the only thing that helps there) and Windows/NTFS. Three tests faked platform: 'darwin' while invoking the real mechanism, so they failed on Linux where /bin/cp -c does not exist. Mechanism selection is now asserted with injected stubs; real filesystem behavior is asserted against whatever the host actually supports. Windows maps chmod onto the read-only attribute alone, so a directory never reports 0o755 and a read-only file reports 0o444. Mode-bit assertions that encoded POSIX semantics are now behavioral (the tree stays removable), and the executable-bit assertion is POSIX-only -- confirmed on NTFS that a read-only hardlinked .exe still runs. * fix(dev): stop a losing publisher from discarding a good cache entry Greptile caught a TOCTOU in the shared Electron dist cache. Quarantining an invalid entry happened before sharing the replacement tree, which takes seconds -- long enough for a sibling worktree to publish a good entry that this one would then rename away. If the follow-up publish also failed, the cache was left empty and every worktree re-downloaded. Stage first, then re-validate immediately before the destructive rename, so an entry that became good during the share is kept. On a failed swap, restore the quarantined entry instead of leaving no entry at all: a stale entry still beats an empty cache, because the next publisher re-validates and replaces it. An entry that cannot be validated is never displaced, matching the pre-staging rule. Also covers the Electron upgrade path end to end: a version bump gets its own cache entry and leaves the previous one for worktrees still on the old branch. * feat(dev): add a script to share existing worktrees' Electron dists An install only shares when Electron is (re)installed, and rebuild-native-deps returns early when the package is already usable -- so a worktree that already has a working dist never reaches the sharing path and keeps its own copy until the next Electron upgrade. pnpm reclaim:electron-dists reports what it would share; --apply does it. Each worktree is converted behind a rename, so an interrupted run leaves a working dist either way, and any worktree that fails is left untouched. Measured on one machine: 677 worktrees, ~195 GiB reclaimable. * fix(dev): keep the reclaim script's error formatting type-safe |
||
|
|
a4cd00ed18 |
fix(wsl): drop the linked-worktree Git route cache after worktree mutations (#17791)
On Windows with a WSL distro configured, `prepareWslLinkedWorktreeGitRouting` caches for 30s which Git owns a drive-letter checkout, by reading that checkout's `.git` marker. `git worktree add/move/remove` rewrites exactly that marker, so the verdict could stay authoritative for up to 30s after it stopped being true. - Add `invalidateWslLinkedWorktreeGitRouting(cwd)`: drops the cached route and the probe retry backoff for that path and for anything under it (a submodule inside the worktree derived its route from the same marker walk). Eight calls at six sites: `worktree add`, `worktree move` (both paths), `worktree remove`, the prepared checkout's add, the finalize move (both paths), and the prepared-worktree discard. Five sites invalidate from a `finally`, because a Git failure can still have rewritten the marker; the prepared checkout's add invalidates on the success path only, since its failure path runs the discard, which has its own `finally`. - Split the parent-directory marker walk into `wsl-linked-worktree-git-route-probe.ts` (the routing module was at the `max-lines` ceiling) and have it report whether the walk settled. A `.git` file with no `gitdir:` line is a half-written marker mid `worktree add`: it is now retried under the existing backoff instead of cached for 30s. The route it yields is unchanged (distro); only the number of parent walks changes. An invalidation only drops cached state; a probe already in flight is left to finish and cache normally, so a mutation landing mid-probe is no worse off than main's 30s TTL. The cost is that a failed mutation also drops a still-correct route: `gitExecFileAsync` and `gitStreamStdout` re-resolve the route after their own `prepareWslLinkedWorktreeGitRouting`, and `gitSpawn` resolves again after the git-admission wait, so a command already in flight for that path can take the empty-cache default and run a host-owned checkout under `wsl.exe git`. It fails once and self-heals on the next command. Reachable only on win32 + configured WSL distro + drive-letter cwd; every other platform and configuration never populates this cache, so the new calls scan two empty maps. |
||
|
|
abc099e4c7 |
fix(worktree): run the create-base warm-up on the routed git host (#17794)
The speculative warm-up that runs while the create composer is open resolved
refs and fetched with host Git even when the project's runtime is a WSL distro,
while both the checkout preparation it feeds (`prepareWorktreeCreateForRepo`,
which already resolves `{ wslDistro }` itself) and the real create path run
inside the distro.
The concrete cost was a discarded fetch: `getCanonicalFetchKey` namespaces the
runtime's remote-fetch cache `wsl:<distro>` vs `local`, so the warm-up's fetch
landed in a namespace create never looks at, and create fetched again. On a
Windows host with no usable host-side Git the probes also failed outright, so
that cohort got no warm-up at all.
Thread the project's worktree Git options through the prefetch (resolved by a
non-throwing helper, because an optimistic warm-up must not surface a
repair-required runtime as a failure) so every probe and fetch runs where create
runs. `gitOptions` is a required argument, so a caller cannot drop the routing
silently. Host-routed calls keep their original arity, so macOS, Linux,
native-Windows-host projects, SSH repos and folder workspaces are unchanged.
Narrower than it looks: for a repo under \\wsl.localhost\<distro>\... the probes
were already routed by cwd, and for a repo on a Windows drive letter host Git
and WSL Git read the same on-disk repository, so the answers were already
correct there. What those cohorts gain is a fetch create can reuse; what they
pay is that the probes now run inside the distro (over /mnt/c for drive-letter
repos, which also newly arms the linked-worktree routing probe) and the
speculative fetch now shares create's per-remote fetch queue, as it always has
on native platforms.
Also collapse the three byte-equivalent copies of `hasLocalWorktreeBaseRef`
(create, prefetch, remote-repo create) into one in
git/worktree-base-ref-probe.ts, drop the host-only `hasLocalCommitObject` that
caused the routing bug, and add the first routing assertions on the create-path
consumers of the now-shared probe.
|
||
|
|
7f63db7d7a |
fix(git): resolve WSL drvfs Git metadata pointers on a Windows host (#17790)
When Orca's runtime is a WSL distro but the repo sits on a Windows drive, git inside the distro writes `/mnt/c/...` into a worktree's `.git` gitfile and its `commondir`, while Orca reads those files back through Win32. `repo-git-marker-scan` returned the pointer verbatim, Windows read it as drive-relative `C:\mnt\c\...`, and the worktree was reported `invalid`. Move that resolver out of `repo-git-marker-scan` into `src/shared/git-metadata-path.ts` and give it exactly one new case: on win32, a drvfs pointer resolved against a base path that is not a WSL UNC path now gets its drive spelling. Every other base/pointer/platform combination is byte-identical to the deleted helper, verified differentially across a base x pointer x platform matrix — macOS, Linux and native Windows are unchanged. `toWindowsWslDrivePath` is factored out of `toWindowsWslPath` so the drvfs matcher has one home; `toWindowsWslPath` itself is unchanged for all inputs, including the line terminators JS `.` excludes (fuzzed 2M inputs, 0 divergences). This changes the marker scan's verdict only. `resolve-git-dir.ts` and the relay's own copy still `path.resolve` the same `/mnt/c/...` pointer in the Win32 namespace, so a worktree that is now accepted still degrades quietly in conflict detection, sparse-checkout detection, the diff stamp and worktree listing. Those parsers are deliberately untouched here; see the PR description. Co-authored-by: Neil <neil@example.com> |
||
|
|
8b2d72114b |
fix(gitlab): stop the native glab known-hosts probe waking an idle WSL distro (#17789)
On Windows with no host `glab.exe`, the cwd-less `glab auth status` known-hosts
probe fell through to `wsl.exe -d <default distro>`. Probe failures are never
cached, so when that WSL leg also fails (glab absent or logged out inside the
distro) every forge detection re-booted the distro; when it succeeds it cached
that distro's auth hosts under the 'native' execution key, which the comment two
lines above the call already forbids. gitlab-auth-and-rate-limit.ts already
passes allowDefaultWslFallback: false for this exact command; the known-hosts
probe now agrees with it.
Connection-keyed probes keep the fallback: glab has no SSH/relay dispatch, so the
`glab api` calls this gates run the same local CLI with no cwd and would
otherwise disagree with the probe. wsl:<distro>-keyed probes were already
unreachable by the fallback, so passing the flag there is inert.
Counted child_process.execFile calls over 3 sequential probes, process.platform
forced to 'win32', host glab mocked ENOENT (wsl.exe / glab.exe spawns):
native key, glab absent in the distro too: 3/3 -> 0/3
native key, glab logged in in the distro: 1/1 -> 0/3, and that distro's
self-hosted hosts stop reaching the native known-hosts list
connection key: 1/1 -> 1/1, unchanged
Residual risk on that second config: a repo on a \\wsl$\ UNC path can be keyed
'native' (no project runtime match) while its own glab calls still route into
WSL by cwd, so it loses the seeded host and must re-derive it through
`glab auth status --hostname`. A co-resident Windows-path repo on the same host
can now write a shared `native\0<host>` unauthenticated negative that stalls that
recovery for one NEGATIVE_ENTRY_TTL_MS window. Fixing that properly means keying
the cache by the host that actually served the call, which needs an exec-layer
API change and is deliberately out of scope here.
Also splits the getGlabKnownHosts suite out of gl-utils.test.ts (796 counted
lines against the 800-line cap for tests) into gitlab-known-host-probe.test.ts.
|
||
|
|
ad760c8b92 |
fix(worktree): reject Windows drive-qualified shared paths in the symlink guard (#17793)
getSafeRelativePath strips leading `/` and `\` before testing absoluteness, so the only rooted spelling that can still reach the guard is a Windows drive designator. It tested that with the host `path.isAbsolute`, which left two gaps: the drive-absolute form `C:/payload` was refused on Windows but admitted as an ordinary relative filename on macOS/Linux, and the drive-RELATIVE form `C:payload` was admitted on every host including Windows, where `win32.resolve(root, 'C:payload')` discards the worktree root and lands under C:'s current directory. That holds for a drive root (`D:\wt`) and for the `\\wsl.localhost\<Distro>\...` UNC root a WSL project uses, both verified. The value reaches the guard from two configs: the per-user Worktree Shared Paths setting alone on the create path (createWorktreeLinkedPaths, called with `repo.symlinkPaths` from orca-runtime.ts:27820 and worktree-remote.ts:2636), and that setting merged with the repo's checked-in `orca.yaml` `worktree.sharedDirectories` on the removal and detection paths (getWorktreeSharedLinkPaths). No repo config is required to reach it. Replace both `isAbsolute` calls with a `/^[a-zA-Z]:/` test, verified by fuzz to be a strict superset of `posix.isAbsolute || win32.isAbsolute` for every post-strip input. No filesystem escape is closed on macOS/Linux, where such an entry resolves to a literal in-worktree filename. Cost: on POSIX, `:` is a legal filename character, so a shared/linked path whose first segment is `<letter>:...` is now refused where it previously worked — it stops being created, and if a worktree already holds an Orca-created symlink there it stops being excluded from the untracked-file filters in all four findExistingWorktreeSymlinkPaths callers, which means a refused non-force worktree removal (remove-registered-local-worktree.ts:91, orca-runtime.ts:30205), a phantom untracked row in Source Control (status-read.ts:90), and a blocked hosted-review creation (hosted-review-creation-git-state.ts:290) — and removeWorktreeLinkedPaths no longer unlinks it, so nothing cleans it up. Accepted because a per-host verdict would defeat the point of judging the same config identically on every host it is evaluated on. Co-authored-by: Neil <neil@example.com> |
||
|
|
e4f77f7d13 | perf(renderer): collapse duplicate reveal atlas rebuilds (#17762) | ||
|
|
69120d5402 |
ci(release): tolerate legacy tags without source maps (#17788)
* test(e2e): seed source control diff before opening panel * ci(release): tolerate legacy tags without source maps |
||
|
|
f8a3f2c7c0 |
test(e2e): do not treat a destroyed renderer as a relaunched runtime (#17785)
* test(e2e): do not treat a destroyed renderer as a relaunched runtime waitForRelaunchedRuntime polled refreshAuthorityRuntimeId with expect.not.stringMatching(previousId). Playwright treats null as a non-match, so an Execution-context-destroyed miss ended the wait as if the client had already reconnected. Poll until a non-null id that differs from the pre-restart process. * test(e2e): wrap cookie-survival restart evaluates as pending misses The cookie spec still opened a post-restart page with a raw evaluate poll. A recycled renderer then timed out as "never materialized". Use the shared fixture helpers so destroyed-context is a miss, not a fail. * test(e2e): leave cookie-survival on its own wait for this PR The relaunch-wait fix made the cookie spec's post-restart echo render time out in CI. Keep that spec out of this change so the destroyed- context wait can land on the helpers restart-survival actually uses. |
||
|
|
a5796ec8eb |
refactor(runtime): split OrcaRuntimeService and compatibility tests (#17605)
* refactor(runtime): split OrcaRuntimeService into focused modules
* test(runtime): cover admission tiers and strict worktree reconciliation
* fix(runtime): preserve owner and structured session visibility
* fix(runtime): port post-extraction compatibility fixes
* fix(runtime): preserve skill-share cancellation barrier
* test(runtime): update identity inventory after extraction
* fix(runtime): preserve hook transport environment cleanup
* fix(runtime): consolidate idle probe imports
* test(runtime): retire split file process allowlist entry
* fix(runtime): route child process types through shared boundary
* test(runtime): preserve worktree host metadata precedence
* fix(runtime): update extracted test seams
* fix(runtime): gate the split's ts-nocheck set and restore the stop-confirmed contract
Audit follow-ups for the OrcaRuntimeService split:
- Freeze the 171 @ts-nocheck files behind a ratchet so no new file can disable
type checking. The split's linear mixin chain cannot express forward
references yet, so the existing suppressions are grandfathered; the baseline
may only shrink.
- Drop the stray @ts-nocheck at the end of orca-runtime-get-status.ts. It sat
after the first statement, where TypeScript ignores it, so the module was
already checked.
- Restore `retireRejectedPty(ptyId, stopConfirmed: boolean)` as a required
argument. The split widened it to optional and patched the resulting error
with `stopConfirmed === true`; an omitted argument would have silently taken
the unverified-stop path instead of failing to compile.
- Guard that every orca-runtime-tests fragment is imported by the compatibility
entrypoint. The fragments are .spec.ts, which no Vitest include glob matches,
so one left out of the list would silently stop running.
* fix(runtime): restore four behaviors the OrcaRuntimeService split dropped
Audit findings against the refactor's true base (
|
||
|
|
1efd4e1a97 | test(e2e): seed source control diff before opening panel (#17784) | ||
|
|
12be5aed9e | fix(browser): refuse devtools for offscreen guests (#17485) | ||
|
|
c5d43b8a24 |
Avoid Linear read re-fetches when workspace scope is unchanged (#17529)
* Avoid Linear read re-fetches when workspace scope is unchanged Derive a stable scope signature that captures only the connected state and workspace identity, ignoring volatile metadata like displayName. Use this in dependency tracking so Linear searches don't re-run on status updates that don't affect which issues can be queried. * Expand workspace scope to detect credential and org changes Cache invalidation key now includes credentialRevision and organizationUrlKey for both workspace and viewer, ensuring Linear reads re-fetch when credentials rotate or organizations are renamed — fields that affect what read operations return. * Include activeWorkspaceId in workspace scope signature URL lookup falls back to the active workspace even when all workspaces are selected, so activeWorkspaceId must be part of the scope signature to ensure reads are keyed correctly. |
||
|
|
f116d2ca2a |
test(ci): retry Windows teardown EPERM and restart evaluate misses (#17780)
Restart-survival polls treated a recycled renderer as a hard failure. Wrap those evaluates so "Execution context was destroyed" is a pending miss. Windows package-lane teardowns after a force-kill used rmSync with force:true only, which does not absorb EPERM; put them on the shared maxRetries:8 policy. |
||
|
|
eff317939a |
fix(terminal): mount one surface per workspace id in the workbench (STA-4846) (#17432)
* fix(terminal): mount one surface per workspace id in the workbench (STA-4846) * test(terminal): pin the workbench projection against under-selecting Losing a surface unmounts live terminals, which is worse than the duplicate mount STA-4846 fixes, so cover every catalog shape that reaches the workbench: local-only rows that name no host, an unqualified row colliding with a host-qualified one, two SSH hosts on one id, folder rows across three hosts, folder ids alongside git worktree ids, and a whole-catalog assertion that the emitted id set equals the distinct input id set. Also pin the `useAllWorktrees` -> `useWorktreeMap` swap: both read the same WeakMap-cached snapshot, so the zustand compare is unchanged. Harden the folder tie-break to require the row to name its own host. `getCatalogOwnerHostId` defaults an unstamped row to `local`, which would let a row that never named a host win the `local` tie and mount another host's path; it now keeps first-wins instead of guessing. * fix(terminal): surface the unresolvable folder-surface collision When two hosts publish the same folder-workspace id and the active workspace's host cannot be resolved, the projection drops one row's folderPath first-wins. That path is the PTY cwd for any tab without a startupCwd, so the drop was silent. Warn on it, and pin the two tie-break branches the unit tests missed: a colliding row that is not the active workspace, and the same collision with the rows in swapped order (a host reconnect re-appends its rows, flipping which row is first mid-session). * test(e2e): ride out Playwright's spurious main-process evaluate rejection `e2e / changed e2e specs` failed on `pr11346-selected-runtime-add.spec.ts` with "Execution context was destroyed, most likely because of a navigation" from the paired client's first `app.evaluate` — the isolated-HOME assert that runs one millisecond after `electron.launch()` resolves, which is before the app is `ready`. Nothing navigates there: Playwright raises that message for any main-process CDP failure that is neither a JS error nor a closed session, and `ElectronApplication.evaluate` is unreliable on Electron 27+ (microsoft/playwright#33737). Reproduced locally, and a plain re-run of the same commit went green. Extract the retry `installTerminalPtyWriteSpy` already carried for this exact message into `retryTransientMainEvaluate`, and use it for the launch-time home read in all three launchers. The read is idempotent and a real boundary escape still throws on the first successful read. Also forward the paired client's process logs before the assert instead of after: this failure reached CI with none of the client's own output, because forwarding had not started yet. * test(e2e): wait on the owning group before asserting a Cmd-J browser tab is active `changed e2e specs` then failed at the remote browser-page step: the store poll had already seen `activeBrowserTabId` land on the mirrored workspace, but `[data-tab-id=...][data-active="true"]` never appeared. `data-active` on a `BrowserTab` is the strip's active tab, which comes from the owning group's `activeTabId` — not from `activeBrowserTabId` — so the DOM assert was racing an activation the poll never waited for. The simulator rows in the same spec already poll the group; the two browser-page rows did not. Poll the same triple for them, so a genuinely stuck group fails with the ids it ended on instead of a bare "element(s) not found". |
||
|
|
406bd0e378 |
perf(relay): cache process-table descendant indexes (#17646)
* perf(relay): cache process-table descendant indexes * fix(relay): keep the process-table index first-wins and narrow Two defects in the memoized index this PR introduced. - Restore the first-wins duplicate-pid tie-break the relay had as `rows.find()`. A process whose argv contains a newline makes `ps` print a continuation line that the lenient parser can accept as a spurious row duplicating a real pid; that row always FOLLOWS the real one, so last-wins let it capture the pane's foreground. The rule now lives in `buildProcessTableIndex`, so the batched evidence resolver's `byPid.get(rootPid)` root lookup gets the same semantics the subsystem had before indexing. - Build only the two indexes a resolver reads. `byPgid`/`byTpgid` have no readers repo-wide, and delegating to a four-map build made a one-pane relay pay more per 500ms capture than the single `childrenByParent` map it replaced -- a regression in the majority topology, in a PR whose point is relay CPU. Matches the same deletion in #17763 line for line so whichever merges second resolves trivially. |
||
|
|
d2aab68ae7 |
Automations ux improvement (#17626)
* Add keyboard navigation to automations UI Improves workflow efficiency by enabling keyboard-driven navigation across automations list, run history, and detail pane tabs. * Add Escape key support to automations detail pane Pressing Escape now clears external and automation run page views, then returns to the automations list. Also improves cross-browser compatibility of keyboard event handling by using Element checks and getAttribute instead of dataset access. * Fix keyboard navigation to let Enter key reach focused controls - Enter key now passes through to focused buttons, links, and other interactive controls - Arrow key navigation through automation run history still works - Prevents intercepting native keyboard behavior of interactive elements * improve test * Move keyboard focus to follow row selection When navigating automation runs with arrow keys, focus must follow the selection so Enter key acts on the newly selected row rather than the previously focused one. |
||
|
|
50938b2dbd |
Serialize filesystem watcher batch flush operations (#17602)
* Serialize filesystem watcher batch flush operations - Prevent dropped events during rapid concurrent file changes - Queue and drain follow-up batches to preserve event ordering - Cancel pending batch work when watchers are torn down * Prevent queued batch drain while debounce timer is armed An armed timer means the debounce window is still open. Drain only after the window closes to avoid splitting related filesystem events across separate payloads. * Remove redundant batch timer cleanup Rely on cancelLocalBatchFlush to handle the batch timer teardown, eliminating duplicate logic in the watcher cleanup path. |
||
|
|
2222e54754 | refactor(test): organize SSH and terminal recovery fixtures (#17751) | ||
|
|
40d245fe45 |
ci(release): gate signing behind release preflight
Prevents SignPath requests until all blocking release gates pass. |
||
|
|
ae35e044f2 |
fix(terminal): keep restored OSC-8 ranges across a no-op resize (#17759)
Cold restore seeded a checkpoint's OSC-8 link ranges and then replayed records
that resize, so any resize record after the checkpoint dropped them and
restored hyperlinks in scrollback lost clickability. Same-size resize records
reach the durable log routinely, because every attach re-asserts the pane's
dimensions and session-output-plane records each one without a same-size
dedupe — so an ordinary reattach was enough to lose the links.
Restored ranges are row-indexed, so clearing them on a reflow is right; a
resize to the size already applied is not a reflow. Gate on the dimensions
actually changing.
Introduced in
|
||
|
|
ad4f068040 |
fix(diff): close large-diff deferral review findings from #17521 (#17758)
* fix(diff): close large-diff deferral review findings from #17521 Deferral keyed "no line counts" off the untracked area, which both prompted ordinary untracked binaries and silently auto-loaded every tracked row when a status pass skipped counting (entry cap hit, numstat failed) — the freeze case the deferral exists for. Decide from the path instead: rows that render as a preview or a binary stub stay automatic, everything Monaco would open as text defers. Also give all three combined-diff virtualizers one shared row estimate, so the PR-review viewers stop estimating a deferred/in-flight large row at 88px while DiffSectionItem renders it at 188px, and drop the dead isLoadOnDemand parameter that estimate covered. * fix(diff): stop deferring cheap uncounted rows the extension list misses The path-only rule relocated friction rather than removing it: every uncounted row deferred unless its extension was in BINARY_FILE_EXTENSIONS, so two classes of tracked row flipped to a "Large diffs are not rendered by default" prompt they had never shown. Tracked binaries outside the list (this repo's own resources/build/icon.icns, plus .tiff/.avif/.psd/.parquet and every extensionless binary) get '-\t-' from `git diff --numstat`, and a submodule whose only change is untracked content inside it gets no numstat row at all while porcelain v2 still reports `1 .M S..U ... sub`. Both are cheap, and both are unreachable from a hardcoded extension list — verified against real git. OR the extension check with two signals already on the entry. A submodule row diffs to a "Subproject commit" line or two whatever it contains, so it is always cheap. And an uncounted row whose siblings in the same pass DID get counts is uncounted for a reason of its own: for a tracked row that reason can only be numstat's binary marker. Untracked rows keep deferring either way, since the scan also skips them past MAX_UNTRACKED_LINE_COUNT_BYTES and their size is exactly what is unknown. No new field crosses git status, the wire, or the section cache; `submodule` and the sibling counts are already there. Fan-out, accepted deliberately: when a pass counts nothing at all — didHitLimit at DEFAULT_GIT_STATUS_LIMIT, or runNumstat returning null — no row has a counted sibling, so the whole combined diff renders as Load prompts. Keeping it. Over 1000 changed entries is precisely the freeze this deferral exists for, and auto-loading that many unbounded Monaco models is the bug, not the mitigation; a numstat failure leaves every size genuinely unknown. Each row still has its own Load diff button, so nothing is unreachable — the only thing missing is a bulk "load all", which would reinstate the freeze on demand. * fix(diff): scope the counted-siblings signal to one counting pass hasCountedSiblings was one boolean over the whole entries array, but that array is not one counting pass. combined-all — the default whenever a branch compare exists — concatenates uncommitted rows with branch-compare rows, and even within the uncommitted set staged and unstaged are separate numstat calls that fail separately. So a single counted branch row vouched for an uncommitted pass that counted nothing (numstat null, or didHitLimit at DEFAULT_GIT_STATUS_LIMIT), and every uncounted row in it auto-loaded into exactly the Monaco freeze the deferral exists to prevent: the guard was off in the default view. Collect the passes that actually counted something, keyed by staging area for status rows and 'compare' for branch/commit rows, and ask that set per row. Untracked rows are unaffected — they never consult the signal. Class 1 of the charter (tracked binaries outside BINARY_FILE_EXTENSIONS) stays open, deliberately. Porcelain v2 reports a modified binary as `1 .M N... 100644` — indistinguishable from text — so only `git diff --numstat`'s `-\t-` knows, and that stdout is parsed on the host (shared/git-uncommitted-line-stats.ts) for both the local and relay status paths. The renderer sees entries, not numstat, so surfacing it per row means a new field on GitStatusEntry and GitBranchChangeEntry that also has to be re-applied in two attachLineStats copies and in the line-stats reuse cache, which persists only {added, removed} and would silently drop it. The one existing field that could carry it — added/removed set to 0 — changes what the host publishes to old clients and mobile, contradicts the documented "undefined for binary files" contract, and collapses the undefined-vs-zero distinction the virtualizer's height estimate reads. So a lone tracked .icns still shows the load prompt; not worth a wire field, and not worth another hardcoded extension. * fix(diff): stop calling an uncounted diff large in the load prompt The deferral prompt had one sentence for two different reasons. A row over MAX_AUTOMATIC_DIFF_CHANGED_LINES really is large. A row with no counts at all — numstat's binary marker, a pass that skipped counting — is deferred because its size is unknown, and "Large diffs are not rendered by default." is simply false for it: a lone tracked resources/build/icon.icns with no counted sibling in its own pass is 4 KB and still says large. Split the copy on the counts the section already carries. No new field on the entry, nothing across the wire, no change to attachLineStats or the line-stats cache — the predicate is renderer-local and mirrors the uncounted branch of shouldLoadCombinedDiffOnDemand, so the two stay in step. |
||
|
|
704167197a |
perf(relay): serve one ps capture per window and pin the batched inventory path (#17763)
Follow-up defect fixes for the batched PTY-inventory evidence path (#17525), now on main. - One memoized `ps` capture serves both the lenient and strict views. The two readers ran byte-identical argv behind separate caches, so a relay serving both forked `ps` twice per 500ms window — the doubling issue #6288 removed. - Drop the `byPgid`/`byTpgid` indexes no resolver reads, plus the zero-caller `parseProcessTableRowsStrict` and `getFreshStrictProcessTableSnapshot`; the batch resolver now reuses the shared index lookup and candidate score instead of private copies. - Restore `getForegroundProcessName`'s ladder contract: the extracted table scan answers null again, so an unconfirmed wrapper fallback publishes the recognized (normalized) name rather than node-pty's raw one. - Pin the SHIPPED `pty.listProcesses` path: one capture and one linear row pass for N panes, and node-pty's own name (never "shell") when the capture cannot disambiguate a `node`/`python` wrapper. - Pin the hidden-pane cadence gate in the production option shape, and move the strict-parser coverage next to the parser it tests. |
||
|
|
26031ca317 |
fix(browser): scroll oversized viewport presets (#17569)
* fix(browser): scroll oversized viewport presets * fix(browser): preserve guest wheel scrolling at viewport edges * fix(browser): keep viewport scroll state synchronized * test: assert partial viewport wheel forwarding |
||
|
|
1a47b9ee85 |
fix(remote): distinguish SSH transport from runtime availability (#17710)
* fix(remote): distinguish transport from runtime availability * fix(remote): preserve transport diagnostics for unavailable runtime * fix(remote): propagate transport diagnostics to host setups * fix(remote): keep unavailable runtimes out of ready setups * fix(remote): preserve unavailable runtime state in settings * fix(remote): preserve reconnecting runtime state * fix(remote): guard stale settings connectivity * fix(remote): preserve diagnostics after main merge * fix(i18n): preserve translations during runtime status merge * fix(remote): refresh settings row health from store * fix(remote): refresh settings row health from store * fix(remote): clear diagnostics generations in tests * fix(settings): refresh runtime availability summary * refactor(runtime): split status slice types * refactor(runtime): reuse status app state type --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
45c4823109 |
Format documentation with consistent line wrapping and table alignment (#17765)
Standardize MDX files across docs with: - Remove trailing semicolons from import statements - Wrap long lines and multi-line component props for readability - Align Markdown table column separators - Normalize text and JSX formatting for consistency |
||
|
|
fa0180dc61 |
perf(renderer): avoid combined-diff tree rebuilds during progressive loads (#17643)
* perf(renderer): avoid combined-diff tree rebuilds during progressive loads * fix(renderer): preserve collapsed combined-diff tree boundaries * perf(renderer): skip unfiltered combined-diff flatten when hiding viewed files * fix(renderer): keep reordered viewed keys in the combined-diff delta The incremental viewedSectionKeys delta walked indices issuing a delete then an add, so a key added at index i and deleted as the previous key at a later index was silently dropped. Fall back to a full recompute when any index's key differs; the progressive-load fast path (stable keys, flipping loading state) is unchanged. |