[PR #223] [MERGED] fix(workflow-pins): judge a ref: step output on a flow WORKFLOWS_REF binding line (BE-9099) #230

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

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/github-workflows/pull/223
Author: @mattmillerai
Created: 8/25/2026
Status: ✅ Merged
Merged: 8/25/2026
Merged by: @mattmillerai

Base: main ← Head: matt/be-9099-flow-binding-step-output-ref


📝 Commits (2)

  • 3863054 fix(workflow-pins): judge a ref: step output on a flow WORKFLOWS_REF binding line (BE-9099)
  • 3f0a723 fix(workflow-pins): register a flow-form resolver's id so its consumer is judged (BE-9099)

📊 Changes

3 files changed (+291 additions, -2 deletions)

View changed files

📝 .github/workflow-pins/README.md (+20 -0)
📝 .github/workflow-pins/check_workflow_pins.py (+69 -2)
📝 .github/workflow-pins/tests/test_check_workflow_pins.py (+202 -0)

📄 Description

ELI-5

check_workflow_pins.py reads a workflow one line at a time and asks that line exactly ONE question: is it a WORKFLOWS_REF binding, or a direct ref: use, or a multi-line ref: opener, or a ref: that reads a step's output? A step written as a single flow mapping — - {uses: actions/checkout@v4, env: {WORKFLOWS_REF: ...}, with: {ref: "${{ steps.x.outputs.ref }}"}} — answers YES to two of those at once, and the binding question was asked first and stopped there. So that checkout got no verdict at all: not dangling, not guarded, not unguarded, and not even the BE-9045 "I couldn't read this job" warning, because nothing was dropped. It just vanished from the lint. Now the binding arm answers the second question too, so the ref: on that line is judged like every other step-output checkout.

What changed

  • .github/workflow-pins/check_workflow_pins.py — inside ref_checkouts' if binding: arm, after the guard-credit block and after resolvers.add(step_id), record the line's step-output sites via _steps_output_sites / _record_steps_output, passing the BE-9045 drop collector exactly as the final else: arm does (PR #222 has merged — it is the tip of main at 5f4a22b — so the drop argument is passed rather than omitted).
  • .github/workflow-pins/tests/test_check_workflow_pins.py — four new tests (265 → 269).
  • .github/workflow-pins/README.md — one sentence in the resolve-then-consume section.

Nothing else moved: the ref_use demotion, _GUARD_BINDING_FLOW_RE, and #222's dropped plumbing are untouched beyond passing drop through.

Why it is safe to widen the binding arm

The new call only fires on a line that genuinely carries a step-output ref:. Every spelling of an ordinary binding line that reaches this arm — block WORKFLOWS_REF: ${{ inputs.workflows_ref }}, its || job.workflow_sha variant, the quoted form, and both flow forms (- env: {…} and env: {…}) — returns None from _steps_output_sites, so the arm is a no-op for them; I probed all five directly. Direct-input ref: uses cannot reach here at all: flow_binding is only set when binding is None and not ref_use, which is the existing # Reporting wins demotion, untouched. And the walk visits each line once with exactly one arm running, so no double-record guard is needed. Empirically: all 265 pre-existing tests pass unchanged, and the real-tree CLI is byte-identical (OK — 11 workflow(s) …, exit 0, zero notices).

Verification of what the ticket names

Reproduced first on main @ 5f4a22b before touching anything: the flow-mapping fixture returned ref_checkouts == [] while the identical line minus the env: binding returned [(13, False, False, 'dangling')] — the reported defect exactly. After the change the same fixture returns [(13, False, False, 'dangling')].

Corpus sweep of this repo's own .github/workflows/, including the half NOT fixed here: 0 lines carry the newly-judged shape (a WORKFLOWS_REF binding and a step-output ref: on one physical line), 0 whole-step flow mappings bind WORKFLOWS_REF at all, and there is 1 step-output ref: in the tree (cursor-review.yml:456, block form, already judged and covered). The one steps: [ hit in the tree is a fixture inside a run: heredoc in test-workflow-pins.yml, not a live job — check_dir emits zero notices for the real tree, which test_this_repos_own_workflows_pass asserts. So the fix is preventive for consumer repos and for future edits here; it changes no verdict on any workflow that exists today.

Deviation from the ticket's test 2

The ticket specified, for test_a_flow_step_binding_does_not_swallow_a_covered_consumer, both ref_checkouts == [(lineno, False, False, True)] and unguarded_ref_checkouts empty. Those two cannot both hold: unguarded_ref_checkouts filters on not guarded, and guarded is that tuple's third element, False. I asserted the tuple — which is the observed behaviour and the one that carries the ticket's actual point (via_step_output is True, not 'dangling', i.e. the resolver was credited and the binding arm no longer swallows the line) — and the site is correspondingly reported unguarded.

That is not an oversight in the fix: the if: rides INSIDE the flow mapping, and _skips_on_empty_output reads only a step-level if: key of its own. It is the same fail-closed stance the flow-form binding takes one arm up — a flow spelling may DEMAND a guard, never excuse one. I checked all three inline spellings (bare, quoted, ${{ … }}-wrapped); none is credited. The remedy is one line, not a rewrite: promoting the if: to its own key is coverage even with env: and with: left as flow maps, and the test now asserts that working spelling alongside the failing one, so this can never harden into "that shape simply cannot pass".

I also used the class's existing resolve_reff typo convention rather than the ticket's steps.typo, and _reusable(PINNED) rather than a default: '' input, to match the neighbouring dangling tests as the ticket asked.

Review round 2 — panel findings addressed (3f0a723)

The 8-model panel raised 5 findings. Three are fixed here, two are deferred with
repros; every thread has a reply and is resolved.

Fixed — the RESOLVER-side twin of this PR's own fail-open (High). The panel
found that resolvers is populated only via _binding_step_id, which reads a
block-form id: at the step's key column and so could not see the id: of a
step written as one flow mapping — the very spelling this PR is about. Verified
before fixing: a flow-form resolver plus the identical block-form consumer
returned ref_checkouts == [] (no verdict at all), where the block-form
resolver spelling correctly returns (14, False, False, True). Writing the
resolver flow-style silently switched the BE-8130/BE-8221 requirement off for
its consumer. _binding_step_id now reads that spelling, gated exactly as
_job_step_ids gates its own flow reading — only where a flow mapping OPENS —
so with: {id: x} on a block key line still cannot register a phantom resolver
(pinned by its own test).

Fixed — a sibling drop the widening surfaced. _STEP_ID_RE's \S+ kept the
} that a flow CONTINUATION line leaves on it (- {env: {…}, / id: r} →
r}) while _job_step_ids read r, so the resolver registered under an id no
consumer could name — the same silent drop, one spelling over. Both readers now
narrow through _step_id_value.

Fixed — site-recording now precedes registration (ordering, not reach). The
resolvers arm of _record_steps_output carries no declared < consumer_start
check of its own, only the step_ids arm does; verified by a block-form
self-referencing step returning (13, False, False, True) rather than
dangling. So test_a_self_referencing_flow_step_is_dangling really was
passing for a different reason than its comment gave, and the widening above
would have changed its verdict. Recording ahead of resolvers.add(step_id)
puts that verdict on ORDER, where the widening cannot reach it. I preferred
this to adding an ordering check to the resolvers arm, which would also have
flipped the block-form self-reference from passing to a hard error — and that
shape is runtime-safe (its self-referential if: is '' != '', so the step
simply skips). The test comment is rewritten to state the real reason.

Fixed — the docs gap (Low). The README claim held only in the negative
direction and the BE-8130 remedy read as already satisfied for a flow-inline
if:. Both now say plainly that an if: riding inside a flow mapping is not
read as coverage, and that the one-line fix is to promote it to the step's own
if: key with env:/with: left as flow maps.

Deferred — comment/quoted text decides which arm a line takes (Medium).
Confirmed, and worse than arm-flipping: it is a false FAILURE on a compliant
workflow. ref: ${{ steps.r.outputs.ref }} under a correct if: returns [],
but the same line with # resolved from inputs.workflows_ref appended returns
[(15, False, False)]. Pre-existing on main, and in a different code path
(comment handling at arm selection in is_ref_use) from the flow-mapping step
readers this PR touches; is_ref_use has several callers, so normalizing wants
its own sweep. Follow-up filed with the repro and proposed fix.

Deferred — _steps_output_sites is not scoped to with: (Medium).
Mechanism confirmed (_ref_values returns both entries, the reader returns the
first), but it is pre-existing — the identical line without the binding takes
the old else arm and behaves the same — and I could not reach a fail-open
through it. Two ref: keys on one physical line means the whole step is a flow
mapping, where the if: is necessarily inline and therefore never coverage, so
the site is reported unguarded whichever ref: was judged; the panel's own
fixture returns dangling. Today the cost is a wrong message, not a missed
finding, so per this repo's follow-up bar it is a thread reply rather than a
ticket.

Also deferred (found while self-reviewing, not panel-raised). A bare -
whose flow mapping sits on the next line fails one level above
_binding_step_id: _step_bounds returns None, so the resolver never
registers and its consumer is silently dropped (_job_step_ids reads
{'r': 8}; ref_checkouts returns []). Same fail-open class, but
_step_bounds is load-bearing for is_guard_step and _consuming_step_bounds
too, so it needs its own test sweep. Follow-up filed.

Verified

  • python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py' — 272 passed, 0 failed (269 after the first commit, 265 on main; round 2 adds 3 more tests and flipped no existing one).
  • python3 .github/workflow-pins/check_workflow_pins.py — OK — 11 workflow(s) declare workflows_ref, none with a default, every ref checkout guarded (0 exempt), exit 0. Unchanged by round 2, which is the load-bearing check on the _binding_step_id widening: no false failure on any real workflow.
  • All three test-workflow-pins.yml smoke tests re-run locally (reintroduced default fails; unguarded checkout fails; unreadable steps: warns without failing) — the full CI job reproduced green.
  • Sibling suites unaffected: groom 365 OK, cursor-review 194 OK, agents-md-integrity 46 OK.
  • Direct probes of the five ordinary binding-line spellings against _steps_output_sites (all None) and of the four if: spellings against coverage (three flow-inline uncovered, one block-key covered).

Residual

Flow-form step if: earns no coverage credit. A checkout written as one flow mapping is now correctly judged, but if its if: is written inside that same flow mapping it can never satisfy _skips_on_empty_output, so such a step is always reported unguarded even when its if: is semantically the exact non-empty test. Fail-closed and consistent with the flow-form binding rule. Round 2 closed the readability half the panel objected to — the BE-8130 error text and the README now both name the flow-inline if: explicitly and give the one-line remedy — so a consumer hitting it no longer reads "add the exact if:" as advice they have already followed. Teaching _skips_on_empty_output the flow spelling itself remains a separate change, deliberately not made here: a flow spelling may DEMAND a guard, never excuse one.

Unrelated, pre-existing, untouched: python3 .github/agents-md-integrity/check_agents_md.py --root . fails on this repo's own tree — AGENTS.md is 305 lines against a 200-line hard ceiling — identically on main before this branch. This repo is not self-enrolled for that checker so nothing gates on it here, and it is outside this ticket's scope.

Unexercised artifacts

The ticket cites its evidence as living in the findings comment of an upstream investigation ticket, which I hold no access to; I re-derived the repro from the ticket body against main @ 5f4a22b instead, and it reproduced exactly as described. No attachments or sub-issues were named.

Provenance

  • Authored by: agent-work loop
  • Verified: python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py': 272 passed, 0 failed; python3 .github/workflow-pins/check_workflow_pins.py: OK, 11 workflows, exit 0; all three test-workflow-pins.yml smoke tests re-run locally and green; groom 365 / cursor-review 194 / agents-md-integrity 46 all OK
  • Deviations: test 2's unguarded_ref_checkouts-is-empty assertion was not written, because it contradicts the tuple asserted in the same criterion — see "Deviation from the ticket's test 2" above. In review round 2, two of the five panel findings were deferred to follow-ups rather than fixed here (arm selection over un-normalized text; _steps_output_sites not scoped to with:) — both pre-existing on main, with repros and rationale in the round-2 section and on their threads

🔄 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/223 **Author:** [@mattmillerai](https://github.com/mattmillerai) **Created:** 8/25/2026 **Status:** ✅ Merged **Merged:** 8/25/2026 **Merged by:** [@mattmillerai](https://github.com/mattmillerai) **Base:** `main` ← **Head:** `matt/be-9099-flow-binding-step-output-ref` --- ### 📝 Commits (2) - [`3863054`](https://github.com/Comfy-Org/github-workflows/commit/3863054e440a9fbb40b65fb1264e806dd62a8611) fix(workflow-pins): judge a `ref:` step output on a flow WORKFLOWS_REF binding line (BE-9099) - [`3f0a723`](https://github.com/Comfy-Org/github-workflows/commit/3f0a7233fc8afb58ba917fc001b558dc6088e58b) fix(workflow-pins): register a flow-form resolver's id so its consumer is judged (BE-9099) ### 📊 Changes **3 files changed** (+291 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflow-pins/README.md` (+20 -0) 📝 `.github/workflow-pins/check_workflow_pins.py` (+69 -2) 📝 `.github/workflow-pins/tests/test_check_workflow_pins.py` (+202 -0) </details> ### 📄 Description ## ELI-5 `check_workflow_pins.py` reads a workflow one line at a time and asks that line exactly ONE question: is it a `WORKFLOWS_REF` binding, or a direct `ref:` use, or a multi-line `ref:` opener, or a `ref:` that reads a step's output? A step written as a single flow mapping — `- {uses: actions/checkout@v4, env: {WORKFLOWS_REF: ...}, with: {ref: "${{ steps.x.outputs.ref }}"}}` — answers YES to two of those at once, and the binding question was asked first and stopped there. So that checkout got no verdict at all: not `dangling`, not guarded, not unguarded, and not even the BE-9045 "I couldn't read this job" warning, because nothing was dropped. It just vanished from the lint. Now the binding arm answers the second question too, so the `ref:` on that line is judged like every other step-output checkout. ## What changed - `.github/workflow-pins/check_workflow_pins.py` — inside `ref_checkouts`' `if binding:` arm, after the guard-credit block and after `resolvers.add(step_id)`, record the line's step-output sites via `_steps_output_sites` / `_record_steps_output`, passing the BE-9045 `drop` collector exactly as the final `else:` arm does (PR #222 has merged — it is the tip of `main` at `5f4a22b` — so the `drop` argument is passed rather than omitted). - `.github/workflow-pins/tests/test_check_workflow_pins.py` — four new tests (265 → 269). - `.github/workflow-pins/README.md` — one sentence in the resolve-then-consume section. Nothing else moved: the `ref_use` demotion, `_GUARD_BINDING_FLOW_RE`, and #222's `dropped` plumbing are untouched beyond passing `drop` through. ### Why it is safe to widen the binding arm The new call only fires on a line that genuinely carries a step-output `ref:`. Every spelling of an ordinary binding line that reaches this arm — block `WORKFLOWS_REF: ${{ inputs.workflows_ref }}`, its `|| job.workflow_sha` variant, the quoted form, and both flow forms (`- env: {…}` and `env: {…}`) — returns `None` from `_steps_output_sites`, so the arm is a no-op for them; I probed all five directly. Direct-input `ref:` uses cannot reach here at all: `flow_binding` is only set when `binding is None and not ref_use`, which is the existing `# Reporting wins` demotion, untouched. And the walk visits each line once with exactly one arm running, so no double-record guard is needed. Empirically: all 265 pre-existing tests pass unchanged, and the real-tree CLI is byte-identical (`OK — 11 workflow(s) …`, exit 0, zero notices). ## Verification of what the ticket names Reproduced first on `main` @ `5f4a22b` before touching anything: the flow-mapping fixture returned `ref_checkouts == []` while the identical line minus the `env:` binding returned `[(13, False, False, 'dangling')]` — the reported defect exactly. After the change the same fixture returns `[(13, False, False, 'dangling')]`. Corpus sweep of this repo's own `.github/workflows/`, including the half NOT fixed here: **0** lines carry the newly-judged shape (a `WORKFLOWS_REF` binding and a step-output `ref:` on one physical line), **0** whole-step flow mappings bind `WORKFLOWS_REF` at all, and there is **1** step-output `ref:` in the tree (`cursor-review.yml:456`, block form, already judged and covered). The one `steps: [` hit in the tree is a fixture inside a `run:` heredoc in `test-workflow-pins.yml`, not a live job — `check_dir` emits zero notices for the real tree, which `test_this_repos_own_workflows_pass` asserts. So the fix is preventive for consumer repos and for future edits here; it changes no verdict on any workflow that exists today. ## Deviation from the ticket's test 2 The ticket specified, for `test_a_flow_step_binding_does_not_swallow_a_covered_consumer`, both `ref_checkouts == [(lineno, False, False, True)]` **and** `unguarded_ref_checkouts` empty. Those two cannot both hold: `unguarded_ref_checkouts` filters on `not guarded`, and `guarded` is that tuple's third element, `False`. I asserted the tuple — which is the observed behaviour and the one that carries the ticket's actual point (`via_step_output` is `True`, not `'dangling'`, i.e. the resolver was credited and the binding arm no longer swallows the line) — and the site is correspondingly reported unguarded. That is not an oversight in the fix: the `if:` rides INSIDE the flow mapping, and `_skips_on_empty_output` reads only a step-level `if:` key of its own. It is the same fail-closed stance the flow-form binding takes one arm up — a flow spelling may DEMAND a guard, never excuse one. I checked all three inline spellings (bare, quoted, `${{ … }}`-wrapped); none is credited. The remedy is one line, not a rewrite: promoting the `if:` to its own key is coverage even with `env:` and `with:` left as flow maps, and the test now asserts that working spelling alongside the failing one, so this can never harden into "that shape simply cannot pass". I also used the class's existing `resolve_reff` typo convention rather than the ticket's `steps.typo`, and `_reusable(PINNED)` rather than a `default: ''` input, to match the neighbouring dangling tests as the ticket asked. ## Review round 2 — panel findings addressed (`3f0a723`) The 8-model panel raised 5 findings. Three are fixed here, two are deferred with repros; every thread has a reply and is resolved. **Fixed — the RESOLVER-side twin of this PR's own fail-open (High).** The panel found that `resolvers` is populated only via `_binding_step_id`, which reads a block-form `id:` at the step's key column and so could not see the `id:` of a step written as one flow mapping — the very spelling this PR is about. Verified before fixing: a flow-form resolver plus the identical block-form consumer returned `ref_checkouts == []` (no verdict at all), where the block-form resolver spelling correctly returns `(14, False, False, True)`. Writing the resolver flow-style silently switched the BE-8130/BE-8221 requirement off for its consumer. `_binding_step_id` now reads that spelling, gated exactly as `_job_step_ids` gates its own flow reading — only where a flow mapping OPENS — so `with: {id: x}` on a block key line still cannot register a phantom resolver (pinned by its own test). **Fixed — a sibling drop the widening surfaced.** `_STEP_ID_RE`'s `\S+` kept the `}` that a flow CONTINUATION line leaves on it (`- {env: {…},` / `id: r}` → `r}`) while `_job_step_ids` read `r`, so the resolver registered under an id no consumer could name — the same silent drop, one spelling over. Both readers now narrow through `_step_id_value`. **Fixed — site-recording now precedes registration (ordering, not reach).** The `resolvers` arm of `_record_steps_output` carries no `declared < consumer_start` check of its own, only the `step_ids` arm does; verified by a block-form self-referencing step returning `(13, False, False, True)` rather than `dangling`. So `test_a_self_referencing_flow_step_is_dangling` really was passing for a different reason than its comment gave, and the widening above would have changed its verdict. Recording ahead of `resolvers.add(step_id)` puts that verdict on ORDER, where the widening cannot reach it. I preferred this to adding an ordering check to the `resolvers` arm, which would also have flipped the block-form self-reference from passing to a hard error — and that shape is runtime-safe (its self-referential `if:` is `'' != ''`, so the step simply skips). The test comment is rewritten to state the real reason. **Fixed — the docs gap (Low).** The README claim held only in the negative direction and the BE-8130 remedy read as already satisfied for a flow-inline `if:`. Both now say plainly that an `if:` riding inside a flow mapping is not read as coverage, and that the one-line fix is to promote it to the step's own `if:` key with `env:`/`with:` left as flow maps. **Deferred — comment/quoted text decides which arm a line takes (Medium).** Confirmed, and worse than arm-flipping: it is a false FAILURE on a compliant workflow. `ref: ${{ steps.r.outputs.ref }}` under a correct `if:` returns `[]`, but the same line with `# resolved from inputs.workflows_ref` appended returns `[(15, False, False)]`. Pre-existing on `main`, and in a different code path (comment handling at arm selection in `is_ref_use`) from the flow-mapping step readers this PR touches; `is_ref_use` has several callers, so normalizing wants its own sweep. Follow-up filed with the repro and proposed fix. **Deferred — `_steps_output_sites` is not scoped to `with:` (Medium).** Mechanism confirmed (`_ref_values` returns both entries, the reader returns the first), but it is pre-existing — the identical line without the binding takes the old `else` arm and behaves the same — and I could not reach a fail-open through it. Two `ref:` keys on one physical line means the whole step is a flow mapping, where the `if:` is necessarily inline and therefore never coverage, so the site is reported unguarded whichever `ref:` was judged; the panel's own fixture returns `dangling`. Today the cost is a wrong message, not a missed finding, so per this repo's follow-up bar it is a thread reply rather than a ticket. **Also deferred (found while self-reviewing, not panel-raised).** A bare `-` whose flow mapping sits on the next line fails one level above `_binding_step_id`: `_step_bounds` returns None, so the resolver never registers and its consumer is silently dropped (`_job_step_ids` reads `{'r': 8}`; `ref_checkouts` returns `[]`). Same fail-open class, but `_step_bounds` is load-bearing for `is_guard_step` and `_consuming_step_bounds` too, so it needs its own test sweep. Follow-up filed. ## Verified - `python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py'` — **272 passed, 0 failed** (269 after the first commit, 265 on `main`; round 2 adds 3 more tests and flipped no existing one). - `python3 .github/workflow-pins/check_workflow_pins.py` — `OK — 11 workflow(s) declare workflows_ref, none with a default, every ref checkout guarded (0 exempt)`, exit 0. Unchanged by round 2, which is the load-bearing check on the `_binding_step_id` widening: no false failure on any real workflow. - All three `test-workflow-pins.yml` smoke tests re-run locally (reintroduced default fails; unguarded checkout fails; unreadable `steps:` warns without failing) — the full CI job reproduced green. - Sibling suites unaffected: groom 365 OK, cursor-review 194 OK, agents-md-integrity 46 OK. - Direct probes of the five ordinary binding-line spellings against `_steps_output_sites` (all `None`) and of the four `if:` spellings against coverage (three flow-inline uncovered, one block-key covered). ## Residual **Flow-form step `if:` earns no coverage credit.** A checkout written as one flow mapping is now correctly judged, but if its `if:` is written inside that same flow mapping it can never satisfy `_skips_on_empty_output`, so such a step is always reported unguarded even when its `if:` is semantically the exact non-empty test. Fail-closed and consistent with the flow-form binding rule. Round 2 closed the *readability* half the panel objected to — the BE-8130 error text and the README now both name the flow-inline `if:` explicitly and give the one-line remedy — so a consumer hitting it no longer reads "add the exact `if:`" as advice they have already followed. Teaching `_skips_on_empty_output` the flow spelling itself remains a separate change, deliberately not made here: a flow spelling may DEMAND a guard, never excuse one. **Unrelated, pre-existing, untouched:** `python3 .github/agents-md-integrity/check_agents_md.py --root .` fails on this repo's own tree — `AGENTS.md` is 305 lines against a 200-line hard ceiling — identically on `main` before this branch. This repo is not self-enrolled for that checker so nothing gates on it here, and it is outside this ticket's scope. ## Unexercised artifacts The ticket cites its evidence as living in the findings comment of an upstream investigation ticket, which I hold no access to; I re-derived the repro from the ticket body against `main` @ `5f4a22b` instead, and it reproduced exactly as described. No attachments or sub-issues were named. ## Provenance - **Authored by:** agent-work loop - **Verified:** `python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py'`: 272 passed, 0 failed; `python3 .github/workflow-pins/check_workflow_pins.py`: OK, 11 workflows, exit 0; all three `test-workflow-pins.yml` smoke tests re-run locally and green; groom 365 / cursor-review 194 / agents-md-integrity 46 all OK - **Deviations:** test 2's `unguarded_ref_checkouts`-is-empty assertion was not written, because it contradicts the tuple asserted in the same criterion — see "Deviation from the ticket's test 2" above. In review round 2, two of the five panel findings were deferred to follow-ups rather than fixed here (arm selection over un-normalized text; `_steps_output_sites` not scoped to `with:`) — both pre-existing on `main`, with repros and rationale in the round-2 section and on their threads --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-26 22:37:34 +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#230
No description provided.