From 87d5bc859f7ec3fad580c198c2635bab6635a6fc Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 10 Oct 2026 10:38:47 +0000 Subject: [PATCH 1/4] fix(ci): align action refs with actions.lock, which publishes the org allowlist Three workflows on main died as `startup_failure` - no jobs, no check runs, no annotations. That signature is a workflow rejected by the organisation's "specific actions allowed" policy at *validation* time, not a failing step. The allowlist is derived from `.github/workflows/actions.lock` (standards `scripts/set-allowed-actions.sh`), and PR #265 bumped workflow refs without a lock refresh, so those refs stopped being on the list GitHub matches: E2E Validation lock: setup-node@v7.0.0 had: @v7.1.0 Static Analysis Gate lock: upload-artifact@v7.0.1, had: @v7.0.2, download-artifact@v8.0.1 @v8.0.2 CodeQL Security ... lock: codeql-action@v4.38.0 had: @v4.38.2 Each rolled-back tag resolves upstream to exactly the commit the lock records - checked per ref against the API (setup-node 820762786026, upload-artifact 043fb46d1a93, download-artifact 3e5f45b2cfb9, setup-ocaml e89b2ded52a6, codeql-action b96794f015df, setup-beam 54075bcc5e24) - so this restores the state the tooling already trusts instead of inventing a pin set. c5-regenerate.yml (setup-ocaml v3.9.0 -> v3.8.0) and release.yml (upload-artifact v7.0.2 -> v7.0.1) carry the same drift and would fail the same way the moment they are triggered; fixing them removes the class, not just the three visible symptoms. CodeQL has a second, independent reason to stay at v4.38.0: v4.38.1+ fails workflow-startup validation estate-wide (recorded in hyperpolymath/phantom-metal-taste#71, mirrored by that repo's dependabot hold). Two drifts are left alone deliberately: `hyperpolymath/smtp-notify-action@v0.5.0` (the lock lags an intentional bump; the workflow is green and org-owned actions are outside the allowlist) and the slsa-generator reusable call in release.yml (the lockfile does not record reusable-workflow calls). Both are the lock's to catch up with via `gh actions-lock`, not code to roll back. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .github/workflows/c5-regenerate.yml | 2 +- .github/workflows/codeql.yml | 4 ++-- .github/workflows/e2e.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/static-analysis-gate.yml | 14 +++++++------- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/c5-regenerate.yml b/.github/workflows/c5-regenerate.yml index 2b1b44d..0655035 100644 --- a/.github/workflows/c5-regenerate.yml +++ b/.github/workflows/c5-regenerate.yml @@ -51,7 +51,7 @@ jobs: path: affinescript - name: Install OCaml - uses: ocaml/setup-ocaml@v3.9.0 + uses: ocaml/setup-ocaml@v3.8.0 with: ocaml-compiler: '4.14.2' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2f55afa..f783fad 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,12 +40,12 @@ jobs: uses: actions/checkout@v7.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@v4.38.2 + uses: github/codeql-action/init@v4.38.0 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.38.2 + uses: github/codeql-action/analyze@v4.38.0 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index eee3880..a6ec77b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@v7.0.1 - name: Set up Node.js - uses: actions/setup-node@v7.1.0 + uses: actions/setup-node@v7.0.0 with: node-version: '25' @@ -158,7 +158,7 @@ jobs: # ---- Node (for smoke test within E2E script) ---- - name: Set up Node.js - uses: actions/setup-node@v7.1.0 + uses: actions/setup-node@v7.0.0 with: node-version: '25' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3841579..4939a2d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,7 +77,7 @@ jobs: git cliff --output CHANGELOG.md - name: Upload updated CHANGELOG.md - uses: actions/upload-artifact@v7.0.2 + uses: actions/upload-artifact@v7.0.1 with: name: changelog path: CHANGELOG.md diff --git a/.github/workflows/static-analysis-gate.yml b/.github/workflows/static-analysis-gate.yml index 280523b..c1e67b8 100644 --- a/.github/workflows/static-analysis-gate.yml +++ b/.github/workflows/static-analysis-gate.yml @@ -117,7 +117,7 @@ jobs: echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload panic-attack findings - uses: actions/upload-artifact@v7.0.2 + uses: actions/upload-artifact@v7.0.1 with: name: panic-attack-findings path: panic-attack-findings.json @@ -251,7 +251,7 @@ jobs: echo "Skipped: Hypatia scanner not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload hypatia findings - uses: actions/upload-artifact@v7.0.2 + uses: actions/upload-artifact@v7.0.1 with: name: hypatia-findings path: hypatia-findings.json @@ -341,7 +341,7 @@ jobs: echo "Skipped: panic-attack not available in this environment." >> "$GITHUB_STEP_SUMMARY" - name: Upload bridge report - uses: actions/upload-artifact@v7.0.2 + uses: actions/upload-artifact@v7.0.1 with: name: bridge-report path: bridge-report.json @@ -365,19 +365,19 @@ jobs: steps: - name: Download panic-attack findings - uses: actions/download-artifact@v8.0.2 + uses: actions/download-artifact@v8.0.1 with: name: panic-attack-findings path: findings/ - name: Download hypatia findings - uses: actions/download-artifact@v8.0.2 + uses: actions/download-artifact@v8.0.1 with: name: hypatia-findings path: findings/ - name: Download bridge report - uses: actions/download-artifact@v8.0.2 + uses: actions/download-artifact@v8.0.1 with: name: bridge-report path: findings/ @@ -439,7 +439,7 @@ jobs: echo "low=$LOW" >> "$GITHUB_OUTPUT" - name: Upload unified findings (fleet scanner picks these up) - uses: actions/upload-artifact@v7.0.2 + uses: actions/upload-artifact@v7.0.1 with: name: unified-findings path: findings/unified-findings.json From e3fbd1b6396ef9be3da95f31e05445d9cbd0aaec Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 10 Oct 2026 10:40:12 +0000 Subject: [PATCH 2/4] fix(ci): bump the watched estate pins to the template's canonical da2c748a `Governance / Workflow security linter` has been red on main for at least 13 days. It is not a repo bug: `gh actions-lock` prepends # This workflow is managed by gh actions-lock. at line 1 of all 20 workflows, and the pin this repo watches (81dbf2dd) tests the licence with `head -1`, so the tool and the gate cannot both be satisfied. standards#727 is that upstream conflict; da2c748a carries the fix (13cb62ac: scan the leading comment block instead of line 1) and is the pin rsr-template-repo itself uses. Evidence, both directions, by running each pin's gate step verbatim against this tree (extracted from the reusable, executed in a worktree): 81dbf2dd head -1 scan -> exit 1, 20 ERROR lines (today's red) da2c748a block scan -> exit 0, "All workflows have SPDX headers" The old pin's `Check SHA-pinned actions` step is also gone at da2c748a, and that matters more than it looks: replaying its grep here finds 43 violations, one of them *inside actions.lock itself* (` uses:` at line 87). That gate pre-dates the lockfile model, so it flags the exact symbolic refs the lock exists to authorise - and it was masked today only because SPDX fails first in the same job. Reordering the headers instead would have been the wrong fix: it satisfies a gate the estate has since deleted, re-breaks on every lock refresh, and would still leave 43 refs to hand-pin. Grants added with the bump, per standards#1035 - a job-level `permissions:` block REPLACES the workflow map and a reusable cannot escalate: hypatia-scan.yml workflow level + actions:read (uploads SARIF + artifact) scorecard.yml job level + actions:read (uploads the report artifact) governance.yml no grant (its reusable has no upload step; least privilege beats copying the sibling diff) mirror.yml moves d135b05b -> da2c748a as well: d135b05b is the pin standards#950 names as "all seven jobs blocking, no advisory job", which is why one dead remote here would fail the whole mirror. Two consequences a reviewer must sequence, both called out in the PR body: this pin adds an `Actions lockfile verify` job that installs `gh actions-lock` and runs `--verify-local`, so it will red until the lock is re-minted for `hyperpolymath/smtp-notify-action@v0.5.0` and the slsa reusable call - a true red naming its cause, not a false one. `Check action pins resolve upstream` was replayed locally and all six SHA-form pins in this repo resolve. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .github/workflows/governance.yml | 2 +- .github/workflows/hypatia-scan.yml | 6 +++++- .github/workflows/mirror.yml | 2 +- .github/workflows/scorecard.yml | 7 ++++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 7ce96ba..7162c52 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -14,4 +14,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index a8ce322..c534530 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -14,7 +14,11 @@ on: permissions: contents: read security-events: write + # standards#1035: hypatia-scan-reusable uploads SARIF (codeql-action/upload-sarif) + # and deposits a report artifact. The callee cannot escalate, and its job-level + # block replaces this map rather than merging with it - so the grant belongs here. + actions: read jobs: hypatia: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 66ce90e..c003271 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -12,5 +12,5 @@ permissions: jobs: mirror: - uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@d135b05bfc647d0c0fbfedc7e80f37ea50f49236 + uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 secrets: inherit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index cd8c045..c9aa653 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -12,8 +12,13 @@ permissions: jobs: scorecard: - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 permissions: contents: read security-events: write id-token: write + # standards#1035: scorecard-reusable runs actions/upload-artifact in the same job + # (043fb46d, v7.0.1). This block REPLACES the workflow-level map, so actions:read + # has to be granted here or the upload step fails - and 3 of 7 estate callers of + # this reusable omit it. + actions: read From d506ccdc8a9635d5f054ddedec19fa3389198d82 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 10 Oct 2026 10:41:27 +0000 Subject: [PATCH 3/4] fix(dependabot): hold CodeQL out of the group bump, and stop watching ecosystems that do not exist Three separate measured defects in one file. 1. The github-actions entry was `patterns: ["*"]` with no hold, which is how the refs in 87d5bc8 got moved under the lock. `ignore` alone does not suppress a grouped bump (phantom-metal-taste#76 measured exactly that), so the ref is excluded from the group as well as ignored. CodeQL carries a second, independent reason to stay put: v4.38.1+ fails workflow-startup validation estate-wide. 2. `npm` declared `directory: "/"`, where this repo has no package.json, while the one manifest it does have - `tools/tree-sitter-twasm/package.json` - sat unwatched. The ecosystem was live; the path was wrong. 3. `mix`, `pip` and `nix` had no manifests anywhere in the tree (`git ls-files` counts: 0 mix.exs, 0 pyproject/requirements/setup.py, 0 flake.nix/shell.nix). An ecosystem with no manifest cannot open a PR; it can only imply coverage it does not provide. Same shape as phantom-metal-taste#77's dead `nix`/`npm` entries, and that repo's second defect - two `ignore:` keys in one mapping, where the parser keeps only the last - is avoided here by using one key. Parsed and re-read after editing: ecosystems are now `[github-actions, cargo, npm]`, `ignore` resolves to `[github/codeql-action]`, the group keeps `patterns: ["*"]` plus `exclude-patterns: [github/codeql-action]`. The comment in the file records what the real guard is and why it is not holding: standards' `dependabot-lock-regen.yml` exists precisely to refresh each open dependabot PR's lockfile, but both of its App-token steps are `skipped` because `vars.APP_ID` is unset, so it warns and exits 0. That is an owner action on standards, not something this repo can fix, and it is the reason a hold comment is doing security-critical work here at all. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .github/dependabot.yml | 53 +++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b4d5ec6..3d92726 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,10 +9,34 @@ updates: directory: "/" schedule: interval: "weekly" + # Why a hold lives here at all. `.github/workflows/actions.lock` is what the + # estate publishes into the organisation's "specific actions allowed" list, so + # a bump that lands WITHOUT a lock refresh moves the workflow onto a ref the + # org no longer matches - and GitHub then rejects the whole workflow at + # validation time: no jobs, no check run, no annotation. That killed E2E + # Validation, Static Analysis Gate and CodeQL Security Analysis here for 9 + # days (see 87d5bc8). The durable guard is standards' own + # `.github/workflows/dependabot-lock-regen.yml`, which regenerates each open + # dependabot PR's lockfile. As of 2026-10-10 it sweeps nothing: its two + # "Mint an App installation token" steps are `if: vars.APP_ID != ''` and both + # are SKIPPED on run 37859881345, because the applier App (STD-R-3) is not + # configured - so the job warns and exits 0, by design, rather than pretending + # to have swept. Until that credential exists, a bump must be followed by a + # manual `gh actions-lock` commit, and refs that must not move are held here. + ignore: + # Second, independent reason for this one: v4.38.1+ fails workflow-startup + # validation estate-wide (hyperpolymath/phantom-metal-taste#71), and + # actions.lock resolves this repo's CodeQL to v4.38.0 (b96794f015df). + - dependency-name: "github/codeql-action" groups: actions: patterns: - "*" + # `ignore` alone does not suppress a grouped bump - measured on + # phantom-metal-taste#76, where the group rewrote the ref anyway. + # exclude-patterns is what actually keeps a held ref out of the group. + exclude-patterns: + - "github/codeql-action" open-pull-requests-limit: 2 # Rust/Cargo @@ -25,29 +49,16 @@ updates: update-types: ["version-update:semver-patch"] open-pull-requests-limit: 0 - # Elixir/Mix - - package-ecosystem: "mix" - directory: "/" - schedule: - interval: "weekly" - open-pull-requests-limit: 3 - - # Node.js/npm + # Node.js/npm - the tree has exactly one JS manifest, the tree-sitter grammar. + # This entry declared directory: "/", where no package.json exists, so it scanned + # a root with nothing to update while the real manifest went unwatched. - package-ecosystem: "npm" - directory: "/" + directory: "/tools/tree-sitter-twasm" schedule: interval: "weekly" open-pull-requests-limit: 3 - # Python/pip - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "weekly" - open-pull-requests-limit: 3 - - # Nix flakes - - package-ecosystem: "nix" - directory: "/" - schedule: - interval: "weekly" +# Removed 2026-10-10 as dead weight, measured with `git ls-files`: mix (no mix.exs), +# pip (no pyproject/requirements/setup.py), nix (no flake.nix or shell.nix). An +# ecosystem with no manifest cannot open a PR, it can only imply coverage. Each is +# one block to re-add if the manifest ever lands. From 8bed49653d748aa70a4c74bd91643ca53ea7880d Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 10 Oct 2026 10:41:54 +0000 Subject: [PATCH 4/4] fix(ci): take the last watched pin to the canonical da2c748a as well e3fbd1b moved governance, hypatia-scan, scorecard and mirror onto the pin the template itself uses; secret-scanner was left on c65436ee. That pin is not wrong - it is one commit behind da2c748a on this file - but a repo whose five estate reusables sit on four different refs is one `gh actions-lock` sweep away from the drift that caused this outage, and pin-divergence is exactly what makes "which gate is running?" unanswerable during an incident. Checked before swapping: da2c748a's secret-scanner-reusable declares the same `workflow_call` inputs (only the optional `runs-on`), so the call site needs no shape change, and its job-level permission grants (contents: read for the checkout, actions: read for workflow-run metadata) are already in place in this caller. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .github/workflows/secret-scanner.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/secret-scanner.yml b/.github/workflows/secret-scanner.yml index 988e3bb..4256c8f 100644 --- a/.github/workflows/secret-scanner.yml +++ b/.github/workflows/secret-scanner.yml @@ -21,5 +21,5 @@ jobs: # caller must grant at least that or the run startup-fails. permissions: contents: read - uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@c65436ee3351cd6b0fa14b142938b195efc77586 + uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@da2c748aad55c1a1dcba00b60fe4a35017bc6540 secrets: inherit