Skip to content

fix(lock): follow local composite actions in the lock chain; merge relock duplicates; drop the composite refusal - #1242

Merged
hyperpolymath merged 3 commits into
mainfrom
claude/exciting-noether-dzrfrr
Oct 14, 2026
Merged

hyperpolymath merged 3 commits into
mainfrom
claude/exciting-noether-dzrfrr

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Closes #1122 (owner-requested 2026-10-14). There is one commit per acceptance criterion; criteria 1 and 2 share a commit.

Background. A local composite action (uses: ./.github/actions/x) has no dependency record of its own. The actions it uses are therefore locked under the calling workflow's entry, which is where gh actions-lock writes them. External composites differ: their dependencies live in their own record's uses: list.

1 + 2. Follow local composites (290482f3)

  • prune-stale.sh. Both of its oracles (the parser and the literal grep) now read the workflow plus every local composite it reaches, so composite refs are no longer pruned. Its parser also strips a KYAML trailing comma, as check-lock-sync.sh already did.
  • check-lock-sync.sh. A workflow's uses: set now includes those of its composites. The issue's planted negative was the two composite lines deleted from signed-push-smoke.yml's entry. It now fails; before this PR it passed.
  • complete-job-refs.sh. Adds composite refs to the calling workflow's entry, so the chain produces them whether or not the tool does.
  • Shared block. The three scripts carry one identical reach block. check-lock-sync.sh is copied to other repos, so it has to stay self-contained, and a test fails if the three copies drift. Composites are followed transitively. A local reusable workflow (./x.yml) has its own entry and is not followed.
  • actions.lock. Running complete-job-refs.sh restored the one line prune-stale had removed: actions/create-github-app-token under signed-push-smoke.yml. Re-running relock-sha-keys, close-lock and prune-stale afterwards changes nothing. With the stricter check, check-lock-sync.sh passes on the real lock.

3. relock-sha-keys merges duplicates (d2c89119)

  • The bug. When the lock already held a SHA-keyed record and the tool added a tag-keyed record for the same commit, the re-key produced two records under one key ("already defined").
  • The fix. A merge pass keeps the first record and drops the duplicate. A list that now names a ref twice keeps it once.
  • Tests. New suite scripts/tests/relock-sha-keys-test.sh. The planted duplicate fails 5 assertions on the old script and passes on the new one. On a copy of this repo's lock, the script changes nothing.

4. Composite repos no longer refused (b07a8f5d)

  • regen-dependabot-locks.sh drops composite-unsupported.
  • regen_lock now runs check-lock-sync.sh after --verify-local. A lock the sync check rejects is reported as unverified and not committed. This is also the gate the PR meets in CI, and it catches job-level reusable refs, which --verify-local cannot see either.

Not done here

The issue's own acceptance run, the full chain with gh actions-lock on main accepted by --verify-local, needs the tool, which is not available in the session that wrote this PR. CI's Actions lockfile verify job runs --verify-local with the real tool on this PR's lock. The owner can run the full chain locally as well.

Testing

  • Mutants. Composite-blind mutants of each of the three scripts fail 2 cases in their suites. The relock duplicate is reproduced on the old script.
  • Suite results:
    • prune-stale-test 18/18
    • check-lock-sync-test 13/13
    • complete-job-refs-test 16/16
    • relock-sha-keys-test 11/11
    • regen-dependabot-locks-test 35/35
  • shellcheck. No warnings on any changed script.
  • Full shell suite. 78 of 82 test files pass. The other 4 fail identically on main in this sandbox, which lacks KYAML-capable yq, the JCS tool, Ruby and gh actions-lock.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GqfpQtLuMczxtgG1U9PkLW


Generated by Claude Code

…gate

standards#1122, criteria 1 and 2. A local composite action (`uses:
./.github/actions/x`) has no dependency record of its own, so the actions
it uses are locked under the CALLING workflow's entry; that is where
`gh actions-lock` writes them. Three scripts read only workflow files:

- prune-stale.sh: both oracles (parser, literal grep) now read the
  workflow and every local composite it reaches, so composite refs are
  no longer pruned. Its parser also strips a KYAML trailing comma, as
  check-lock-sync.sh already did.
- check-lock-sync.sh: a workflow's uses: set includes its composites'.
  The issue's planted negative (the two composite lines deleted from
  signed-push-smoke.yml's entry) now fails; it passed before.
- complete-job-refs.sh: adds composite refs to the calling entry, so the
  chain produces them whatever the tool does.

The three share one reach block word for word (check-lock-sync.sh is
copied to other repos, so it stays self-contained); a test fails if the
copies drift. Composites are followed transitively; a local reusable
workflow (./x.yml) is its own entry and is not followed.

actions.lock: complete-job-refs restored the one line prune-stale had
removed: actions/create-github-app-token under signed-push-smoke.yml
(used by .github/actions/signed-push). relock-sha-keys, close-lock and
prune-stale then change nothing.

Tests: composite fixtures, nested and KYAML-quoted, in all three suites.
Composite-blind mutants fail 2 cases in each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqfpQtLuMczxtgG1U9PkLW
standards#1122, criterion 3. When the lock already holds a SHA-keyed
record for a commit and `gh actions-lock` adds a tag-keyed record for
the same commit, re-keying the tag to the SHA left two records under one
key, which the lockfile reader rejects ("already defined"). A new pass
keeps the first record and drops the duplicate with its body, and a list
(workflow entry, record uses:) that now names a ref twice keeps it once.

New suite scripts/tests/relock-sha-keys-test.sh: the planted duplicate
fails 5 assertions on the old script and passes on the new; plain re-key,
tag-kept-when-still-written and idempotence are covered too. On a copy of
this repo's lock the script changes nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqfpQtLuMczxtgG1U9PkLW
standards#1122, criterion 4. regen-dependabot-locks.sh refused any repo
with .github/actions/ as `composite-unsupported`, because the chain
dropped composite refs and --verify-local still passed the result. The
chain now keeps and adds them, and regen_lock runs check-lock-sync.sh
after --verify-local: a lock it rejects is `unverified`, not committed.
That is also the gate the PR meets in CI, and it catches job-level
reusable refs, which --verify-local cannot see either.

Tests: the composite fixture now regenerates (`changed`); a lock the
sync check rejects is `unverified`.

The issue's own acceptance run (the full chain with `gh actions-lock` on
main, accepted by --verify-local) needs the tool, which is not available
in the session that wrote this; it is left to the owner.

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

coderabbitai Bot commented Oct 14, 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 24 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: ca11d66b-3d5c-4218-9033-b4e0f46ec80d
📥 Commits

Reviewing files that changed from the base of the PR and between 507b89f and b07a8f5.

⛔ Files ignored due to path filters: 1
  • .github/workflows/actions.lock is excluded by !**/*.lock
📒 Files selected for processing: 11
  • docs/reorg/2026-10-14-tidy/issue-triage-1049-1234.adoc
  • scripts/check-lock-sync.sh
  • scripts/complete-job-refs.sh
  • scripts/prune-stale.sh
  • scripts/regen-dependabot-locks.sh
  • scripts/relock-sha-keys.sh
  • scripts/tests/check-lock-sync-test.sh
  • scripts/tests/complete-job-refs-test.sh
  • scripts/tests/prune-stale-test.sh
  • scripts/tests/regen-dependabot-locks-test.sh
  • scripts/tests/relock-sha-keys-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.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

K9 contract conformance

run https://github.com/hyperpolymath/standards/actions/runs/38496144712

K9 normative contract typecheck

k9_contract.ncl typechecks

K9 contract self-test

== the bash mirrors cannot drift from the normative contract ==
ok   leash_levels mirrors k9_contract.ncl
ok   core_capabilities mirrors k9_contract.ncl
ok   contract_version mirrors k9_contract.ncl
ok   schema_major mirrors k9_contract.ncl
== capability arithmetic (§8) ==
ok   capability_ok fs.read accepted
ok   capability_ok rollback.apply accepted
ok   capability_ok x-acme.gpu.alloc accepted
ok   capability_ok x-acme rejected
ok   capability_ok x-.gpu rejected
ok   capability_ok fs.delete rejected
ok   capability_ok  rejected
== the extractor ==
ok   extracts pedigree.security.leash
ok   extracts pedigree.component_type
ok   extracts pedigree.metadata.name
ok   pedigree leash is not reported as top-level leash
ok   required_capabilities for a quiet component
ok   required_capabilities follows allow_network
== the envelope strip keeps line numbers (§3.6) ==
ok   line 1 becomes a comment
ok   line count is preserved
ok   schema_version stays on line 5
== L3: signature presence is not verification (§10) ==
ok   no verifier -> K9-C001 is SKIPPED, never a pass
ok   the skip states presence does not authorise 'Hunt
ok   verifier accepts -> verdict 'Verified, no K9-C001 finding
ok   verifier refuses -> K9-C001 error, verdict 'Rejected
== the fixture runner's attribution cannot be fooled by a filename ==
ok   every extracted finding is well-formed rule+layer
ok   the rule that really fired is attributed
ok   a rule named only in the filename is NOT attributed
ok   K9-C001 is present as a skipped finding
ok   and that same finding is NOT extractable as a rejection
== the dialect discriminator agrees with the coordination grammar (§4.3) ==
ok   magic file opening '---' is coordination
ok   magic file opening 'metadata:' is coordination
ok   plain file opening '---' is unclaimed
ok   plain file opening 'metadata:' is unclaimed
== no Nickel reserved word is used as an identifier ==
ok   the contract and all 27 fixtures avoid Nickel's reserved words

self-test: all assertions passed

K9 conformance fixtures

== positive controls (must pass) ==
ok   extension-capability.k9.ncl
ok   extension-fields.k9.ncl
ok   hunt-fully-granted.k9.ncl
ok   kennel-data.k9.ncl
ok   library-base.ncl
ok   yard-typed-config.k9.ncl

== negative controls (must fail, by the named rule) ==
ok   L0-K9-E001-bad-magic.k9.ncl (rejected by K9-E001 at L0)
ok   L0-K9-E002-nul-byte.k9.ncl (rejected by K9-E002 at L0)
ok   L0-K9-E003-crlf.k9.ncl (rejected by K9-E003 at L0)
ok   L0-K9-E004-no-spdx.k9.ncl (rejected by K9-E004 at L0)
ok   L0-K9-E005-unclaimed-body.k9.ncl (rejected by K9-E005 at L0)
ok   L0-K9-S012-library-with-pedigree.ncl (rejected by K9-S012 at L0)
ok   L0-K9-S014-stray-leash.ncl (rejected by K9-S014 at L0)
ok   L1-K9-S001-no-pedigree.k9.ncl (rejected by K9-S001 at L1)
ok   L1-K9-S002-wrong-major.k9.ncl (rejected by K9-S002 at L1)
ok   L1-K9-S003-todo-component-type.k9.ncl (rejected by K9-S003 at L1)
ok   L1-K9-S004-unknown-leash.k9.ncl (rejected by K9-S004 at L1)
ok   L1-K9-S005-missing-name.k9.ncl (rejected by K9-S005 at L1)
ok   L1-K9-S006-unknown-capability.k9.ncl (rejected by K9-S006 at L1)
ok   L1-K9-S007-ungranted-flag.k9.ncl (rejected by K9-S007 at L1)
ok   L1-K9-S008-hunt-signature-not-required.k9.ncl (rejected by K9-S008 at L1)
ok   L1-K9-S009-hunt-no-signature-block.k9.ncl (rejected by K9-S009 at L1)
ok   L1-K9-S010-hunt-empty-side-effects.k9.ncl (rejected by K9-S010 at L1)
ok   L1-K9-S011-recipes-at-yard.k9.ncl (rejected by K9-S011 at L1)
ok   L1-K9-S013-dangling-import.k9.ncl (rejected by K9-S013 at L1)
ok   L2-K9-N001-two-segment-version.k9.ncl (rejected by K9-N001 at L2)
ok   L2-K9-N001-wrong-field-type.k9.ncl (rejected by K9-N001 at L2)

fixtures: 6 positive, 21 negative (0 needing nickel), 0 failure(s)

K9 corpus conformance (L2)

[validate-k9] 14 conforming, 0 grandfathered (layer all, contract v1.0.0)

@hyperpolymath
hyperpolymath marked this pull request as ready for review October 14, 2026 01:33
@hyperpolymath
hyperpolymath merged commit 50cfbd5 into main Oct 14, 2026
63 of 65 checks passed
@hyperpolymath
hyperpolymath deleted the claude/exciting-noether-dzrfrr branch October 14, 2026 01:35
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.

Lock repair chain drops composite-action edges, and --verify-local accepts the result

2 participants