fix(ci): retry track record snapshot push races - #354
Open
i-xtsu-sixyou-ken-mei wants to merge 2 commits into
Open
fix(ci): retry track record snapshot push races#354i-xtsu-sixyou-ken-mei wants to merge 2 commits into
i-xtsu-sixyou-ken-mei wants to merge 2 commits into
Conversation
i-xtsu-sixyou-ken-mei
force-pushed
the
fix/track-record-snapshot-push-race
branch
2 times, most recently
from
September 2, 2026 14:31
ab558d4 to
ebc590c
Compare
Use GITHUB_REF_NAME instead of hard-coded main so workflow_dispatch on a feature branch does not push to main. Add --autostash so rebase tolerates tracked design-token outputs left dirty by pnpm build packages (rebase refuses a dirty tree even when already up to date). Wrap rebase in if ! with ::error:: instead of bare errexit, and use neutral push-rejected wording. Update runbook to describe rebase-then-fast-forward push with bounded retries. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Intent
Prevent the daily track-record snapshot workflow from failing after a successful publish/verify when the target branch advances before the workflow pushes its generated
track-record-meta.jsoncommit. Harden the push against bothmainraces andworkflow_dispatchusage, plus latent dirty-tree failures frompnpm build packages.Context
Run 33587728101 (2026-09-02) generated, pinned, verified, and committed the new snapshot metadata locally, then failed with a non-fast-forward rejection:
main -> main (fetch first)The workflow already serializes
track-record-snapshotruns with its own concurrency group, but that does not prevent PR merges or other automation from advancing the branch while this multi-minute job is running. The original final step committed against the checkout SHA and immediately ran a baregit push, so any concurrent branch update makes the push stale. Additionally,pnpm build packagesregenerates tracked design-token outputs (packages/design-tokens/dist/**,packages/design-tokens/src/generated/tokens.ts); a plaingit rebaserefuses a dirty tracked tree even when already up to date, which would turn a stale-generated-file state onmaininto a daily cron failure.CI red (
code-quality/@zapengine/control-center#dup:check) on the first PR build was pre-existing — the merge was built against a stale base (00bef131); the clones were fixed onmainby PR #353. After rebase onto603ba375, all 11 checks onebc590c1are green.Scope
origin/$branchafter creating the generated metadata commit (branch =$GITHUB_REF_NAME:mainonschedule, the chosen branch onworkflow_dispatch).origin/$branchwith--autostashbefore pushing (tolerates dirty tracked design-token outputs; stash never includes untracked.track-record/).::error::instead of being overwritten — do notgit rebase --abort(runner is ephemeral;--abortwould exit 128 when rebase never started and hide the diagnostic).docs/track-record-pipeline.mdrunbook: Flow ends with rebase-then-fast-forward push; Failure handling scopes "no retry" to generate/publish/verify scripts and documents the push retry + conflict policy.Out of scope
backtest-refresh.yml:106/distribution-snapshot.yml:104still end with baregit push— same latent race, kept out of scope here; follow-up is a shared composite action (see Reviewer notes).Product contract / invariants
Affected invariant:
None. This only hardens the workflow's generated-meta commit delivery against concurrent branch updates.
Why:
The existing behavior intends to publish the verified snapshot metadata; the change preserves that behavior while removing repository-write and dirty-tree races.
Acceptance criteria
origin/$GITHUB_REF_NAMEbefore pushing (with--autostash).workflow_dispatchfrom a feature branch rebases onto and pushes to that branch, not hard-codedmain.pnpm build packagesdoes not block rebase when already up to date.::error::and does not overwrite the concurrent change.docs/track-record-pipeline.mdmatches the shipped behaviour.Implementation
.github/workflows/track-record-snapshot.yml(lines 74–95): replace the singlegit pushwith a bounded 3-attempt loop:Plus an explanatory comment about
--autostashcoveringpnpm build packagesside-effects.docs/track-record-pipeline.md: Flow last line → "commit the meta change, rebase it onto the latest branch tip, push fast-forward only (retried up to 3 times ...)"; Failure handling → only retry is the final push (fetch/rebase/push, 3 attempts), conflict fails closed, generate/publish/verify still have no retry.Verified non-issues:
bash -e {0}(errexit, no pipefail;if/!exempt;exit 0in loop ends step);git fetch origin <branch>updatesrefs/remotes/origin/<branch>under checkout@v7 refspec;fetch-depth: 0gives full history; empty commit after rebase is dropped and push no-ops with exit 0; credentials viahttp.extraheader+contents: write; no husky hooks at runtime (HUSKY=0at install); cron line unchanged sopnpm lint schedulesparity holds; workflow YAML outside prettier's scope.Contract alignment
AGENTS.mdremains accurate.Validation
yq -r '.jobs.snapshot.steps[-1].run' .github/workflows/track-record-snapshot.yml > step.sh→bash -n step.shPASS;yqparse of whole file PASS (no actionlint/prettier locally).origin, bot clone + writer clone, extracted block underbash -ewithGITHUB_REF_NAME=main, after samegit config/add/commitpreamble):mainafter bot commit) → rebase + push succeed,origin/mainlinear with bot commit on top, exit 0 ✓::error::Rebase onto origin/main failed ...path, exit 1,origin/mainunchanged ✓git rebaserefuses (proves finding 3);--autostashvariant proceeds and pushes ✓GITHUB_REF_NAME=release/xstyle branch name → refspecs resolve ✓bash scripts/check-schedules-registry.sh— PASS ("OK: GitHub Actions cron schedules match registry").pnpm build packagesin worktree thengit status --porcelain --untracked-files=no— tree clean today;--autostashretained as latent protection.docs/track-record-pipeline.mdagainst final workflow — PASS.ebc590c1had 11/11 green; new head625bdf7bpushed,gh pr checks 354 --watchexpected all green (see Handoff).Validation gaps
The hosted concurrent-write race itself can only be exercised by a live GitHub Actions run while another writer advances the branch; deterministic simulation above covers the local git mechanics. The next scheduled/manual workflow run will exercise the change in the hosted environment.
Known unrelated failures
The Node 20 deprecation warning shown during post-job cleanup in the reported run is unrelated to the non-fast-forward push failure and is not addressed here.
Reviewer notes
Please focus on the final
Commit updated track record metastep. The intended safety property is that the bot only ever fast-forwards; it refreshes/rebases its own generated commit rather than overwriting concurrent repository changes.Follow-up (out of scope): same latent race exists in sibling workflows
backtest-refresh.yml:106anddistribution-snapshot.yml:104(both end with baregit push; only cron offsets separate them). A shared composite action that encapsulates fetch → rebase --autostash → push HEAD:$branch with bounded retries is the clean fix for all three.