From d71c35c89afd1f12ca3f9721a16277737edac191 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 11:16:46 +0000 Subject: [PATCH 1/3] ci: governance pin da2c748 -> 934b879; close actions.lock at the new pin At da2c748, governance's Actions lockfile verify ran gate scripts from a fixed helper pin (874ffe5) with an unpinned gh-actions-lock. Under v0.2.0 it failed on every job-level reusable ref, after accepting each as coverage. standards 934b879 runs the gate at its own commit with v0.2.0 pinned (standards#1250). Locally, with the real gh-actions-lock v0.2.0 (SHA-256 checked): - the da2c748-era gate fails resource-algebra main (exit 1), as CI does; - standards main's gate passes main and this change (exit 0). actions.lock: the standards record is rewritten at the new pin with its called workflows' uses: union (close-lock.sh); the four refs that union adds get records; the old record and the three refs only it named are pruned. check-lock-sync.sh: 0 dangling edges, every entry referenced. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y6FD63UrriRBHqmhhEca9t --- .github/workflows/actions.lock | 57 ++++++++++++++++------------ .github/workflows/governance.yml | 2 +- .github/workflows/hypatia-scan.yml | 2 +- .github/workflows/mirror.yml | 2 +- .github/workflows/rust-ci.yml | 2 +- .github/workflows/scorecard.yml | 2 +- .github/workflows/secret-scanner.yml | 2 +- CHANGELOG.adoc | 15 ++++++++ 8 files changed, 53 insertions(+), 31 deletions(-) diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 9cbb47a..746dbf0 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -31,13 +31,13 @@ workflows: '.github/workflows/estate-rules.yml': - 'actions/checkout@v7.0.1' '.github/workflows/governance.yml': - - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540' + - 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82' '.github/workflows/groove-check.yml': - 'actions/checkout@v7.0.1' '.github/workflows/guix-policy.yml': - 'actions/checkout@v7.0.1' '.github/workflows/hypatia-scan.yml': - - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540' + - 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82' '.github/workflows/k9-validate.yml': - 'actions/checkout@v7.0.1' - 'hyperpolymath/k9-ecosystem@main' @@ -47,7 +47,7 @@ workflows: '.github/workflows/main-estate-audit.yml': - 'hyperpolymath/cicd-suite@55556cf5ba71ba744695861503c13148d3915a5d' '.github/workflows/mirror.yml': - - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540' + - 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82' '.github/workflows/ossf-best-practices.yml': - 'actions/checkout@v7.0.1' '.github/workflows/pages.yml': @@ -74,11 +74,11 @@ workflows: '.github/workflows/runtime-policy.yml': - 'actions/checkout@v7.0.1' '.github/workflows/rust-ci.yml': - - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540' + - 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82' '.github/workflows/scorecard.yml': - - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540' + - 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82' '.github/workflows/secret-scanner.yml': - - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540' + - 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82' '.github/workflows/security-policy.yml': - 'actions/checkout@v7.0.1' '.github/workflows/sonarqube.yml': @@ -140,16 +140,16 @@ dependencies: commit: 'sha1-9000827ccba6bdab643e8b6fd33ac0654aef8333' owner_id: 44036562 repo_id: 192626254 - 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a': - ref: '043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - commit: 'sha1-043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - owner_id: 44036562 - repo_id: 192625955 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f': ref: 'v7.0.0' commit: 'sha1-bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' owner_id: 44036562 repo_id: 192625955 + 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9': + ref: 'cf430e030ddbb5b0abf93d22962f4752f3646cd9' + commit: 'sha1-cf430e030ddbb5b0abf93d22962f4752f3646cd9' + owner_id: 44036562 + repo_id: 192625955 'actions/upload-artifact@v7.0.2': ref: 'v7.0.2' commit: 'sha1-cf430e030ddbb5b0abf93d22962f4752f3646cd9' @@ -162,14 +162,19 @@ dependencies: repo_id: 496012378 uses: - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' + 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed': + ref: '22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' + commit: 'sha1-22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' + owner_id: 42048915 + repo_id: 356423100 'dependabot/fetch-metadata@v3.1.0': ref: 'v3.1.0' commit: 'sha1-25dd0e34f4fe68f24cc83900b1fe3fe149efef98' owner_id: 27347476 repo_id: 371068214 - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772': - ref: '6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' - commit: 'sha1-6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' + 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f': + ref: 'e2a55d2ffb04f378e9626c28d38b36d230d1e12f' + commit: 'sha1-e2a55d2ffb04f378e9626c28d38b36d230d1e12f' owner_id: 1940490 repo_id: 260749683 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393': @@ -197,11 +202,6 @@ dependencies: commit: 'sha1-b96794f015dfd88f77b49b1c93e0fa7110f94c63' owner_id: 9919 repo_id: 259445878 - 'github/codeql-action@cdf488f595d80d6e07e03d4674febd5ab45fa938': - ref: 'cdf488f595d80d6e07e03d4674febd5ab45fa938' - commit: 'sha1-cdf488f595d80d6e07e03d4674febd5ab45fa938' - owner_id: 9919 - repo_id: 259445878 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406': ref: 'abea47f85e598557f500fa1fd2ab7464fcb39406' commit: 'sha1-abea47f85e598557f500fa1fd2ab7464fcb39406' @@ -240,21 +240,23 @@ dependencies: commit: 'sha1-c1c9fa07992a02c1fd3d67a0dc1b08cccb852aef' owner_id: 6759885 repo_id: 1352485172 - 'hyperpolymath/standards@da2c748aad55c1a1dcba00b60fe4a35017bc6540': - ref: 'da2c748aad55c1a1dcba00b60fe4a35017bc6540' - commit: 'sha1-da2c748aad55c1a1dcba00b60fe4a35017bc6540' + 'hyperpolymath/standards@934b879f6068295e42dac20d80e010d555ebec82': + ref: '934b879f6068295e42dac20d80e010d555ebec82' + commit: 'sha1-934b879f6068295e42dac20d80e010d555ebec82' owner_id: 6759885 repo_id: 1116521501 uses: - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9' - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' - - 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' - - 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' + - 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9' + - 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' + - 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f' - 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393' - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124' - - 'github/codeql-action@cdf488f595d80d6e07e03d4674febd5ab45fa938' + - 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63' - 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406' - 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc' + - 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6' - 'swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6' - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555' 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc': @@ -262,6 +264,11 @@ dependencies: commit: 'sha1-2d1146689b8cda280b9bc96326124645441f03bc' owner_id: 67707773 repo_id: 421101922 + 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6': + ref: '0c5077e51419868618aeaa5fe8019c62421857d6' + commit: 'sha1-0c5077e51419868618aeaa5fe8019c62421857d6' + owner_id: 108928776 + repo_id: 512644635 'oven-sh/setup-bun@v2.2.0': ref: 'v2.2.0' commit: 'sha1-0c5077e51419868618aeaa5fe8019c62421857d6' diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 73366ea..8ef98da 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -18,4 +18,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@934b879f6068295e42dac20d80e010d555ebec82 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 997e008..e95d4ed 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -27,4 +27,4 @@ permissions: jobs: hypatia: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@934b879f6068295e42dac20d80e010d555ebec82 diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 35c5683..c118db0 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -13,7 +13,7 @@ permissions: contents: read jobs: mirror: - uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 + uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@934b879f6068295e42dac20d80e010d555ebec82 # Explicit secrets map — no `secrets: inherit` (Hypatia WH008, alert #131). # All seven are the callee's complete optional contract (standards # mirror-reusable.yml@da2c748); behaviour is unchanged, future secrets diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 05369dd..4f27132 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -16,4 +16,4 @@ permissions: contents: read jobs: rust-ci: - uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 + uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@934b879f6068295e42dac20d80e010d555ebec82 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index f2ce703..ff00f43 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -16,7 +16,7 @@ permissions: jobs: scorecard: - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@934b879f6068295e42dac20d80e010d555ebec82 # Reusable called-workflow permissions are CAPPED by the caller's grants; # without security-events: write here the scorecard SARIF upload fails with # startup_failure (hypatia WF018). id-token: write enables OIDC publish. diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 9a7b136..403f0b3 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -26,4 +26,4 @@ jobs: # is therefore the cap GitHub applies to the called workflow. permissions: contents: read - uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 + uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@934b879f6068295e42dac20d80e010d555ebec82 diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 64f181c..744f792 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,6 +1,21 @@ == Changelog +== 2026-10-14 — governance pin `da2c748` → `934b879` + +* The six standards reusables (governance, hypatia-scan, mirror, rust-ci, + scorecard, secret-scanner) move to hyperpolymath/standards `934b879`. + At `da2c748`, governance ran its lock gate from a fixed helper pin with an + unpinned gh-actions-lock. Under v0.2.0 that gate failed *Actions lockfile + verify* on every job-level reusable ref, although it had accepted each one + as coverage. The new pin runs the gate at its own commit, with v0.2.0 + pinned (standards#1250). +* `actions.lock`: the `hyperpolymath/standards` record is rewritten at the + new pin with its called workflows' `uses:` union, and closed over the four + refs that union adds (setup-bun, setup-deno, upload-artifact, + rust-toolchain). The old record and the three refs only it named are + removed. + == 2026-10-14 — MinMax and morphisms (resource-algebra#3) * `Instances/MinMax.agda`: the min-max (bottleneck) instance on the Loss From 43b7040ff3a069b4906171f64f13f479809f537c Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 11:30:29 +0000 Subject: [PATCH 2/3] ci(scorecard): Grant actions read permission --- .github/workflows/scorecard.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ff00f43..77510c4 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -21,6 +21,7 @@ jobs: # without security-events: write here the scorecard SARIF upload fails with # startup_failure (hypatia WF018). id-token: write enables OIDC publish. permissions: + actions: read contents: read security-events: write id-token: write From 2108dd7e5a23eccb17f9fb67c1840fb62fbe7615 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 11:33:04 +0000 Subject: [PATCH 3/3] test(ci): add regression coverage for actions.lock SHA refs and dependency closure --- tests/workflows/actions_lock_sha_refs_test.sh | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100755 tests/workflows/actions_lock_sha_refs_test.sh diff --git a/tests/workflows/actions_lock_sha_refs_test.sh b/tests/workflows/actions_lock_sha_refs_test.sh new file mode 100755 index 0000000..20218c0 --- /dev/null +++ b/tests/workflows/actions_lock_sha_refs_test.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# +# PR #7 regression: the four SHA-keyed leaves added by standards must keep +# their immutable ref, rather than the tag gh-actions-lock assigned to them. +# Separate tag-keyed records remain valid. This test is offline. +# Usage: bash tests/workflows/actions_lock_sha_refs_test.sh [workflow-directory] +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +WF_DIR="${1:-$REPO/.github/workflows}" +LOCK="$WF_DIR/actions.lock" +FIXTURE=$(mktemp -d) +trap 'rm -rf "$FIXTURE"' EXIT + +fail() { echo "FAIL: $*" >&2; exit 1; } +command -v jq >/dev/null || fail "jq is required" +yq --version | grep -q 'github.com/mikefarah/yq/' || fail "mikefarah yq v4 is required" +yq -o=json '.' "$LOCK" > "$FIXTURE/lock.json" + +# Use the standards pin actually called by governance, so a later standards +# update does not require copying its commit into this test. +STANDARDS=$(jq -er ' + [.workflows[".github/workflows/governance.yml"][] + | select(startswith("hyperpolymath/standards@"))] + | select(length == 1) | .[0] +' "$FIXTURE/lock.json") || fail "governance must lock exactly one standards reusable" + +# Assertions on parsed data, independent of the sync gate (which checks edges +# but deliberately does not inspect ref/commit metadata). +immutable_record() { + local key="$1" file="$2" + jq -e --arg key "$key" ' + ($key | split("@")[1]) as $sha + | .dependencies[$key] as $record + | ($sha | test("^[0-9a-f]{40}$")) + and ($record.ref == $sha) + and ($record.commit == ("sha1-" + $sha)) + ' "$file" >/dev/null +} + +# Run the production gate on the real workflows before negative controls, so +# an unrelated fixture/setup failure cannot masquerade as a useful rejection. +bash "$REPO/scripts/check-lock-sync.sh" "$WF_DIR" > "$FIXTURE/sync.log" 2>&1 \ + || { cat "$FIXTURE/sync.log" >&2; fail "the current lock must be in sync and closed"; } +mkdir -p "$FIXTURE/workflows" +cp -a "$WF_DIR/." "$FIXTURE/workflows/" + +checked=0 +while IFS='|' read -r action former_ref owner_id repo_id; do + key=$(jq -er --arg standards "$STANDARDS" --arg action "$action" ' + [.dependencies[$standards].uses[] | select(startswith($action + "@"))] + | select(length == 1) | .[0] + ' "$FIXTURE/lock.json") || fail "$action must occur exactly once in standards transitive coverage" + + immutable_record "$key" "$FIXTURE/lock.json" \ + || fail "$key must have matching immutable ref and commit" + jq -e --arg key "$key" --argjson owner "$owner_id" --argjson repo "$repo_id" ' + .dependencies[$key] | .owner_id == $owner and .repo_id == $repo + ' "$FIXTURE/lock.json" >/dev/null || fail "$key lost its repository identity" + + # Reproduce each of the four exact pre-PR tag substitutions. Even though + # the key and commit still carry the SHA, ref must not float to a tag. + jq --arg key "$key" --arg tag "$former_ref" ' + .dependencies[$key].ref = $tag + ' "$FIXTURE/lock.json" > "$FIXTURE/mutated.json" + if immutable_record "$key" "$FIXTURE/mutated.json"; then + fail "$key accepted its former floating ref $former_ref" + fi + + # A valid-looking, different SHA is still a broken key/ref/commit binding. + jq --arg key "$key" ' + .dependencies[$key].commit = "sha1-0000000000000000000000000000000000000000" + ' "$FIXTURE/lock.json" > "$FIXTURE/mutated.json" + if immutable_record "$key" "$FIXTURE/mutated.json"; then + fail "$key accepted a mismatched commit" + fi + + # Delete only this leaf record, keeping its incoming standards edge and + # all other workflow/lock data intact. Preserve gh-actions-lock layout: + # the production gate parses that layout, not yq's reformatted output. + awk -v key="$key" ' + $0 == " \047" key "\047:" { skip = 1; next } + skip && /^ \047/ { skip = 0 } + !skip { print } + ' "$LOCK" > "$FIXTURE/workflows/actions.lock" + if bash "$REPO/scripts/check-lock-sync.sh" "$FIXTURE/workflows" > "$FIXTURE/sync.log" 2>&1; then + fail "sync gate accepted a missing record for $key" + fi + if ! grep -qF 'DANGLING EDGES' "$FIXTURE/sync.log" \ + || ! grep -qF "$key" "$FIXTURE/sync.log"; then + cat "$FIXTURE/sync.log" >&2 + fail "sync gate did not identify the missing $key" + fi + + # upload-artifact and setup-bun also have direct tag users. Their records + # must coexist with the SHA records; the repair must not globally ban tags. + if [[ "$action" == actions/upload-artifact || "$action" == oven-sh/setup-bun ]]; then + jq -e --arg key "$key" --arg alias "$action@$former_ref" --arg tag "$former_ref" ' + .dependencies[$alias].ref == $tag + and (.dependencies[$alias].commit == .dependencies[$key].commit) + and ([.workflows[][] | select(. == $alias)] | length > 0) + ' "$FIXTURE/lock.json" >/dev/null || fail "$action lost its separate referenced tag record" + fi + + checked=$((checked + 1)) + echo "PASS: $action SHA binding, identity, negative controls and closure" +done <<'CASES' +actions/upload-artifact|v7.0.2|44036562|192625955 +denoland/setup-deno|v2.0.5|42048915|356423100 +dtolnay/rust-toolchain|v1|1940490|260749683 +oven-sh/setup-bun|v2.2.0|108928776|512644635 +CASES + +[[ "$checked" == 4 ]] || fail "expected to check all four changed dependency records" +echo "PASS: actions.lock SHA-ref regression controls ($checked dependencies)"