mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
relay-split/setup-node-cache
10019
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
f546f53a4e |
docs: update Android APK link to 0.0.47 (#17764)
Update the README download links to the latest mobile Android release. |
||
|
|
c558d7e083 |
Activate terminal splits before inherited CWD resolution (#17601)
* perf(terminal): activate splits before cwd resolution * test(terminal): prove split focus before cwd publish * fix(terminal): release stale split cwd fence * test(terminal): add visible split activation latency benchmark * docs(reliability): clarify split benchmark provenance * fix: preserve deferred split handoffs across remounts * fix: fence late deferred split closes * docs(reliability): record exact split benchmark runs * test(reliability): fail benchmark on artifact write errors * test(reliability): attribute split activation phases * docs(reliability): record schema-v2 split benchmark * refactor(terminal): collapse duplicated split-handoff and write-queue paths - Drop the discardDeferredSplitPaneHandoff alias for its identical clear twin. - Fold the deferred-cwd resolve/reject settle handlers into one applier. - Extract settlePaneCwdDeferredSpawn for the repeated read-clear-write pattern. - Share one head-index FIFO primitive between the ordinary and reply queues. * fix(terminal): stop retaining a promise reaction per acknowledged write Racing every accepted write against one queue-lifetime cancel promise kept a reaction record alive until that promise settled: 200k acknowledged writes retained 88.6MB, now 0.1MB. Give each in-flight write its own cancel, and split the shared FIFO primitive into its own module. Also sanitize the split-latency benchmark report at its single serialization point so shared artifacts no longer carry the machine-local repo path or unbounded cleanup error text. * fix(terminal): settle deferred split input when the spawn is abandoned An abandoned deferred spawn returns before transport.connect(), so nothing drained the pre-connect buffer: sendInputAccepted's promise never settled and a paste into that pane hung forever. Clear the buffer on the abandon fence. Also re-derive the pre-connect retention cap from the clipboard-paste ceiling rather than the 16MB single-write ceiling; it is held twice per pane across up to 64 deferred splits, so 5.59M code units guarded the wrong thing. * fix(terminal): release the deferred cwd fence on a rejected reattach A daemon createOrAttach can turn an apparent fresh spawn into a reattach; when that reattach is refused the spawn ends with deferredSplitSpawn/pendingCwd still set, permanently arming the pre-bind detach refusal. The release no-ops when a PTY did bind, so it only fires where the fence would otherwise leak. The stale-generation return above is deliberately left alone: a newer connect already owns the pane there, and the fence is not generation-scoped. |
||
|
|
4ac8a8912c |
fix(startup): install the app environment with the userData decision (#17755)
src/main/index.ts decided where userData lives at module scope, then installed the AppEnvironment port ~180 lines later inside the single-instance-lock block. Every statement in that gap was a latent failure: a path resolve there either threw 'AppEnvironment not initialized' and killed the process, or — with the accessor installed but the decision not yet run — would have memoized the pre-override directory in getCanonicalUserDataPath() for the whole session. The first outcome shipped. #16761/#16698/#17509 were one statement landing in that gap and killing every macOS `orca serve` across 1.4.190-1.4.192; #16762 moved that call but left the gap. Install the port and capture the canonical path immediately after the two calls that decide them, so the window is zero rather than small. Both are inert at this point — ElectronAppEnvironment holds no state and calls `app` lazily per accessor, and initDataPath only joins strings — so nothing that depended on the old position moves with them. The secret store stays where its pre-ready Keychain note applies. The throw is kept and still covers the case it should: resolving a path before the decision has run. Guarded by a source-level assertion that the decision, the install and the capture stay adjacent. Fixes #17750 |
||
|
|
59facfb71e |
Show live tool progress in native chat (#17597)
* Show live tool progress in native chat * fix(native-chat): scope live tool indicator to current turn * fix(native-chat): settle orphaned live tool rows * fix(native-chat): keep live tools running without lifecycle metadata * fix(native-chat): keep working status stable during streaming * fix(native-chat): anchor turn status below prompts * fix(native-chat): preserve turn status and legacy tool activity * fix(native-chat): limit turn status UI to structured Codex --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
8ac1c6e2ac |
perf(git): bound ref and worktree scans (#17655)
* perf(git): bound ref and worktree scans * fix(repo-search): clamp oversized ref limits * fix(worktree): keep strict worktree listing unshared The shared-scan re-export flipped every `listWorktreesStrict` caller from an isolated subprocess to the coalesced scan. `git worktree prune` in the removal recovery path does not bump the scan generation, so a post-prune verification could join a pre-prune scan, see the stale row, and report a successful removal as a stale registration. The same gap defeats the post-archive-hook rechecks that exist to catch an external Git client locking the row. Restore the unshared export and make coalescing opt-in via `listWorktreesSharedStrict`, which existing callers already use deliberately. * fix(git): separate a proven absent ref from a failed probe `show-ref --verify --quiet` exits 1 for a missing ref, but so does `wsl.exe` when its own launch fails, so reading any exit 1 as absence collapsed `unverifiable` into `exited`. A genuine miss prints nothing while a wrapper failure always explains itself, so require empty stderr alongside the exit code; a runner that reports no stderr at all keeps its exit-code contract. That same signal removes a spawn regression: `show-ref` is a direct-git read under WSL, and the runner retried any numeric exit through the user's interactive login shell. The replaced `for-each-ref` exited 0 on a miss, so absence never retried; every absent probe now would. Treat a quiet exit 1 as Git control flow and skip the fallback. Also narrow the hosted-review suffix fallback: the replaced `refs/remotes/*/<base>` could not cross a slash, but `show-ref -- <base>` matches at any depth, so `origin/feature/main` answered a query for `main` and submitted a review against a base the provider rejects. Refresh the real-binary compatibility contract to the shipped excludes, and assert exact probe concurrency rather than an upper bound so a regression to serial probing fails. |
||
|
|
a5be10815c |
perf(terminal): drop the headless snapshot cache, keep the spawn lock (#17752)
Removes the snapshot memoization added in #17667 and everything that served it: the epoch, markMutated/markWritten, the no-op resize gate, and the HeadlessSnapshotCache module. The shared/exclusive spawn lock from the same PR stays — it is the half that carries the measured win. Why: a controlled A/B on merged main could not show the cache paying for its memory. Same worktree, same sessions, reattach stable_adoption per session: cache + resize gate (as merged) 16 / 67 / 78 / 87 ms cache off, gate on 17 / 55 / 68 / 80 ms cache off, gate off 13 / 62 / 73 / 83 ms Indistinguishable. Cold 4-tab activation was likewise unchanged (217-296ms without the cache vs 226-309ms with), which is expected — a first attach is always a miss. The reason it under-delivers is a design fact the original PR missed: attach does not request the full buffer. terminal-host-session-create.ts passes resolveDaemonSessionScrollbackRows() — a deliberate 1000-row live window, capped because unbounded retention once OOM-killed a host. Serializing 1000 rows is cheap, so there was little for a cache to save on that path. Against that, the cache retained up to MAX_CACHED_SNAPSHOT_BYTES (4MB) per entry across MAX_CACHED_SNAPSHOT_WINDOWS (2) entries per emulator, for the session's lifetime, with no aggregate budget across sessions. It also carried an invalidation contract that produced three separate over-invalidation bugs during review (the parse fence, setCwd/setLastTitle, and the no-op resize). The lock fix is unaffected and independently measured: the `options` phase, which is pure queueing, went 0/125/212/291ms -> 1/1/1/1ms across a 4-tab worktree activation and stays there. |
||
|
|
8f15f217a2 |
Preserve user-set workspace names across branch changes (#17448)
* fix(worktrees): preserve user workspace names across branch changes * test(worktrees): cover pinned rename metadata * fix(workspaces): address display-name review edge cases * fix(workspaces): keep automatic names fresh across refreshes * fix(workspaces): preserve legacy CLI labels * fix(workspaces): preserve display-name provenance across hosts * fix(workspaces): honor legacy display-name provenance * fix(workspaces): fence display-name refresh races * fix(workspaces): accept peer renames from provenance-less hosts The old-host preserve fence kept a pinned local label on every refresh, which also suppressed a legitimate rename another client persisted through the same host until app restart. Narrow it to labels the host re-derived itself (branch short name, or path basename when detached); any other changed label in a mode-less response is explicit meta a peer wrote there. Stale prior-label responses stay covered by the downstream staleness fence, in-flight writes by the pending fence. * refactor(workspaces): unify display-name pin derivation Three call sites (renderer optimistic update, local IPC updateMeta handler, remote worktree.set handler) each restated the same formula; a future edit to one would silently skew provenance between paths. |
||
|
|
b44ef1e59d |
fix(skills): narrow computer-use discovery boundary (#17736)
* fix(skills): narrow computer-use discovery boundary * chore: remove merge-formatting noise * fix(skills): name browser page automation surfaces |
||
|
|
20a12a6a46 |
perf(codex): share one launch-prep hook install across a spawn burst (#17669)
* perf(codex): share one launch-prep hook install across a spawn burst Codex launch prep runs a full managed-hook install on every local PTY spawn, and both install lanes serialize globally per Codex home. Opening a multi-pane worktree therefore paid N full installs back to back, and a resumed Codex pane prepares twice. Concurrent spawns for the same runtime home now share one run; the promise is dropped as soon as it settles, so the next launch still re-reads hooks.json and the user's trust state. Also split the `host_env` spawn-timing phase, which spanned the entire Codex preamble and pinned that cost on the env builder that ran last. * refactor(codex): unify the two hook-install single-flight lanes Both the WSL and launch-prep lanes now share one generic in-flight helper instead of duplicating the map bookkeeping. Also routes the WSL launch-prep install through the serialized variant, which closes the same per-spawn serialization gap on WSL that the native lane just got. * refactor: extract the shared in-flight run dedupe The codex hook service and the GitHub conflict-summary cache had grown near-identical private copies of the same single-flight helper. Both now use one module, which also keeps the hook service clear of the 300-line budget. The shared copy keeps the identity check on clear so a late settle cannot evict a newer entry for the same key. |
||
|
|
4ca232c159 |
perf(terminal): cut cold worktree-switch attach latency (#17667)
* perf(terminal): let a worktree's terminal spawns run concurrently
The per-worktree terminal mutation guard was a FIFO mutex, so activating a
multi-tab worktree made each tab wait for every predecessor's whole spawn.
Measured with ORCA_PTY_SPAWN_TIMING=1 on a 4-tab worktree, the `options`
phase was a pure-queueing staircase: 0 / 125 / 212 / 291ms.
The invariant that guard protects is spawn-vs-sleep exclusion, never
spawn-vs-spawn. Replace it with a writer-preferring shared/exclusive lock:
spawns share, sleep still excludes, and a queued sleep blocks later spawns
so a stream of spawns cannot starve it into its 12s deadline.
Same worktree after: options = 2 / 3 / 12 / 34ms, and stable_adoption
flattens from 65/398/398/312ms to a steady ~253ms.
The existing folder-workspace control assertion asserted the FIFO behavior
this removes, so it is re-based on sleep-vs-spawn (which still queues) and
joined by a case asserting concurrent same-worktree spawns.
* perf(terminal): memoize the headless snapshot per mutation epoch
Attaching a viewer serializes the session's whole headless buffer
synchronously on the daemon event loop, so every reattach of a quiescent
session re-serialized identical bytes. Measured with ORCA_PTY_SPAWN_TIMING=1,
stable_adoption was 253-281ms per session on reattach.
Move snapshot assembly into HeadlessSnapshotCache and memoize its expensive
parts (the serialize, the OSC link walk, the frame-restore fields) on a
mutation epoch that every emulator state mutation bumps, so a cache hit is
byte-identical by construction rather than merely fresh-enough. The async
write path bumps on entry and again in the parse-completion callback, so a
snapshot taken mid-parse can never be retained.
Reattach of a quiescent session after: stable_adoption 12ms. Sessions with
output since their last snapshot re-serialize exactly as before.
Retention is capped: an entry is held for the session's lifetime once it goes
quiescent, and a renderer may request 50k scrollback rows, so oversized
payloads serve normally but are not retained. Cache hits clone nested values
so a caller mutating its snapshot cannot corrupt later ones.
* perf(terminal): keep the snapshot cache warm across zero-byte parse fences
Review follow-up. flushParsedWrites() is write(''), used purely as a parse
fence, and every getSettledSnapshot runs one — so the epoch bump on an empty
write evicted the attach entry on each checkpoint read, defeating the cache
for any session that gets checkpointed.
Zero bytes cannot mutate the buffer: the OSC and mouse-mode scans are no-ops
on '' and the partial-escape tail is idempotent, so skip the bump for empty
data. Real writes still bracket themselves, and any write a fence orders
behind has already bumped on its own completion.
Also invalidate on dispose, so a post-dispose read can never be served a
pre-dispose entry, and freeze the emulator's public method surface in a test:
the cache's correctness rests on every mutator calling markMutated(), which is
convention rather than a type, so a new method should be a deliberate decision
about invalidation instead of a silent stale-snapshot bug.
* refactor(terminal): apply elegance review to the attach-latency fixes
Reuse: waitForMutationGrant hand-rolled the deadline race that
settleBeforeDeadline (same directory, four existing callers) already owns.
Using it also picks up the timer.unref() the local copy lacked, which was
keeping the Node event loop alive for up to the 12s sleep deadline.
Simplify: drop the waiter `abandoned` flag. The timeout path sets it and
splices the waiter out in the same synchronous block, so no queued waiter can
ever be observed abandoned and both reads were unreachable. The splice is what
actually does the work; the comment now carries why that makes a
grant-after-timeout unrepresentable. drain() then collapses into its loop
condition and reuses markActive() instead of inlining it twice.
Extract markWritten() so the zero-byte parse-fence rationale lives at one
mutation gate instead of being restated at three call sites.
Match the daemon's byte-accounting convention: the retention cap is now
expressed in bytes with code-unit sizing, like MAX_COLD_RESTORE_CACHE_BYTES
next door, so the two retention budgets read in one unit. Same effective cap.
The public-surface guard test caught markWritten on the first run, which is
the behavior it was added for.
* perf(terminal): stop discarding the snapshot cache on non-memoized fields
Second elegance round, and it found the same class of bug as the parse-fence
one: cwd and lastTitle are read fresh on every build and were never memoized,
yet setCwd/setLastTitle bumped the epoch — discarding a whole serialize to
update a field the cache does not hold. OSC 7 cwd updates land on every `cd`,
so this was a live cost on exactly the busy sessions the cache targets. The
invariant is "every mutation of a memoized part", not "every state mutation";
both docblocks said the latter and are corrected.
Drop the dispose bump too. A post-dispose getSnapshot re-serializes the
disposed terminal to byte-identical content, so the bump bought nothing and
only reached into a disposed xterm — verified by probe, not assumed. Its test
asserted zero serializations after dispose, which no implementation could
violate; it passed with the bump deleted.
Also memoize rehydrateSequences (a string, so no clone needed) instead of
rebuilding it on every hit, derive the frameRestore type from
buildFrameRestoreSnapshotFields so a new field cannot flow through at runtime
while the type omits it, inline the single-caller resolve() into build(), and
move the write() entry bump after the sync early-return so the three bumps map
1:1 onto sync / async-pre-parse / async-post-parse.
The surface guard is sorted in source and renamed to say what it freezes: the
prototype, TS-private members included.
* fix(terminal): correct the fence justification and key the cache by window
The markWritten docblock claimed "zero bytes cannot mutate the buffer". That
is false, and I verified it: `_core.writeSync('')` drains xterm's pending queue
and applies it. The exemption is still correct, but for a different reason —
a fence cannot introduce an *unattributed* mutation, because any bytes it
drains belong to a queued async write whose own completion callback bumps
first. The two write regimes are exhaustive: with writeSync present nothing
can queue, without it every write is async and self-bumps. A comment asserting
a false invariant is worse than no comment, since the next change may rely on
it, so it now states the real one.
Key the cache by scrollback window instead of a single slot. Consumers ask for
different windows against the same emulator — attach passes the full window
while agent/text reads pass 0 — so one slot thrashed to a 0% hit rate whenever
they alternated, silently removing the benefit on runtime-side emulators. Two
entries cover every caller pair in the tree.
Drop the epoch counter: markMutated already nulls the retained entry and an
entry is only ever stored under the current epoch, so the comparison could
never fail. Invalidation is simply "clear the cache".
* refactor(terminal): name the lock sides shared/exclusive for a third caller
Rebasing onto main surfaced a semantic conflict the merge applied cleanly:
main added runWorktreeTerminalMutation (terminal orphan adoption, #17159) as a
third caller of the guard this PR changed. It needs the exclusive side —
adoption reconciles a worktree's terminal records, so it must not interleave
with a spawn registering a pty or with a sleep, which is exactly the semantics
it was written under when the guard was a plain mutex.
With three operations, naming the sides after two of them no longer fits, so
the kinds are now `shared` (spawn) and `exclusive` (sleep, adoption) — what
they do rather than who calls them.
* perf(terminal): do not invalidate the snapshot cache on a no-op resize
Re-measuring the final rebased build caught the cache barely working on the
path it exists for. Every attach re-asserts the pane's dimensions, and resize()
bumped unconditionally, so a reattach of a fully idle session missed its own
cached snapshot and re-serialized.
Measured on the same 4-tab worktree, reattach of sessions verified quiescent
(no buffer change over 4s), stable_adoption per session:
before this commit: 98 / 382 / 395 / 418 ms
after: 16 / 67 / 78 / 87 ms
A resize to the size already applied changes nothing the snapshot reads, so it
now returns early — which also stops it clearing restoredOscLinks, correct
since no rows shifted.
|
||
|
|
18e8fe4770 |
fix(serve): install supervisor disconnect quit after app environment init (#16762)
Moves installServeSupervisorDisconnectQuit(isServeMode) out of module scope in src/main/index.ts to just after setAppEnvironment() and initDataPath(). The call resolves the serve update handoff path through getCanonicalUserDataPath(), which throws by design until the app environment accessor is installed. At module scope that throw was unconditional on macOS whenever the CLI set ORCA_SERVE_UPDATE_HANDOFF_PATH — which it does by default — so every `orca serve` process died at startup before it could listen, and the supervising service manager restarted it into the same crash. Reported in #16761, #16698 and #17509; shipped in 1.4.190 through 1.4.192. Guards added so it cannot drift back: a source-level ordering assertion that also pins the call synchronous and inside the single-instance block, and a runtime test that keeps the real path resolver, since the existing suite mocks it and therefore could never have caught this. Fixes #16761 Fixes #16698 Fixes #17509 |
||
|
|
8bebcf5def |
fix(terminal): preserve large agent prompt pastes (#17718)
* fix(terminal): preserve large agent prompt pastes * fix(terminal): guard oversized SSH PTY writes * fix(terminal): avoid timer delay for generic sends * fix(pty): propagate provider write refusals |
||
|
|
02a7742406 |
fix(artifacts): raise desktop sharing limit to 5 MiB (#17708)
* fix(artifacts): raise desktop sharing limit to 5 MiB * fix(artifacts): enforce recovery content limit * fix(artifacts): bound recovery request envelopes * fix(artifacts): clarify oversized request error |
||
|
|
aa658d28e3 |
test(updater): stop a slow module import from failing the next test (#17726)
* test(updater): stop a slow module import from failing the next test
`updater.ts` is 2.4k lines. Its first transform in a worker costs ~1.4s idle
but 45s+ when the machine is oversubscribed, which is past the 30s
`testTimeout`. Vitest cannot cancel the timed-out test body, so the abandoned
continuation went on to call `setupAutoUpdater` during the *next* test — with
the harness already reset — and failed it with:
AssertionError: expected "vi.fn()" to be called 1 times, but got 2 times
That is the exact signature of the abandoned-instance timer flake fixed in
#17649/#17663, so a machine-load timeout reads as that regression returning and
sends the reader hunting in the wrong place.
Two changes, in `updater-test-module-loader.ts`:
- `loadUpdaterModule()` replaces every `await import('./updater')` in the suite.
It records the test that asked for the module and throws if the import
resolves after that test ended, stranding the continuation so the timeout
stays the only reported failure. This removes the trap.
- `warmUpdaterModule()` imports the module once in `beforeAll`. The transform is
cached across `vi.resetModules()` — only a file's first import pays it — so
warming moves that one slow import onto the 60s `hookTimeout` and leaves every
in-test import at re-evaluation cost (~25ms idle).
Measured on a 16-core mac, first vs later import in one file: 1439ms / 25ms
idle, 8339ms / 149ms under 40 CPU hogs, 45521ms / 15182ms under 400.
Under 400 hogs the suite went from 15 files and 22 tests failing (15 timeouts
plus 7 misleading assertion failures) to 23/23 files and 269/269 passing. Under
900 hogs it degrades into 14 plain `Hook timed out in 60000ms` failures and zero
assertion failures.
* fix: tighten the fence, surface its warning, stop patching timers on warm-up
Review findings on the loader:
Drop trackRealTimers() from warmUpdaterModule(). It was inert — updater.ts
arms no timers at module scope — and actively harmful for the 5 files that
build their own mocks and never call clearTrackedRealTimers(). Those files
previously had pristine timer globals; the warm-up installed a wrapper that
was never restored and whose armed-handle set grew unbounded.
Key the fence on TestRunner.getCurrentTest() instead of currentTestName.
Nothing ever clears currentTestName, so the fence only fired once the *next*
test had started; a continuation resolving during the timed-out test's own
teardown, or after the file's last test, was still handed the module. The
last-test case mattered: the harness afterAll has already cleared timer
tracking by then.
Emit the diagnostic through process.emitWarning. The throw lands on a promise
vitest already settled, so the message explaining why the continuation was
stranded was discarded and reached nobody — which was the entire payoff.
Widen the loader test's race margin 50ms -> 500ms. It gated on the
test-to-test transition completing in 50ms, so the regression test for a
contention bug could itself fail under contention.
|
||
|
|
08d95b9979 |
test(native-chat): remove initial-snapshot recovery race in watch-error test (#17722)
Root cause: the test cleared the injected tail-reader failure *before*
writing the recovered transcript line. The capped rotation retry loop is
still firing at that point, so a retry drain could succeed against the
still-empty file, consume the pending initial drain, and emit an empty
initial snapshot (`[], false, 0, undefined, undefined`). The later manual
watch callback then took the append path, and `u-recovered` never reached
onInitialSnapshot -- producing the CI failure
`expected [ false, +0, ...(5) ] to deeply equal ArrayContaining{...}`.
That empty-snapshot-then-append sequence is correct product behavior, so
this is a test bug: write the content first, then clear the failure, so no
drain can ever observe a readable-but-empty transcript. The assertion now
checks the exact recovered snapshot instead of a flattened
arrayContaining, so an empty recovery snapshot fails loudly.
|
||
|
|
a381b47437 |
test(cursor): widen Windows hook spawn budget to fix ETIMEDOUT flake (#17721)
* test(cursor): widen Windows hook spawn budget to fix ETIMEDOUT flake `package (windows)` failed once on an unrelated packaging PR with `spawnSync cmd.exe ETIMEDOUT` at hook-service.test.ts:78. This is an infrastructure-timing flake, not a logic race: the assertion is `expect(result.error).toBeUndefined()` and `ETIMEDOUT` only means the spawnSync `timeout` elapsed. Cursor is the heaviest of the hook-service suites on Windows. Its managed command is the PowerShell encoded launcher, so one hook run is cmd.exe -> powershell.exe -> cursor-hook.cmd -> curl.exe: four process creations, one of them a CLR start that installer-utils.ts itself documents as ~300ms warm and "visibly slow". The sibling suites (codex, grok, agent-hooks/installer-utils) spawn the .cmd directly and set no per-spawn timeout at all, so 15s here was a one-off, not a convention. Raise the per-spawn budget 15s -> 30s to match WINDOWS_PROCESS_TEST_TIMEOUT_MS in src/shared/setup-agent-sequencing*.test.ts and the 30-90s used by the real-subprocess tests in src/main/browser. 30s is ~30x the warm cost of the chain, which leaves room for CPU contention and Defender scanning of the freshly written .cmd on a packaging runner. The default vitest testTimeout is also 30s, which would have become the new binding constraint (the protocol case runs 16 chains back to back), so give the four spawning cases 120s. That keeps ETIMEDOUT - which names the stuck process - as the failure you see, instead of an opaque case timeout. No product behavior changes and no end-to-end coverage of the Windows launcher is removed. * fix: halve the case timeout and correct the contention rationale Review found the stated cause wrong. pr.yml runs "Test Windows-specific boundaries" before "Build package inputs", so electron-builder is not running. The real contender is that vitest invocation itself: ~25 files at maxWorkers 4, including five real-Electron suites and two node-pty tests. 120s was over-provisioned. windows-hook-payload-delivery.test.ts drives the identical PowerShell chain on the same job with a 60s case budget; 60s gives the same property here (16 warm spawns plus one 30s outlier) and halves time-to-signal on a genuinely stuck chain. Also record that 30s deliberately exceeds the product's own MANAGED_HOOK_TIMEOUT_SECONDS (10s) — this test gates launcher correctness, not user latency, so the SLA is not the right bound. Left windows-hook-payload-delivery.test.ts at 15s: its value is deliberate, set to mirror Claude Code abandoning a hook at 10s. |
||
|
|
872bd51d47 |
fix(native-chat): reland large structured command results (#17720)
* fix(native-chat): preserve large structured command results (#17707) * fix(native-chat): preserve large structured command results * chore: place native chat validation artifacts under docs * chore: drop stale root package config * fix(native-chat): enforce rebuilt lifecycle append slots --------- Co-authored-by: Merge Sim <sim@local> * chore: omit native-chat reland planning docs * fix(native-chat): remove journal store import cycle * fix(native-chat): keep journal factory acyclic --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
dc5db4b01c |
fix(lint): merge duplicate process-table-snapshot imports (#17724)
main is red on `static analysis`: oxlint's code-quality pass runs with --deny-warnings, and agent-foreground-process-batch.test.ts imports '../../shared/process-table-snapshot' twice (lines 5 and 13), tripping "Modules should not be imported multiple times in the same file". Introduced by #17525. It blocks every open PR, none of which can go green until this lands. |
||
|
|
9477b5fcbb |
feat(ssh): batch process evidence in PTY inventory (#17525)
* feat(ssh): batch process evidence in PTY inventory * fix(ssh): accept Linux kernel process rows and make no-evidence polling push-driven * fix(ssh): preserve process evidence polling semantics --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
894ed75abb |
Revert "fix(native-chat): preserve large structured command results (#17707)" (#17719)
This reverts commit
|
||
|
|
5fe37729ea |
fix(native-chat): preserve large structured command results (#17707)
* fix(native-chat): preserve large structured command results * chore: place native chat validation artifacts under docs * chore: drop stale root package config * fix(native-chat): enforce rebuilt lifecycle append slots --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
68de1be517 |
fix: satisfy GitLab hook and test lint gates (#17694)
* fix: satisfy GitLab hook and test lint gates * Rename electron-vite target config to .cts The .cts extension keeps the config as CommonJS, allowing electron-vite to load each parallel target without sharing its timestamp-named ESM temp file. |
||
|
|
aabcc57366 |
fix(runtime): publish remote control outages to host surfaces (#17531)
* fix(runtime): publish remote control diagnostics to renderer * test(runtime): account for diagnostics bridge listener * fix(i18n): add runtime connection state labels * test(runtime): clean up shared control connection * fix(runtime): fence diagnostics by shared-control capability * fix(runtime): preserve authoritative transport state * fix(runtime): preserve diagnostic overlay lifecycle * fix(runtime): avoid publishing unchanged diagnostics state --------- Co-authored-by: Merge Sim <sim@local> |
||
|
|
db84894eef | fix(runtime): isolate paired terminal creates from host focus (#17713) |