* fix: preserve newer hosted review lookup ownership
* refactor: reuse the shared lookup generation sequence
Drops this coordinator's private counter for the identical allocator added on
the pull-request branch, so the "never reuse a generation id" invariant lives in
one place. The file is byte-identical on both branches, so either may merge
first.
* docs(store): describe the lookup generation sequence by its contract, not its callers
The JSDoc claimed the sequence was "shared by the pull-request and hosted-review
request coordinators", but the pull-request call site arrives in a sibling
change, so on this branch alone the comment named a caller that does not exist.
Describe what the module provides instead: one process-lifetime monotonic
allocator for lookup-ownership stamps, safe to share across every cache because
callers compare stamps for equality only, never order or magnitude. That stays
accurate whether one coordinator draws from it or several, so it needs no edit
when the second call site lands.
Applied identically on the sibling branch so the file stays byte-identical and
the two add/add introductions keep merging without conflict.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: preserve newer pull request lookup ownership
* refactor: share one lookup generation sequence between review coordinators
The sibling hosted-review fix needs the same never-reused generation id, so
move the allocator into src/renderer/src/store/lookup-generation-sequence.ts
instead of keeping a second private counter per coordinator.
Also assert in the lifetime test that a stale lookup cannot publish into
prCache and that the live lookup's answer is what lands there.
* docs(store): describe the lookup generation sequence by its contract, not its callers
The JSDoc claimed the sequence was "shared by the pull-request and hosted-review
request coordinators", but the hosted-review call site arrives in a sibling
change, so on this branch alone the comment named a caller that does not exist.
Describe what the module provides instead: one process-lifetime monotonic
allocator for lookup-ownership stamps, safe to share across every cache because
callers compare stamps for equality only, never order or magnitude. That stays
accurate whether one coordinator draws from it or several, so it needs no edit
when the second call site lands.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: remove PDF export temp files after setup failures
* fix(export): keep PDF temp cleanup to files this export created, and time out a hung load
Two holes in the temp-document cleanup:
- The write was not an exclusive create, so anything already sitting at the
generated temp path (a symlink planted in the shared temp dir) would be
written through, and the cleanup would then unlink an entry this export did
not create. The write now uses `flag: 'wx'`, and the one failure that means
"this path is not ours" (EEXIST) skips cleanup entirely.
- The 60s timeout only covered render-and-print, started after the load had
already finished. An export document whose script never yields fires neither
`did-finish-load` nor `did-fail-load`, so the load await hung forever and the
hidden window plus its temp file leaked for the life of the app. The timer now
starts before `loadFile` and races the whole load-render-print sequence.
Tests cover the preserved foreign file, a never-settling load, a load that
resolves without either event, `did-fail-load`, and that one export's cleanup
cannot touch a concurrent export's in-flight temp file.
* perf: skip macOS DNS probes for unrelated errors
* review(dns-probe-admission): single-source the probe gate and widen resolution-failure coverage
The admission guard duplicated the hint predicate at a second call site, so a gate
that drifted stricter than the hint test would silently drop the DNS diagnostic for
a real lookup failure. Route both through isMacTailscaleDnsHintCandidate, evaluated
once per call, and add a parity test asserting admission never changes the message
the ungated hint decision produces.
Also: widen the predicate to the resolution-failure wordings it missed (EAI_NONAME /
EAI_FAIL / ENODATA / getaddrinfo / "could not resolve" / "name or service not known" /
ERR_NAME_RESOLUTION_FAILED), gate the probe on process.platform === 'darwin' explicitly
rather than relying on the reader's internal check, make the hint idempotent so a
re-wrapped hinted message neither duplicates copy nor re-probes, and rewrite the
cache-window test to assert the resolver state the next relevant error reports instead
of pinning an exact probe count.
* perf: share pending plugin translation startup requests
* fix(plugins): retry a wedged language-pack startup request instead of joining it
Sharing the pending startup request removed the duplicate IPC call, but the
suppression was permanent: plugins:listLanguagePacks awaits plugin discovery,
so a request that never settles left ensurePluginLanguagePacksLoaded a no-op
for the session. Every later consumer joined a request that would never
finish, where before each one retried — loaded stayed false, pinning the UI on
built-in translations and suppressing the TCC notices that gate on it.
Bound the join to a window instead of a boolean: a request that is merely slow
is still shared, one past the window is treated as wedged and a later consumer
starts its own. Adds the stalled-request test the change was missing.
* fix: retire stale review lookups after cache invalidation
* test: pin the fence a retired review lookup answer has to clear
The extraction widened canAdoptDetachedAnswer: a retired owner is no longer in
the in-flight index, so with no replacement reader the scope generation is the
only thing stopping its pre-invalidation "no review" from being adopted as
current — which would short-circuit the lookup for the review Orca just opened.
Cover that interleaving, and restore the invariant comments the extraction
dropped (token identity, expire idempotency, and that a size-cap eviction
forfeits the wall-clock sweep).
Preserve supported Hermes YAML values and comments while installing or removing Orca hooks.
Adapted from manthis and Pr1p proposals #22366 and #20632.
Co-authored-by: Maxime AUBURTIN <m@hellomax.io>
Co-authored-by: Chen <zwq19980411@gmail.com>
Keep keyboard focus in the workspace list during selection changes, and transfer it to the selected terminal on Enter only when focus actually moves.
Based on Kelvin Amoaba proposal #22911.
Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
Deliver Dispatch mail and ordinary replies to the current lead Run, preserving original delivery receipts and pre-bind mail draining.
Based on Seongho Bae proposals #22977 and #22979.
Co-authored-by: Seongho Bae <seonghobae@me.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Highlight Typst files and diffs through the existing lazy TextMate provider.
Based on contributor proposal #22884.
Co-authored-by: Quan Nguyen <qnguyen.dev2@gmail.com>
Keep grouped commas intact when building search filters for ripgrep and Git.
Based on contributor proposal #22915.
Co-authored-by: KAPUIST <thsxornjs12@gmail.com>
Show unread emphasis in compact workspace rows using the existing pane acknowledgment state.
Based on drakeo338’s bounded PR #22870. pinhaum reported #22857 and proposed the broader #22899 variant; the local candidate preserves the existing acknowledgement policy.
Co-authored-by: drakeo338 <paranoyouz@gmail.com>
Retain Japanese middle dots and tildes in rooted and explicitly relative terminal file paths; preserve existing path routing and suffix handling.
Co-authored-by: Yi-111-a <47240345+Yi-111-a@users.noreply.github.com>
Infer zero weekly Grok usage for a confirmed explicit zero quota while retaining unreported and monthly precedence safeguards.
Adapted from huiq777’s PR #22303 and the payload analysis from deminit02-hue in issue #20657. The accepted exception is limited to explicit zero cap.
Co-authored-by: Hui <a3239737781@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* ci: share mobile route analysis and scope mobile test runs
* ci: cover mobile web runner process dependencies
* test: verify mobile web selectors through the new runner
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* fix(terminal): recognize a Git Bash launcher by its install layout
* fix(terminal): require git-bash.exe in the launcher install-layout check
* test(terminal): pin each Git Bash launcher install-layout marker as necessary
* perf(history): coalesce tombstone directory rescans
* perf(history): reuse one tombstone listing instead of re-reading per completion
Refilling the 64-slot tombstone removal window used to list the whole
`.pending-delete` directory again, synchronously, after every removal that
finished. A backlog of 1,024 tombstones cost 1,026 listings of a directory
that starts 1,024 entries long, all on the main process thread.
Each history root now keeps the names from its last listing and takes the
next one from memory when a slot frees, listing the directory again only
once that list runs out. The listing moved to `fs.promises.readdir`, so it
no longer blocks the main thread.
This replaces the previous coalescing-on-setImmediate approach, which left
the directory being re-listed once per completion batch and needed a
deferred-roots set, an event-loop turn of latency, and an unref'd immediate
whose freed slots could go unfilled at exit. Holding the names removes all
three.
Two behaviours are kept deliberately:
- Freed slots are offered across roots, so a root displaced at the shared
cap is not stranded until the next startup.
- A listing cannot re-submit a removal that was already under way when it
started, including one that finished before the listing resolved.
Five real-filesystem samples per version, 1,024 tombstone directories each
holding a meta.json, macOS arm64 / Node 24. Medians: directory listings
1,026 to 6, names enumerated 494,348 to 1,077, blocking main-thread time in
this path 820 to 7 ms, total drain 886 to 75 ms. The 886 ms breaks down as
266 ms of `readdirSync`, ~554 ms of per-entry work over those 494k names
and its garbage, and ~70 ms of actual recursive rm, which is the unchanged
floor the remaining 75 ms consists of. Average concurrent removals return
to 62 of 64 from the 46 a deferred refill left idle.
Adds coverage for a listing that fails mid-drain with later completions
still able to recover, and for both roots draining under one shared cap.
The heavy drain tests now run on real timers so real `fs.promises` and
microtask ordering are exercised.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(history): warn when a tombstone root read fails for a reason other than absence
readTombstoneNames swallowed every readdir error, so EACCES or ENOTDIR on the
initial enumeration left tombstones in place with no diagnostic until a later
completion happened to re-read. An absent root stays silent; it is the norm.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* perf: avoid repeatedly encoding retained VM recipe output
* perf: capture retained VM recipe output as raw bytes in the shared byte buffer
The previous commit added a third byte-retention buffer to the repo. This
replaces it with the one that already existed and removes the remaining
encoding work.
`runRecipeCommand` no longer calls `setEncoding('utf8')` on the child's stdout
and stderr. It keeps the raw `Buffer` chunks and runs one `StringDecoder` per
stream to feed the existing string callbacks, which is exactly how
`setEncoding` is implemented, so callbacks see the same characters at the same
boundaries. With the bytes already in hand the capture encodes nothing: the
4,194,304 bytes the ring still encoded for 4 MiB of output drop to 0, and the
UTF-8 continuation trim collapses from one scan per chunk to a single scan when
the tail is decoded.
Retention is now `GrowingByteBuffer.appendRetainedSuffix`, which had no
production consumer. It gained an O(1) head offset, so `discardPrefix` and
`retainSuffix` mark bytes dead instead of moving the whole tail and `append`
slides or grows only when the head offset runs out of room. Quick Open path
accumulation and the SOCKS handshake buffer get that win too. Without the
offset the per-chunk memmove costs 12.36 ms for 4 MiB; with it, 0.25 ms against
the ring's 0.53 ms and the old per-chunk re-encode's 265.78 ms.
Two behaviour notes. Odd capture limits are clamped once at entry instead of
carrying a per-chunk coercion path no production caller could reach, so an
infinite or NaN cap is now bounded at 1 MiB rather than retaining everything.
And malformed UTF-8 yields a different tail: replacement characters no longer
inflate the byte count, so a malformed tail keeps more of what the recipe
actually wrote.
The encoding-budget assertions no longer spy on `Buffer` itself, where any
unrelated allocation in the same tick could flip them. They count bytes through
the capture's own buffer class and still assert the deterministic oracle: at
most 5 MiB moved for 4 MiB of output, exactly 4 MiB appended, 1 MiB decoded,
and the stored chunks identical to the Buffers the stream delivered.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(vm-recipe): emit Buffers from the doctor stream doubles
Dropping setEncoding('utf8') means stdout and stderr now deliver Buffers, so
the hand-rolled EventEmitter doubles emitting strings threw inside the data
listener — the capture retained nothing and the exit path never settled.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(ssh): name the missing unzip when Bun archive extraction cannot start
Extracting the downloaded Bun runtime shells out to `unzip` on POSIX hosts,
which a minimal Debian/Ubuntu install does not ship. runProcess rejects a
missing program with a bare `spawn unzip ENOENT`, which the caller's
non-zero-exit branch never sees, so the operator got an errno instead of a
remedy. Translate that one errno into a message naming the tool and the
ORCA_UNZIP_BIN override.
* fix(ssh): reuse the canonical absence predicate for extractor launch failures
isDefinitiveAbsence is the repo's single errno allowlist for "definitively not
there", and it also covers ENOTDIR — which spawn throws synchronously when a
configured ORCA_UNZIP_BIN has a regular file for a parent. That case previously
escaped as a bare `spawn ENOTDIR` naming no path at all.
Also correct the comment: a deleted working directory is not a second source of
these errnos, because runProcess leaves cwd unset and the child inherits the
parent's without resolving it. Verified on macOS, Linux and Windows.
* perf(mobile): coalesce stalled connection log persistence
* fix(mobile): bound connection-log writes and flush the log before the app suspends
The coalescing pass counted a pending persistence attempt per append and then
burned that whole budget on unblock. With failing storage, 500 appends during a
stall released ~1000 back-to-back `setItem` calls — and slow storage and failing
storage are the same device condition, so the amplification fired in exactly the
scenario the coalescing was for.
The counter is gone. A single `dirtyHosts` flag replaces it: the host's revision
always holds the newest entries, so counting appends bought nothing but writes.
The loop re-reads the revision after each save, so a stall costs the in-flight
snapshot plus one attempt at the newest one, whatever the append count. That also
retires the compound `finally` condition and its unreachable `(… ?? 1) - 1`.
A failed snapshot no longer gets an immediate second `setItem` against a store
that just rejected. It gets one retry after 200 ms, and none at all once a newer
snapshot is queued, because that snapshot already carries the same entries.
`flush()` closes a data-loss gap that predates the coalescing: a write that
failed was only retried by the next append, so when the disconnect was the last
thing to happen the entries explaining it never reached storage. It drains the
in-flight save and makes one more attempt at the newest snapshot, wired to
AppState `background` the way `subscribeConnectionRevivalTriggers` wires resume.
Two revisions tests asserted the retry budget as intended behaviour (6 writes
for 3 appends; 3 for one failure) and now assert one write per snapshot
generation instead. `connection-log-buffer.test.ts` is untouched, including its
requirement that one transient failure self-heals without another append — that
is what the single delayed retry keeps.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(mobile): type the background-flush test mock so the tests ratchet passes
The new test copied `ReturnType<typeof vi.fn>` from
connection-revival-triggers.test.ts, which is grandfathered in the
tests-typecheck baseline for that exact TS2345. The ratchet only shrinks,
so type the mock instead of adding a baseline entry.
---------
Co-authored-by: Claude <noreply@anthropic.com>
The inline AI-note draft card in the diff view used a fourth, hand-rolled
box-shadow tier with raw rgba values instead of the documented shadow-xs
token, so it didn't track theme/token updates like the rest of the UI.
Restated the value under `.dark` too, since it shares selector specificity
with `.orca-diff-comment-popover`'s dark shadow later in the file and would
otherwise lose the cascade to that unrelated rule.
* fix(kimi): don't crash if config.toml is deleted mid-write
writeConfigToml checked existsSync(configPath) then called statSync(configPath)
to preserve the file's mode, with no error handling in between. If the file
was deleted in that window (e.g. a concurrent uninstall), statSync threw
ENOENT and the exception escaped install()/getStatus() uncaught.
Now a missing-file stat during that race is treated the same as a missing
file at the check: fall back to the default 0o600 mode and continue the
write. Any other stat error still throws, preserving the existing
mode-read-failure behavior.
* fix(kimi): use isDefinitiveAbsence for the config delete-race check
Reuse the repo's canonical absence check instead of a hand-rolled ENOENT
comparison, per review feedback on #23293. isDefinitiveAbsence also covers
ENOTDIR (an ancestor directory replaced by a file), which the inline check
missed but is equally "the config is definitively not there."
* perf: avoid rescanning partial notebook output frames
* perf(notebook): stream bridge frames and skip the unused size accounting
The reader buffered every record of a chunk before delivering the first one, and
asked the framer for byte accounting it can never use. An unbounded line limit
cannot reject, so the per-segment `Buffer.byteLength` and the rejection-prefix
retention were pure overhead for the notebook and Codex readers; both are now
skipped once, behind a hoisted check. Frames are handed to the consumer as each
line completes, so the first output of a chunk paints without waiting for the
last.
The dropped buffer only ever preserved a trailing partial record across a
consumer throw, which cannot help: that throw leaves the stdout 'data' listener
and takes main down with it. Rejections are no longer discarded either — the
bridge keeps fd 1 to itself, so an unreadable line means the frame channel is
damaged and now says so.
Tests move into notebook-kernel.test.ts beside the reader's existing coverage,
and add the never-terminated record and a guard that no record bytes are
measured when no limit applies.
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
The WSL "is this path gone?" probe decided a path was missing by matching
GNU coreutils' exact wording, `stat: cannot statx ...`. BusyBox writes
`stat: can't stat ...`, so on an Alpine-style distro a successful orphaned
worktree delete was still reported as a permanent failure, on every retry.
Match only the trailing strerror text, which is POSIX and already pinned to
English by the probe's LC_ALL=C. Permission failures still report failure.
* perf(push): keep retention sweeps from overlapping
* perf(push): drain a saturated retention sweep instead of idling out the tick
The overlap guard on the shared prune timer removed a side effect the sweeper had
been relying on: overlap was the only thing that let a backlog exceed the
50-batch-per-call cap inside one 60-second tick. With the guard, a sweep that
spent its whole budget went idle for the rest of the interval, so a large backlog
drained far slower exactly when retention matters most.
The timer is now a chained setTimeout rather than an interval. `deleteInBatches`
reports whether it exhausted its batch budget, `prune()` returns
`{ deleted, saturated }`, and a saturated sweep is rescheduled immediately. The
connection gate hands a freed slot to the longest waiter, so one serial sweeper
looping back to back still parks a single statement ahead of a worker claim: claim
latency keeps the value the guard bought while the maximum drain rate returns to
what it was before. The loop is self-limiting and stops once the backlog clears.
A sweep that has not settled a full interval after it started now logs
`orca_push_prune_overdue` with its target. Admission waits have no timeout, so a
lost slot release could previously wedge retention permanently and silently.
Chaining makes the overlap guard structural, so there is no flag to scope. The two
single-DELETE sweeps state through `unbatchedSweep` that they have no batch budget
to exhaust, which keeps the immediate-resume path readable as delivery-only.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* perf: bound wildcard segment work in nested repository scans
* perf: bound the ** path walk in nested repository scans, not just one segment
The wildcard-segment fix left the larger blowup in place. A short .gitignore line
made only of `**` segments still costs exponential work in the outer path walk:
`**/**/.../z` at 24 segments and 73 characters, against an eight-segment
candidate, takes about 49 million recursive calls and ~150 ms here — larger than
the 25 ms single-segment case this branch removes. Rules are inherited down the
tree and re-checked for every directory, so that price is per directory, the
runtime's 15s scan timeout fires, and the user silently gets a short repo list.
Two independent bounds, both kept:
- `**` spans zero or more segments, so `**/**` accepts exactly what `**` accepts.
Parsing now collapses a run of them to one segment, taking the reported shape
from 49M recursive calls to 26 matcher steps.
- The walk memoizes on (pattern index, candidate index), so no other arrangement
of `**` can reintroduce the blowup. A rule holding at most one `**` is already
linear and skips the table, because allocating it costs more than the walk it
would save on the shapes real ignore files contain.
The budget suite's process-CPU ceiling is replaced by a matcher step counter read
through `readNestedRepoGlobMatchSteps`, so an algorithmic regression fails the
suite rather than passing on a fast machine. Each bound has its own budget case,
and each fails when only the other is applied.
The equivalence oracle now also covers multi-segment and anchored patterns
including `**`, checked against the pre-change per-segment expression walking
uncollapsed segments. Code-unit and metacharacter cases are unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Replace the copy-a-command startup dialog for diverged JSON/SQLite profile
state with Use SQLite / Use JSON buttons. The choice relaunches Orca into the
locked recovery preflight, applies it, then starts normally. Adds a
current-sqlite recovery selector (and --current-sqlite CLI flag) that archives
the diverged JSON and republishes it from SQLite.
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* perf(relay): release canceled AI Vault startup requests
* test: pin the historical hourly package version fixture
* test: provide the historical package version through build options
* test(ai-vault): cover delayed readiness rejection across restart
* perf: stop scanning shared paths when suggestions are full
* fix(i18n): restore diff note draft catalog entries
* fix(windows): synchronize native terminal table lifetime
* fix: restore catalog entries required by the current CI baseline
* fix(i18n): make AI the recipient of diff notes
* fix(ci): preserve focused Playwright file selection
* test(ci): require focused commands on every platform
Assert each golden platform step and its focused arguments before deduplicating Playwright discovery probes.
* test: pin the historical hourly package version fixture
* test: provide the historical package version through build options
* test(windows): retain PTY stress failure evidence
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* fix(ssh): suppress workspace echoes acknowledged during stale reads
* fix(ssh): preserve newer workspace observations during resync
* fix(ssh): retain newer own acknowledgements during stale reads
* fix(ssh): deliver peer snapshots cached by rejected patches during stale reads
A stale-revision patch reply caches the peer snapshot under a new host
observation token, but the renderer only records a conflict and blocks
uploads. Suppressing an identical stale read then left the peer change
unapplied. Only suppress when the intervening write kept the pre-read
token, which is what a contiguous own acknowledgement does.
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* fix(ci): preserve focused Playwright file selection
* test: isolate historical hourly build version inputs
* test: align historical package input with shared CI repair
* test: inject hourly package version without module mocking
* test: share the hourly package input contract across CI repairs
* test(ci): require focused commands on every platform
Assert each golden platform step and its focused arguments before deduplicating Playwright discovery probes.
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: Neil <neil@stably.ai>
* fix: close abandoned PDF.js development asset streams
* test: keep casting safety comments attached after formatting
* fix(i18n): restore diff note draft catalog entries
* fix: guard closed PDF streams and preserve foreign listeners
* fix(i18n): make AI the recipient of diff notes
* test(browser): report the phase of WebRTC probe timeouts
* test: pin the historical hourly package version fixture
* test: provide the historical package version through build options
---------
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
* test(persistence): start the fault timer after the real worker is ready
* test(orcad): preserve launcher startup phase evidence on timeout
* test: isolate historical hourly build version inputs
* test: align historical package input with shared CI repair
* test: inject hourly package version without module mocking
* test: share the hourly package input contract across CI repairs
---------
Co-authored-by: m4air <m4air@m4airs-Air.localdomain>