chore(ci): bump standards pins fcb566cf → e977cc67, regenerating actions.lock in the same commit - #226
Conversation
Cures all three red checks on main, which share one cause: burble was pinned to a standards SHA predating two upstream fixes. governance / Actions lockfile verify \ both are the same wrapper defect, Workflow syntax and action locks / fixed by standards#946 scan / Hypatia Neurosymbolic Analysis severity list omitted "warn" Bumped, all to e977cc67: governance.yml:21, hypatia-scan.yml:25, scorecard.yml:20, secret-scanner.yml:21, spark-theatre-gate.yml:18 (reusable refs) workflow-safety.yml:46,54 (curl URLs) and both sha256sum digests recomputed from the fetched files: update-actions-lock.sh dd5b1308... -> 476027a9... estate-baseline.toml 2361fc7e... -> 877902ec... actions.lock is regenerated in the SAME commit. Bumping the YAML alone would recreate the desync that killed this repo at startup (#223): the stale SHA appears in the lock as five per-workflow list items and one dependency block. `gh actions-lock` cannot do this itself -- it is blind to job-level reusable-workflow refs (github/gh-actions-lock#129) and leaves the standards entries untouched -- so the entry was updated by hand, including its transitive uses: list, which the reusables moved: editorconfig-checker v2.2.0 -> v3.0.0 github/codeql-action v4.37.9 -> v4.38.1 Verified before pushing: scripts/check-lock-sync.sh rc=0 post-#946 wrapper --verify-local on this tree rc=0 (predicts step 6) no `uses: $/...` rewrites 0 reusable workflow_call contracts compatible both directions mirror.yml (pinned 4d104d32) is deliberately out of scope. Closes #225 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3hgXxWm6umMgZkjYyHnnm
Status: 2 reds left, both diagnosed, neither a defect in this PRCured by this PR ✅ — 1.
|
…it past #946 (#962) ## What this fixes The lock gate is staged from a **third pin**. There are not two pins in this system, there are three: 1. the caller's `uses: hyperpolymath/standards/...@<sha>` ref, 2. `actions.lock`'s record of that ref, and 3. **a SHA hardcoded inside `governance-reusable.yml`** for its own `actions/checkout` of the lock-gate tooling. Bumping a caller cannot reach the third one. A *called* reusable workflow has no reliable context exposing its own commit (`github.workflow_sha` resolves to the **caller's**), which is what forces the hardcode in the first place. `standards#946` fixed `scripts/update-actions-lock.sh` so advisory findings stop counting toward the blocking tally — and did not bump pin 3. So every caller kept being judged by the **pre-#946** verifier, including [`metadatastician/burble#226`](metadatastician/burble#226), which had bumped its own pin *specifically to pick that fix up* and still went red on `governance / Actions lockfile verify`. Measured, not inferred: the failing job logs `HEAD is now at 4f7f02c`, and the two wrappers disagree on the same tree — | wrapper staged from | `--verify-local` rc | `grep -c is_advisory_category` | |---|---|---| | `4f7f02ca` (what CI ran) | **1** | 0 | | `e977cc67` (post-#946) | **0** | present | ## Why no existing control caught it The pin's **shape** was already guarded, correctly: `tests/test_governance_reusable_shape.sh:63-64` asserts `ref: [0-9a-f]{40}` and refuses `ref: main`, scoped to the `actions-lock-verify` job. Its **currency** was guarded by nothing. A perfectly well-formed 40-hex SHA can point at stale tooling, and this one did for the whole life of #946. That is the guard/consumer trap in its plainest form: **the guard asks "is this 40 hex characters?", the consumer needs "does this contain today's verifier?"** The file already carried a `⚠ BUMP THIS whenever ... changes` comment. A comment is not a gate, and this PR is the difference. Separately, `scripts/tests/governance-reusable-contract-test.sh` bound its checkout assertions only to the step named `Checkout the pinned Standards policy helpers` — the **dupkey** step. The lock gate is a *different* step, `Checkout standards for the lock gate`, and the two share the nouns "checkout", "pinned" and "standards", so a name-match guard written for one proves nothing about the other. It now names the lock-gate step too. ## The predicate, and why it is not the obvious one The obvious assertion — *the pin contains the working tree's helpers* — **deadlocks**. A PR that edits a helper would have to pin to its own merge commit, which does not exist yet. Unsatisfiable-in-PR is the same failure class as a required check that can never report. So the assertion is: > the pinned commit must already contain everything on the **compare** ref, > path-scoped to the step's own `sparse-checkout:` list. - **`pull_request`** → compare is the PR's base SHA. A PR that edits a helper **passes** (its edit is not on base yet). A PR opened while `main` is *already* stale is **forced to bump**, and can, because the needed commit exists. - **`push` to `main`** → compare is `HEAD`. Red exactly when a helper change has just landed and the bump is owed; healed by the very next PR, which the `pull_request` run will not let through unbumped. Under this predicate, **#954 would have been forced to bump after #946 landed**, and burble#226 would have gone green on its first attempt. The pin is therefore **one change behind by construction**. That is inherent, it is acceptable, and the comment at the pin now says so rather than asking a human to remember. Comparison is **path-scoped**, so a rebase or any unrelated commit cannot fail it — only a real divergence in the staged tooling can. **Scope is read out of the step's own `sparse-checkout:` list, never hardcoded**, so adding a file to what the gate stages automatically extends what the guard protects. A hardcoded list here would itself be a guard asking a different question than its consumer. ## Verification `scripts/tests/check-lock-gate-pin-freshness-test.sh` — **10 controls**, each against a throwaway git repo with real commits, fully offline: | control | asserts | |---|---| | stale pin is refused | rc=1, names `scripts/update-actions-lock.sh` | | stale report is path-scoped | never names the unrelated file that also changed | | fresh pin is accepted | rc=0 | | unrelated divergence does not fail it | a rebase must not redden the gate | | `ref: main` is refused | pinning is the point | | abbreviated sha is refused | 40-hex only | | **renamed step fails loudly** | rc=1 — the exact way the contract test lost its subject | | **unresolvable pin fails, not skips** | a skip is indistinguishable from a pass | | missing `ref:` is refused | would follow the default branch | | empty staged scope is refused | nothing to compare is not a free pass | **Meta-mutant.** Removing the path scoping from the guard (`git diff --name-only $pin $compare -- $paths` → without `-- $paths`) kills **exactly the two controls that assert it**, 8 passed / 2 failed. Restored, 10/10. **Contract-test mutants.** `ref: main` → `FAIL: the lock gate is not staged from an immutable 40-hex commit`. Renaming the step → `FAIL: governance workflow has no step named 'Checkout standards for the lock gate'`. Both rc=1. Full suite: **all 51 test files pass** on this branch. The guard **fails the job**. It is not `continue-on-error` and it is not a `::warning::`, which cannot fail a job. ## Notes - `self-test.yml` gains `fetch-depth: 0`. The guard compares two commits and **fails rather than skips** on an unresolvable pin, so the history is a requirement, not an optimisation. This is a `with:` change only — no `uses:` ref moves, so `actions.lock` is untouched. - The new Self Test step passes the base SHA through `env:`, not by interpolating an expression into the `run:` body. The repo's own injection scanner (`tests/test_tag_ruleset_canon.sh`) still passes. - `grep -A N` cannot delimit the step block: it is 19 lines today, so any fixed `N` is either short of the `ref:` or long enough to capture the **next** step's `ref:` and assert against the wrong pin. Both the guard and the contract test take the range from `- name:` to `- name:` with awk. ## ⚠ This does not turn burble#226 green on its own `metadatastician/burble#226` pins standards at `e977cc67`, and **that** copy of `governance-reusable.yml` still carries `4f7f02ca` at the lock-gate step. Pin 3 travels with the pinned YAML. Sequence: **merge this → take the resulting SHA → re-bump burble#226 to it** (all 9 sites plus `actions.lock`, transitive `uses:` list re-extracted against a positive control) → then #226 can go green. ## Out of scope, filed separately The **dupkey** pin `317101e0` is also stale — 45 files differ under `scripts/` versus `main`. Its `sparse-checkout` is the whole `scripts` directory, so the same predicate applied verbatim would be permanently red and useless; it needs a scope narrowed to what that step actually executes. Per the stopping rule that is an issue with acceptance criteria, not scope for this PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01X3hgXxWm6umMgZkjYyHnnm Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
✨ Finishing Touches🧪 Generate unit tests (beta)❌ Error committing Unit Tests locally.
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. Comment |
|
Note Unit test generation is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
|
Failed to create Coding Agent finishing-touch task. |
Closes #225.
All three checks that were red on
mainafter #224 merged share one cause: burble was pinnedto
hyperpolymath/standards@fcb566cf(2026-09-07), which predates two upstream fixes. Both are nowon standards
main, so this is a pure pin bump with no upstream work outstanding.Workflow syntax and action locks(step 6)standards#946, merged 09-22T09:46Zgovernance / Actions lockfile verify(step 5)scan / Hypatia Neurosymbolic Analysis(step 9)warn; 14 of 144 findings arewarnmainWhat changed
Six workflow files, nine replacements, all to
e977cc67:governance.yml:21,hypatia-scan.yml:25,scorecard.yml:20,secret-scanner.yml:21,spark-theatre-gate.yml:18— reusable-workflow refsworkflow-safety.yml:46,54— the twocurlURLs, and bothsha256sum -c -digests,recomputed from the fetched files (
dd5b1308…→476027a9…,2361fc7e…→877902ec…)Plus
actions.lock, regenerated in the same commit.mirror.yml(pinned4d104d32) is deliberately out of scope.Why the lock had to move with it
Bumping the YAML alone would have recreated the exact desync that killed this repo at startup in
#223. The stale SHA lived in
actions.locktoo — five per-workflow list items and one dependencyblock.
gh actions-lockcannot do this itself. It is blind to job-level reusable-workflow refs(github/gh-actions-lock#129), so a regeneration run left the standards entries untouched and
instead pruned eight unrelated orphan entries. The standards entry was therefore updated by hand,
including its transitive
uses:list, which the reusables moved upstream:editorconfig-checkergithub/codeql-actionThat transitive list was not guessed. The extraction method was positive-controlled first: run
against the old reusables it reproduced the seven entries already recorded in the lock exactly,
with no additions or omissions; only then was it trusted against the new ones.
Verified before pushing
scripts/check-lock-sync.sh--verify-localrun against this treeuses: $/...rewrites.github/workflows/e977cc67workflow_callcontractsThe gate added in #224 was also mutant-tested against this exact change class: a fake SHA on
governance.yml:21with the lock untouched takes it fromrc=0torc=1, flagged in bothdirections. It is not vacuous for the one thing this PR does.
Expectations
This crosses ~15 days of standards history across five reusables at once. Per the standing stopping
rule, any new red that appears after the bump is an issue with acceptance criteria, not a reason
to revert — the pins are stale in every direction today, and reverting restores the three reds in
#225.
Two housekeeping notes, neither blocking: the eight orphan lock entries
gh actions-lockwanted toprune are left alone as unrelated churn, and the tool's duplicate-banner bug
(github/gh-actions-lock#130) was stripped from the five touched files so the diff carries only the
deliberate change.
🤖 Generated with Claude Code
https://claude.ai/code/session_01X3hgXxWm6umMgZkjYyHnnm
Summary by CodeRabbit