Skip to content

fix: the shadow snapshot was measuring git's background work - #867

Merged
MongLong0214 merged 1 commit into
mainfrom
fix/capture-shadow-maintenance-flake
Sep 7, 2026
Merged

fix: the shadow snapshot was measuring git's background work#867
MongLong0214 merged 1 commit into
mainfrom
fix/capture-shadow-maintenance-flake

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

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 array diff named exactly one entry:

- file .git/objects/maintenance.lock e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

e3b0c442…b7852b855 is the SHA-256 of the empty string, so the missing entry is an empty lock file created by git's auto-maintenance between the two snapshots. Nothing capture --shadow did produced or consumed it — the test was measuring git's background work.

Why not filter the snapshot

byteSnapshot is exhaustive on purpose: its exhaustiveness is the assertion that the command is read-only. A *.lock filter would make it non-exhaustive and give a real stray file somewhere to hide. So the fixture denies git the background work instead — gc.auto 0 and maintenance.auto false on the temporary repository — and the snapshot stays total.

Verified

  • test/capture-shadow.test.ts 3/3 locally
  • test/dogfood.test.ts 10/10 on this commit
  • test/ is not in SOURCE_INPUTS, so no canonical rebuild or manifest change is needed

Note

createTestRepo in test/git-fixtures.ts does not disable maintenance either. No other suite compares .git byte for byte today, so nothing else is exposed — but that is why this is fixed in the fixture rather than in the walker.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 1 commit in origin/main..c95049951ec1c0342c6f927973e2c1e1e3f674e0
Active constraints: 4 limits · 7 ruled-out · 2 warnings — from 4 records over 1 changed path

Active constraints for the paths this PR touches

Limits (4)

  • r-shadowsnapshotlock c950499 — 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
  • r-rebuildworknobodyreads 1a66b26 — the deadline is still only checked between batches and before the expensive half of one, so a late batch of 1024 commits can overshoot by whatever that batch costs -- bounded by one batch rather than by the whole scan. And an unsigned-mode index now carries '' where it carried git's verdict, so a reader wanting the cached status without turning signature mode on no longer gets it; none exists
  • r-unattshadow b7b532a — together the two features measure how often an unattended pipeline would have written, and remove the asking from the writing -- neither half can say whether what gets written is worth a reader's attention, so shadow's number for an unattended repository is a volume, not a value
  • r-shadow511 d093bef — shadow measures commits whose transcripts are gone, so its numbers describe the substitution of a committed message and patch for a transcript -- they say nothing about what capture would record over a live session, and no shadow output may be read as a pipeline baseline

Ruled out (7)

  • r-shadowsnapshotlock c950499 — 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
  • r-shadowsnapshotlock c950499 — 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
  • r-unattshadow b7b532a — keeping the unattended branch's inline prepare body | the shadow refactor exists so both entry points share one side-effect-free half, and two copies of the same hashing and policy logic would drift the first time either changed
  • r-unattshadow b7b532a — checking unattended consent in the live path only | the refusal for mode "off" already lives in the shared half of prepare, and a consent check that guards one door but not the other is no guard for the next entry point added
  • r-shadow511 d093bef — quoting the historical-run numbers | they measure a committed message substituted for a missing transcript, and a number from the wrong instrument becomes a baseline the moment anyone repeats it
  • r-shadow511 d093bef — backfilling records from shadow's output | the draft is an approximation no agent judgment ever stood behind, and publishing it as lore would launder a substitution into the thing lore exists to prevent
  • r-shadow511 d093bef — deleting the instrument because its first question failed | the failure belongs to history's missing transcript, not to the pipeline, and a live session supplies what the first run could not

Warnings (2)

  • r-unattshadow b7b532a (claim) — the consent check now lives in the shared half of prepare, so a third entry point inherits it -- but it fires only on a declaration, and shadow writes nothing because it routes around createPending and stage, not because it is exempted from consent
  • r-shadow511 d093bef (claim) — the draft adapter nominates only explicit decision language, so a silent commit may have had a live capture that simply chose different words -- a silence rate is the adapter's conservatism as much as history's

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

…and's

`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
@MongLong0214
MongLong0214 force-pushed the fix/capture-shadow-maintenance-flake branch from a2a5fde to c950499 Compare September 7, 2026 02:52
@MongLong0214
MongLong0214 merged commit 7fe3849 into main Sep 7, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant