Commit Graph
9855 Commits
Author SHA1 Message Date
Neil 7a69357856 fix(worktree): widen git-common watch on event-batch overflow (#17916) 2026-09-01 20:25:42 -07:00
Neil a7db6c336b perf(git): skip the sparse probe for worktree listings that never read it (#18050)
Three main-process call sites list a repo's worktrees to read `worktree.path`
and nothing else, but went through the annotated listing, so each one paid a
sparse-checkout probe per worktree and cached the result nobody consumed:

- `registered-worktree-roots-cache.ts` rebuilds the filesystem-auth authorized
  roots. `invalidateAuthorizedRootsCache()` fires on every worktree create and
  remove, plus repo add/clone/settings changes, so this reruns constantly.
- `filesystem-source-control-ai-targets.ts` checks whether a local repo owns a
  worktree path.
- `hosted-review.ts` verifies a worktree belongs to the repo before granting
  access.

The probe is an `fs.stat` of the per-worktree `info/sparse-checkout` plus, when
that file is non-empty, a git config read. On a WSL-hosted repo both cross 9p.
#17859 cached it and #17932 keyed that cache on the distro, which fixed a wrong
answer but also meant the distro-less callers above populate a second entry per
worktree — probed cold, revalidated on their own five-minute loop, and read by
nobody. Worktree create/remove clears the sparse cache and dirties the roots
cache together, so both variants go cold at once and the discarded half is
re-probed in full on the next auth check.

`listRepoWorktreeGraph` routes those callers to `listWorktreeGraph`, which
already existed as the annotation-free listing (#17655).

Doing only that would have cost a second `git worktree list`. The scan cache
keys in-flight scans on a `kind`, and graph and lenient were separate kinds, so
a roots rebuild overlapping a sidebar refresh would spawn its own subprocess
where the two previously coalesced. That is a real regression on macOS, Linux
and native Windows, where `getLocalProjectWorktreeGitOptions` returns `{}` and
both callers land on the identical key; on WSL they already differ by distro and
never shared.

So the annotated listing is now the graph listing plus annotation, rather than a
parallel scan of its own: `listWorktrees` awaits `listWorktreeGraph` and
annotates the rows it returns. Both soften a Git failure to `[]`, so they can
share one listing; strict keeps its own because it must be able to reject. The
two kinds ran Git twice before and now run it once, so the overlap case gets
strictly faster instead of paying for the opt-out.

An annotated scan holds two in-flight entries now (its own, plus the graph
listing it shares). Keeping its own entry matters: `detectSparseCheckoutCached`
dedupes revalidation but not the initial fill, so two concurrent badge readers
sharing only the graph scan would both probe.

Per-platform delta:
- macOS/Linux: fewer probes on the three call sites; one `git worktree list`
  instead of two when a graph and an annotated scan overlap.
- native Windows, no WSL: same, and the saved subprocess is the expensive half.
- Windows + WSL: the largest win. The discarded probes were 9p round-trips
  re-paid cold after every worktree create/remove.
- SSH/relay: none. `listRepoWorktreeGraph` returns through the same provider
  branch as `listRepoWorktrees` before reaching local Git.
- folder workspaces: none. Both return the same synthetic folder worktree.

Not in this change:
- The badge listing itself. It still probes, still annotates, and still keys on
  the distro exactly as #17932 left it.
- The remaining `listRepoWorktrees` callers. They read `isSparse`, or feed rows
  to something that does.
2026-09-01 20:19:56 -07:00
Neil 42d9ac1767 chore(deps): resolve 81 of 83 Dependabot alerts in docs/site and mobile (#18061)
docs/site: bump next 16.2.1 -> 16.3.4 (with eslint-config-next) and vercel
50.37.0 -> 59.11.1, then refresh transitives. The 16.3.x jump is required:
16.2.x hard-pins the vulnerable postcss@8.4.31 and sharp@^0.34.5, while
16.3.x pins postcss@8.5.23 and sharp@^0.35.4.

Five packages are exact-pinned by vercel's own subpackages, so they get
scoped overrides. Scoped rather than blanket because a bare undici override
would drag the 6.x/7.x consumers in the tree down to 5.x.

mobile: bump browserslist 4.28.2 -> 4.28.8.

Two alerts stay open, both in mobile:

- decode-uri-component@0.2.2 (#285). An override to 0.5.0 breaks the tree:
  0.5.0 is ESM-only with a default export, but query-string@7.1.3 is CJS and
  does `require('decode-uri-component')`, so parse() throws
  "decodeComponent is not a function" and takes URL parsing in expo-router
  and @react-navigation/core with it. Both pin query-string@^7.1.3; the fix
  has to come from upstream moving to query-string 8+.
- image-size@1.2.1 (#179, #180) via metro. No patched version exists on any
  release line, so there is nothing to override to.

Verified: docs/site build, tests, lint, tsc and frozen install; mobile
typecheck, 3985 tests and frozen install.
2026-09-01 20:17:41 -07:00
Neil 3777070eaf fix(worktree): restore the stale-cleanup signal after the module split (#18058)
* fix(worktree): restore the stale-cleanup signal after the module split

Moving stale-preparation cleanup into its own module took
`staleCleanupInFlight` with it, but `hasPendingWorktreeCreatePreparations`
still read it directly. Both sides were green in isolation — the reference
arrived on main while the split was in review — so the break only appeared
once they merged, and it fails typecheck for every branch built on main.

Expose the predicate from the module that owns the map, and cover the
signal with a test so the idle gate's "a create is imminent" answer cannot
silently regress again.

* test(worktree): anchor the pending-signal test on the scan, not on await depth
2026-09-01 20:08:09 -07:00
Brennan BensonandMerge Sim 7f6cf271ce fix(terminal): preserve panes when restored PTY owner is unverifiable (#17860)
* fix(terminal): preserve unverifiable restored pane bindings

* test(terminal): cover unverifiable restored pane identity

* fix(terminal): settle direct SSH retry on unverifiable owner

* fix(terminal): make owner warning actionable

* fix(terminal): harden owner warning recovery feedback

* test(terminal): consolidate fixture imports

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-01 19:53:11 -07:00
Jinwoo Hong a7fda48fe3 feat(telemetry): measure macOS stale-daemon adoption and cwd denials (#18043)
* feat(telemetry): measure macOS stale-daemon adoption and cwd denials

Adds two enum-only PostHog events so #17696 can be sized instead of guessed at:

- daemon_adopted: once per macOS launch that keeps a daemon an earlier app
  launch forked (invisible to daemon_lifecycle, which only sees replacements).
  Carries app-version match, spawner-path class (installed app / Squirrel
  ShipIt cache / other / missing), the existing TCC attribution verdict, and
  the bucketed live-session count.
- daemon_pty_cwd_denied: the symptom itself. The daemon probes the requested
  cwd in its own process (only its TCC context counts) and returns an additive
  cwdReadableByDaemon field; the app emits only when the daemon was denied AND
  the app can read the same path, so a missing or genuinely unreadable cwd
  never counts. Non-permission errors read as readable on purpose.

Both emitters swallow every failure; nothing here can delay or fail daemon
startup or a PTY spawn. Off macOS neither event fires. The new wire field is
optional, so older daemons and clients are unaffected.

* fix(telemetry): keep cwd-denial classification inside the swallow guard

Read the pid record at emit time (inside the try) rather than passing the
adapter's startup snapshot: a throwing app-environment read can no longer
escape spawn(), and a denial after a respawn is billed to the daemon that
actually spawned the PTY.
2026-09-01 22:33:39 -04:00
Jinjing 0352c239c2 Add Copy Session ID menu item to terminal tabs (#18039)
* Add Copy Session ID menu item to terminal tabs

Adds a menu item to copy the active pane's agent session ID when available.
The item only appears when the session is still live and has reported an ID.

* Add Copy Session ID i18n strings and e2e test

- Add localized strings for Session ID context menu item
- Add e2e test coverage for copying session ID from terminal tabs
- Fix dev build permissions when copying private Electron app bundles

* Drop the Electron dev-bundle fix from this branch

It landed on main as 519af49a58, which restores write permission inside
copyPrivateTree itself rather than at the dev runner's call site, so every
caller of the private-copy contract is covered and not just this one. That
commit also fixes the test that should have caught the crash: the wrapper ran
with stdio: 'ignore', so a hard failure presented as a bare timeout.

This branch predated that commit and carried a narrower duplicate, mixed into
an i18n/e2e commit where it did not belong.

* refactor: use dedicated i18n keys for copy session ID toasts

Replace auto-generated translation keys with specific, dedicated keys
for copy session ID success and error messages. This improves
maintainability and makes the strings easier to translate across all
supported languages.
2026-09-01 19:20:48 -07:00
Neil d7123591ce perf(git): pack the loose refs Orca's own fetches leave behind (#17857)
* perf(git): pack the loose refs Orca's own fetches leave behind

Orca strips git's auto-maintenance off every fetch it issues
(GIT_FETCH_SKIP_AUTO_MAINTENANCE_CONFIG_ARGS) and never compensated, so
nothing in an Orca-driven checkout ever packs refs. One real machine
reached 36,574 loose refs, where `git show-ref -- main` costs 5.2s and
every worktree create pays for it.

Add an idle-time, per-repo `git pack-refs --all --prune`, armed by the
fetches that create the debt. It runs only after ten minutes of quiet on
that repo, only above 1000 loose refs (probed with a walk bounded by that
threshold, not by the backlog), one at a time across the whole app, at
the background admission tier, and never while an agent is working, a
create is prepared or in flight, a worktree removal is deleting refs, the
app is quitting, or the machine is on battery. A user who set
`maintenance.auto=false` or `gc.auto=0` has opted out.

Measured on a 36,001-loose-ref fixture (macOS/APFS, git 2.44):
`show-ref` 5.5-12.2s -> 30-49ms, `for-each-ref` 4.0-10.8s -> 43-48ms.

Also fixes a pre-existing bug the split exposed: `--path-format=absolute`
is ignored before git 2.31, and taking rev-parse's stdout raw collapsed
every repo on such a host onto one fetch-serialization key.

Refs #17828

* perf(git): make idle ref maintenance preemptible and cheaper to probe

The idle veto was one-directional: it stopped a pack from starting during
a create, removal, or agent work, but nothing stopped those from starting
during a pack. A user-clicked Fetch, a branch delete, or a worktree
removal that needed `packed-refs.lock` mid-rewrite could fail with
`unable to create packed-refs.lock` -- a git error with no visible cause.

Make the pack cancellable end to end. An AbortSignal now reaches the
`pack-refs` child and both pre-pack probes, and `pause()` aborts what is
running, waits for it to actually stop, and holds a suspension count so
nothing new starts until the caller releases. Every entry point that
deletes a ref takes that pause: gitFetch, gitPull, gitFastForward,
removeWorktree, forceDeleteLocalBranch, prepareWorktreeCreateCheckout,
addWorktree. Five more triggers close the rest of the window: battery
drop, window focus, quit, the attempt deadline, and any other git command
queueing for an admission slot.

Judge a pack by re-probing the backlog rather than by the child's exit
code. Measured in the field: another Orca session moved a branch
mid-pack, git reported `cannot lock ref`, skipped that ref and packed the
rest -- 36,688 loose refs down to 3. On a machine running several
sessions that is the normal case, and retrying it would be wrong.

Probe with one batched `readdir` per directory instead of streaming
`opendir`, which issues a thread-pool round trip every 32 entries: 177ms
-> 23ms on a real 36,600-ref repository, with half the event-loop lag.
The walk stays strictly sequential so it can never occupy more than one
of libuv's four filesystem threads.

`PackRefsLockOwnership` makes a lock left by SIGKILL attributable, and
only reclaims one when a marker exists, the lock is older than any
pack-refs could run for, and the recorded process is gone.

Refs #17828

* fix(git): wait out the packed-refs lock instead of killing the pack

Measured on Git 2.55/APFS with 37k loose refs: a full `pack-refs --all
--prune` takes 23-32s but holds `packed-refs.lock` for only 0.03-1.37s of
it. The other ~95% is the prune phase, during which a concurrent `fetch
--prune`, `branch -D` or `update-ref` succeeds every time -- per-ref locks
last microseconds and git retries for `core.filesRefLockTimeout`.

So the abort-on-everything design was strictly harmful. SIGTERM into the
prune loop strands an empty `refs/**/*.lock` about one time in five
(9/30, 5/40, 6/30 kills): `tempfile.c` opens the lock O_EXCL before
`activate_tempfile()` links it into the list the signal handler walks,
and a pack does ~36k lock cycles. Afterwards `update-ref -d` on that ref
fails with `cannot lock ref ... File exists`, permanently. On Windows
`taskkill /f` never runs git's handlers at all, so an abort inside the
rewrite strands `packed-refs.lock` every time.

Never signal the child. `packRefs` no longer takes an abort signal; it
polls `packed-refs.lock` and reports the window through a
`PackedRefsLockReporter`. `pause()` resolves when the lock is released --
bounded, and free during the prune -- while the suspension counter still
blocks new attempts. Battery and window-focus become do-not-start rather
than stop-what-is-running, and quit waits for the lock and lets the child
finish orphaned.

For strands that already exist, `PackRefsLockOwnership` now also reclaims
`refs/**/*.lock` under the same three conditions plus a 0-byte check, and
a lock carrying our own not-yet-reclaimable marker records `locked` with
a 30min retry instead of the 6h failure cooldown -- so a Windows strand
self-heals in half an hour rather than six.

Reverts the git admission-scheduler event bus, which existed only to
drive the abort this removes.

Refs #17828

* test(git): make the ref-maintenance waits survive a loaded runner

CI shard 4/8 failed on `restarts every armed countdown when the user does
ref work themselves`, which passes locally. The `until()` helper spun a
fixed 200 event-loop turns and then returned silently, so on a contended
runner the filesystem probe had not finished and the assertion that
followed failed with an unrelated message.

Bound the wait by wall clock instead and throw a named error, which
immediately exposed a second latent bug: the single-flight test's second
wait could never succeed, because the deferred repo's retry is on a faked
`setTimeout` that spinning the real loop never advances. It had been
passing only because the old helper gave up quietly. Add a timer-aware
variant for those, and have the countdown test await a signal the fake
pack resolves rather than polling at all.

Verified stable across five sequential runs and once under load average
32 with six concurrent suites.

Refs #17828
2026-09-01 19:06:44 -07:00
Neil fdfe354045 test(relay): bind test WebSocket servers to loopback
A control-handshake test that expects a timeout was instead getting
'Unexpected server response: 401' about once in fourteen runs. A slow machine
cannot turn a timeout into a 401 -- that needs a real HTTP response, so the
connection was reaching a different server.

new WebSocketServer({ port: 0 }) binds the wildcard address while the client
dials 127.0.0.1. On macOS those differ, and with SO_REUSEADDR a foreign process
can hold the more specific 127.0.0.1:P and win the connection. Caught live: a
wildcard bind took port 52584, which a running Orca app already held on
loopback, and Orca answered the probe. A listener that checks a token answers
401.

Ten constructions across seven files now pass host: '127.0.0.1', so the
reservation covers the address the client dials and a duplicate bind is refused.

Adds a ratchet, because this is not authors forgetting a convention: all 30+
.listen(0, ...) sites already pass '127.0.0.1', while 7 of 7 ws constructions
did not. ws accepts { port } alone and binds the wildcard silently, so nothing
told them. The guard pins the wildcard count, and pins separately at zero the
option shapes it cannot read -- spreads and variable option objects fail rather
than being exempted, and a recognized-construction floor catches the matcher
going blind, which otherwise reads exactly like a clean tree.

mobile/scripts/mock-server.ts stays on the wildcard deliberately: a phone
reaches it over the LAN.
2026-09-01 19:05:42 -07:00
Neil 8b7d778a2e perf(git-common): bound the fs-stat fan-out in the worktree pollers (#17839)
* perf(git-common): bound the fs-stat fan-out in the worktree pollers

snapshotGitCommon and snapshotBase issued one fs op per candidate via
Promise.all/a serial loop, unbounded by worktree count. At 973 live
worktrees this queued ~6,800 concurrent stat calls (measured peak 6000
in a 1000-entry synthetic benchmark) onto libuv's 4-thread default
pool, starving every other main-process fs operation for the scan's
duration (~1s). Bound both to concurrency 8 via the existing
forEachWithConcurrency helper, matching the precedent in
exact-ref-probe.ts and worktree-head-identity-reader.ts. Peak
concurrent stats dropped 6000 -> 48 in the benchmark; wall time was
essentially unchanged (495ms -> 541ms), since the real bottleneck was
never total scan time but pool starvation of unrelated work.

Also make the no-native-watch and crash-fuse polling fallbacks in
worktree-git-common-watch.ts / worktree-git-common-narrow-watch.ts
self-calibrate their cadence: on platforms/paths where this poller is
the sole change signal, a fixed 2s cadence at hundreds of worktrees
approaches a permanent scan loop. Stretch the interval so a scan stays
a bounded fraction (10%) of its own cadence, capped at 30s, floored at
the configured base interval. Left the reconciliation backstop (fixed
30s cadence, already accepted) and checkPendingMarkers (bounded by
concurrent-worktree-creation count, not total count) untouched.

Fixes #17828

* perf(git-common): split the tripwire from the per-entry sweep cadence

Review on #17839 found a real staleness trade-off: adaptiveCadence
gated ALL detection (worktree add/remove, HEAD, dirty refs, AND
per-entry commit signals) behind one stretched interval, so on the
crash-fuse polling fallback the reviewer measured cadence sitting at
5.4-10s sustained and hitting the 30s cap once a single scan reached
3s at 973 worktrees -- worse than the pre-#17828 fixed ~2s+250ms
baseline for signals users notice immediately (sidebar worktree list,
branch labels).

Split snapshotGitCommon into a cheap structural "tripwire" (readdir,
worktreesDir signature, primary-file signatures, newly-appeared
entries -- ~5-6 fs ops, O(1) in worktree count) that always runs on
the fixed pollIntervalMs, and the O(n) per-entry sweep (commit/dirty
detection) that alone is gated by the adaptive cadence via a
nextSweepDueAt deadline. Existing, unchanged entries are carried over
by reference on a tripwire-only tick (no re-stat), so diffing produces
no spurious events; genuinely new entries are still stat'd immediately
so worktree add remains real-time. This keeps everything on one
ticking-flag-guarded loop (no new concurrency/race surface) --
scheduling stays fixed at pollIntervalMs; only nextSweepDueAt stretches.

Also drop the adaptive-cadence seed heuristic entirely: nextSweepDueAt
starts at 0, so the first regular tick after bootstrap sweeps
unconditionally on its own schedule instead of guessing an initial
interval from the bootstrap snapshot's duration (which could stretch
the very first tick to 10-30s on a slow disk).

Documented that worktree-git-common-watch.ts's adaptiveCadence call
site is unreachable in production (Electron only ships
darwin/linux/win32, both covered by NARROW_WATCH_PLATFORMS) rather
than implying it protects real users. The reachable path is the
narrow-watch crash-fuse fallback in worktree-git-common-narrow-watch.ts.

Filed #17878 to track the real long-term fix: periodically retrying
the upgrade back to the narrow watch after a crash-fuse trip, so the
degraded/polling state doesn't need to be tuned at all once the
underlying failure clears.

* perf(git-common): gate per-entry structural stats on the entry-dir signature

Every real git write inside a worktree admin entry (HEAD, index,
config.worktree, locked) goes through a lock file + rename, which moves
the entry directory's own mtime/ctime/size signature. Only `gitdir`
(worktree move/repair) is rewritten in place, and that's already covered
by the periodic ungated backstop (INDEX_BACKSTOP_TICKS). The previous
comment claiming structural leaves "change in place every tick" was
wrong; verified against git 2.55 across checkout, commit, amend, reset,
ref updates, stash, worktree lock/unlock, config --worktree, and index
writes.

Gate all six per-entry stats behind the entry dir's own signature instead
of stat-ing every leaf unconditionally every tick: an unchanged entry now
costs one stat per tick instead of six, and a changed one still costs six
(bounded by change rate, not worktree count). This also fixes the actual
in-flight fan-out: forEachWithConcurrency(entries, 8) previously still
issued 6 stats per in-flight entry (48 real concurrent ops); with the
gate, warm ticks issue ~1 stat per entry, so true in-flight tracks the
concurrency limit directly.

This makes the follow-up adaptive-cadence machinery from the prior commit
unnecessary: the crash-fuse and no-narrow-watch polling fallbacks no
longer need to stretch their own cadence, since a warm sweep across
hundreds of worktrees is now cheap regardless of interval. Revert both
call sites to a fixed pollIntervalMs and delete the adaptive-cadence
option, the split tripwire/sweep cadence, and the seed heuristic — none
of it earns its complexity once the real per-entry cost is fixed at the
source. Per-entry staleness on the crash-fuse path returns to a fixed 2s
+ 250ms debounce instead of the previous 5.4-30s adaptive stretch.

Refs #17828
2026-09-01 19:04:03 -07:00
Neil e89321192a perf(worktree): batch remote conflict probes, re-arm the prepared checkout (#17829)
* perf(worktree): batch remote conflict probes, re-arm the prepared checkout

A repo with many remotes paid one `git show-ref --verify` subprocess per
remote on every branch-conflict check during create. Ask one
`git cat-file --batch-check` over stdin instead; it reports a missing ref
as data rather than a failed exit, so a batch stays as decidable as the
per-ref probe. Hosts that cannot feed stdin, and undecided batches, still
fall back to the per-ref path.

The prepared checkout was single-use, so the second create in a row paid
the full cold `git worktree add`. Re-arm it in the background after one is
consumed; the existing TTL and preparation limit still bound it.

The create timing recorder existed but its phases were never emitted and
did not cover preflight, leaving a multi-second gap in the trace with no
attribution. Add `resolve_name`/`prepare_push_target` phases and record the
breakdown, plus the unattributed remainder, on the create span.

* fix(worktree): format the conflicting review number eagerly for the create error

* perf(worktree): re-arm a prepared checkout only for a burst of creates

Re-arming after every consumed preparation spends a full checkout and
~200MB of disk on a user who created one worktree and stopped, then pays
an unexplained delete when the TTL expires five minutes later. Track when
each preparation key was last consumed and only replace it when a second
create lands inside the burst window, so the warm second create is still
free and an isolated create costs nothing.

* fix(worktree): address review findings on the create-path batching

Three findings from PR review:

The `batched.found` fallback in the remote-conflict probe was unreachable
— a present ref is decisive, so `found` never survives with `unknown`
set, and the guard above already returns that case.

`rearmPreparation` checked for an existing preparation before recording
the consume, so a prefetch that re-armed the key while create finalized
swallowed the timestamp and made the next create look isolated when it
was really mid-burst.

Create runs some phases concurrently, so summing phase durations
double-counted overlap and understated `unattributed_ms` — the one
number that matters when a create is slow for no visible reason. Measure
the union of the phase intervals instead.

* refactor(worktree): move stale-preparation cleanup into its own module

The preparation module crossed the 300-line budget. Crash recovery is a
separate concern from the pool itself — it discards preparations another
process left registered, single-flighted per repo and runtime so a burst
of arming calls shares one worktree listing.

* test(worktree): make the re-arm test able to fail

The burst test armed a preparation manually after the second consume, so
the third checkout appeared whether or not the re-arm produced it — the
assertion passed with re-arming disabled. Drop that arming call so the
third checkout can only come from the re-arm, and assert the consume
results rather than discarding them.
2026-09-01 19:01:59 -07:00
Neilandkaluli123123 3f5c54332d fix(github-project): sort and group empty field values last in both directions
compareSort early-returned 1 for a missing value — before the trailing
DESC flip — but expressed the same idea as `cmp = 1` for an empty
users/labels list, which that line then negated. Descending order
therefore scattered empty cells across both ends of the table.
getFieldValueForGrouping had the matching defect: an empty list fell
through to deriveStringValue and produced a blank-label group that the
header renders as the literal "All".

Both paths now share one predicate, which also covers `text: ''` and
`date: ''` — reachable because the view normalizer maps a null GitHub
text/date to the empty string.

Co-authored-by: kaluli123123 <295758798+kaluli123123@users.noreply.github.com>
2026-09-01 19:01:45 -07:00
Jinwoo Hong fd33f9b0f9 fix(review-notes): classify send failures and mirrored tabs (#18023)
* fix(review-notes): classify send failures

* fix(review-notes): honor structured runtime error codes

* test(review-notes): use full runtime error envelope

* chore: remove unrelated merge formatting

* refactor(review-notes): share runtime failure codes

* fix(review-notes): classify structured runtime timeouts
2026-09-01 21:52:36 -04:00
Neil 401664298f fix(preload): make a dropped bridge key a compile error
The split silently dropped jira.searchUsers and
runtimeEnvironments.retryControlConnection. Neither failed typecheck: the bridge
modules carried no satisfies annotation and the composed api object was
unannotated, so a missing key was only a runtime TypeError in the renderer.

Annotates each module against PreloadApi, the type window.api is already
declared as, so the contract supplies the shape rather than a parallel copy.
Deleting jira.searchUsers now fails with TS2741 naming the key.

Turning this on surfaced 106 places where a bridge locally annotated
Promise<unknown> or unknown[] over a contract that declares concrete types --
the bridge was erasing types the renderer relied on. Those annotations are gone.

Also exposes app.awaitBeforeUnloadCheckpoint, which was declared and called but
never actually on the bridge, so the lazy-chunk recovery reload optional-chained
to a no-op and navigated without joining the checkpoint. The missing key was
caught by the new annotation rather than by hand.
2026-09-01 18:24:49 -07:00
Neil 894c5fe36a test(orchestration): fail loudly on an unexpected second detection call
The mock overwrote resolveDetection on every call, so a second invocation would
strand the first promise and hang to a 30s timeout instead of naming what
changed. A test that hangs rather than fails is how a real bug gets mistaken for
infrastructure noise.
2026-09-01 18:22:59 -07:00
Neil 4efc86a33c feat(app): open Markdown files from the OS in the floating workspace (#17906)
* feat(app): open Markdown files from the OS in the floating workspace

Registers Orca as a Markdown handler on macOS, Windows and Linux, and opens
an OS-handed .md/.markdown/.mdx file as a floating-workspace editor tab —
the one editor surface that needs no project. Works cold-start and when
Orca is already running.

Main buffers the paths and both pushes to a live renderer and answers a
pull on renderer mount, mirroring SkillShareDeepLinkState. The buffer is
only released once delivery is possible: the renderer's pull is what proves
its ui:openMarkdownFiles listener is attached, because a push into a window
whose renderer has not subscribed is dropped by Electron with no error. Both
the push and the pull restore an undelivered batch, and a renderer reload
clears the latch so the fresh renderer re-proves itself. Paths are stat'd and
proven to be files before authorizeExternalPath sees them.

Windows association is registered by hand in the NSIS include rather than
through electron-builder's `fileAssociations`: app-builder-lib emits
APP_ASSOCIATE, whose first line overwrites Software\Classes\.md's default
value with no backup — silently taking .md from whichever editor owns it,
for every existing user on their next update — and APP_UNASSOCIATE never
restores it. The hand-rolled registration is additive (ProgID +
OpenWithProgids + SupportedTypes) and leaves the user's default alone;
verified end to end on a real Windows 11 host.

Co-authored-by: Wooseong Kim <innocarpe@users.noreply.github.com>
Co-authored-by: Jaydev <java-jaydev@users.noreply.github.com>

Closes #10138

* fix(os-open): register the new listener in the IPC inventory, and guard a non-array payload

CI caught two things the local run did not.

useIpcEvents-lifecycle.test.ts is an inventory of every App-lifetime IPC
listener and the exact order they register in; ui.onOpenMarkdownFiles now
appears there, positioned after the workspace-shortcut bridge's last
listener, which is where it actually registers.

Chasing that failure surfaced a real gap: the pending-open payload crosses
the preload boundary, so a stale or mismatched preload can resolve with
something that is not an array, and reading .length off it threw inside the
promise chain instead of failing at the boundary. Array.isArray now gates it,
with a regression test.
2026-09-01 17:35:38 -07:00
Neil d48ab96144 test: stop two suites failing for reasons unrelated to their subject
The zsh wrapper test relocated into a fixed-name directory in shared temp, so a
single killed run left it behind and every later run on that machine failed with
ENOTEMPTY, permanently. Makes the name unique while keeping the non-ASCII
component the test exists for.

The palette budget asserted a helper named percentile95 that returns
sorted[floor(n * 0.95)] -- the maximum of the batch. Asserting worst-case
wall-clock under a parallel runner measures scheduler preemption: the asserted
quantity ranged 123-343ms across 20 saturated windows and blew the 220ms budget
in 6 of them, while the fastest sample of those same batches held at 19-32ms.
Asserts the fastest sample instead and adds a deterministic fan-out ceiling, so
the guard counts work rather than time. Budgets are unchanged.
2026-09-01 17:21:10 -07:00
Neil f2fa4a7754 fix(worktrees): drop an unreachable runtime arm from the retirement gate
`findExactRepoOwner` already refuses a repo carrying both a runtime
`executionHostId` and a `connectionId` -- `resolveRepoOwnershipEvidence` calls
that pair contradictory, and one non-owned candidate voids the whole lookup.
There is also no way for a `connectionId` to yield a `runtime:` host id, since
`toSshExecutionHostId` always emits `ssh:`. The runtime arm of
`connectionMatchesHost` could therefore never decide anything, and the test
meant to pin it was passing through the contradiction gate instead.

Keep the SSH arm, which does gate, and record where the runtime refusal
actually comes from. Unreachable code on a destructive path reads as a
guarantee it is not making.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil 398aeccdfe fix(worktrees): retire runtime-host metadata a scan proved gone
A paired client's WorktreeMeta for a runtime host is exempt from
gcStaleWorktreeMeta -- that GC skips any row that is not local on both the
repo and the meta's hostId -- so a scan-proven removal is the only thing that
ever retires one. Both halves of that path were gated to `ssh:`, so the client
kept a row for every remote worktree it had ever seen and dropped none.

The renderer already computed the removals for runtime hosts and purged its
own in-memory state with them; only the persisted half bailed. Widen it, and
the matching main-side handler, to runtime hosts. `OffHostExecutionHostId`
names the set precisely: the hosts the local-only GC skips.

Also require `source === 'git'` before retiring anything. `session-fallback`
reports `authoritative: true` but is the truncated, visibility-filtered
`worktree.list` reply from a host too old for `worktree.detectedList`; its
omissions are no evidence a checkout is gone. That guard did not matter while
this only ran the in-memory purge, and does now that it deletes rows.

A repo that reaches its checkouts over a connection is still never condemned
under a runtime host id -- the host that executes owns that verdict.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil 05a7d39058 test(runtime): make the off-host sweep case a real control
The row was stamped `ssh:build-box`, which
`captureNativeLocalWorktreeMetadataScanExpectation` filters out before the
prune runs -- so it survived whether or not any host gate existed and pinned
nothing.

Stamp it `local` so it is a genuine prune candidate whose directory really is
missing, and make the fixture identical to the first case apart from
`connectionId`. That pairing is what proves the behavior: the same fixture
without a connection loses the row. Deleting any single gate would not show
it, since four independent checks derive from `connectionId` on this path.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil ff8b4d08ab fix(runtime): sweep missing local worktree metadata on the host that owns it
`pruneMetadataMissingFromAuthoritativeLocalScan` had exactly one caller:
`ipcMain.handle('worktrees:listAll')`. A headless runtime host has no
renderer, so it never ran, and that host's `worktreeMeta` grew without bound
even for its own local repos -- 129 of 139 rows dangling on the profile in
#17776.

Run it from the runtime's own detected listing instead. That is the same
trigger on the same evidence: `listDetected` already prunes lineage on an
authoritative scan, and a paired client refreshing a remote repo calls
`worktree.detectedList`, so the host now sweeps exactly when the desktop
would have.

The expectation is captured before the scan, because listing can mutate
metadata synchronously before its first await. WSL-routed repos are excluded
for the reason the desktop listing excludes them: the listing runs in the
distro and reports Linux paths while metadata can hold UNC ones, and v1
cannot prove those aliases equivalent. A runtime needing repair throws rather
than resolving routing, which is likewise no basis for deleting rows.

The prune's own gates still apply, so an SSH- or otherwise off-host repo is
never swept from a local stat -- the execution host owns that verdict.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil 1a11f82fcc test(persistence): cover each session scalar as an orphan's only residue
`activeWorktreeId`, `activeWorkspaceKey` and `activeWorktreeIdsOnShutdown` are
pruned by bespoke rules rather than by owner key, so no owner-key loop reaches
them and each has to be able to seed the sweep alone. The sweep already handles
all three -- the census seeds from them and `removeRepoFromWorkspaceSession`
clears them -- but nothing pinned it, and dropping that seeding turns all three
cases red.

The `activeWorkspaceKey` case uses the canonical `worktree:<id>` form, so it
also covers unwrapping the workspace key before the repo id is visible.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil 87f3e907dd test(persistence): assert the sleeping-agent cleanup reached disk
The self-clearing check loaded a second store, but that constructor runs the
sweep itself. If the first flush had not persisted the cleanup, the second load
would have redone it in memory and the assertion would have passed without
meaning anything. Read the profile back and assert the map is empty there first.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil 2f105b23d1 fix(persistence): sweep sleeping-agent-only residue and stop mis-seeding orphans
Review found three holes in the load-time sweep.

`sleepingAgentSessionsByPaneKey` and `terminalSurfaceTombstonesByPaneKey` are
pruned by the worktreeId they name, not by their own key, but
`pruneWorktreeStateForRepo` only collected owner keys from `worktreeMeta` and
`lastVisitedAtByWorktreeId`. An orphan whose only residue was a sleeping agent
therefore survived the sweep and re-seeded it on the next load, so the store
never self-cleared and every launch scheduled another save. Collect owner keys
from those records too, which fixes `removeProject` for the same shape.

`ownerKeyBelongsToRepo` is restored to its original body. Reordering its two
readings was not behavior-preserving as claimed: for a repo named `folder` or
`worktree`, checking the workspace-key reading first flips the result. The
census now uses `ownerKeyWorktreeIds`, which returns both readings, and seeds
only when neither names a live repo -- seeding one reading of a key whose
other reading is live would hand the removal pass a live row to delete.

Seed from `activeWorktreeId`, `activeWorkspaceKey` and
`activeWorktreeIdsOnShutdown`, which are pruned by bespoke rules and so were
reachable by no owner-key loop, and record why
`terminalTopologyRevisionByRepoId` stays excluded.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil a2aea5d0b0 fix(persistence): sweep rows owned by deregistered repo ids at load
Deregistering a project stranded every row it owned. Each pruning path is
gated on the repo still being in `state.repos`, so once an id leaves the
catalogue its metadata, identity aliases, lineage and session rows became
unreachable forever -- and on a paired client they rendered as phantom
worktrees under an "Unknown" project.

Reconcile against the repo catalogue on load instead: any repo id that owns
rows but is absent from `state.repos` has its rows removed through the same
path `removeProject` uses. Host-independent and session-independent, because
an orphan has no owner that could object -- which is also why this reaches a
client's mirror of a remote host's session partition, something no local
removal can do.

Only a full `<repoId>::<path>` locator seeds the orphan set; bare keys can be
folder workspace ids or repo-keyed revisions, and guessing wrong there would
delete live state. `retiredWorktreeNamesByRepo` is deliberately untouched so a
re-added repo cannot reissue a name onto a cwd that still holds a prior
occupant's agent state.

Test fixtures that wrote worktree rows without registering their repo were
relying on orphans surviving a reload; they now register the repo they name.

Refs #17776
2026-09-01 17:20:17 -07:00
Neil 519af49a58 fix(dev): keep the shared Electron dist writable for the dev app
pn dev crashes on macOS in any worktree that adopted the shared Electron dist.
publishSharedElectronDist marks the cache entry read-only, which hardlink
sharing needs, but clonefile preserves mode -- so the dist lands 0555, the dev
runner copies it into out/electron-dev unchanged, and the first plutil -replace
on Info.plist fails with a permission error. The shipped zip has that file at
0644; on disk it is 0555, so the mode is ours, not upstream's.

copyPrivateTree now restores write permission. Its contract is a private tree
the caller goes on to patch, and its one production caller is the dev runner.

The test that should have caught this ran the wrapper with stdio: 'ignore', so a
hard crash presented as a bare 20s timeout. It now captures the wrapper's output
into the failure message, and waits long enough for the two synchronous swiftc
builds and a codesign --deep over ~280MB that precede the assertion.
2026-09-01 17:16:26 -07:00
Neilandkaluli123123 1e82f66e80 fix(agents): clear the unread completion marker when acknowledging agents (#17924)
Acknowledging is one action against two records, but only
clearTerminalPaneUnread cleared unreadAgentCompletionPanes. Acking from
the Activity page, the dashboard drawer or the popout bridge left the tab
dot, the ⌘J row and the floating-workspace dot lit with nothing left to
read; only the terminal-view auto-ack path cleared both.

Cleared inside the existing set so one ack is one commit, and only the
agent marker is touched — clearTerminalPaneUnread also drops
unreadTerminalPanes, which would silence a BEL the user never saw.

Refs #15445 (step 2 of that issue's fix; steps 1 and 3 remain open).

Co-authored-by: kaluli123123 <295758798+kaluli123123@users.noreply.github.com>
2026-09-01 17:15:15 -07:00
Neilandkaluli123123 80a52bb9b3 fix(git): recover commit ref badges on Git older than 2.43 (#17923)
GIT_HISTORY_COMMIT_FORMAT asked for decorations with %(decorate:…), which
Git 2.43 introduced. Older Git prints the placeholder verbatim and exits
zero, so nothing raised and every commit in the Source Control panel
silently lost its branch, remote and tag badges.

The record now also carries %D (Git 2.10) on its own line, selected by an
exact match against the unexpanded placeholder — a ref name can never
contain the \x1f that Git expands inside the echoed text. %n emits the %D
line on both sides of the boundary, so the message index is fixed and a
missed match degrades to no badges rather than a corrupted message.

The decoration separator is now bound to the field that produced the text
instead of sniffed from it. A lone decoration carries no separator, so the
old sniff split `refs/heads/feat,one` into two bogus refs.

Verified against real Git 2.38.1 and 2.49.1.

Co-authored-by: kaluli123123 <295758798+kaluli123123@users.noreply.github.com>
2026-09-01 17:14:57 -07:00
Neil c8937936eb refactor(mobile): pin the terminal WebView payload and split its widest slice
The payload is one concatenated string, so slice boundaries follow document
order rather than responsibility -- but join is associative, so cutting a slice
into consecutive slices is byte-identical by construction. Splits the widest
slice, which carried fit-scale, a DECSET scanner and the write queue together
with no room left under the line cap.

Adds a hash guard. The behavioral tests each execute one region of the payload
in a vm, so an edit to an uncovered region shipped silently; the composed output
is now pinned by sha256 and length.

Derives the source-file list from the composer's own imports instead of a second
hardcoded list a new slice had to be added to by hand -- the same silent
subject-loss shape already found twice elsewhere in this repo.
2026-09-01 16:38:22 -07:00
Neil 73fcdea23c fix(palette): recompute quick-action availability when runtime status changes
buildQuickActionContext reads runtimeStatusByEnvironmentId transitively through
getClientCreationActionPolicy, but the split dropped it from the memo deps. The
store replaces the Map identity on update, so the palette held availability from
a snapshot that never refreshed -- offering a browser action against a provider
that had gone away, or hiding one that had come back.

exhaustive-deps could not catch it: the read is behind a void statement, which
the rule does not see.
2026-09-01 16:38:06 -07:00
Neil 2c559fa96a test(child-process): make the import ratchet able to fail
never grows asserted offenders.length <= ALLOWLIST.length, but the two
membership assertions already force those equal, so it could not fail. The
comment claimed it caught a swap -- one file migrated off child_process, one
added -- which is exactly the case it let through.

Pins the true count and asserts both directions, so a swap fails and a pin left
stale-high after a migration also fails rather than banking ground twice. Gives
the console-visibility ratchet the same test: it had no count assertion at all
and the same gap.

Also anchors the owner-directory exemption with a trailing slash, so a future
src/shared/child-process-foo.ts is scanned rather than silently exempt.
2026-09-01 16:37:50 -07:00
Neil 93a258c81d fix(worktrees): reclaim orphaned pr-* fork remotes (#17842)
* fix(worktrees): reclaim orphaned pr-* fork remotes

pr-* remotes Orca adds for fork-PR worktrees were only ever pruned by a
single worktree's own removal, and only when that removal had complete
provenance metadata, no branch pinning it, and actually ran through Orca.
Legacy metadata missing remoteCreated, "preserve branch on delete" pinning
the remote via branch.*.remote config after the worktree is gone, and
worktrees removed outside Orca entirely all left the remote behind
forever -- one real user accumulated ~50 leaked remotes this way.

Add a repo-scoped reconciliation sweep that inverts the existing cleanup
predicates over every pr-* remote instead of one removal, reusing
sameGitHubRemoteUrl/hasBranchConfigUsingRemote so no new safety logic is
introduced. It only touches a remote some worktree's persisted pushTarget
explicitly recorded Orca creating (remoteCreated: true) -- naming and URL
shape alone are not proof of provenance. Runs opportunistically alongside
existing single-target cleanup (including RuntimePreservedBranchCleanup's
force-delete path), rate-limited per repo, and fire-and-forget so it never
adds latency to the worktree-removal path a user is waiting on.

Fixes #17828

* test(worktrees): set a local git identity in the pr-remote fixture

CI runners have no global git identity, so `git commit` in the fixture
repos failed with "Author identity unknown" -- only passed locally because
dev machines have one. Set user.name/user.email (plus commit.gpgSign and
core.hooksPath, matching src/main/git/repo-remote-drift-real.test.ts) as
local repo config in both the main and cloned "fork" fixture repos, so the
test is independent of the runner's global config, signing setup, or hooks.
2026-09-01 15:59:03 -07:00
Jinwoo Hong 7873f73d80 fix(daemon): keep attach cancellation behind client timeout (#17816) 2026-09-01 17:52:43 -04:00
Brennan BensonandMerge Sim 7314ada3fb fix(native-chat): ignore stale restored working status (#17995)
* fix(native-chat): ignore stale restored working status

* fix(native-chat): sample status freshness per epoch

* Revert "fix(native-chat): sample status freshness per epoch"

This reverts commit 8e49b1badd.

* test(native-chat): include hook status timestamps

* test(native-chat): include visibility hook timestamp

---------

Co-authored-by: Merge Sim <sim@local>
2026-09-01 14:31:23 -07:00
Neil 28214e1ea1 fix(linux): stop re-extracting the AppImage on inode metadata churn
The extracted-payload cache key hashed ctime alongside dev/ino/size/mtime.
ctime moves on any inode metadata write -- `chmod +x`, which every AppImage
user is told to run, plus `chown`, an ACL or SELinux relabel, and a backup
restore -- none of which alter a byte of the payload.

Measured on Ubuntu 24.04: `chmod +x` leaves dev, ino, size and mtime
identical and moves ctime alone, so the key changed and the next launch paid
a full ~519 MB re-extraction and a multi-second stall to rebuild a payload it
already had, then pruned the old generation.

Key on content identity instead. An in-place content change moves mtime and
almost always size; a replacement moves the inode. The existing
replace-in-place test still passes.
2026-09-01 14:06:16 -07:00
Neil a310150e6a fix(linux): bound the CLI registration lock wait
`retries: 1000` caps the attempt count, not elapsed time, so at up to 1s per
attempt an IPC-driven registration could hang ~16 minutes against a wedged
holder with no feedback.

A legitimate holder is bounded by the extraction timeout, so wait that plus
slack and then fail with a message naming the lock file, rather than hanging.
`maxRetryTime` is forwarded verbatim to the `retry` package by proper-lockfile.
2026-09-01 14:06:16 -07:00
Neil 1767858ea7 fix(linux): reclaim superseded AppImage payloads and packaged symlinks
Pruning removed 3215 of 3216 files from a superseded generation and always
stranded resources/app.asar, leaking ~105 MB per version update. Electron's
asar shim reports a *.asar file as a directory, so the recursive remove tried
to rmdir a real file and failed with ENOTEMPTY; the .catch(() => {}) hid it.
Reproduced end to end on Ubuntu 24.04: 519M -> 623M across one update, and
519M again once the payload is actually reclaimed.

removeExtractedAppImagePayload holds process.noAsar for the removal, counted
so overlapping removals cannot hand the shim back early, and the prune site
now warns with the path instead of swallowing the rejection. All three
removal sites use it -- staging cleanup and displaced roots leaked the same
way.

Also reclaim symlinks left by a packaged deb/rpm install, which the
extracted-cache-only rule turned into a hard conflict on a deb -> AppImage
migration, and name the remedy in the conflict error.
2026-09-01 14:06:16 -07:00
Neil bbd2047066 refactor(linux): import bundled launcher directly 2026-09-01 14:06:16 -07:00
Neil 95212ef572 docs(linux): make headless AppImage extraction runnable 2026-09-01 14:06:16 -07:00
Neil a2c8859f76 fix(linux): accept extracted AppImage runtimes with APPDIR only 2026-09-01 14:06:16 -07:00
Neil d19a8cdf86 fix(linux): fence AppImage terminal shim mounts 2026-09-01 14:06:16 -07:00
Neil 0079fe2fa8 test(cli): assert registration lock serialization 2026-09-01 14:06:16 -07:00
Neil 4c24a28df0 refactor(linux): trim AppImage CLI registration seams 2026-09-01 14:06:16 -07:00
Neil da4a83bd22 fix(linux): give the CLI one entrypoint by extracting the AppImage once 2026-09-01 14:06:16 -07:00
Neil 2be53521a7 refactor(task-page): fold the task page into one provider-grouped tree
The page had two competing splits: an Aug-25 folder split that the Aug-30
oversized-surfaces split stranded, and the 47 flat files that replaced it. The
orphaned tree had no non-test importers, yet eight ratchet files still asserted
against it, so their invariants stopped constraining shipping code -- which is
how six regressions reached main unnoticed. Those ratchets were repointed and
the regressions fixed earlier; this removes the tree they were guarding.

Moves the live files into task-page/{github,gitlab,jira,linear} and drops the
now-redundant prefix, matching the new-workspace sibling.

Makes the source-family walker recursive first: it listed a single flat
directory, so moving the files under it would have emptied the family and turned
every ratchet built on it into a no-op without failing.
2026-09-01 13:16:43 -07:00
Neil 8fef5820ff fix(renderer): restore behavior the UI split dropped
The oversized-UI-surfaces split was cut from a stale branch and reverted merged
work. getClientCreationActionPolicy entered Terminal.tsx in #13909 and left in
the split, taking six call sites with it, so every action-time creation gate in
the terminal and floating surfaces was gone. Restores those and the other
behavior the split dropped, each ported from the pre-split reference:

- Cmd/Ctrl+S dispatched a bare Event with no detail, so the only listener always
  bailed on detail?.fileId and the chord never saved. Its resolver had been left
  orphaned, imported by nothing but its own test.
- Terminal and floating create actions lost their availability gates, their
  toasts, and their catch handlers; one path throws on unavailable, so it was a
  silent unhandled rejection.
- Both outermost workbench wrappers lost the browser guest paint retention
  branch, and the census entry covering them was deleted in the same commit.
- The Space Analyzer header counted omitted items the list no longer rendered,
  and a worktree whose items were all omitted showed the empty state.
- The terminal root lost its tab topology projection, so every tab-title update
  re-rendered it.
- The titlebar tab bar stopped being passed clientHostedBrowserRows, leaving
  client-hosted pages uncloseable before a worktree has a layout.
- Parking diagnostics lost their exempt-route counts and crash breadcrumb.
- A suppressed inherited-terminal frame began buying a freshness scan the
  pre-split early return skipped.

Adds regression tests for each, all verified to fail against the pre-fix code.
Restores three deleted assertions whose invariants are still live, and replaces
a concatenated source-boundary fixture with per-module pinning so a symbol is
again asserted against the module that must own it.

Deletes three orphaned trees the splits stranded: a duplicate ResourceUsage
surface, cmd-j-match-relevance, and an agent-session claim-key module whose
logic the record store already owns. Makes two non-recursive test walkers
recursive, one of which silently skipped every nested CLI handler group.
2026-09-01 12:51:17 -07:00
Neil 79990464d2 fix(store): merge the duplicated agent-status-launch-config import
The rename collapsed agent-status-map-helpers into agent-status-launch-config,
leaving two import statements for the same module.
2026-09-01 12:27:01 -07:00
Neil 8cc7634051 refactor: name modules for their domain instead of 'helpers'
Renames seven -helpers modules for the concept their functions operate on, and
splits three that were genuine grab-bags -- each had a clean cleavage along its
importers, which is the signal AGENTS.md describes for a file holding more than
one responsibility.

Leaves keybindings/definitions-core-1..4 alone: definitions.ts spreads them in
order, so their concatenation order is the command palette order and regrouping
them thematically would be a user-visible change. Records that reasoning in a
comment so it is not re-litigated.
2026-09-01 12:27:01 -07:00
Neil de8aaac344 refactor(mobile): name terminal WebView modules for their contents
fragment-01..10 were arbitrary line-count slices of one template literal. Two
seams fell mid-expression -- inside buildMouseClickInput and inside the touchmove
listener -- so those pieces had no identity to name. Re-splits at real statement
boundaries and names each for what it holds.

The composed output is byte-identical: sha256 42cc000f..., 729776 bytes, verified
before, after the regroup, and after formatting. Also fixes two ratchet tests that
read fragment paths directly, one of which duplicated the composer's file list.
2026-09-01 12:27:01 -07:00
Neil fc68d2c3a2 refactor(preload): name bridge modules for what they expose
The split named these -part-N, which says nothing. Renames each for the group of
bridge methods it actually exposes and folds the single-method window-reveal
module into the window-controls module it belongs with.

Verified by walking the composed contextBridge surface before and after: 1060
keys, identical nesting and value types, zero delta. The bridge modules carry no
satisfies annotation, so a dropped key here is a runtime error in the renderer
rather than a typecheck failure.
2026-09-01 12:27:01 -07:00