Repository navigation
test(actions-lock): closure contract corpus shared with standards - #941
Conversation
Owner decision on standards#963, criterion 5: Hypatia is the authority
for actions.lock closure. standards adds `close-lock.sh --check` as a
local pre-push guard, and a shared corpus binds the two implementations.
test/fixtures/actions_lock/closure/ is a byte-identical copy of
standards' scripts/tests/fixtures/actions-lock-closure/. It holds five
locks and expected.tsv:
- closed v0.0.2;
- closed v0.0.3 with hostname;
- closed, with owner/repo spelled in a different case from its record;
- dangling, with a record's nested uses: naming a ref that has no record
(the standards#963 shape);
- dangling, with a workflows: ref that has no record.
The new test reads expected.tsv at compile time (@external_resource).
It checks that there is one row per lock, that ActionsLock.parse/1
returns {:ok, _} for each closed lock, and that a dangling lock fails as
workflow_dependencies_missing or transitive_dependencies_missing with
exactly the listed refs, both sides compared through normalise_ref/1.
The parser is unchanged. At aa34a4c it already agrees on all five rows.
Verification: Elixir 1.14 / OTP 25, running the parser and the test
files standalone because hex.pm is unreachable from this session.
- This file passes 6/6.
- actions_lock_test.exs is unchanged, at 23 tests with 1 failure. The
failure is the legacy Hypatia.Rules case, which needs the full engine,
exactly as on main.
- With validate_transitive_dependencies stubbed to :ok, the new test
fails exactly the dangling-nested row.
- Code.format_string!/1 leaves the file unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbbnKqskQA5mj5gfMniPzi
|
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 53 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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
I'll watch the run on Generated by Claude Code |
Implements the recommendation for #963 criterion 5, as the owner decided: - **Hypatia stays the authority** for `actions.lock` closure. - A deterministic local check guards the same property before push. - A shared corpus of test lockfiles binds the two implementations. The Hypatia half is hyperpolymath/hypatia#941. ## What changes **`scripts/close-lock.sh --check [DIR | LOCKFILE]`** - Reports the dangling edges the repair passes would close, and nothing else: no `gh` call, no write. - An edge dangles when a ref under `workflows:` or in a record's nested `uses:` has no top-level record of its own. - owner/repo compare case-insensitively and refs compare exactly. That is the same normalisation as GitHub's parser, #1294/#1314, and Hypatia's `ActionsLock.normalise_ref/1`. - Exit codes: 0 closed, 1 dangling, 2 no lock. **`scripts/lock-selfcheck.sh`** - A dangling edge becomes a third POISON verdict, after unkeyed refs and self-SHA refs. - It runs inside the existing "Lockfile self-consistency" job, so no required-check name changes. **`.githooks/pre-commit`** - When `actions.lock` is staged, the hook checks the staged content. - Like the canonical-names guard, it runs only where the repository carries `scripts/close-lock.sh`. **`scripts/tests/fixtures/actions-lock-closure/`**: five locks plus `expected.tsv`: - closed v0.0.2 - closed v0.0.3 with `hostname` - closed case-variant - the #963 nested shape - a dangling `workflows:` ref Hypatia at `aa34a4cc` agrees on all five. hyperpolymath/hypatia#941 vendors a byte-identical copy and tests `ActionsLock.parse/1` against the same table. If either side drifts, one of the two suites goes red. ## Tests - **`close-lock-test`: 34/34, 8 of them new.** - Each corpus row is checked by exit code and by the exact dangling list. - The lock bytes must be unchanged, `gh` must not be called, and a missing lock exits 2. - Two mutants each fail exactly one row: no case folding, and ignoring `workflows:` refs. - **`lock-selfcheck-test`: 9/9, 2 of them new.** - The base fixture lock had no `dependencies:` section, so it was not closed (Hypatia rejects that shape). It now carries the record. - A mutant without the closure verdict fails both new cases. - **Unchanged and passing:** `apply-baseline-test` 52/52, `exemption-ratchet-test` 14/14, `hypatia-blocking-gate-test` 46/46. - **This branch's own state:** its lock is closed, and `lock-selfcheck.sh HEAD` reports SELF-CONSISTENT. No licence headers are touched. The new fixture files carry MPL-2.0 SPDX from birth. Refs #963 · companion: hyperpolymath/hypatia#941 --- _Generated by [Claude Code](https://claude.ai/code/session_01TbbnKqskQA5mj5gfMniPzi)_ Co-authored-by: Claude <noreply@anthropic.com>
## Summary This is the last step of the baseline-gate stack. `governance.yml` and `hypatia-scan.yml` pass `hypatia-ref: aa34a4c` (#940), which predates the rule fixes that clear this repo's own false positives. This PR moves both to **hypatia `main` at `23dc74d`**, which contains: - #938: ActionsLock accepts v0.0.3 locks (already in `aa34a4c`); - #942: WH013 judges writes per job and skips PAT/app-token writes. This clears `estate-rescan.yml`; `inbox-steward-intake.yml` was also repaired in #943; - #947: RE005 skips `trap` handlers, and job extraction survives comments between jobs. `uses:` refs are unchanged, so `actions.lock` is untouched. ## What this PR's governance run will show This PR's own `governance / Validate Hypatia Baseline` run is the measurement: it builds the scanner at `23dc74d`. The jump from `aa34a4c` also brings #931's new rules (WF026, WF027, PIN-003), which haven't been measured on this repo yet. hex.pm is blocked in my session, so I can't build the escript to pre-run them. **Expected remaining findings, by class:** - **RE001 (harden-runner):** most of the original 18 jobs, including the 2 dispatched label workflows, which must stay action-free. - These need an owner decision; the harden-runner steps were removed again in #948 because the lock has no record. - **WH013, RE004, RE005, `invalid_actions_lock`:** expected 0. I'll post the measured list here once the run completes. ## Type of change - [x] 🔧 Build / CI / tooling ## How has this been verified? - `actionlint` 1.7.7 is clean on both files. - `23dc74d` is `origin/main` at the time of writing. `git log aa34a4c..23dc74d` contains #942, #947, #945, #931, #941 and #936. ## Checklist - [ ] My commits are **signed**. The session's signing shim is platform-mediated. - [x] I ran the project's own checks/tests locally and they pass. - [x] New files carry the correct SPDX (none added). - [x] Docs are updated (the pin comment names what it carries). - [x] I have not introduced a soundness hole. --- _Generated by [Claude Code](https://claude.ai/code/session_01CYR5bf5FBLS7cXQK72aVNM)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
This implements the owner's decision on hyperpolymath/standards#963, criterion 5:
actions.lockclosure.close-lock.sh --check, so the same defect is caught before push.A lock is closed when every ref under
workflows:and in a record's nesteduses:has a top-level record of its own, comparing owner/repo case-insensitively and refs exactly. This is theworkflow_dependencies_missing/transitive_dependencies_missingcheck inActionsLock.Changes
test/fixtures/actions_lock/closure/scripts/tests/fixtures/actions-lock-closure/(checked with sha256 anddiff -r): five locks plusexpected.tsv. Each file carries its own SPDX header, so the existing.reuse/dep5entry for verbatimgh actions-lockoutput is left alone.test/rules/actions_lock_closure_corpus_test.exsexpected.tsvat compile time (@external_resource). Checks there is exactly one row per lock, closed locks parse{:ok, _}, and each dangling lock fails with the expected kind and exactly the listed refs, both compared throughnormalise_ref/1.ActionsLock.parse/1ataa34a4ccclosed-v0.0.2.lock{:ok, _}closed-v0.0.3.lock(withhostname: 'github.com'){:ok, _}closed-case-variant.lock(Example/Rust-Cache@…vs recordexample/rust-cache@…){:ok, _}dangling-nested.lock(the standards#963 shape)example/setup-python@v2transitive_dependencies_missingdangling-workflow-ref.lockexample/generator@4444…workflow_dependencies_missinglib/is unchanged. The parser already agrees on all five rows. The case-variant row agrees becausenormalise_ref/1lowercases owner/repo as each ref is read, which is the same rule standards' check applies.Testing
hex.pm is unreachable from this session, so
mix testcannot fetch deps. I ran the parser and test files standalone with Elixir 1.14 / OTP 25 (compiledlib/rules/actions_lock.ex, then ran the test files under ExUnit):actions_lock_closure_corpus_test.exsactions_lock_test.exs(unchanged)Hypatia.Rulescase, which needs the full engine. Same onmain.validate_transitive_dependenciesstubbed to:okdangling-nested.lockCode.format_string!/1on the new fileCI's full
mix testis the authoritative run.Keeping the copies in step
The moduledoc says to change both copies together. If either side edits its copy alone, its own suite still passes against its own table, so drift becomes visible only when the other side is re-synced or re-run against it. A cross-repo checksum gate would need network access in one of the two CIs, so I left it out. It can be added if you want the stronger guarantee.
Generated by Claude Code