From 2cf324e662f9197705e435eb6ae4e7c4665014dc Mon Sep 17 00:00:00 2001 From: CreatorGhost Date: Sun, 12 Jul 2026 00:47:15 +0530 Subject: [PATCH 1/2] fix(snapshot): fail closed when git check-ignore errors Ported from upstream anomalyco/opencode#36357. --- packages/opencode/src/snapshot/index.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 4da9bc3ca864..a91379bef795 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -120,7 +120,16 @@ const layer: Layer.Layer() + // git check-ignore: 0 = some ignored, 1 = none ignored; any other code is an error. + // Fail closed: treat every candidate as ignored so gitignored files cannot leak into + // snapshot patches and later be restored or deleted by /undo (see #28033). + if (check.code !== 0 && check.code !== 1) { + yield* Effect.logWarning("git check-ignore failed; treating all candidates as ignored", { + code: check.code, + stderr: check.stderr, + }) + return new Set(files) + } return new Set( check.text .split("\0") From bafe8dd534514bc178540ecb95f063d4bddbfb78 Mon Sep 17 00:00:00 2001 From: CreatorGhost Date: Sun, 12 Jul 2026 07:52:38 +0530 Subject: [PATCH 2/2] fix(snapshot): use distinct sentinel for git spawn failures Addresses CodeRabbit Major finding on the #36357 port: spawn failures no longer masquerade as exit code 1, so check-ignore fail-closed cannot be bypassed by a failed git invocation. --- packages/opencode/src/snapshot/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index a91379bef795..f7b2e5608466 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -92,7 +92,10 @@ const layer: Layer.Layer Effect.succeed({ - code: ChildProcessSpawner.ExitCode(1), + // -1 is a sentinel for "git failed to run at all". Callers that give + // exit code 1 a meaning (check-ignore: "none ignored") must not see + // spawn failures as code 1, otherwise fail-closed paths get bypassed. + code: ChildProcessSpawner.ExitCode(-1), text: "", stderr: err instanceof Error ? err.message : String(err), }),