Skip to content

test(ci): pin required status contexts to the jobs that report them - #1923

Open
seonghobae wants to merge 8 commits into
mainfrom
ci/pin-required-status-contexts
Open

test(ci): pin required status contexts to the jobs that report them#1923
seonghobae wants to merge 8 commits into
mainfrom
ci/pin-required-status-contexts

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

.github is excluded from the organization required-workflow ruleset (repository_name.exclude lists it), so its default branch is guarded by classic branch protection with twelve named required status contexts. GitHub matches a context on the check-run name — a job's name: when it has one, and the job id otherwise.

That makes renaming such a job a repository-wide outage rather than a local edit. Branch protection keeps waiting for a context nothing will ever report, so every pull request stays blocked with no failing check to point at. Nothing in this suite pinned these names.

Why now

.github/workflows/ is under active consolidation — 21 consolidation/coalescing commits between 2026-09-01 and 2026-09-05 (#1826 OSV/Scorecard, Gitleaks, Noema/OpenCode/Strix bootups, SBOM attestation, empty-PR cleanup, head coalescing folded into the scheduler). Folding jobs together is precisely the edit that renames or removes them.

The drift this guards against is not hypothetical — it already exists: job id opencode-review-target reports the context opencode-review. Renaming only the name: breaks protection while the job id still looks correct.

What it checks

Each of the twelve live contexts is pinned to the workflow whose job reports it, accepting either spelling GitHub accepts:

context reported via
opencode-review, coverage-evidence, required-workflow-bootstrap, noema-review, Detect CodeQL languages explicit name:
trivy-fs, scorecard, dependency-review, scan-pr-queue, osv-scan job id (no name: declared)
CodeQL compatibility analysis (actions|python) matrix template (${{ matrix.language }})

That split is worth noting: four of these jobs declare no name: at all, so their context is the YAML key. A guard that only looked for name: would pass while leaving them unprotected.

Verification

The test failed on unmodified main on the first attempt — my initial mapping assumed every context came from a name: line, and the four id-derived ones exposed that. Fixed, then verified in both directions:

  • green on unmodified main
  • fails when a job with an explicit name: is renamed (opencode-reviewopencode-review-consolidated)
  • fails when a job identified only by its id is renamed (trivy-fstrivy-filesystem)

Both negative cases matter, because a guard that passes today but wouldn't catch the rename it exists to catch is worse than none. Full suite: 2886 passed, 1 skipped, 21 subtests. interrogate: 100%.

If a context is ever deliberately retired, update branch protection first, then this test — changing the test alone re-arms the outage. The docstring says so.

🤖 Generated with Claude Code

.github is excluded from the organization required-workflow ruleset, so its
default branch runs classic branch protection with twelve named required status
contexts. GitHub matches those on the check-run name, which comes from a job's
name: when it has one and from the job id otherwise.

Renaming or folding away such a job is therefore a repository-wide outage rather
than a local edit: protection keeps waiting for a context nothing will report, so
every pull request stays blocked with no failing check to point at. Nothing in the
suite pinned these names, and the two identifiers can drift apart -- they already
have, since job id opencode-review-target reports the context opencode-review.

The hazard is live because .github/workflows/ is under active consolidation (21
consolidation/coalescing commits between 2026-09-01 and 2026-09-05), and folding
jobs together is exactly the edit that renames them.

Verified the guard fails for both spellings: renaming an explicit name: and
renaming a job id each trip it, and it is green on unmodified main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 33 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: f81a5244-3926-45e8-8f13-3e3d0b640d0f

📥 Commits

Reviewing files that changed from the base of the PR and between 4302463 and dc146b4.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • tests/test_branch_protection_required_context_jobs.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…names

A sweep of branch protection across all 76 organization repositories found 13
using classic protection, several of which pin the job names these central
workflows declare: opencode-review and coverage-evidence are each required by 7
repositories, strix by 5, scan-pr-queue by 4, required-workflow-bootstrap by 3,
and coverage-source-tree by 2. strix and coverage-source-tree are required by
siblings but not by .github, so pinning only this repository's own contexts left
them unguarded -- renaming either blocks every pull request in those repositories.
admit-current-head is required by none, so it stays out.

The check also had a false negative. It accepted either a `name:` match or a job
id match, so renaming a job's `name:` passed whenever the id still matched, even
though GitHub names the check run after `name:` when one is present. It now
resolves each job's effective check-run name instead. Verified by mutation:
renaming a job id, and renaming a `name:` on a job that also has an id, are both
caught -- the latter was not caught before this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@seonghobae

Copy link
Copy Markdown
Contributor Author

Live proof, found the same day this PR was opened. Sweeping branch protection across all 76 organization repositories turned up two that already hit exactly this failure, both from consolidation commits merged on 2026-09-04:

repo commit what happened result
contextual-orchestrator f16bd35 (#1054) renamed 4 jobs (Hypothesis property tests -> Property and coverage-guided fuzzing, ...) protection still requires the old names; every post-#1054 PR blocked -- ContextualWisdomLab/contextual-orchestrator#1079
bandscope 314ddea (#1165) removed codeql.yml protection still requires Analyze (python) / Analyze (javascript-typescript); default setup not-configured -- ContextualWisdomLab/bandscope#1170

Neither repository has a failing check to point at. Both look like queue congestion from the outside. This is the failure this test exists to make loud.

The sweep also drove the two additions in the second commit here: strix (required by 5 sibling repositories) and coverage-source-tree (by 2) are not in .github's own protection, so pinning only this repository's contexts would have left them unguarded.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Merged origin/main into this branch at 56c04764 to clear the dirty state. Sole conflict was CHANGELOG.md -- #1937, #1926 and #1922 all prepended entries after this branch did; resolved keep-both (their three sections, this PR's section on top), zero markers left. Diff against main is unchanged: the guard test plus its CHANGELOG section, nothing else.

Gates on the merged head: 2896 passed, 1 skipped, 21 subtests; interrogate 100%. The two positive controls (renaming a name:, renaming a job id) were re-verified before the earlier push and the merge touched neither the test nor any workflow file.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Merged origin/main at f2f91b806 (#1939, round-robin catalog fill) into this branch → head 0eb9144b. Two reasons, both from the post-#1939 recovery note: the required strix/noema runs bind workflow_sha at creation, so a re-run of a failed job would execute the pre-#1939 sidecar — only a new event binds the current one; and this branch had gone dirty against that same commit. Sole conflict was CHANGELOG.md again (#1939 prepended a section); resolved keep-both, this PR's section on top, zero markers. Diff against main is unchanged: the guard test plus its CHANGELOG section.

Gates on 0eb9144b: guard 3 passed; 2899 passed, 1 skipped, 21 subtests; interrogate 100%.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Merged origin/main at fe827e13 (#1943, #1944, #1945) into this branch → head bbb49edc. Third CHANGELOG-only conflict on this PR in ~24h — each round is other PRs prepending entries at the same anchor. Resolved keep-both again (their sections, this PR's on top), --diff-filter=U confirmed CHANGELOG was the sole conflicted file, zero markers. Diff against main is unchanged: the guard test plus its CHANGELOG section.

Gates on bbb49edc: guard 3 passed; 2907 passed, 1 skipped, 21 subtests; interrogate 100%.

One note for anyone reading the PR state: GitHub reported mergeable_state=unknown for over 30 minutes before this — it was masking dirty. A fresh GET pair settled it, and a local git merge-tree --write-tree origin/main <head> confirmed the conflict independently of GitHub's lazy compute.

Copy link
Copy Markdown
Contributor Author

BandScope now provides a live consumer-repository instance of the same required-context drift class, but across the organization required-workflow boundary rather than inside .github itself.

Fresh 2026-09-06 evidence: protected ContextualWisdomLab/bandscope@develop is still 314ddeae7b775a4957594b599358c8255617eb2e and requires 14 contexts, including retired Analyze (javascript-typescript) and Analyze (python). #1165 removed the repository codeql.yml. The current central required CodeQL PR source instead emits CodeQL compatibility analysis (${{ matrix.language }}); BandScope #866 run 34001982927 is currently injected from that required workflow and begins with Detect CodeQL languages.

I updated BandScope #1172 with the exact repair contract: preserve CodeQL coverage, migrate the two stale required contexts to CodeQL compatibility analysis (javascript-typescript) / (python), do not restore a duplicate repository scanner or manufacture a pass-through success, and verify both names on an unchanged exact PR head before closure.

For this guard lane, please treat required-workflow producer names plus consumer branch/ruleset required-context names as one rollout invariant. A central workflow can remain perfectly self-consistent while consumer repositories are permanently blocked on retired contexts.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Merged origin/main at efb89269 (sidecar pin bump) into this branch → head 386db983. Fourth CHANGELOG-only conflict on this PR; same cause (a prepended entry at the shared anchor), same keep-both resolution, --diff-filter=U confirmed CHANGELOG was the sole conflicted file, zero markers. Diff against main is unchanged: the guard test plus its CHANGELOG section.

Gates on 386db983: guard 3 passed; 2907 passed, 1 skipped, 21 subtests; interrogate 100%.

GitHub reported mergeable_state=unknown for this round too (third time it has masked dirty here); a local git merge-tree --write-tree origin/main <head> showed the conflict at once.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Merged origin/main at fb2ae81d (17 commits: #1947, #1949, #1950 sidecar preflight fixes plus ADR-0029 docs) into this branch → head 5c9920a9. Fifth CHANGELOG-only conflict on this PR; same keep-both resolution, --diff-filter=U confirmed CHANGELOG was the sole conflicted file, zero markers. Diff against main is unchanged: the guard test plus its CHANGELOG section.

Because 17 commits is a wide window for the exact hazard this test guards, I checked the 13 pinned contexts statically against origin/main's workflow blobs before merging: all 13 still declared, none of the 17 commits touched a workflow file.

Gates on 5c9920a9: guard 3 passed; 2923 passed, 1 skipped, 21 subtests; interrogate 100%.

@seonghobae

Copy link
Copy Markdown
Contributor Author

No PR-side fix exists for the failing CodeQL compatibility analysis contexts, so I am not pushing.

Both (python) and (actions) on head 5c9920a9 fail identically — CodeQL scan dispatched. The dispatch workflow will rerun this exact failed CodeQL job after publishing its terminal verdict. — and Detect CodeQL languages succeeded. The compatibility job dispatches the scan and fails closed waiting for a verdict that never returns, because the dispatch handler is rejected at the dispatcher allowlist (#1929). Nothing in this PR (a test file plus its CHANGELOG section) touches that path; a push would only re-fan the same closed failure.

What is in flight: the allowlist variable's updated_at moved to 2026-09-06T07:01:34Z today (it had read 2026-07-16 on every prior check), and two codeql-scan-dispatch runs for this exact head were created at 07:02:19Z and 07:02:55Z — 34018067448, 34018093596 — one minute later. They are queued. Their outcome settles both whether the allowlist actually changed and whether these two contexts can go green on this head without a push.

@seonghobae

Copy link
Copy Markdown
Contributor Author

noema-review on head 5c9920a9: no PR-side fix, and I am not pushing.

Run 34014209957 (created 2026-09-06 05:31Z, pull_request_target, so it bound the post-#1939 sidecar from main): sidecar provisioned in 10.5 min ("healthz and provider-route preflight confirmed after 393s"), then the verdict phase ran 26.9 min and ended:

##[error]Noema gateway transport failed: HTTPError: HTTP Error 502: Bad Gateway;
         caller attempts=1, duration=1610.3s, phase=response_error, served_model=deepseek-ai/deepseek-v4-flash-0731

Two things the log establishes: the pool is no longer NVIDIA-only — it tried OpenRouter routes (openrouter_cohere_north_mini_code_free, openrouter_dots_studio_…), each failing fast as HTTPError transient — so #1939 is in effect; and served_model is the last route the walk reached, not the culprit (1610 s is roughly six candidates at the per-candidate budget). This is a gateway-side failure, not anything a test file plus a CHANGELOG section can influence.

It is also systemic, not this PR: noema-review runs created after #1939 merged (2026-09-05T17:25Z) stand at 12 success / 45 failure / 47 cancelled — 79% of completed runs failing. A push here would re-roll a non-deterministic gateway failure at queue cost; it is not a fix. Reported to the gateway lane with the cause breakdown.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Merged origin/main at 43024633 (#1953, strix sandbox-bootstrap fix) into this branch → head dc146b4c, per the Autofix merge-conflict event. Sixth CHANGELOG-only conflict on this PR; keep-both, --diff-filter=U confirmed CHANGELOG was the sole conflicted file, zero markers. Diff against main is unchanged: the guard test plus its CHANGELOG section. All 13 pinned contexts verified still declared on the new main before merging.

On the opencode-review failure that arrived with the same event: No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head — the review dispatch never returns a verdict because the handler is rejected at the dispatcher allowlist (#1929). No PR-side fix exists; this push re-fans it regardless. It also makes this PR's new runs the first post-#1953 review specimen from this head — outcome will go to #1939.

Gates on dc146b4c: guard 3 passed; 2931 passed, 1 skipped, 21 subtests; interrogate 100%.

@seonghobae

Copy link
Copy Markdown
Contributor Author

The two re-dispatches for this head created after the allowlist variable was touched have now completed: 34018067448 rejected at the dispatcher allowlist (step 3, actor=opencode-agent[bot]). The edit did not add the app identity, so both CodeQL compatibility analysis contexts stay red on this head with no PR-side action available. Evidence and the reading trap (the unexpanded %s echo line) are on #1929.

Copy link
Copy Markdown
Contributor Author

BandScope consumer evidence is still live on 2026-09-06 and the failure mode is now isolated more precisely in ContextualWisdomLab/bandscope#1172.

Fresh protected develop@314ddeae7b775a4957594b599358c8255617eb2e still requires 14 contexts, including retired Analyze (javascript-typescript) and Analyze (python). #1165 removed the repository PR CodeQL workflow that emitted those names. The centrally injected CodeQL PR producer reports CodeQL compatibility analysis (${{ matrix.language }}) and starts with Detect CodeQL languages; restoring a repository-local scanner would therefore duplicate the canonical owner rather than repair the rollout.

For required-workflow consumers, the prevention contract here should cover the consumer protection mapping as well as central workflow job names: a central producer rename/removal and each protected repository's required-context migration must be one rollout. BandScope closure requires migrating the stale contexts to CodeQL compatibility analysis (javascript-typescript) and (python), observing terminal central success on an unchanged PR head, and only then removing the old names. No pass-through success job or gate weakening.

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