fix(desktop): stabilize snapshot sidecar lifecycle#31283
Open
Hona wants to merge 4 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens snapshot capture and Desktop sidecar lifecycle handling by avoiding stale Git index locks during snapshot operations, ensuring early child-process failures don’t get masked by late stdin pipe errors, and preventing Desktop from keeping an unexpectedly-terminated sidecar marked as the active server.
Changes:
- Snapshot capture now uses a scoped temporary Git index (
GIT_INDEX_FILE) and snapshot diffs/patches compare tree hashes rather than relying on--cached. - Child-process spawning now settles/interrupts configured stdin before exposing exit, preserving original non-zero exit status/diagnostics over late pipe errors; tests added.
- Desktop sidecar lifecycle now tracks “expected” vs “unexpected” exits and clears the current server reference only when the current sidecar terminates unexpectedly; tests added.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/opencode/test/snapshot/snapshot.test.ts | Adds regression coverage for stale shared index.lock not blocking snapshot capture. |
| packages/opencode/src/snapshot/index.ts | Implements temp-index snapshot capture and updates patch/diff logic to compare tree hashes. |
| packages/desktop/src/main/sidecar-lifecycle.ts | Introduces a small lifecycle helper to classify expected vs unexpected sidecar exits. |
| packages/desktop/src/main/sidecar-lifecycle.test.ts | Adds unit coverage for lifecycle classification and “current sidecar” checks. |
| packages/desktop/src/main/server.ts | Exposes a sidecar exit settlement promise and marks stop-initiated exits as expected. |
| packages/desktop/src/main/index.ts | Clears the active server reference only on unexpected exit of the current sidecar. |
| packages/core/test/process/process.test.ts | Adds coverage ensuring early git failure isn’t replaced by a secondary stdin pipe error. |
| packages/core/test/effect/cross-spawn-spawner.test.ts | Adds coverage that stdin is settled before exit is observed. |
| packages/core/src/cross-spawn-spawner.ts | Changes exitCode behavior to settle stdin and preserve primary exit failures over pipe errors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+196
to
199
| const add = Effect.fnUntraced(function* (env?: Record<string, string>) { | ||
| yield* sync() | ||
| const [diff, other] = yield* Effect.all( | ||
| [ |
This was referenced Jun 8, 2026
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.
TLDR;
Problem
OpenCode snapshots use Git with a NUL-delimited path list written to child-process stdin. If the snapshot repository contains a stale
index.lock, Git exits before consuming that input. On Windows, the outstanding overlapped pipe write can then complete aswrite EOF; if stdin lifecycle finishes after process completion, that late socket error can terminate the Desktop sidecar instead of preserving Git's real exit status and lock diagnostic.The shared snapshot index also means the stale lock continues blocking later captures. This change isolates each capture in a scoped alternate index, persists its baseline as an immutable Git tree ref, and settles child stdin before exposing process completion.
Headless repro
Run with Node 24 and Git for Windows. This uses the same
git add --pathspec-from-file=- --pathspec-file-nulshape as snapshot capture:Before this change, the process terminates with the same failure observed in Desktop diagnostics:
Changelog
Fixed
Improved