Uh oh!
There was an error while loading. Please reload this page.
ci(2212): the fixtures drift check must fail when it cannot run - #536
Conversation
`Backend fixtures drift check` is being armed as a required context
(backend#2212). Its activation-phase fail-open has to go first: when
BACKEND_CONTRACTS_TOKEN was unreadable the step printed a warning and exited 0,
so a check that never executed reported as a passing one. Inert-not-red was the
right call while the secret did not exist; the secret has existed since
2026-08-05, and once the context is required an exit-0-when-unable is strictly
worse than an advisory guard, because it also looks solved (backend#2183).
`cli` is PUBLIC, so the two reasons the token can be missing are different
things and the step now splits three ways:
token present -> run the check
absent, fork PR -> FAIL. GitHub withholds repo secrets from forks by
design, so the check genuinely cannot run. A maintainer
verifies internal/api/testdata/*.json by hand and
applies `skip-fixtures-drift` -- a permanent artifact on
the PR, the same model as skip-fr-gate. Silently passing
forks would fail open on exactly the contributions that
deserve the most scrutiny.
absent, same-repo -> FAIL. Rotated, removed or expired: a misconfiguration
that used to read as a clean run.
`types: [.., labeled, unlabeled]` added to the pull_request trigger, because
without them the default opened/synchronize/reopened means applying the override
label changes nothing until the next push -- the same defect Bugbot caught on
version-bump-gate-caller.yml's skip-version-gate.
Every ${{ }} goes through env:, none into the run: body.
Mutation-proved, all five paths, by running the step body against a stubbed
sync script:
override label present exit 0 (OVERRIDDEN warning)
token present exit 0 (real check ran)
token absent, fork PR exit 1 (could not run)
token absent, same-repo exit 1 (secret missing)
token present, script reports drift exit 3 (exec propagates the status)
The last one matters: `exec` replaces the shell, so a real drift failure still
fails the step rather than being swallowed.
Label `skip-fixtures-drift` created on this repo.
Refs tracebloc/backend#2212
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>LukasWodka
commented
Aug 20, 2026
bugbot run |
Uh oh!
There was an error while loading. Please reload this page.
I claimed in chat that this PR was unaffected by the Dependabot finding on averaging-service#367. Wrong, and this repo is the worse case of the two. Dependabot branches live in THIS repo, not a fork, so `github.event.pull_request.head.repo.fork` is FALSE on them -- verified on the real #530: head.repo.fork=false, head.repo.full_name=tracebloc/cli. Their runs still receive only Dependabot-scoped secrets, so BACKEND_CONTRACTS_TOKEN is empty. Under the previous commit that combination landed in the "absent, same-repo -> misconfiguration -> FAIL" branch, which would have blocked every Dependabot PR once the context is armed. Not theoretical: this repo has 4 Dependabot PRs, #530 is OPEN right now, and it currently reports `Backend fixtures drift check: success` -- the fail-open passing vacuously on a live PR today. So Dependabot gets a fourth branch, passing with a ::notice::. Safe for the same structural reason as averaging-service#367, via a different always-running guard: a dependency bump cannot alter internal/api/testdata/*.json, and if it did, internal/api/contracts_test.go replays every fixture through the real decode paths under the REQUIRED `Test` check with no token. Drift against the pinned backend ref is re-checked by the push run on develop/main, where Actions secrets are available. Mutation-proved, all five: Dependabot PR (fork=false, no token) exit 0 (notice: deferred) fork PR, no token exit 1 (could not run) human same-repo, no token exit 1 (secret missing) token present exit 0 (real check ran) override label exit 0 (OVERRIDDEN warning) PR_AUTHOR uses github.event.pull_request.user.login, not github.actor, so it stays correct across re-runs. Refs tracebloc/backend#2212 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LukasWodka
commented
Aug 20, 2026
Self-correction, pushed. I said in chat that this PR was unaffected by the Dependabot finding on tracebloc/averaging-service#367. Wrong — and Dependabot branches live in this repo, not a fork, so Their runs still get only Dependabot-scoped secrets, so Not theoretical: this repo has 4 Dependabot PRs, #530 is open right now, and it currently reports Fixed with a fourth branch that passes with a All five branches mutation-proved: |
LukasWodka
commented
Aug 20, 2026
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3e3429c. Configure here.
saqlainsyed007
left a comment
There was a problem hiding this comment.
Correctness review — approving.
Arms the backend-fixtures-drift check for use as a required context by replacing the activation-era "warn + exit 0" fail-open with fail-closed behavior. Traced every path:
- No unintended fail-open remains. The only
exit 0branches are the explicitskip-fixtures-driftlabel, the real check viaexec(a drift/non-zero exit still fails the step), and the Dependabot deferral. Anything else falls through toexit 1— no fall-through green. execfails closed — ifsync-backend-fixtures.shwere missing/non-executable, a non-interactive bash shell exits non-zero rather than continuing to a passing branch.- Branch ordering is right — Dependabot (
head.repo.fork=false, no Actions secret) is handled before the fork/misconfig split, so weekly bumps aren't blocked, and the deferral is safe because a dependency bump can't touchinternal/api/testdata/*.json. set -uo pipefailis safe — every referenced var is declared inenv:, sopush/workflow_dispatchruns (nullpull_requestcontext) resolve to empty/falsewithout tripping-u.- Security — all
${{ }}interpolation goes throughenv:, none into therun:body; thedependabot[bot]login can't be spoofed from a fork, and forks are caught byIS_FORKregardless. types: [..., labeled, unlabeled]correctly added so applying/removing the override label actually re-runs the gate.
The earlier Bugbot finding (Dependabot PRs landing in the misconfig branch) is already fixed by the fourth branch and its thread is resolved. CI is green, including the token-present path of this check on this same-repo PR. LGTM.
Uh oh!
There was an error while loading. Please reload this page.
saadqbal
commented
Aug 20, 2026
/fr-pass CI/tooling-only change, no product surface. Verified by this repo's own checks; the staging journey does not exercise workflow code. |
Why
Backend fixtures drift checkis being armed as a required context (tracebloc/backend#2212). Its activation-phase fail-open has to go first.When
BACKEND_CONTRACTS_TOKENwas unreadable, the step printed a warning andexit 0— a check that never executed reported as a passing one. "Inert, not red" was the right call while the secret did not exist. It has existed since 2026-08-05, and once the context is required, exiting 0 when unable is strictly worse than an advisory guard, because it also looks solved. That is the shape that let a stale R8 manifest merge in backend#2183.cliis public, so this needs three branches, not twoThe two reasons the token can be missing are not the same thing:
exit 0+ warningskip-fixtures-driftexit 0+ warningGitHub withholds repo secrets from forks by design, so on a fork PR the check genuinely cannot run. Passing those silently would fail open on exactly the contributions that deserve the most scrutiny — so it fails, and a maintainer who has checked
internal/api/testdata/*.jsonby hand appliesskip-fixtures-drift. The label is a permanent artifact on the PR, the same model as the existingskip-fr-gate. Created on this repo with a matching description.The trap this repo has already hit once
types: [..., labeled, unlabeled]is added to thepull_requesttrigger. Without it the defaults areopened/synchronize/reopened, so applying the override label would change nothing until the next push — making the override look broken. That is precisely the defect Bugbot caught onversion-bump-gate-caller.yml'sskip-version-gate.Mutation proof
All five paths, running the actual step body against a stubbed sync script:
Plus the case that would be easy to break while "fixing" this one:
execreplaces the shell, so a real drift failure still fails the step rather than being swallowed by the wrapper.Security
Every
${{ }}goes throughenv:— none reaches therun:body.IS_FORKandOVERRIDEare workflow-context booleans, and the token stays a secret reference.Test plan
types, stepenvkeys and the absence of${{ }}inrun:all assertedRefs tracebloc/backend#2212
🤖 Generated with Claude Code
Note
Medium Risk
Changes required CI gate semantics and fork/Dependabot bypass paths; misconfiguration or missing override labels can block PRs, but avoids fail-open on a security-sensitive contract check.
Overview
Arms the Backend fixtures drift check for use as a required CI context by replacing the activation-era warn and exit 0 path when
BACKEND_CONTRACTS_TOKENis unreadable with fail-closed behavior, so a check that never ran cannot report green.The step now branches on token presence, PR source, and override: with a token it
execssync-backend-fixtures.sh --check(real drift failures still fail the job);skip-fixtures-driftexits 0 with a warning and leaves the label as the audit record; Dependabot PRs without the token pass with a notice; fork PRs and same-repo misconfiguration exit 1 with targeted errors. Workflow context (IS_FORK,PR_AUTHOR,OVERRIDE) is passed only viaenv:, not inline in the shell script.pull_requesttriggers now includelabeled/unlabeled(andready_for_review) so applying or removing the override label re-runs the gate without waiting for another push.Reviewed by Cursor Bugbot for commit 3e3429c. Bugbot is set up for automated code reviews on this repo. Configure here.