From c95049951ec1c0342c6f927973e2c1e1e3f674e0 Mon Sep 17 00:00:00 2001 From: operator Date: Mon, 7 Sep 2026 10:49:14 +0900 Subject: [PATCH] the shadow snapshot was measuring git's background work, not the command's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `check (24)` failed on #866 with `expected [ …(39) ] to deeply equal [ …(40) ]` in `capture --shadow > runs prepare and verify against history without changing the worktree or .git`. The diff named one entry: - file .git/objects/maintenance.lock e3b0c442...b7852b855 `e3b0c442...b7852b855` is the SHA-256 of the empty string, so the missing entry is an empty lock file that git's auto-maintenance created before the `before` snapshot and removed before the `after` one. Nothing the command did produced or consumed it. The obvious repair -- filter `*.lock` out of `byteSnapshot` -- is the wrong one. That snapshot is exhaustive precisely because it is the evidence that `capture --shadow` is read-only, and a filter is a place a real stray file can hide. So the fixture denies git the background work instead: `gc.auto 0` and `maintenance.auto false` on the temporary repository leave nothing transient for the snapshot to disagree about, and the snapshot stays total. Record-Id: r-shadowsnapshotlock Provenance: authored Certainty: firm Blast: local Undo: easy Ruled-out: filtering lock files out of byteSnapshot | it makes the snapshot non-exhaustive, and the snapshot's exhaustiveness is the whole assertion -- a stray file written by the command would then be able to hide behind the same filter Ruled-out: retrying the flake and moving on | the trigger is git's auto-maintenance heuristic, which fires on repository state rather than on chance, so it returns on the next branch that reaches the same threshold Limit: this fixture only; other suites build repositories through createTestRepo, which does not disable maintenance either, and would show the same shape if any of them ever compared .git byte for byte Verified: test/capture-shadow.test.ts passes 3/3 locally after the change; the failing CI diff named exactly one entry and it was the maintenance lock Unverified: the failure was observed once, on one runner; this removes the mechanism rather than demonstrating the mechanism's absence over repeated runs --- test/capture-shadow.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/capture-shadow.test.ts b/test/capture-shadow.test.ts index 6a44d41c..dc74fe52 100644 --- a/test/capture-shadow.test.ts +++ b/test/capture-shadow.test.ts @@ -81,6 +81,17 @@ const makeRepository = (): { cwd: string; since: string; secret: string } => { git(cwd, ['init', '--quiet']); git(cwd, ['config', 'user.email', 'shadow@example.test']); git(cwd, ['config', 'user.name', 'Shadow Test']); + // `byteSnapshot` below is exhaustive on purpose -- it is the evidence that + // `capture --shadow` touched nothing. That makes it sensitive to files whose + // lifetime belongs to git rather than to the code under test: a run on CI + // (2026-09-07, run 34073112868) captured `.git/objects/maintenance.lock` in + // `before` and found it gone in `after`, and the empty-file digest + // e3b0c442...b7852b855 in the diff is what identified it. Filtering the + // snapshot would have been the wrong repair, because then the snapshot would + // stop being exhaustive and a real stray file could hide behind the filter. + // Deny git the background work instead, so there is nothing transient to see. + git(cwd, ['config', 'gc.auto', '0']); + git(cwd, ['config', 'maintenance.auto', 'false']); writeFileSync(join(cwd, 'README.md'), '# fixture\n'); commit(cwd, 'initial fixture'); const since = git(cwd, ['rev-parse', 'HEAD']).trim();