Commit Graph
10202 Commits
Author SHA1 Message Date
Neil 1603810dde perf(worktree): make head-identity refresh incremental (#17843)
* perf(worktree): make head-identity refresh incremental

Head-identity refresh re-read `gitdir` + `HEAD` + a loose ref for every
linked worktree on every watcher burst. On a 973-worktree checkout that is
~2,800 metadata reads (~1.0s of main-process fs I/O) per event, and the
debounced pipeline fires on every commit in any worktree — so fleet-wide
agent activity degenerated into a continuous scan loop.

Watcher events already name the admin dir that changed. Classify each event
into a head-identity scope, memoize per-entry identities, and re-read only
the scoped entries. Refs resolved during a pass are replayed onto cached
entries that share the same branch, so `git worktree add --force` siblings
stay current without extra reads.

Invalidation stays conservative: an absent scope (watcher failure, event
overflow, cold start) means a full re-read, `packed-refs` writes invalidate
every entry, misses are never memoized, and one refresh per minute is
promoted back to a full re-read to bound the window where a ref moves with
no event under any admin dir.

Measured on the reported 989-entry checkout (macOS/APFS): one-worktree
commit 2,816 -> 2 file reads, 61ms -> 0.5ms p50 with an identical page
cache; a 20-worktree debounce burst costs 57 reads / 9.7ms; an external
`git worktree add`/`remove` costs one readdir / 1.0ms.

Refs #17828

* fix(worktree): harden incremental head-identity invalidation

Two holes found in self-review:

- An admin entry name removed and immediately reused inside one debounce
  window coalesced into a listing-only scope, so the reused entry kept
  serving the removed worktree's cached head. Name the entry alongside the
  listing on every `worktrees/<name>` create/delete.
- A non-ENOENT `readdir` failure on `worktrees/` collapsed the memo to the
  primary row, which then re-emitted every identity on recovery. Mirror
  worktree-git-common-polling: only a genuinely absent dir means empty; any
  other error keeps the previous listing.

* fix(worktree): let empty-scope bursts still take the head re-baseline

Adversarial review found the 60s full-rebaseline promotion was unreachable
whenever the triggering burst had an empty head-identity scope: the skip
guarded on the raw caller scope and returned before `resolveScope` ran, so
`lastFullReadAtMs` was never re-evaluated. A repo whose only churn is
`git worktree lock`/`unlock` or a sparse toggle — Orca's own prepared-checkout
flow locks and unlocks on every create — could starve the promotion forever
and hold a stale head indefinitely. Resolve the scope first and skip on the
effective scope.

Also stop deferring an add/remove that arrived while the `worktrees/` listing
was transiently unreadable: forget the memoized listing so the next refresh
re-enumerates whatever its scope, instead of waiting for another listing event.

Both fixes carry a test verified to fail without them.

* fix(worktree): return head-read completeness instead of sniffing the memo

Adversarial review round two. Six fixes, each with a test verified to fail
without it.

- `readGitCommonHeadIdentities` now returns `{ identities, listingComplete }`.
  The refresh layer was inferring "enumeration failed" from `cache.entryNames
  === null`, a reader-owned field whose null also means "cold start" — fragile
  in production and impossible to express in a mock.
- A read discarded by teardown, or one that could not enumerate `worktrees/`,
  no longer arms the 60s freshness clock.
- A queued refresh whose re-run met a destroyed window (macOS recreates the
  window while the watch lives on) was cleared and dropped. It now stays armed
  and is folded into the next request.
- An incomplete listing carries forward the baseline rows it could not observe,
  so recovery does not report every linked worktree as changed.
- The baseline advances after notifying, so a send into destroyed chrome leaves
  the move to be retried instead of diffing it away.
- A scope naming an entry the memoized listing does not know now forces a
  re-enumeration instead of resolving to zero work — this removes an unstated
  dependency on `diffGitCommon` emitting a dir-level create for new entries.
- Overflow states FULL at its construction site rather than relying on a
  downstream `?? FULL` for an absent field.

Also documents the load-bearing invariant behind the empty-scope skip (an empty
scope only reaches the refresh from a structural burst, which forces
`emit: false` and is always paired with a catalog notification for every repo
on the watch), and strengthens two tests that could not distinguish the
behaviour they claimed.

* fix(worktree): bound head-identity staleness with a one-shot catch-up

The previous re-baseline was opportunistic: it rode the next refresh, so a ref
that moves with no watched write (`git update-ref refs/heads/x` from a sibling
worktree) stayed stale until an event happened to arrive after the interval.
Pre-PR the very next event anywhere in the repo corrected it, so this was a
real narrowing of correctness, not just a pre-existing gap.

Arm a one-shot, unref'd timer when a SCOPED pass completes, firing one full
re-baseline an interval after the last full read, then disarming. A full pass
disarms instead of arming, so it never becomes a background poll, and the timer
only exists after an event — an idle repo still schedules nothing and reads
nothing. Cost is O(1) timer per active repo and at most one full read per
interval: the same operation the old code ran per event, 60x rarer.

This also converts "stale until some later event" into "stale at most one
interval, period", which is what bounds the blast radius of any invalidation
bug in the scoping itself.

Cleared on watch disposal. Three tests, each verified to fail without its fix:
the catch-up runs with no further events; a quiet repo issues no background
reads and the timer disarms after firing; disposal stops it.

* fix(worktree): treat an unreadable head as unknown, not absent

Reported independently by two PR reviewers. `readTrimmedFile` collapsed every
errno to `null`, so an EIO/EACCES/ENFILE on a `gitdir`, `HEAD`, loose ref, or
`packed-refs` read was indistinguishable from the file being absent — and the
caller deletes the cached identity on `null`. Same conflation AGENTS.md forbids
for the SSH verdict vocabulary: loss of contact is not evidence of absence.

Reads now report three outcomes, and an unknown:

- keeps the entry's last verified identity instead of evicting it,
- is never replayed onto siblings sharing the branch as "this ref is gone",
- marks the entry unverified so the very next pass re-reads it whatever its
  scope, and
- reports the pass incomplete, so it cannot arm the freshness clock.

The reviewers' stated consequence — that an evicted entry stays evicted until
the next full pass — did not hold, because `!cache.entries.has(name)` already
forced a re-read. The real cost was that one EMFILE evicted every entry it
touched and the next pass re-read all of them, which is exactly the full scan
this PR exists to remove, plus a spurious re-publish of every row.

Renames `listingComplete` to `complete`: it now covers entry reads too.
2026-09-01 02:53:05 -07:00
NeilandNeil dff2ff0ec3 fix(git): read the diff working tree and stamp through the host path spelling (#17896)
Git can execute inside a WSL distro against a raw Linux worktree path while Node,
on the Windows side, reads the same files back through Win32. `path.join(
'/home/me/repo/feature', 'src/file.ts')` on win32 produces the drive-relative
`\home\me\repo\feature\src\file.ts`, which resolves against whatever the current
drive happens to be and almost always ENOENTs. The same mis-spelling hits the
drvfs form, where `/mnt/c/repo` should read as `C:\repo`.

Two consequences, both on the Node side only (git already works, because it gets
the Linux path as its cwd and resolves it inside the distro):

- getDiff's unstaged working-tree read missed, `readWorkingTreeFile` mapped ENOENT
  to `exists: false`, and an existing file rendered as DELETED in the diff view.
- `readWorktreeDiffStamp` could not find `.git`, so the stamp was null, the settled
  diff cache neither hit nor stored, and every diff respawned `git show` - two
  `wsl.exe` spawns the cache exists specifically to avoid.

Both now spell the worktree directory for the reading host first, via a new
`resolveWorktreeHostPath` wrapper around the resolver that landed in #17804.
The wrapper exists because `resolveGitMetadataPath` trims: a gitfile payload
carries a trailing newline, but a directory name may legally begin or end with
whitespace on POSIX, so the wrapper keeps the caller's spelling whenever the
resolver only trimmed it. The stamp's opaque `value` still embeds the caller's
original `worktreePath`, so settled-cache identity is byte-identical and no cache
key moves.

`readWorktreeDiffStamp` was already `Promise<WorktreeDiffStamp | null>` with one
caller that treats null as a cache miss, so no new nullability enters the type
system and the resolver's never-null-for-a-non-empty-pointer contract is
untouched. The only unspellable input is an empty worktree path, handled locally
as "not provably unchanged" in the stamp and as a read *failure* (not a proven
deletion) in file-diff.

What changes for users

| Platform | Delta |
|---|---|
| macOS | No change. An absolute POSIX path is returned verbatim, including one whose directory name carries leading or trailing whitespace. |
| Linux | No change. Same reason. |
| Native Windows (no WSL) | No change. A `C:\...` or `\\server\share\...` path is already absolute for win32 and passes through verbatim. |
| Windows + WSL, UNC worktree path (`\\wsl.localhost\Ubuntu\...`) | No change. Already absolute for win32; passes through verbatim. This is today's common case. |
| Windows + WSL, drvfs worktree path (`/mnt/c/repo`) | Fixed. Reads as `C:\repo` instead of the drive-relative `\mnt\c\repo`. Needs no distro name. |
| Windows + WSL, Linux worktree path with a named distro (`/home/me/repo`) | Fixed. Reads as `\\wsl.localhost\Ubuntu\home\me\repo`. The deleted-file misrender goes away and the diff cache starts hitting. |
| Windows, POSIX path, no distro and not a drvfs mount | No change. Passes through verbatim, same ENOENT, same existing fallback. |
| SSH | No change. `runtime-git-diff-commands.ts` and the `git:diff` IPC both route to `provider.getDiff` for a connection, so this local code is never reached. |
| Relay / remote | No change. No RPC param, wire field, stream opcode, or published content is touched; the relay host runs the same local code and gets the same fix. |
| Folder workspace (non-git) | No change. `.git` is absent either way, `resolveGitDir` returns the same fallback, and the stamp stays null exactly as today. |
| GitLab / other providers | Not applicable. No provider-specific or review code is touched. |

What this does NOT do

- It does not fix `resolveGitDir` itself. For a drvfs repo whose worktree Orca
  already spells `C:\repo\feature`, the gitfile payload `gitdir: /mnt/c/repo/.git/
  worktrees/feature` is still mis-resolved by `path.resolve` to
  `C:\mnt\c\repo\.git\...`, so the stamp still returns null in that shape. Separate
  change, separate PR; this one neither fixes nor regresses it.
- It does not touch submodule path resolution. `resolveSubmoduleWorktreePath` is
  the path-escape guard and has a near-identical twin in the relay; changing it
  without escape tests on both is out of scope.
- It does not change `readHeadComponent`'s `commondir` resolution. The relative
  `../..` git actually writes takes the identical `path.resolve` branch, and an
  absolute POSIX `commondir` under a WSL UNC `gitDir` already resolves correctly
  because the UNC root is `\\wsl.localhost\<distro>\`.
- It does not reorder drvfs-before-UNC inside the shared resolver. That changes the
  identity of returned strings and needs a real Windows+WSL box.
- It does not add any Git command, option, or version dependency.

Costs and residual risk

- One extra pure function call per diff read. No I/O added or removed on the
  unaffected paths.
- Translation still trims. `resolveWorktreeHostPath` preserves whitespace only when
  no translation happened; a guest directory named `/home/me/repo ` loses its
  trailing space on a Windows reader. Reachable only on win32, where such a name is
  not addressable anyway, and the previous behavior for that shape was a
  drive-relative miss.
- A relative worktree path (no caller passes one) is now resolved against the
  process cwd instead of joined relative to it. Same file in every case except a
  relative name that itself ends in whitespace.
- `UNSPELLABLE_WORKING_TREE_READ`'s `exists`/`failed` fields are correct but not
  observable today: the stamp is null for the same input, so nothing can be cached
  and `reusable` cannot be read back. They are there so the branch stays right if
  `loadDiff` ever gains a second caller. The test pins the observable part - that no
  read lands on a cwd-relative path.
- Every test here mocks `node:fs/promises` and spoofs `process.platform`. They prove
  which path string reaches `stat`/`readFile`, which is the right assertion, but
  none of this has executed against a real 9p mount on a Windows+WSL box and this
  repo's CI has no such runner.
- Honest framing of the trigger: I could not demonstrate a mainline path that hands
  `getDiff` an untranslated POSIX worktree path on Windows today -
  `translateWslOutputPaths` UNC-translates worktree paths whenever a distro is
  known, `getWslHome` returns the UNC spelling, and `resolveWslRepoWorktreeBasePath`
  normalizes a configured Linux base. The drvfs case is the most plausible live one.
  Treat this as defense-in-depth that is a strict no-op on every configuration above
  except the two marked Fixed.

Verification

- `npx vitest run src/main/git src/shared/git-metadata-path.test.ts` -> 196 files /
  2241 tests passed, 2 files and 5 tests skipped. One failure,
  `git-admission-storm-measurement.test.ts > reports bounded-concurrency before and
  after measurements` (ENOENT scandir on its own temp state dir), is pre-existing
  and environmental: it fails identically in isolation and spawns real git children
  without touching any changed module.
- `npx vitest run src/main/git/status-diff-settled-cache.test.ts` -> 21/21 (16
  pre-existing, 5 new). `npx vitest run src/shared/git-metadata-path.test.ts` ->
  25/25 (19 pre-existing, 6 new cases across 3 new tests).
- `npx oxfmt --write` then `npx oxlint` on all five changed files -> clean.

Mutation checks - all eight production substitutions were reverted one at a time
and the suite re-run. Each fails at least one test, and no new test survives its
own mutation:

| Reverted | Failing test |
|---|---|
| file-diff working-tree read -> `worktreePath` | reads the working tree through the host spelling instead of reporting a deletion; invalidates when the working tree file is edited under the host spelling |
| stamp working-tree component -> `worktreePath` | invalidates when the working tree file is edited under the host spelling |
| stamp `.gitmodules` stat -> `worktreePath` | invalidates when .gitmodules appears under the host spelling |
| stamp `resolveGitDir` -> `worktreePath` | stamps through the host spelling so the second read does not respawn git |
| `options` threading at the `readWorktreeDiffStamp` call | stamps through the host spelling...; invalidates when .gitmodules appears... |
| wrapper's untrimmed preservation -> return the resolver's value | keeps whitespace that belongs to the directory name (both cases) |
| `UNSPELLABLE_WORKING_TREE_READ` -> a cwd-relative `readWorkingTreeFile` | reads nothing relative to the cwd when the worktree path has no host spelling |
| stamp's null early return -> `hostWorktreePath ?? worktreePath` | reads nothing relative to the cwd when the worktree path has no host spelling |

The settled-cache tests seed the fake filesystem through the platform-bound `path`
module rather than `path.win32`, so they assert real behavior on a POSIX CI host as
well as on Windows and are not gated on the host platform.

Co-authored-by: Neil <79079362+brennanb2025@users.noreply.github.com>
2026-09-01 02:39:48 -07:00
NeilandNeil e4a708da17 fix(wsl): read untracked line counts through the host's worktree spelling (#17897)
Untracked line counts, and the untracked share of the branch line total, come
from direct lstat/open calls rather than from git. When git executes inside a
WSL distro the worktree path can be a guest path, which Win32 reads as
drive-relative (`/home/me/repo`) or as a literal `C:\mnt\c\repo`. Every lstat
then fails, countFileAdditions swallows the error into `{}`, the file renders
with no +N, and the branch total silently undercounts it.

Route only those two filesystem reads through resolveWorktreeFilesystemPath,
which translates a guest-rooted path via the shared resolveGitMetadataPath.
Both call sites produce the same string, so the stat-keyed untracked cache
still hits across them. resolveGitMetadataPath itself is not modified, so its
other callers are untouched.

The wrapper translates only when the host is win32 and the path is a guest
spelling: exactly one leading slash, and unchanged by trim(). Each condition
is load-bearing.

- `//wsl.localhost/...` and `//wsl$/...` are UNC spellings that also start with
  `/`, and translating one prepends the distro root a second time, so a
  worktree that reads fine today would ENOENT on every untracked file. The same
  single-leading-slash guard is used, for the same reason, by
  resolveWslRepoWorktreeBasePath in src/shared/wsl-paths.ts.
- resolveGitMetadataPath returns `rawPath.trim()`, so a worktree directory name
  with leading or trailing whitespace (legal on ext4) would be re-spelt onto a
  different directory. Leaving those verbatim keeps them exactly as they read
  today.
- Off win32 the resolver is already an identity for these inputs; the platform
  check makes the macOS/Linux no-op structural rather than derived.

Per platform:
- Windows + WSL, worktree spelled as a guest path: untracked +N appears and the
  branch total includes it.
- Windows + WSL, worktree already spelled `\\wsl.localhost\...`,
  `//wsl.localhost/...` or `//wsl$/...`: unchanged, returned verbatim.
- Native Windows: `C:\...` is unchanged. One shape does change: a
  `/mnt/<drive>/...` worktree now resolves to `<Drive>:\...` instead of being
  passed through to a guaranteed lstat failure. Native Windows does not produce
  that spelling, and if it were reached the new result is the correct file.
- macOS / Linux: unchanged; not win32, returned verbatim, whitespace included.
- SSH / relay: unchanged. Remote status runs in the relay, which builds its own
  branch-total input and does not pass filesystemWorktreePath.
- Folder workspaces / GitLab: unaffected, no workspace-kind or provider
  behavior is touched.

No fail-closed degradation: attachLineStats still returns
`stagedStats !== null && unstagedStats !== null`, and createBranchLineTotalInput
still has no early return. The wrapper returns `string`, never null, so an
unmappable worktree keeps its old spelling and its old (missing) untracked
counts rather than dropping the staged/unstaged counts as well.

The `filesystemWorktreePath` field on computeGitBranchLineTotal is optional and
does not touch the lease key, so the coalescing/cooldown identity is unchanged.

Co-authored-by: Neil <neil@orca.local>
2026-09-01 02:39:31 -07:00
Neil 0443d42638 fix(git): give WSL-hosted repos one FETCH_HEAD lock lane (#17898)
The fetch lock key is the worktree's resolved common Git directory. On a Windows
host two derivations split one repo into several lanes, so sibling fetches on the
same repo race the FETCH_HEAD write the lock exists to serialize.

- Windows aliases \\wsl$ to \\wsl.localhost and folds the distro name and any
  drvfs tail case-insensitively, so two spellings of one repo produced two keys.
  The finished key now goes through foldWslUncPathCaseInsensitiveParts. This is a
  pure function of the finished key, so equal keys stay equal: it can only merge.
- Under a drive-spelled base, git-in-WSL's `/mnt/c/repo/.git` gitfile and
  commondir pointers are read by path.resolve as the non-existent
  C:\mnt\c\repo\.git. The commondir read then fails and every linked worktree got
  its own dead-end key while the main checkout keyed on C:\repo\.git. Such a
  pointer now goes through toWindowsWslDrivePath.
- realpath and stat take no AbortSignal, so cancelling a fetch still blocked
  behind a hung 9P/UNC lookup. Both are wrapped in waitForPromiseWithSignal. The
  rejection keeps today's synthetic AbortError shape, including when the caller
  aborts with its own reason, because callers classify on error.name.
- The hand-rolled gitfile regex is replaced by the shared
  parseGitdirMarkerPayload, matching git's own read_gitfile_gently.

A WSL UNC base is deliberately excluded from the pointer translation. win32
path.resolve already carries such a base's distro onto a guest-rooted pointer
(\\wsl.localhost\Ubuntu\home\me\wt + /mnt/c/repo/.git ->
\\wsl.localhost\Ubuntu\mnt\c\repo\.git), and a main worktree's `.git` is a
directory with no pointer to translate, so its key stays on that UNC spelling.
Rewriting only the linked worktrees to C:\... would have split one repo across
two lanes - the opposite of the intent. A test now pins that layout.

Direction of every key change, re-derived over a ten-layout matrix that runs both
this code and an emulation of the pre-change derivation under Win32 path rules:
nine layouts either merge or are byte-identical. The fold is merge-only by
construction; the pointer translation's sole delta is C:\mnt\c\X -> C:\X, and
C:\mnt\c\X is derived from bytes that live at C:\X, so it can only join a
worktree to its own common dir. The tenth layout is the one narrowing: the shared
parser accepts `gitdir:` only at offset 0 where the old regex accepted it on any
line, so a `.git` file with a leading blank line falls through to the parent walk
(C:\repo\.git\FETCH_HEAD -> C:\.git\FETCH_HEAD). Nothing can race there - git
2.44 refuses that same file with `fatal: invalid gitfile format`, so no fetch
runs in such a worktree at all. Native Windows repos with no WSL, SSH, relay and
folder workspaces are byte-identical.

hostPath() returns the same node:path submodule Node itself selects, so it is a
no-op on every real host; it exists so the Win32 derivation is testable off
Windows. resolveGitFetchHeadCommand's argument parsing is untouched, and
--git-dir gitfile dereferencing is deliberately not added: it would make N
worktrees of one repo serialize fetches that run in parallel today.
2026-09-01 02:39:17 -07:00
Neil f2db24bca0 fix(worktree): reclaim a prepared checkout whose discard failed in-process (#17899)
Speculative create-preparation evicts entries past the 3-entry limit and the
5-minute TTL, and both paths swallowed a failed `discardPreparedWorktree`.
The only other reclaim path, `cleanupStalePreparations`, skips any preparation
whose lock-reason pid is still alive, so a discard that failed inside the
running app stranded its scratch checkout and its locked worktree registration
until restart.

Record the failed discard keyed by host (repo path + WSL distro) and prepared
path, and retry it the next time a fresh preparation starts for that host.
The retry is kicked off before `listWorktreeGraph` but never awaited, so it
runs alongside the stale scan and the `worktree add` instead of sitting in
front of the user's create. It is capped at 3 attempts and warns when it gives
up. Enrolment is unconditional: `prepareWorktreeCreateCheckout` self-discards
on failure, but only best-effort, so a checkout that failed on a busy handle
can strand the same registration.
2026-09-01 02:39:03 -07:00
Neil 95cd6969a0 docs(wsl): note that the distro VHDX is a high-water mark, not a leak (#17900)
WSL2 keeps the guest filesystem in a dynamically-expanding ext4.vhdx.
Deleting files inside the distro frees the blocks for ext4 to reuse but
never shrinks the host-visible file, so engineers watching speculative
worktree preparation and mirrored worktrees write into a distro see the
vhdx grow and reasonably ask whether we leak disk.

Records the measurement taken on WSL 2.7.11.0 / Ubuntu-24.04 (a second
fresh incompressible 1 GiB after deleting the first cost zero growth,
measured as size on disk via GetCompressedFileSize), how to locate the
vhdx across all three install layouts, and a complete elevated diskpart
recipe for compacting existing slack. Scopes the sparse-flag observation
to the measured machine and states that peak-tracking is the best case
for block reuse, not a guarantee against drift.

The reclaim steps state their preconditions rather than reading as
directly runnable: --set-sparse needs the distro stopped and WSL 2.5 or
newer. .wslconfig is given as %UserProfile%\.wslconfig -- it lives in
the Windows user profile, not inside the distro at ~/.wslconfig.

Per-platform delta: documentation only, no production code. No behavior
change on macOS, Linux, native Windows, WSL, SSH, relay, folder
workspaces, or any git provider.
2026-09-01 02:38:47 -07:00
Jinjing 997d3e1b55 Import browser cookie search to tab (#17627)
* Add browser history search to the new-tab omnibox

- Display matching pages from browser history in the tab entry panel
- Extract address-bar history scoring into reusable `browser-history-match` module
- Rank by match tier (host prefix > substring > title > tail) then frecency

* Replace History icon with ExternalLink for browser search results

* Replace ExternalLink with Globe icon for browser search results

* Fix browser history matching for workspace docs and recency rankings

Promote path-prefix matches to top tier for entries without a host (workspace
docs), and clamp the recency bonus so future timestamps cannot outrank fresh
visits. Includes tests for both path-prefix promotion and recency bonus
clamping behavior.

* Cache browser history by identity and snapshot omnibox entries

- Use WeakMap to cache prepared browser history entries by identity, so
  re-parsing is skipped for the same snapshot
- Change omnibox to read a history snapshot at menu open (via getState)
  instead of subscribing to live updates, preventing background navigations
  from reshuffling results mid-keystroke
- Support fully-qualified URL prefix matching (e.g., `https://github.com`)
  to preserve address-bar behavior
- Fix percentile calculation in performance tests (nearest-rank method)

* Break browser history ties with URL for stable snapshot ordering

When browser history entries tie on tier, score, and recency, the sort
order can become non-deterministic, especially when combining browser and
document history that may be reordered in snapshots. Add normalizedUrl
as the final tie-breaker to guarantee consistent ordering.
2026-09-01 02:31:29 -07:00
Neil a573c5706b docs(child-process): note split-relocated allowlist entries 2026-09-01 02:07:33 -07:00
Neil c31e7b0d9a docs(main): restore rationale comments lost in the startup and cookie splits 2026-09-01 02:07:33 -07:00
Neil 15c4f1c447 fix(runtime): defer process incarnation lookup for plain sends
(cherry picked from commit d522a3e15a56c0d6d1b92b67414d51f6bf78e987)
2026-09-01 02:07:33 -07:00
Neil 1d9b5306ac chore(child-process): prune stale import allowlist
(cherry picked from commit 92d39fc984)
2026-09-01 02:07:33 -07:00
Neil 83513b82d1 test(terminal): follow Hangul lifecycle extraction
(cherry picked from commit b62361fad271809819aa01711e437197065e3f75)
2026-09-01 02:07:33 -07:00
Neil d5db0bedfc fix(main): route browser cookie key commands through runner
(cherry picked from commit e321eccb41)
2026-09-01 02:07:33 -07:00
Neil de84a9f999 fix(main): resolve runtime lazily in observers
(cherry picked from commit 369496b27c)
2026-09-01 02:07:33 -07:00
Neil d462766cb0 refactor(main): split filesystem git remote handlers
(cherry picked from commit 2146cff06a)
2026-09-01 02:07:33 -07:00
Neil fbaa125a35 fix(main): merge split startup imports
(cherry picked from commit c0f38db5de)
2026-09-01 02:07:33 -07:00
Neil 5b4e7edb50 refactor(main): split backend services and startup
(cherry picked from commit a33573328b)
2026-09-01 02:07:33 -07:00
Neil c475d23d9b chore(max-lines): prune preload baseline entry
(cherry picked from commit aad2e1ec54)
2026-09-01 00:57:54 -07:00
Neil 85b95aada2 fix(preload): merge split plugin imports
(cherry picked from commit 682ff4f5be)
2026-09-01 00:57:54 -07:00
Neil 7e8337b155 test(preload): census split GitHub bridge owners
(cherry picked from commit cd08e4e91c)
2026-09-01 00:57:54 -07:00
Neil d1abe28471 refactor(preload): split bridge API modules
(cherry picked from commit b77e31873b)
2026-09-01 00:57:54 -07:00
Neil 488f66c56a fix(renderer): preserve accepted visibility snapshots
(cherry picked from commit bc5f8a5ac5b8e8ad3fc59250795842e663bad0ee)
2026-09-01 00:28:37 -07:00
Neil ca12be1921 chore(max-lines): prune runtime baseline entries
(cherry picked from commit 08820a4386)
2026-09-01 00:28:37 -07:00
Neil 08b8f271e6 fix(renderer): merge split runtime imports
(cherry picked from commit 90bf505afe)
2026-09-01 00:28:37 -07:00
Neil 41015f9393 refactor(renderer): split runtime and store modules
(cherry picked from commit 207bef0198)
2026-09-01 00:28:37 -07:00
Neil 41ef1ddd80 fix(renderer): document pre-split render-time refs in workspace search
Both assignments are verbatim from the pre-split monolith; the split relocated
them onto changed lines, which the React Doctor gate scans.
2026-09-01 00:06:23 -07:00
Neil 6c48d7c0d7 fix(lint): drop unused exhaustive-deps suppression after split
The split's dependency list no longer trips the rule, so the directive is dead
and the changed-code quality gate rejects it.
2026-09-01 00:06:23 -07:00
Neil f05981dc29 refactor(automations): extract list toolbar and drop dead list projection 2026-09-01 00:06:23 -07:00
Neil 074f135ee1 style(renderer): format extracted palette modules 2026-09-01 00:06:23 -07:00
Neil 0c638b0fe5 fix(renderer): preserve palette create telemetry
(cherry picked from commit b13c2b1960)
2026-09-01 00:06:23 -07:00
Neil aad0c632a7 fix(renderer): preserve Codex sign-in behavior
(cherry picked from commit 81f4803e6f)
2026-09-01 00:06:23 -07:00
Neil 936595a8e5 fix(automations): preserve destination form save semantics
(cherry picked from commit a793b07c12)
2026-09-01 00:06:23 -07:00
Neil e897acc471 fix(renderer): preserve floating editor visibility
(cherry picked from commit bcf715b76e)
2026-09-01 00:06:23 -07:00
Neil 01d6725be3 fix(renderer): preserve status localization parity
(cherry picked from commit 0a802c70e1)
2026-09-01 00:06:23 -07:00
Neil e8e94ecaab chore(max-lines): prune stale lifecycle baseline
(cherry picked from commit 42bf3eb674)
2026-09-01 00:06:23 -07:00
Neil bae9ea8c79 fix(renderer): document intentional render-time refs
(cherry picked from commit 6fc78dd869)
2026-09-01 00:06:23 -07:00
Neil 6848a6958c refactor(renderer): finish worktree palette extraction
(cherry picked from commit ea8e6e595a)
2026-09-01 00:06:23 -07:00
Neil a8d9acb3f6 style(renderer): format palette project candidates
(cherry picked from commit 1e93d66854)
2026-09-01 00:06:23 -07:00
Neil 29c760726b fix(renderer): restore worktree palette behavior after split
(cherry picked from commit 0b78c1cbbc)
2026-09-01 00:06:23 -07:00
Neil 3e262c1fc6 fix(automations): preserve Escape drill-out behavior
(cherry picked from commit 790680dfc2)
2026-09-01 00:06:23 -07:00
Neil 3fe63b5104 fix(status-bar): preserve workspace-space review and agent freshness
(cherry picked from commit 9455e318fa)
2026-09-01 00:06:23 -07:00
Neil b94a65a4fc fix(lint): preserve TaskPage effect suppressions after split
(cherry picked from commit 4a3bc23670)
2026-09-01 00:06:23 -07:00
Neil cd986c7759 fix(status-bar): preserve Git refresh ordering after split
(cherry picked from commit e65d298afb)
2026-09-01 00:06:23 -07:00
Neil cc2451ca25 chore(max-lines): prune refactored file suppressions
(cherry picked from commit 787228ee46)
2026-09-01 00:06:23 -07:00
Neil 3349a460e2 fix(renderer): preserve extracted lifecycle and retention behavior
(cherry picked from commit 1731f2a2f3)
2026-09-01 00:06:23 -07:00
Neil 7123146ac2 fix(renderer): keep split imports lint-clean
(cherry picked from commit c778ac7a7a)
2026-09-01 00:06:23 -07:00
Neil 286c21005b refactor(renderer): split oversized UI surfaces
(cherry picked from commit da89be4345)
2026-09-01 00:06:23 -07:00
Brennan BensonandMerge Sim 0b2912b507 fix(mobile-native-chat): retire an image echo glued with the send beside it (#17783)
* fix(mobile-native-chat): retire an image echo glued with the send beside it

A message sent with images could render two or three times over, with the copy
carrying the photos sorting below the reply that answered it — and it never
cleared.

A send issued while the agent is mid-turn is glued onto the agent's input line
with any send adjacent to it, so the pair lands as one transcript row whose text
is the concatenation. Every retirement path then declined the pair:

- The image matcher wanted the whole row to equal the echo's text, so a glued
  row never bound. That also stranded the local preview: the phone's photo never
  reached the authoritative row.
- The exact-count path skips image echoes by design.
- The glue path excluded image echoes too, which made one a *barrier* — splitting
  the run so the text-only send beside it was left alone, and a lone match is
  rejected as an ordinary landing.

So neither echo could ever retire, and the unmatched image echo fell through to
the trailing bucket, which is what put it below the reply.

Match a glued row in the image matcher, and let an image echo take part in the
glue pass once its preview has been rebound. It stays a barrier while unbound,
so the existing guarantee is kept: an image echo never retires before its local
preview reaches the transcript row, or the photo would disappear.

* fix(mobile-chat): require image provenance for glued prefix matches

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-01 00:05:00 -07:00
Jinjing e7f15367a8 fix(agent-send-target): acknowledge delivery even when picker closes bef (#17835)
Delivery callbacks and telemetry belong to the completed send operation, not to the
picker instance that launched it. Remove early returns that skipped delivery
acknowledgment and success toast when the popover was already closed.
2026-08-31 23:51:32 -07:00
Neil 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.
2026-08-31 23:35:08 -07:00