Repository navigation
fix(lock): close-lock gives reusable-workflow records their called workflow's uses: - #1247
Conversation
…rkflow's uses: close-lock wrote every record as a leaf. For a job-level reusable call that is not enough: on hyperpolymath/palimpsest-license#177 (2026-10-14) a lock listing `hyperpolymath/standards@<sha>` with leaf records made GitHub refuse Governance, Hypatia Security Scan and Secret Scanner at startup (startup_failure, no jobs), while every step-only workflow ran. In the estate locks whose reusable callers do start (my-lang, burble), that record carries a nested `uses:` equal to the union of the external `uses:` of the workflow files called at that ref, keyed owner/repo@ref. close-lock now reads each called file at its ref (transitively, for a reusable that calls another), writes that list on new records, completes records an earlier run left as leaves, and fails closed (SKIP, exit 3, nothing written or changed) when a called file cannot be read. Tests: 11 new cases in close-lock-test.sh, including planted 404 and malformed bodies; five mutants (no list, no completion, no fail-closed guard, no recursion, no sub-path strip) are each caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LET2eHeDbN3b4WuxYjMUX9
|
Warning Review limit reachedYou'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 35 minutes. View limit details
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 |
K9 contract conformancerun https://github.com/hyperpolymath/standards/actions/runs/38544883820 K9 normative contract typecheckK9 contract self-testK9 conformance fixturesK9 corpus conformance (L2) |
Rebuilt in CI with the estate chain and standards close-lock 23e258b (hyperpolymath/standards#1247): each hyperpolymath/standards@<sha> record now lists the external uses: of the workflows called at that ref, the shape the estate's working locks have, instead of the [] caller entries. The governance gate at standards b26d3fb (gh-actions-lock v0.1.6) and check-lock-sync both pass; the workflow files are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LET2eHeDbN3b4WuxYjMUX9
|
Result from a real lock. This branch's The lock was rebuilt from scratch in CI (palimpsest-license run 38544820697) with
On the commit carrying that lock (palimpsest-license On Generated by Claude Code |
|
@coderabbitai review Generated by Claude Code |
|
K9 contract conformancerun https://github.com/hyperpolymath/standards/actions/runs/38548163250 K9 normative contract typecheckK9 contract self-testK9 conformance fixturesK9 corpus conformance (L2) |
K9 contract conformancerun https://github.com/hyperpolymath/standards/actions/runs/38548532360 K9 normative contract typecheckK9 contract self-testK9 conformance fixturesK9 corpus conformance (L2) |
|



The defect
scripts/close-lock.shwrites every record it adds as a leaf: ref, commit, owner and repo IDs, and no nesteduses:. For an action that is correct. For a job-level reusable workflow ref it is not.Measured on hyperpolymath/palimpsest-license#177 (2026-10-14). The lock was built by this chain (
gh actions-lockv0.1.6 →relock-sha-keys→complete-job-refs→close-lock→prune-stale). It listedhyperpolymath/standards@<sha>under each reusable caller, with the leaf recordsclose-lockwrote. On that commit:startup_failure, with no jobs.[]and no standards records, all three started.The gate didn't catch any of this:
update-actions-lock.sh --verify-localand the networked--verifyboth reported the leaf-record lock valid.What a working lock looks like
I compared locks whose reusable callers do start:
hyperpolymath/my-lang(Governance runs, withreferenced_workflowsresolved) andmetadatastician/burble. In both, the record for a reusable ref carries a nesteduses:list. That list is exactly the union of the externaluses:of the workflow files the repo calls at that ref:owner/repo@ref, so a sub-path such as/initis dropped./,$/anddocker://refs are excludedChecked against my-lang's lock:
standards@9e9513bstandards@8435558A reusable can call another one;
ci-pipeline.ymlcallssecret-scanner-reusableandrust-ci-reusableat571cc73. In that case the inner key appears in the outer record's list, as in cicd-squabbler's lock.close-lock.shlanded on 2026-10-01 (#1116). Those working locks predate it, and nothing inscripts/writes nested lists. So any lock the chain builds or rebuilds after a standards pin moves now gets leaf records for its reusable refs.regen-dependabot-locks.shruns the same chain, and so would an estate-widepropagate-workflow-pins.shrun.The fix
close-lock.shnow does the following:owner/repo/.github/workflows/<file>@<ref>in the caller's workflows.<ref>through the contents API, and follows any reusable calls inside it. Each (key, file) pair is read once, so recursion terminates.uses:. The existing passes then close the refs that list names.~ <key>. Correct records are left byte-identical.jobs:), it writes no record for that ref, changes no existing record, and exits 3. That is the same contract as an unresolvable ref today, so a leaf is never written where a list is owed.Action records stay leaves, exactly as before. A reusable that uses no external action also stays a leaf. The comment in
complete-job-refs.shnow says whatclose-lockadds.Tests
scripts/tests/close-lock-test.shis offline: its stub now serves planted workflow files. It runs 19 passed, 0 failed, the 8 existing cases plus 11 new ones:owner/repo@ref. The sub-path,./,docker://, quotes and comments are all handled. The listed refs are closed, and a second run is byte-identical.jobs:) gives SKIP and exit 3Mutants (
TARGET=copies), each caught:Unchanged suites, run locally:
regen-dependabot-locks(35/35),complete-job-refs(16/16),check-lock-sync(13/13),prune-stale(18/18),relock-sha-keys(11/11).mainwithout this change, and none of them usesclose-lock:lock-selfcheck,update-actions-lockandcheck-actions-lock-gate. These look like missing local tooling.shellcheck -S warningis clean, and so isgit diff --check.End-to-end result
This commit's
close-lock.shrebuilt the lock on hyperpolymath/palimpsest-license#177 from scratch in CI (run 38544820697). Details are in the comment below.hyperpolymath/standards@<sha>records with their nesteduses:. Pass 2 closed the 9 actions those lists name. Pass 3 found nothing dangling.e55c4c3), Governance, Hypatia Security Scan and Secret Scanner all start, and Governance creates all 15 jobs. On8f1aff5the same chain with the old leaf records left all three instartup_failure. Nesteduses:is the only difference between the two locks.mainwas merged into that PR (878e935), the lock gate, check-lock-sync and every other governance job pass.Order for the estate
This should merge before any estate-wide
propagate-workflow-pins.shrun. A pin bump changes the reusable refs, so with the oldclose-lock.shevery bumped repo would get leaf records, and its Governance, Hypatia and Secret Scanner would stop starting. A startup failure posts no check run, so those repos would look green.🤖 Generated with Claude Code
https://claude.ai/code/session_01LET2eHeDbN3b4WuxYjMUX9