[PR #307] [MERGED] feat(workflow-pins): assert every uses: ref is SHA-pinned, and pin the last two #310

Closed
opened 2026-10-26 22:38:19 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/github-workflows/pull/307
Author: @mattmillerai
Created: 9/18/2026
Status: ✅ Merged
Merged: 9/18/2026
Merged by: @mattmillerai

Base: main ← Head: matt/be-15255-pin-actions-sha


📝 Commits (4)

  • 5614a72 feat(workflow-pins): assert every uses: ref is SHA-pinned, and pin the last two
  • 5156fb6 fix(workflow-pins): hold container images to a digest, and stop the flow scan reading prose as structure
  • 19be329 docs(workflow-pins): unnest the KNOWN_UNPINNED entry-format code span
  • bbd595e Merge remote-tracking branch 'origin/main' into matt/be-15255-pin-actions-sha

📊 Changes

8 files changed (+1107 additions, -29 deletions)

View changed files

📝 .github/workflow-pins/README.md (+78 -2)
📝 .github/workflow-pins/check_workflow_pins.py (+362 -11)
📝 .github/workflow-pins/tests/test_check_workflow_pins.py (+599 -1)
📝 .github/workflows/cursor-review.yml (+2 -2)
📝 .github/workflows/pr-size.yml (+2 -2)
📝 .github/workflows/test-refresh-reviewers.yml (+2 -2)
📝 .github/workflows/test-workflow-pins.yml (+55 -3)
📝 AGENTS.md (+7 -6)

📄 Description

ELI-5

AGENTS.md says to pin every GitHub Action by its full commit SHA, because a tag can be moved by whoever owns the action. Two refs in .github/workflows/test-refresh-reviewers.yml were still on tags — and they had drifted twice since anyone noticed (@v6 → @v7 → @v7.0.0), because Dependabot only ever swaps a tag for a narrower tag and never writes a SHA. This pins both, and adds the lint that stops them coming back.

What changed

The two pins. actions/checkout@v7 → @3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 and actions/setup-python@v7.0.0 → @5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 — the SHAs already used at 73 and 13 other sites here. I verified against GitHub that both tags currently resolve to exactly those commits (gh api repos/actions/<a>/git/matching-refs/tags/v), so this freezes the code those steps already ran rather than changing it.

The assertion. check_action_pins in .github/workflow-pins/check_workflow_pins.py fails any uses: under .github/workflows/ whose ref is not a full 40-hex SHA. It is a separate walk rather than a fourth branch of check_dir, because check_dir returns early for any file that does not declare a workflows_ref input — which is most of that directory, and is precisely where both floating refs lived. A pin rule that only covered reusables would have missed the exact drift it exists to catch.

KNOWN_UNPINNED is the debt list, with the same self-draining contract as KNOWN_EXEMPT: a stale entry is an error. Entries are (workflow filename, full uses value) pairs — the file alone would pre-exempt every other action in it, the ref alone would pre-exempt the same floating ref wherever it were copied, and keying on the value means a Dependabot tag bump retires the entry rather than leaving it silently protecting nothing. That last part is how this debt hid in the first place. It ships empty: both refs it was written for are pinned in this change.

What the walker does and does not read. Block (- uses: a/b@ref) and flow spellings, the latter including YAML's implicit single-pair form (steps: [uses: a/b@ref]); skips ./ paths, the one form naming nothing pinnable, and REPORTS the skip; refuses a uses: whose value it cannot read on its line (uses: >-, or the value on the line below) rather than passing it unchecked; and skips |/> block-scalar text, a single-line run: (shell, not YAML), a trailing comment, and any match inside a quoted scalar — which together are the whole reason this is not a grep. A container image is NOT skipped: it is held to @sha256:<64-hex>, because docker://alpine:3.20 is a mutable tag on exactly the terms @v7 is. The two gaps that remain (a key spelled through a YAML escape; an action input literally named uses) are recorded as documented limits in the README — both need a YAML parser, a dependency this repo deliberately does not carry.

A one-scan refactor. _in_block_scalar now answers from a new single-pass _block_scalar_mask. Calling the per-index form once per uses: line made the lint 6.7× slower (0.76s → 5.1s); with the mask it is 0.79s. Behaviour is unchanged, deliberately — the state machine is the same code moved, pinned by an equivalence test asserting the mask equals the per-index answer at every index, on top of the 341 pre-existing tests that already exercise it.

Version comments. The four outliers on the artifact actions are normalised to the majority # v7 / # v8 spelling. Every SHA in the repo now carries exactly one spelling. create-github-app-token's two deliberate pins are untouched.

Deviation from the plan — the smoke-test fixtures are now SHA-pinned

The plan said explicitly not to touch the @abc fixtures in test-workflow-pins.yml. I changed two of them, in the heredocs that write temp-dir fixtures, and this is the one judgment call worth a reviewer's attention.

The block-scalar skip handles the stated hazard: the lint does not flag those lines where they sit, and check_workflow_pins.py exits 0 on the repo (155 refs pinned). But the heredocs are written out to real files that the smoke steps then lint with --workflows-dir, where they are structure. Leaving them on @abc had two consequences:

  • "an unreadable steps: warns but does not fail" asserts exit 0 and would have gone red outright — CI failure on a ref that is not what it tests.
  • "an unguarded ref checkout fails the lint" only asserts a non-zero exit. A pin error would have satisfied that on its own, so the step would keep passing even if the unguarded-checkout check it exists for stopped firing entirely. Confirmed after the change: that fixture now produces exactly one error — the unguarded-checkout one — and zero pin errors.

@abc is not load-bearing in either fixture — neither step's assertion depends on the ref being a non-SHA. One existing unit test (test_notices_never_fail_the_run) does the same substitution locally on its shared fixture, for the same reason and with the same reasoning in a comment; the ~145 other @abc string fixtures are untouched, because they exercise check_dir, which this change does not touch.

I also added a fourth smoke step covering the pin check at the CLI, on a fixture that declares no workflows_ref at all — so nothing but the pin check can account for its non-zero exit.

Review round — the panel's ten findings (5156fb6)

Eight fixed, one declined with the census, one recorded as a documented limit.

The Medium, and the only hole a floating ref could have walked through. _unpinnable exempted every docker:// value on the grounds that images are "immutable by registry digest" — but nothing checked that a digest was there, and the suite encoded docker://alpine:3.20, a mutable tag, as PASSING. Images are now held to @sha256:<64-hex>, matched whole, so a truncated digest fails too, and a 40-hex commit SHA does not pin an image any more than a sha256: digest pins an action. Local paths remain the one unpinnable form.

The silent miss. YAML's implicit single-pair flow form (steps: [uses: a/b@v7]) carries neither { nor ,, so it matched neither pattern and passed unchecked — the one direction this lint must never fail in. [ joins the boundary class.

Three false positives on compliant workflows, all in the mid-line flow scan: a trailing comment (# was {uses: a/b@v7}), a single-line run: echo '{"uses": "a/b@v1"}', and a flow mapping split across physical lines, where the block pattern claimed the continuation line and carried }] into the ref. The scan now runs comment-stripped; a single-line run: is skipped on its own key (anchored on the LINE's key, so steps: [{id: a, run: …}, {uses: …}] is untouched); and _uses_value trims trailing flow punctuation. Remaining matches are quote-masked on the strict reading, not the weak one — the weak reading would let a stray apostrophe in an unquoted scalar ([{name: Don't, uses: a/b@v7}]) wedge a scalar open and drop a real floating ref out of coverage. A false alarm is loud; a silent miss is not.

Two tests that could not fail. The mask-equivalence test was assertEqual(mask, [_in_block_scalar(…)]) after _in_block_scalar became one line over the mask — it compared the mask with itself, so the rewrite the PR body called "pinned by an equivalence test" was pinned by nothing. The expected mask is now a literal. And the pin smoke step asserted only a non-zero exit, which a traceback satisfies; it now greps for by tag, not by commit SHA.

Reporting. _unpinnable's reason was discarded despite a docstring saying it "keeps the skip visible at the call site"; it is now printed (not pinnable <file>:<line>: <value> — <reason>) and counted, so swapping a pinned ref for a local path cannot quietly shrink the coverage number. A uses: with no @ at all is no longer described as pinned "by tag" — different defect, different remedy, own message. And one KNOWN_UNPINNED entry covering two identical sites is counted once, matching check_dir.

Declined: the # v7.0.1 → # v7 normalisation. The rule is one SHA, one spelling — not "prefer the major". Before this PR, upload-artifact@043fb46 was 12×# v7 / 2×# v7.0.1 and download-artifact@3e5f45b was 14×# v8 / 2×# v8.0.1, while checkout@3d3c42e is 75×# v7.0.1 and setup-python@5fda3b9 is 14×# v7.0.0. So the four edits move outliers onto their own SHA's majority, and the new pins take theirs for the same reason. # vN is also the spelling AGENTS.md documents.

Acceptance

All seven criteria are met. The two refs are pinned; the assertion exists with a self-draining exemption list; it does not flag the heredoc fixture and the repo lint exits 0; tests cover a floating ref failing, a SHA-pinned ref passing, and a stale entry erroring; the four version-comment outliers are normalised with create-github-app-token left alone; the suite is green.

Sweeps

  • .github/workflows/: 156 uses: directives, 155 live (one is the heredoc fixture). 155/155 SHA-pinned, 0 floating, 0 exempt.
  • The half not being fixed — outside .github/workflows/: 27 uses: directives across docs/callers/*.md, README.md, two directory READMEs and one Python test fixture. All 27 are placeholders or truncated illustrations (@<full-commit-sha>, @<sha>, @8c4ff3e…); none teaches a floating tag, and none is executable. Nothing to fix there, and the new lint deliberately does not reach them.
  • Version-comment coverage: 0 of 155 SHA-pinned refs lack a trailing version comment, so that half of the convention is fully satisfied today — but it is not linted (see Residual).
  • Unreadable uses: spellings (uses: >-, value on the next line): 0 occurrences in the repo, so the new refusal changes nothing today.

Provenance

  • Authored by: agent-work loop
  • Verified: python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py': 382 passed, 0 failed (341 before this PR, 39 new, 2 rewritten); python3 .github/workflow-pins/check_workflow_pins.py: exit 0, 155 refs SHA-pinned, 0 exempt, 0 not pinnable, 0.82s (the pre-refactor baseline was 0.76s; the 6.7x regression the mask fixed is not back); all four test-workflow-pins.yml CLI smoke steps extracted and reproduced locally against 5156fb6: 4/4 as specified, including the exit-0 one and the new grep assertion; check_agents_md.py --root .: passed (the same 2 pre-existing warnings — 156-line AGENTS.md, no CODEOWNERS); the other six Python suites (agents-md-integrity, cursor-review, groom 402, public-repo-hygiene 204, refresh-reviewers 53, linear-ticket): all OK; both pin SHAs and all four version comments verified against GitHub's tag refs, and the spelling census re-run against origin/main.
  • Deviations: the two test-workflow-pins.yml heredoc fixtures are SHA-pinned against the plan's explicit instruction, and one existing unit test's fixture is locally substituted — both explained under "Deviation from the plan" above. The review round then went beyond the stated acceptance criteria in one place: docker:// images are now CHECKED rather than exempted, which no criterion asked for, because the exemption as written carved out a category of the drift the check exists to ban.

Residual

The # vN version comment is not linted, only the SHA. The convention is "full commit SHA with a trailing # v1 comment", and the new check asserts only the first half. All 155 pinned refs satisfy the second half today, so there is nothing to fix — but nothing stops a future ref landing as a bare SHA with no comment, which is the form that makes a pin unreadable at review time. Worth its own change; it was outside the stated acceptance criteria and adding it would have widened this diff into unrelated files.

Comment whitespace is inconsistent and unlinted. 38 of the 155 pinned refs write two spaces before the # and 118 write one (e.g. bump-pr-derisk-callers.yml:147, test-linear-ticket.yml:41,45). Purely cosmetic, no runtime effect, and the plan named only the four version-text outliers, which are fixed. I left the whitespace alone rather than churning 38 lines across the workflow directory in a PR about pinning; normalising it is a one-line sed whenever someone wants it.

The refusal of uses: >- denies a spelling GitHub Actions itself accepts. I checked rather than assumed: that YAML is valid and Actions would resolve it, so this is a policy refusal, not a claim the capability does not exist. It is stated as such in the error message, which names the one-line remedy (write the action inline). It affects 0 sites in the repo today. If a legitimate need for the folded spelling ever appears, the right fix is to teach the walker the continuation line, not to add an exemption — the debt list deliberately cannot silence this error.

This repo still runs no pinact / zizmor on itself. The new check is a hand-rolled 40-hex assertion, not the pin-validation that consumer CI runs, so it enforces the SHA shape but not everything those tools check. Bringing either into this repo's own CI is a separate change with its own dependency and runtime cost.

Closes #172


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Comfy-Org/github-workflows/pull/307 **Author:** [@mattmillerai](https://github.com/mattmillerai) **Created:** 9/18/2026 **Status:** ✅ Merged **Merged:** 9/18/2026 **Merged by:** [@mattmillerai](https://github.com/mattmillerai) **Base:** `main` ← **Head:** `matt/be-15255-pin-actions-sha` --- ### 📝 Commits (4) - [`5614a72`](https://github.com/Comfy-Org/github-workflows/commit/5614a723e49a51b6ed247942d277143c37db929a) feat(workflow-pins): assert every `uses:` ref is SHA-pinned, and pin the last two - [`5156fb6`](https://github.com/Comfy-Org/github-workflows/commit/5156fb6866a774c4b2cc1929c504053b6854b2e5) fix(workflow-pins): hold container images to a digest, and stop the flow scan reading prose as structure - [`19be329`](https://github.com/Comfy-Org/github-workflows/commit/19be3291c5d4bd329a36e793733444b9d2e8695d) docs(workflow-pins): unnest the KNOWN_UNPINNED entry-format code span - [`bbd595e`](https://github.com/Comfy-Org/github-workflows/commit/bbd595e62d111ce96fe615f8968bcb8b46bdf68e) Merge remote-tracking branch 'origin/main' into matt/be-15255-pin-actions-sha ### 📊 Changes **8 files changed** (+1107 additions, -29 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflow-pins/README.md` (+78 -2) 📝 `.github/workflow-pins/check_workflow_pins.py` (+362 -11) 📝 `.github/workflow-pins/tests/test_check_workflow_pins.py` (+599 -1) 📝 `.github/workflows/cursor-review.yml` (+2 -2) 📝 `.github/workflows/pr-size.yml` (+2 -2) 📝 `.github/workflows/test-refresh-reviewers.yml` (+2 -2) 📝 `.github/workflows/test-workflow-pins.yml` (+55 -3) 📝 `AGENTS.md` (+7 -6) </details> ### 📄 Description ## ELI-5 `AGENTS.md` says to pin every GitHub Action by its full commit SHA, because a tag can be moved by whoever owns the action. Two refs in `.github/workflows/test-refresh-reviewers.yml` were still on tags — and they had drifted twice since anyone noticed (`@v6` → `@v7` → `@v7.0.0`), because Dependabot only ever swaps a tag for a narrower tag and never writes a SHA. This pins both, and adds the lint that stops them coming back. ## What changed **The two pins.** `actions/checkout@v7` → `@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1` and `actions/setup-python@v7.0.0` → `@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0` — the SHAs already used at 73 and 13 other sites here. I verified against GitHub that both tags currently resolve to exactly those commits (`gh api repos/actions/<a>/git/matching-refs/tags/v`), so this freezes the code those steps already ran rather than changing it. **The assertion.** `check_action_pins` in `.github/workflow-pins/check_workflow_pins.py` fails any `uses:` under `.github/workflows/` whose ref is not a full 40-hex SHA. It is a separate walk rather than a fourth branch of `check_dir`, because `check_dir` returns early for any file that does not declare a `workflows_ref` input — which is most of that directory, and is precisely where both floating refs lived. A pin rule that only covered reusables would have missed the exact drift it exists to catch. `KNOWN_UNPINNED` is the debt list, with the same self-draining contract as `KNOWN_EXEMPT`: a stale entry is an error. Entries are `(workflow filename, full uses value)` pairs — the file alone would pre-exempt every other action in it, the ref alone would pre-exempt the same floating ref wherever it were copied, and keying on the value means a Dependabot tag bump *retires* the entry rather than leaving it silently protecting nothing. That last part is how this debt hid in the first place. It ships empty: both refs it was written for are pinned in this change. **What the walker does and does not read.** Block (`- uses: a/b@ref`) and flow spellings, the latter including YAML's implicit single-pair form (`steps: [uses: a/b@ref]`); skips `./` paths, the one form naming nothing pinnable, and REPORTS the skip; refuses a `uses:` whose value it cannot read on its line (`uses: >-`, or the value on the line below) rather than passing it unchecked; and skips `|`/`>` block-scalar text, a single-line `run:` (shell, not YAML), a trailing comment, and any match inside a quoted scalar — which together are the whole reason this is not a `grep`. A container image is NOT skipped: it is held to `@sha256:<64-hex>`, because `docker://alpine:3.20` is a mutable tag on exactly the terms `@v7` is. The two gaps that remain (a key spelled through a YAML escape; an action *input* literally named `uses`) are recorded as documented limits in the README — both need a YAML parser, a dependency this repo deliberately does not carry. **A one-scan refactor.** `_in_block_scalar` now answers from a new single-pass `_block_scalar_mask`. Calling the per-index form once per `uses:` line made the lint 6.7× slower (0.76s → 5.1s); with the mask it is 0.79s. Behaviour is unchanged, deliberately — the state machine is the same code moved, pinned by an equivalence test asserting the mask equals the per-index answer at every index, on top of the 341 pre-existing tests that already exercise it. **Version comments.** The four outliers on the artifact actions are normalised to the majority `# v7` / `# v8` spelling. Every SHA in the repo now carries exactly one spelling. `create-github-app-token`'s two deliberate pins are untouched. ## Deviation from the plan — the smoke-test fixtures are now SHA-pinned The plan said explicitly not to touch the `@abc` fixtures in `test-workflow-pins.yml`. I changed two of them, in the heredocs that write temp-dir fixtures, and this is the one judgment call worth a reviewer's attention. The block-scalar skip handles the stated hazard: the lint does not flag those lines where they sit, and `check_workflow_pins.py` exits 0 on the repo (155 refs pinned). But the heredocs are written out to real files that the smoke steps then lint with `--workflows-dir`, where they *are* structure. Leaving them on `@abc` had two consequences: - **"an unreadable `steps:` warns but does not fail" asserts exit 0** and would have gone red outright — CI failure on a ref that is not what it tests. - **"an unguarded ref checkout fails the lint" only asserts a non-zero exit.** A pin error would have satisfied that on its own, so the step would keep passing even if the unguarded-checkout check it exists for stopped firing entirely. Confirmed after the change: that fixture now produces exactly one error — the unguarded-checkout one — and zero pin errors. `@abc` is not load-bearing in either fixture — neither step's assertion depends on the ref being a non-SHA. One existing unit test (`test_notices_never_fail_the_run`) does the same substitution locally on its shared fixture, for the same reason and with the same reasoning in a comment; the ~145 other `@abc` string fixtures are untouched, because they exercise `check_dir`, which this change does not touch. I also added a fourth smoke step covering the pin check at the CLI, on a fixture that declares no `workflows_ref` at all — so nothing but the pin check can account for its non-zero exit. ## Review round — the panel's ten findings (5156fb6) Eight fixed, one declined with the census, one recorded as a documented limit. **The Medium, and the only hole a floating ref could have walked through.** `_unpinnable` exempted every `docker://` value on the grounds that images are "immutable by registry digest" — but nothing checked that a digest was there, and the suite encoded `docker://alpine:3.20`, a mutable tag, as PASSING. Images are now held to `@sha256:<64-hex>`, matched whole, so a truncated digest fails too, and a 40-hex commit SHA does not pin an image any more than a `sha256:` digest pins an action. Local paths remain the one unpinnable form. **The silent miss.** YAML's implicit single-pair flow form (`steps: [uses: a/b@v7]`) carries neither `{` nor `,`, so it matched neither pattern and passed *unchecked* — the one direction this lint must never fail in. `[` joins the boundary class. **Three false positives on compliant workflows**, all in the mid-line flow scan: a trailing comment (`# was {uses: a/b@v7}`), a single-line `run: echo '{"uses": "a/b@v1"}'`, and a flow mapping split across physical lines, where the block pattern claimed the continuation line and carried `}]` into the ref. The scan now runs comment-stripped; a single-line `run:` is skipped on its own key (anchored on the LINE's key, so `steps: [{id: a, run: …}, {uses: …}]` is untouched); and `_uses_value` trims trailing flow punctuation. Remaining matches are quote-masked on the **strict** reading, not the weak one — the weak reading would let a stray apostrophe in an unquoted scalar (`[{name: Don't, uses: a/b@v7}]`) wedge a scalar open and drop a *real* floating ref out of coverage. A false alarm is loud; a silent miss is not. **Two tests that could not fail.** The mask-equivalence test was `assertEqual(mask, [_in_block_scalar(…)])` *after* `_in_block_scalar` became one line over the mask — it compared the mask with itself, so the rewrite the PR body called "pinned by an equivalence test" was pinned by nothing. The expected mask is now a literal. And the pin smoke step asserted only a non-zero exit, which a traceback satisfies; it now greps for `by tag, not by commit SHA`. **Reporting.** `_unpinnable`'s reason was discarded despite a docstring saying it "keeps the skip visible at the call site"; it is now printed (`not pinnable <file>:<line>: <value> — <reason>`) and counted, so swapping a pinned ref for a local path cannot quietly shrink the coverage number. A `uses:` with no `@` at all is no longer described as pinned "by tag" — different defect, different remedy, own message. And one `KNOWN_UNPINNED` entry covering two identical sites is counted once, matching `check_dir`. **Declined: the `# v7.0.1` → `# v7` normalisation.** The rule is one SHA, one spelling — not "prefer the major". Before this PR, `upload-artifact@043fb46` was 12×`# v7` / 2×`# v7.0.1` and `download-artifact@3e5f45b` was 14×`# v8` / 2×`# v8.0.1`, while `checkout@3d3c42e` is 75×`# v7.0.1` and `setup-python@5fda3b9` is 14×`# v7.0.0`. So the four edits move outliers onto their own SHA's majority, and the new pins take theirs for the same reason. `# vN` is also the spelling AGENTS.md documents. ## Acceptance All seven criteria are met. The two refs are pinned; the assertion exists with a self-draining exemption list; it does not flag the heredoc fixture and the repo lint exits 0; tests cover a floating ref failing, a SHA-pinned ref passing, and a stale entry erroring; the four version-comment outliers are normalised with `create-github-app-token` left alone; the suite is green. ## Sweeps - **`.github/workflows/`:** 156 `uses:` directives, 155 live (one is the heredoc fixture). **155/155 SHA-pinned**, 0 floating, 0 exempt. - **The half not being fixed — outside `.github/workflows/`:** 27 `uses:` directives across `docs/callers/*.md`, `README.md`, two directory READMEs and one Python test fixture. **All 27 are placeholders or truncated illustrations** (`@<full-commit-sha>`, `@<sha>`, `@8c4ff3e…`); none teaches a floating tag, and none is executable. Nothing to fix there, and the new lint deliberately does not reach them. - **Version-comment coverage:** 0 of 155 SHA-pinned refs lack a trailing version comment, so that half of the convention is fully satisfied today — but it is *not* linted (see Residual). - **Unreadable `uses:` spellings** (`uses: >-`, value on the next line): **0 occurrences** in the repo, so the new refusal changes nothing today. ## Provenance - **Authored by:** agent-work loop - **Verified:** `python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py'`: 382 passed, 0 failed (341 before this PR, 39 new, 2 rewritten); `python3 .github/workflow-pins/check_workflow_pins.py`: exit 0, 155 refs SHA-pinned, 0 exempt, 0 not pinnable, 0.82s (the pre-refactor baseline was 0.76s; the 6.7x regression the mask fixed is not back); all four `test-workflow-pins.yml` CLI smoke steps extracted and reproduced locally against 5156fb6: 4/4 as specified, including the exit-0 one and the new grep assertion; `check_agents_md.py --root .`: passed (the same 2 pre-existing warnings — 156-line `AGENTS.md`, no CODEOWNERS); the other six Python suites (agents-md-integrity, cursor-review, groom 402, public-repo-hygiene 204, refresh-reviewers 53, linear-ticket): all OK; both pin SHAs and all four version comments verified against GitHub's tag refs, and the spelling census re-run against `origin/main`. - **Deviations:** the two `test-workflow-pins.yml` heredoc fixtures are SHA-pinned against the plan's explicit instruction, and one existing unit test's fixture is locally substituted — both explained under "Deviation from the plan" above. The review round then went beyond the stated acceptance criteria in one place: `docker://` images are now CHECKED rather than exempted, which no criterion asked for, because the exemption as written carved out a category of the drift the check exists to ban. ## Residual **The `# vN` version comment is not linted, only the SHA.** The convention is "full commit SHA *with a trailing `# v1` comment*", and the new check asserts only the first half. All 155 pinned refs satisfy the second half today, so there is nothing to fix — but nothing stops a future ref landing as a bare SHA with no comment, which is the form that makes a pin unreadable at review time. Worth its own change; it was outside the stated acceptance criteria and adding it would have widened this diff into unrelated files. **Comment whitespace is inconsistent and unlinted.** 38 of the 155 pinned refs write two spaces before the `#` and 118 write one (e.g. `bump-pr-derisk-callers.yml:147`, `test-linear-ticket.yml:41,45`). Purely cosmetic, no runtime effect, and the plan named only the four *version-text* outliers, which are fixed. I left the whitespace alone rather than churning 38 lines across the workflow directory in a PR about pinning; normalising it is a one-line `sed` whenever someone wants it. **The refusal of `uses: >-` denies a spelling GitHub Actions itself accepts.** I checked rather than assumed: that YAML is valid and Actions would resolve it, so this is a policy refusal, not a claim the capability does not exist. It is stated as such in the error message, which names the one-line remedy (write the action inline). It affects 0 sites in the repo today. If a legitimate need for the folded spelling ever appears, the right fix is to teach the walker the continuation line, not to add an exemption — the debt list deliberately cannot silence this error. **This repo still runs no `pinact` / `zizmor` on itself.** The new check is a hand-rolled 40-hex assertion, not the pin-validation that consumer CI runs, so it enforces the SHA shape but not everything those tools check. Bringing either into this repo's own CI is a separate change with its own dependency and runtime cost. Closes #172 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-26 22:38:19 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/github-workflows#310
No description provided.