Skip to content

fix(lock): close-lock keeps a moved tag as a leaf's ref: when no current tag names the pin - #1323

Merged
hyperpolymath merged 2 commits into
mainfrom
claude/compassionate-fermat-9x7vpe
Oct 20, 2026
Merged

hyperpolymath merged 2 commits into
mainfrom
claude/compassionate-fermat-9x7vpe

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

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, 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

…ent tag names the pin

close-lock refused to write a leaf record for a SHA-pinned action when no
current tag pointed at that SHA (SKIP, exit 3). dtolnay/rust-toolchain moves
`v1`, so old pins such as @e2a55d2 (standards@75bd5994 mirror-reusable.yml)
and @02cb101 (panic-attack@b689a74 scan-and-report.yml) became unclosable,
and every caller's actions.lock with them (measured on
hyperpolymath/echidna#446, run 39423933176).

GitHub accepts a record whose ref: names a tag that has since moved.
Measured on standards run 39286638218 (2026-10-19 16:36 UTC): the runner
used the lockfile, downloaded dtolnay/rust-toolchain@e2a55d2 from a record
with ref: 'v1', and the step passed. v1 was already 683b588 by then
(Dependabot moved standards to it at 14:45 UTC, e926751).

Fallback, tried only when the tag listing succeeded and found nothing, in
order:
  1. the same key's record in the lock as it stood before gh actions-lock
     (new --prior FILE; regen_lock now snapshots and passes it), used only
     when its commit is the key SHA and its owner/repo ids match the API;
  2. the `# <tag>` comment at a call site of that SHA (this repository's
     workflows and the called workflows close-lock already reads).
Either candidate is written only if it is a tag now (commits/tags/<t>) and
its commit descends from the pin (compare status ahead|identical). Branch
names (`# stable`), deleted tags, self-SHA refs (standards#1271) and tags on
another line are refused, and the key SKIPs with exit 3 as before.

Tests: close-lock 34 -> 53 offline cases (both sources, and every refusal
as a planted positive). regen-dependabot-locks 59 -> 61 (close-lock gets
the pre-regeneration lock byte for byte; nothing is left in the checkout).
lock-selfcheck 9/9 unchanged. All pass under mawk and gawk; nine guard
mutants are each killed.

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

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next included review available in 37 minutes.

Check out review usage here.

View limit details

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 1782ec6f-2d99-4f4c-8e8d-65376ebd285d
📥 Commits

Reviewing files that changed from the base of the PR and between 5900141 and f9f98d0.

📒 Files selected for processing: 4
  • scripts/close-lock.sh
  • scripts/regen-dependabot-locks.sh
  • scripts/tests/close-lock-test.sh
  • scripts/tests/regen-dependabot-locks-test.sh
  • 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.

@hyperpolymath
hyperpolymath marked this pull request as ready for review October 20, 2026 14:14
@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-20T14:31:07.416749Z f9f98d0 New commits
ℹ️ 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.

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

DEED–K9 green

🟢 conformant · commit f9f98d058080 · run 39439569390

Category Status Pass Fail Indeterminate N/A Since previous revision
DEED syntax 🟢 conformant 4 0 0 0 ↔ unchanged
K9 contract typecheck 🟢 conformant 1 0 0 0 ↔ unchanged
K9 validator self-test 🟢 conformant 37 0 0 0 ↔ unchanged
K9 positive/negative controls 🟢 conformant 27 0 0 0 ↔ unchanged
K9 live corpus (L0–L2) 🟢 conformant 12 0 0 2 ↔ unchanged
K9 live corpus (L0–L2): 2 finding(s) / scope notes
  • 2-protocols/k9-coordination/examples/minimal.k9 — not_applicable: Routed to another dialect; not validated as a K9 contract.
  • coordination.k9 — not_applicable: Routed to another dialect; not validated as a K9 contract.

Scope: DEED syntax and K9 contracts L0–L2. L3 signature verification and routed coordination dialects are not certified. Green is limited to this scope. Orange is missing evidence; grey is an explicit scope exclusion. Logs and report.json are in the dated conformance-evidence artifact on the linked run.

@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath enabled auto-merge (squash) October 20, 2026 14:31
@hyperpolymath
hyperpolymath enabled auto-merge (squash) October 20, 2026 14:35
@hyperpolymath
hyperpolymath merged commit 01c9b0c into main Oct 20, 2026
61 of 63 checks passed
@hyperpolymath
hyperpolymath deleted the claude/compassionate-fermat-9x7vpe branch October 20, 2026 14:36
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