mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
* perf(worktrees): converge the trash sweep instead of re-walking doomed trees
`transientLockRemovalOptions()` only asked for `maxRetries` on Windows, and
`removeHostTree`'s retry ladder was gated on `process.platform === 'win32'`.
A concurrent writer is not Windows-specific: Spotlight/`mds`, a scanner, or a
live process writing under the tree surface the same EBUSY/ENOTEMPTY/EPERM on
macOS and Linux. So on POSIX the startup sweep got exactly one attempt per
entry, failed, and re-issued the same guaranteed-to-fail walk on every launch.
- Extend the retry policy to every platform. Windows keeps its error set,
its message fallback, and its delays; the message fallback stays
Windows-only because POSIX always sets a code.
- Persist a per-entry failure ledger in the trash root so a repeatedly
failing entry is retried on a 15m/1h/6h ladder rather than on every launch.
Nothing is abandoned: the ladder clamps, records are pruned when the entry
goes, and a torn ledger fails open to a full sweep.
- Defer the sweep behind first paint, so its recursive readdir/rm no longer
competes with window creation and worktree-catalog hydration.
* fix(worktrees): keep Node's per-level rm retries Windows-only
Node's rimraf hands every child back to the retrying entry point
(`_rmchildren` -> `rimraf`), so `maxRetries` is applied once per directory
level and compounds: a permanently-failing leaf at depth d costs roughly
`retryDelay * 36 * 9^(d-1)`. Measured on macOS against one `chflags uchg`
file at depth 2, `{recursive, force}` rejected in 1 ms while
`{maxRetries: 8, retryDelay: 150}` had not settled after 5 minutes.
Handing those options to POSIX removals turned every `removeHostTree` on a
worktree residue (`node_modules/.pnpm/...`, a dozen levels deep) into a
promise that never settles -- wedging the serialized trash-deletion queue,
hanging the sweep on its first failing entry so no backoff is ever recorded,
and leaving the unregistered-worktree removal IPC pending forever.
Keep the cross-platform retry where this PR put it -- the bounded outer
ladders that re-issue one whole `rm` against the same already-chosen path --
and restore `transientLockRemovalOptions()` to Windows-only `maxRetries`.
Also guard the deferred first-window task: off whenReady's promise chain a
synchronous throw is an uncaughtException, which the pipe-error guard
re-throws fatally.
* fix(worktrees): make host tree removal see through Electron's asar shim
The 267 stranded trash entries were not a concurrent-writer race. Electron
patches `fs` so a `*.asar` file reports `isDirectory() === true`, so Node's
recursive `rm` descends into the archive, `rmdir`s a real file, and fails the
parent with ENOTEMPTY — deterministically, on every attempt. Every worktree
that has run `pnpm install` carries a `default_app.asar`, which is why every
residue stopped at the same path.
Route `removeHostTree` through `original-fs` (Electron's unpatched `fs`, with a
`node:fs/promises` fallback outside Electron) instead of retrying a failure that
can never succeed. `removalPath`, `rmOptions` and the Windows retry ladder are
byte-identical to `main`.
Reverts the POSIX retry ladder, the `isTransientRemovalError` widening, the
sweep backoff ledger and the inverted `does not retry host removal failures
outside Windows` ratchet — none of them were fixing the actual failure.
* fix(worktrees): drop the stray orchestration test and bundle the asar guard like production
`orchestration-statement-compilation.test.ts` belongs to #18420 and was swept
into this branch by accident. It imports `./prepared-statement-cache`, which
does not exist here, so `tsc -p config/tsconfig.node.json` failed on this
branch. Removed; typecheck is clean again.
The Electron asar guard pre-externalized `original-fs` in its own Vite build,
which is not what the shipped bundle does. Mirror `isExternalMainModule` from
electron.vite.config.ts instead, so the guard also proves the production
bundler leaves `createRequire(__filename)('original-fs')` as a runtime require
— if that ever became a static import or got folded, production would silently
degrade to the shimmed `fs` while the old test kept passing.