Skip to content

fix(ci): close actions.lock (Hypatia invalid_actions_lock) by moving stale reusable pins upstream - #446

Merged
hyperpolymath merged 3 commits into
mainfrom
claude/stoic-brown-26h7xi
Oct 20, 2026
Merged

hyperpolymath merged 3 commits into
mainfrom
claude/stoic-brown-26h7xi

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

.github/workflows/actions.lock (v0.0.3) was not transitively closed. It listed two job-level reusable refs under workflows:, and neither had a top-level record:

  • slsa-framework/slsa-github-generator@f7dd8c54… (generator-generic-ossf-slsa3-publish.yml)
  • hyperpolymath/panic-attack@b689a74… (security-scan.yml)

Since hyperpolymath/hypatia#938 (scanner aa34a4cc), Hypatia reports this as invalid_actions_lock / {:workflow_dependencies_missing, …} in both workflow_audit and workflow_hardening. That keeps governance / Validate Hypatia Baseline red once the hypatia-ref bump from hyperpolymath/standards#1315 lands.

Why the lock couldn't simply be regenerated. I ran the estate repair chain (gh actions-lock → relock-sha-keys → complete-job-refs → close-lock → prune-stale, standards 3c00687a) on main's pins. close-lock stops with exit 3 on two leaf refs:

  • dtolnay/rust-toolchain@e2a55d2…: pinned by standards@75bd5994 mirror-reusable.yml
  • dtolnay/rust-toolchain@02cb101e…: pinned by panic-attack@b689a74 scan-and-report.yml

dtolnay moved its v1 tag, so no tag names either commit any more. close-lock correctly refuses to write a self-SHA leaf (cf. standards cf8f9121). Upstream main of both repos now pins 683b588, the current v1. The fix is therefore to move the stale caller pins, then run the chain again.

Changes

  • security-scan.yml: panic-attack scan-and-report.yml b689a74 → 8955f615. Only one line changes in the called file: the dtolnay pin.
  • governance, hypatia-scan, mirror, scorecard, secret-scanner, spark-theatre-gate: standards reusables 75bd5994 → 3c00687a (standards#1315). This also brings in hypatia-ref aa34a4cc. The workflow_call inputs and secrets are unchanged; hypatia-scan-reusable gains a verdict output.
  • actions.lock: the chain's output for this exact tree, regenerated in CI (sha256 58d32e2d…). It was not edited by hand.
  • Deleted zz-one-off-lock-regen.yml, a scratch workflow left behind by Claude/new session th4e9w lockregen #441. It was reused on this branch to run the chain (this session has no GitHub API access), then removed.

RSR Quality Checklist

Required

  • Tests pass: no code changes; lock checks below
  • Code is formatted: workflow lines changed only in their pin and comment
  • Linter is clean: actionlint clean on all workflows
  • No banned language patterns
  • No unsafe blocks without // SAFETY: comments: n/a
  • No banned functions: n/a
  • SPDX license headers present on all new/modified source files
  • No secrets, credentials, or .env files included

As Applicable

  • Docs/deeds/roadmap: n/a (CI pins only)

Testing

Measured in run 39423933176, on three variants of the caller pins:

Variant close-lock check-lock-sync, gate @75bd59 and @3c00687a Hypatia@aa34a4cc invalid_actions_lock
v0: pins as on main 2 unresolvable pass 2 → 2
v1: panic-attack bump only 1 unresolvable (e2a55d2) pass 2 → 2
v2: this PR 0 dangling (re-run idempotent) pass 2 → 0
  • Locally, on the final tree: check-lock-sync.sh passes, and an independent count of edges with no matching key gives 0.
  • Known gap: raw gh actions-lock --verify (v0.2.0) fails on every variant, main's current lock included. It cannot see job-level reusable uses:, so it calls exactly the records close-lock requires "unused". The estate verifier update-actions-lock.sh --verify-local (what the governance gate runs) accepts that coverage and passes. With the maintainer's agreement, that wrapper counts as the verify criterion here.
  • Risk to watch on this PR's CI: callers now run 17 newer standards commits, so Governance or Hypatia may surface new baseline findings unrelated to the lock.

Generated by Claude Code

Repurposes the leftover one-off workflow from #441 to regenerate echidna's
own lock in CI with the estate repair chain, since this session cannot
reach the GitHub API. Removed again in a later commit on this branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jzCHZx4Rt65NDaGLDrjLp
actions.lock v0.0.3 named two job-level reusable refs under `workflows:`
with no top-level record (slsa-github-generator@f7dd8c54 and
panic-attack@b689a74). Hypatia at aa34a4cc reports that as
invalid_actions_lock in workflow_audit and workflow_hardening.

The estate repair chain (gh actions-lock, relock-sha-keys,
complete-job-refs, close-lock, prune-stale; standards 3c00687a) could
not close the lock with the old pins. standards@75bd5994
(mirror-reusable.yml) and panic-attack@b689a74 pin
dtolnay/rust-toolchain at e2a55d2 and 02cb101e. dtolnay moved its v1
tag, and no tag names either commit any more, so close-lock refuses to
write their leaf records. Upstream main of both repos pins 683b588,
the current v1, so the callers move there:

- security-scan.yml: panic-attack scan-and-report.yml -> 8955f615
  (one line changes in the called file: the dtolnay pin)
- governance, hypatia-scan, mirror, scorecard, secret-scanner and
  spark-theatre-gate: standards reusables -> 3c00687a (standards#1315,
  hypatia-ref aa34a4cc; same workflow_call inputs and secrets)

The lock is the chain's output for this exact tree, generated in CI
(sha256 58d32e2d...). It was not edited by hand. Measured in that run:
close-lock reports 0 dangling refs, check-lock-sync.sh passes,
check-actions-lock-gate.sh passes at 75bd5994 and at 3c00687a, and the
Hypatia scan at aa34a4cc finds 0 invalid_actions_lock (2 before).

Raw `gh actions-lock --verify` (v0.2.0) still flags the job-level
reusable records as unused, on main's lock too. It cannot see
job-level `uses:`. The estate verifier (update-actions-lock.sh
--verify-local) accepts that coverage and passes.

The one-off lock-regen workflow left behind by #441 is deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jzCHZx4Rt65NDaGLDrjLp
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: af4fb044-9765-4a6d-8222-bf187abd3c74
📥 Commits

Reviewing files that changed from the base of the PR and between c020b63 and fe04b5d.

⛔ Files ignored due to path filters: 1
  • .github/workflows/actions.lock is excluded by !**/*.lock
📒 Files selected for processing: 8
  • .github/workflows/governance.yml
  • .github/workflows/hypatia-scan.yml
  • .github/workflows/mirror.yml
  • .github/workflows/scorecard.yml
  • .github/workflows/secret-scanner.yml
  • .github/workflows/security-scan.yml
  • .github/workflows/spark-theatre-gate.yml
  • .github/workflows/zz-one-off-lock-regen.yml
💤 Files with no reviewable changes: 1
  • .github/workflows/zz-one-off-lock-regen.yml

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout: 58
  • GitHub Check: governance / Exemption ratchet
  • GitHub Check: scan / gitleaks
  • GitHub Check: scan / shell-secrets
  • GitHub Check: scan / rust-secrets
  • GitHub Check: governance / JS runtimes — bun
  • GitHub Check: MVP Smoke
  • GitHub Check: governance / JS runtimes — environment provisions Bun first
  • GitHub Check: spark-theatre-gate / SPARK Theatre Gate
  • GitHub Check: zig-ffi / unified-api-adapter purity
  • GitHub Check: analyze (javascript-typescript, none)
  • GitHub Check: governance / Code quality + docs
  • GitHub Check: T1 / eprover
  • GitHub Check: T1 / glpk
  • GitHub Check: T1 / chuffed
  • GitHub Check: T1 / cvc5
  • GitHub Check: governance / JS runtimes — deno
  • GitHub Check: T1 / alt-ergo
  • GitHub Check: governance / Debt ratchet
  • GitHub Check: governance / Allowlist Preflight
  • GitHub Check: T1 / z3
  • GitHub Check: governance / Actions lockfile verify
  • GitHub Check: governance / JS runtimes — pnpm
  • GitHub Check: governance / Trusted-base reduction policy
  • GitHub Check: governance / UUID v8 conformance
  • GitHub Check: governance / Guix packaging policy (Nix retired)
  • GitHub Check: governance / Licence consistency
  • GitHub Check: T1 / spass
  • GitHub Check: analyze (actions, none)
  • GitHub Check: T1 / vampire
  • GitHub Check: governance / Security policy checks
  • GitHub Check: governance / UUID v7 conformance
  • GitHub Check: governance / Check Workflow Staleness
  • GitHub Check: governance / Well-Known (RFC 9116 + RSR)
  • GitHub Check: governance / Workflow security linter
  • GitHub Check: hypatia / Hypatia Neurosymbolic Analysis
  • GitHub Check: governance / Language / package anti-pattern policy
  • GitHub Check: T1 / minizinc
  • GitHub Check: zig-ffi / fmt + test (ffi/zig)
  • GitHub Check: T1 Guix manifest check
  • GitHub Check: governance / Live Actions policy (credentialed advisory)
  • GitHub Check: Groove manifest check
  • GitHub Check: Banned-runtime check
  • GitHub Check: Boot Gate
  • GitHub Check: Validate DEED manifests
  • GitHub Check: Validate eclexiaiser manifest
  • GitHub Check: Hypatia neurosymbolic scan
  • GitHub Check: Validate K9 contracts
  • GitHub Check: Patch Bridge CVE triage
  • GitHub Check: Dependency audit
  • GitHub Check: Empty-linter (invisible characters)
  • GitHub Check: PR (address)
  • GitHub Check: Proof safety regressions
  • GitHub Check: rust-ci / Detect Cargo.toml
  • GitHub Check: Type-check Agda proofs
  • GitHub Check: Type-check Idris2 ABI definitions
  • GitHub Check: lint-workflows
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: lint-workflows
🔇 Additional comments: 7
.github/workflows/governance.yml: 1

21-21: LGTM!

.github/workflows/hypatia-scan.yml: 1

24-24: LGTM!

.github/workflows/mirror.yml: 1

27-27: LGTM!

.github/workflows/scorecard.yml: 1

29-29: LGTM!

.github/workflows/secret-scanner.yml: 1

26-26: LGTM!

.github/workflows/security-scan.yml: 1

27-27: LGTM!

.github/workflows/spark-theatre-gate.yml: 1

23-23: LGTM!

📝 Summary

Summary by CodeRabbit

  • Chores
    • Updated the reusable workflow revisions used for governance, security scanning, secret detection, scorecard checks, mirroring, and quality gates.
    • Updated the security scan workflow to use a newer scanning revision.
    • Removed the temporary, one-off workflow used to regenerate workflow lock data.

Seven GitHub Actions workflows now use updated reusable workflow commit references. The one-off workflow for regenerating the actions lock was removed.

Changes: GitHub Actions workflow maintenance

Layer / File(s) Summary
Update reusable workflow pins
.github/workflows/{governance,hypatia-scan,mirror,scorecard,secret-scanner,security-scan,spark-theatre-gate}.yml
Seven workflow callers now reference updated commits. The security scan workflow’s pin-date and revision comments also changed.
Remove one-off lock regeneration
.github/workflows/zz-one-off-lock-regen.yml
The workflow that regenerated and printed the actions lock and checksum was removed.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix

Possibly related PRs

  • hyperpolymath/echidna#410: Updates the same reusable workflow callers and addresses pinned workflow references and actions lock consistency.
Merge Risk: ⚪ Minimal · up to fe04b

The workflow pins and lockfile are synchronized, and removing the temporary regeneration workflow does not affect supported workflows.

Architecture risk: 🔵 Low · up to fe04b

The changed surface does not map to a changed system, dependency edge, entrypoint, or external dependency.

Changed systems: None identified.

Architecture concerns
No architecture-level concerns identified.

Review details

Before / after behavior

  • observed — Modified behavior in .github/workflows/governance.yml: The governance job’s reusable workflow reference was updated from commit 75bd5994470661a099df562d6d5ad2fe744dbd66 to 3c00687a5bbd0d881d351db8502417b5d0a8b0b0.
  • observed — Modified behavior in .github/workflows/hypatia-scan.yml: The Hypatia scan job’s reusable workflow reference was updated from 75bd5994470661a099df562d6d5ad2fe744dbd66 to 3c00687a5bbd0d881d351db8502417b5d0a8b0b0.
  • observed — Modified behavior in .github/workflows/mirror.yml: The mirror job’s reusable workflow reference was updated from commit 75bd5994470661a099df562d6d5ad2fe744dbd66 to 3c00687a5bbd0d881d351db8502417b5d0a8b0b0.
  • observed — Modified behavior in .github/workflows/scorecard.yml: The analysis job now uses the updated pinned Scorecard reusable workflow commit, replacing the previous pinned commit.
Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly identifies the CI fix: closing actions.lock by updating stale reusable workflow pins.
Description check Passed The description covers the summary, changes, testing, known verification limitation, checklist, and rationale. The omitted Screenshots section is not required for this workflow-only change.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (7 skipped: 7 unsupported.)

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Copy link
Copy Markdown
Owner Author

T1 Guix manifest check (Live Provers) is red on this PR, but the failure is not from this change.

  • It fails at Install Guix with E: Unable to locate package guix: ubuntu-latest has no guix apt package. The same step fails the same way on main at c020b63 (job 118357196668).
  • This PR changes no file live-provers.yml reads. The job is already allowed to fail (continue-on-error: true # allow-fail: issue=#416 expires=2026-11-30).
  • No fix exists yet. CI: allow-fail units need an owner and an expiry (ultraplan Phase 2.3) #416 tracks the job; one option is installing Guix from the upstream binary tarball instead of apt. Not re-run: the failure is deterministic.

Generated by Claude Code

@hyperpolymath
hyperpolymath marked this pull request as ready for review October 20, 2026 13:02
@hyperpolymath
hyperpolymath enabled auto-merge (squash) October 20, 2026 13:02
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T13:05:05.122713Z fe04b5d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@hyperpolymath
hyperpolymath merged commit 13cbda9 into main Oct 20, 2026
86 of 89 checks passed
@hyperpolymath
hyperpolymath deleted the claude/stoic-brown-26h7xi branch October 20, 2026 13:15
hyperpolymath added a commit to hyperpolymath/standards that referenced this pull request Oct 20, 2026
…ent tag names the pin (#1323)

## Summary

`scripts/close-lock.sh` won't write a leaf record for a SHA-pinned
action when no current tag points at that SHA. It prints `SKIP … (cannot
resolve a symbolic tag for leaf SHA)` and exits 3.
`dtolnay/rust-toolchain` moves `v1`, so old pins can no longer be
closed. Two examples: `@e2a55d2…` (standards@75bd5994
`mirror-reusable.yml`) and `@02cb101…` (panic-attack@b689a74
`scan-and-report.yml`). Every caller lock that reaches those pins is
stuck with them (hyperpolymath/echidna#446, run 39423933176).

This PR adds a fail-closed fallback. It does not bring back self-SHA
leaf refs (cf8f912, standards#1271).

## Measurement: GitHub accepts a `ref:` naming a tag that has since
moved

[Run
39286638218](https://github.com/hyperpolymath/standards/actions/runs/39286638218),
job `mirror / mirror-radicle`, standards@333b06a0, 2026-10-19 16:36 UTC.
The runner log shows:

```
Using locked action versions from the workflow's lockfile
Download action repository 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f' (SHA:e2a55d2ffb04f378e9626c28d38b36d230d1e12f)
```

The record it used was `ref: 'v1'`. "Setup Rust" passed. By then `v1`
was already `683b588`: Dependabot moved standards' own pin to it at
14:45 UTC that day (e926751, #1300). So `ref:` is not re-resolved
against the tag's current target.

## Change

Fallback, tried **only when the tag listing succeeded and found no
tag**, in this order:

1. **Prior record.** This is the same key's record in the lock as it
stood before `gh actions-lock` rewrote it. close-lock reads it from a
new `--prior FILE` option. `regen_lock` in `regen-dependabot-locks.sh`
now snapshots the lock before the tool runs and passes it in. The record
is reused only when its `commit` is the key SHA and its
`owner_id`/`repo_id` equal what the API returns now.
2. **Call-site comment.** This is the `# <tag>` comment on a `uses:` of
that SHA. close-lock looks in this repository's workflows and in the
called workflows it already reads.

A candidate is written only when both of these hold:
- it is a tag of that repository now (`commits/tags/<t>`), and
- that tag's commit descends from the pin (`compare/<sha>...<tagsha>`
reports `ahead` or `identical`), which means the tag moved forward past
the pin.

Anything else is refused, and the key SKIPs with exit 3 as before: a
branch name such as `# stable`, a deleted tag, a self-SHA ref, or a tag
on another line. The SKIP line now lists why each candidate was refused.

## Testing

Offline, locally:

| Suite | Before | After |
|---|---|---|
| `close-lock-test.sh` | 34 | **53**, 0 failed |
| `regen-dependabot-locks-test.sh` | 59 | **61**, 0 failed |
| `lock-selfcheck-test.sh` | 9 | 9, 0 failed (needs mikefarah yq v4) |

- **New cases.** Both sources are covered: a prior record, including a
case-folded key; a comment in a called workflow; a comment in a KYAML
local workflow; and fall-through from a refused prior record to the
comment. Each refusal is a planted positive: wrong commit, wrong
`owner_id`, self-SHA (refused by name before any lookup), a tag that
doesn't exist, `# stable`, a tag that doesn't descend from the pin, a
comment on another action's pin, and a failed tag listing (no fallback
on an API error).
- **Mutants.** Nine guard mutants were each killed: commit check, id
check, ancestry check, tag existence, SHA-name check, fallback on a list
error, key match, no prior source, and no comment source.
- **awk.** The suites above, plus check-lock-sync, relock-sha-keys,
complete-job-refs, update-actions-lock, relock-standards-pin and
validate-actions-lock, pass under both mawk and gawk.
- **shellcheck.** No new findings in the scripts. The test file only
gains more SC2015 notes, from the `&& ok || bad` idiom it already uses.

**Not yet done: a live run.** These lookups can't be made from this
session, so the fallback has not run against the real API. Going by the
API docs, `commits/tags/<t>` and the compare `status` behave as the stub
assumes, but that is unverified until CI runs them.

## Known limit

This alone does not close echidna@c020b63, the pre-#446 tree. The
panic-attack call site reads `@02cb101… # v1`, which closes through
source 2. But standards@75bd5994's call site reads `@e2a55d2… # stable`,
which is a branch and so correctly refused. Echidna's own lock never
held an `e2a55d2` record. The record *does* exist in
standards@75bd5994's own `actions.lock` (`ref: 'v1'`), which is the lock
GitHub validates the callee against. Whether to add that as a third
source is an open question for a stacked follow-up.

## Related

`gh actions-lock --verify` v0.2.0 reports job-level reusable records as
"Unused lockfile entry". That conflicts with close-lock's closure
requirement. `update-actions-lock.sh --verify-local` already accepts
that coverage. It may be worth an upstream issue on
github/gh-actions-lock.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01JHfuPCv1pEiqkQGG5DQASb)_

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants