Skip to content

fix(gitstate): fetch fresh and retry so a rejected push can't silently lose a baseline#19

Merged
pedromvgomes merged 1 commit into
mainfrom
feature/sundowner-twilight
Jul 13, 2026
Merged

fix(gitstate): fetch fresh and retry so a rejected push can't silently lose a baseline#19
pedromvgomes merged 1 commit into
mainfrom
feature/sundowner-twilight

Conversation

@pedromvgomes

Copy link
Copy Markdown
Contributor

Problem

Despite #18 (record the baseline when running on main), wardnet PRs still hit coverage cache misses — see wardnet/wardnet#888. The main-push run for merge-base 9f10325f did measure and commit a baseline, but the push was rejected and silently discarded:

! [rejected]  bulwark-state-staging-... -> bulwark-state (fetch first)
error: failed to push some refs to 'https://github.com/wardnet/wardnet'
recorded coverage baseline for 9f10325f...: rust: 85.8%, typescript: 93.9%   ← printed anyway

WriteBaseline staged its commit on the local origin/bulwark-state tracking ref, last updated by the job's checkout ~8 minutes earlier. wardnet's CI is busy enough that a concurrent run advances the remote branch inside that window, so the push is non-fast-forward — and the rejection was swallowed (return nil), a "best-effort" policy written for the PR-side cache-miss path but fatal to the record-on-main path, where the push is the point. inforge never hits this because its bulwark-state sees far less concurrent traffic.

Fix

  • Each attempt fetches the fresh remote ref immediately before creating the staging worktree.
  • A rejected push is retried (up to 3 attempts) from the re-fetched ref.
  • If the fetched branch already carries the exact content (a concurrent run recorded the same SHA), that's success — no empty commit, no error. Differing content (e.g. healing a poisoned {} entry) still overwrites.
  • A push that never lands is returned as an error; cmd/bulwark/coverage.go already downgrades it to a failed to record warning, so the misleading "recorded" line disappears without failing the build.

The new stale-ref test also surfaced a latent data race under -race: executil wired the child's stdout and stderr into the same capture buffer from two os/exec copy goroutines. The shared buffer is now mutex-guarded, with its own regression test.

Testing

TDD: both gitstate tests reproduced the exact failures RED first (stale-ref push silently lost; rejected push returning nil), confirmed RED again with the fix reverted, then GREEN. go build ./..., go test -race ./..., and golangci-lint run ./... all clean.

Merge Commit Message

fix(gitstate): fetch fresh and retry so a rejected baseline push can't be silently lost; sync executil's shared capture buffer

https://claude.ai/code/session_01WE4fbpPvaMDfXeQW2zoBzh

…y lose a baseline

WriteBaseline staged its commit on the local origin/bulwark-state tracking
ref, which is as stale as the job's checkout — wardnet's scan runs for ~8
minutes between the two, and any concurrent run that lands a baseline in that
window advances the remote. The resulting non-fast-forward push was rejected
AND swallowed (return nil), so the record-on-main path printed "recorded
coverage baseline" while the baseline was in fact lost, and every PR against
that main commit re-hit a cache miss (wardnet/wardnet#888).

Each attempt now fetches the fresh remote ref immediately before staging,
retries a rejected push (up to 3 attempts), treats already-identical remote
content as success, and returns an error when the push never lands so the
caller reports "failed to record" instead of "recorded".

The new stale-ref test also surfaced a latent data race under -race:
executil wired stdout and stderr into the same capture buffer from two
os/exec copy goroutines. The shared buffer is now mutex-guarded.

Claude-Session: https://claude.ai/code/session_01WE4fbpPvaMDfXeQW2zoBzh
@pedromvgomes
pedromvgomes merged commit d773591 into main Jul 13, 2026
4 checks passed
@pedromvgomes
pedromvgomes deleted the feature/sundowner-twilight branch July 13, 2026 13:14
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