fix(desktop): give worktree git the budget the runtime already allows - #164
Merged
Merged
Conversation
The verify job spends 643s of 1062s compiling the Tauri shell — 61% — on every pull request, including ones that only touch TypeScript. Only native sources, their manifests, or the build inputs they embed can change that output, so the compile now runs when those change and is skipped when they do not. The decision fails open: an unreadable base, a missing diff, or a path nobody thought of all resolve to compiling. A gate that skips work it should have done is the failure this repo already paid for once, when a typecheck that did not cover apps/desktop let a broken release cut. Splitting the renderer bundle out is what makes the skip safe. Tauri's beforeBuildCommand bundles the renderer, so skipping the shell would have skipped that too — and a bundling failure, a bad dynamic import or a missing asset, is invisible to both typecheck and vitest. Measured, the bundle is 26s against the shell's 643s, so it runs every time and only the expensive half is conditional. A TypeScript-only pull request now pays 26s where it paid 643s. A native one pays the bundle twice, 4% more, which is the price of the coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first version of this gate was both dead and dangerous, and an adversarial read of it found three things I had not. Dead: actions/checkout defaults to fetch-depth 1, so neither end of the PR range is in the object store. `git diff base head` exits 128, the fail-open branch fires, and the shell is compiled every time. The optimisation saved nothing while looking like it worked. Dangerous: `Smoke packaged CLI` is the one step that consumes target/release, and it was not gated. The moment the skip started working it would have run against a binary that was never produced. Incomplete: the path rule missed inputs that provably reach the build — updater-signature-verifier, a [[bin]] Cargo compiles from outside src-tauri; resources/, whose icons and entitlements generate_context! embeds; scripts/, which beforeBuildCommand runs; .npmrc and pnpm-workspace.yaml, which change module resolution. It also missed git's own quoting: core.quotePath puts a quote before a non-ASCII path, which defeated the anchor. config/scripts/check-shell-build-gate.sh holds the cases, each with the reason that path is a build input, so the next edit to the rule has to answer for them. One known limit is recorded rather than fixed: a rename whose destination lands outside every pattern. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Creating a worktree failed intermittently with "failed to read resource write response: Resource temporarily unavailable (os error 35)". That is the desktop client's own read deadline. `git worktree add` runs synchronously inside the request and the runtime allows it two minutes (gitWorktreeCommandLimit, manager.go:28), but the call passed no timeoutMs and inherited the 1500ms default meant for status reads. Checkout of this repository measures 0.84-1.25s, so the deadline was a coin flip — which is why it failed sometimes rather than always. The mismatch did more than report badly. When the client's deadline fires it drops the socket, the runtime's request context is cancelled, and exec.CommandContext kills git part-way through: measured, git died at 301ms with 1673 of 7246 files written. What is left is a worktree git has registered and marked locked with reason "initializing", a branch that exists, and no record of any of it in the runtime. Neither `git worktree prune` nor `git worktree remove --force` clears it, because the app's own removal only ever passes a single --force. The next attempt then fails with "a branch named 'x' already exists", which is a stranger error than the first one. Retrying is not the fix and the transport is right to refuse it: writes may already have landed, so only GETs replay. Removal gets the same budget, since timing out there abandons a half-removed worktree for the same reason. The sibling calls in this file already do this — clone passes ten minutes, create-on-host sixty seconds. Worktree create and delete were the ones that ran git without saying so. Found by an adversarial review of my own earlier conclusion. I had searched apps/, packages/ and runtime/ for the error string, decided it was not in the repo, and guessed at a non-blocking-socket EAGAIN needing a retry. The string is at native/rust-host/src/runtime_status_probe.rs:867 — a directory I never searched — and the cause is a deadline, not a missing retry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
无法创建平行宇宙 — os error 35
Creating a worktree failed intermittently with:
That is not a network hiccup. It is the desktop client's own read deadline, surfacing as EAGAIN from
set_read_timeout(native/rust-host/src/runtime_status_probe.rs:867).The mismatch
git worktree addgitWorktreeCommandLimit, manager.go:28)The deadline was a coin flip, which is why it failed sometimes rather than always.
It corrupts, not just reports
When the deadline fires the client drops the socket, the runtime's request context is cancelled, and
exec.CommandContextkills git part-way through. Measured: git died at 301ms with 1673 of 7246 files written.What survives:
lockedwith reasoninitializingNeither
git worktree prunenorgit worktree remove --forceclears it — the app's own removal passes a single--force. The next attempt then fails witha branch named 'x' already exists, a stranger error than the first.Why not a retry
The transport already refuses to replay non-GET requests, correctly: writes may have landed. And a replay would inherit the same 1500ms. The bug is the deadline.
Removal gets the same budget — timing out there abandons a half-removed worktree for the same reason.
The sibling calls in this file already do this: clone passes ten minutes, create-on-host sixty seconds. Worktree create and delete were the two that ran git without saying so.
How this was found, and what I got wrong first
I searched
apps/,packages/andruntime/for the error string, concluded it was not in the repo, and guessed at a non-blocking-socket EAGAIN that needed a retry.Both halves were wrong. The string is in
native/— a directory I never searched — and the cause is a deadline, not a missing retry. An adversarial review of that conclusion is what turned it up.Verification
pnpm --filter @pebble/desktop typecheck && vitest run— the command the release build runsoxlint— clean🤖 Generated with Claude Code