mirror of
https://github.com/Comfy-Org/comfy-typescript-sdk.git
synced 2026-11-04 23:47:27 +00:00
[PR #169] [MERGED] test(workflow-pins): pin the parser with a subprocess fixture matrix #170
Labels
No labels
audit:ai-readiness
audit:community
audit:github
audit:health-scoring
audit:policy
audit:release
audit:supply-chain
bug
cursor-review-pin
effort:medium
effort:small
effort:trivial
priority:nice-to-have
priority:recommended
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/comfy-typescript-sdk#170
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/Comfy-Org/comfy-typescript-sdk/pull/169
Author: @mattmillerai
Created: 9/21/2026
Status: ✅ Merged
Merged: 10/9/2026
Merged by: @mattmillerai
Base:
matt/be-15270-resync-cursor-review-pins← Head:matt/be-16299-check-workflow-pins-regression-suite📝 Commits (5)
f852e7atest(workflow-pins): pin the parser with a subprocess fixture matrix8e5570dtest(workflow-pins): make the block-scalar decoys actually bite471c54btest(workflow-pins): make the round-2 cases pin what their names claimbb3b2a1ci(workflow-pins): fail closed on a job-leveluses:this scanner cannot read (#170)7599eeeMerge #153's tip into the regression-suite branch📊 Changes
4 files changed (+1414 additions, -90 deletions)
View changed files
📝
.github/workflows/ci.yml(+373 -66)➕
scripts/__fixtures__/ci-cursor-review.83c0c87.yml(+75 -0)📝
scripts/check-workflow-pins.mjs(+187 -24)➕
scripts/check-workflow-pins.test.ts(+779 -0)📄 Description
STACKED — merging lands on
matt/be-15270-resync-cursor-review-pins(owned by Matt Miller, PR #153), NOTmain.scripts/check-workflow-pins.mjsexists only on #153's branch, so this PR is based on it rather than onmain. GitHub retargets this PR tomainonce #153 merges; do not read anything below as "ready to merge to main".ELI-5
There is a little script that checks the two SHAs in
.github/workflows/ci-cursor-review.ymlstill point at the same upstream commit. It learned a lot of fiddly YAML rules over four review rounds — whichuses:lines are real callers, where aworkflows_ref:is allowed to sit, which ones are decoys inside a shell script. None of that was written down as a test, so the next person to touch the file had nothing to fail against. This PR writes those rules down as tests (34 cases today) that run the script for real and check what it prints and what it exits with.What this adds
Originally two new files, listed below. Since then, stacked PR #170 (fail closed on a job-level
uses:the scanner cannot read) has been squash-merged into this branch. So the diff against #153 now also changesscripts/check-workflow-pins.mjsand.github/workflows/ci.yml, and adds cases toscripts/check-workflow-pins.test.ts. #170's own PR description explains those changes.scripts/check-workflow-pins.test.ts— 26 vitest cases originally, 34 now that #170 added its own that drivescripts/check-workflow-pins.mjsas a subprocess.scripts/__fixtures__/ci-cursor-review.83c0c87.yml—83c0c87's realci-cursor-review.yml, copied verbatim (git show 83c0c87:.github/workflows/ci-cursor-review.yml, byte-identical). This is the historical split the lint was written for:uses:@ebfd9e44…with a# … (ffcc3f5)comment andworkflows_ref: ffcc3f5b….Subprocess rather than
import, for two reasons: the script's contract is its exit status plus stdout/stderr, and it callsprocess.exiton every failure path, which an in-process import would turn into a killed test run. The script resolves its repo root from its own file location (new URL("..", import.meta.url)) with no--rootflag, so each fixture root gets a copy of the script inside it; the real repo is exercised in place.Fixture bodies are line arrays joined with
\n, not template literals — YAML is indentation-significant, and an array keeps every leading space visible in review instead of tangled with the surrounding TypeScript indent.Cases
Real repo: lints clean and says "all pins agree";
--print-pin cursor-review.ymlwrites a bare 40-hex SHA and nothing else.Historical fixture: exits 1 with
2 problem(s)and both halves named — the stale comment and the staleworkflows_ref.The three pin assertions: split pins; stale parenthesised comment; stale bare comment; a date beside the parenthesised short SHA (passes); a branch-pinned
uses:(fails).Where a
workflows_refinput may live: absent with awith:block and with none at all; a sibling ofuses:; nested under another mapping; inside abody: |block scalar withinwith:; only in a preceding job'swith:;with:written aboveuses:;with: # inputswith a flush-left#comment inside the block.Shapes that must not read as a caller: a
uses:decoy inside arun: |under a- name:step and inside the dash-carrying- run: |spelling — both written as heredoc body lines, sousesis the first token after the indent and the line is a real candidate for the caller regex; a- run: |block ending at itsshell:sibling; a caller written after a multi-line block scalar; a whole-line# uses:comment above the real caller (the header ofci-cursor-review.ymldoes this); a quoted"uses":key and value, both in sync and split; a composite-action- uses:step, which pins once and has no input to split — asserted as a caller count beside a real job-level caller, so the case fails both if the step is read as a caller and if nothing parses at all.--print-pin: with the block-scalar decoy present; with two callers (refuses to choose); with a caller of a different reusable only, asserting the mode-specific wording so a fall-through to lint mode cannot pass it; with aworkflows_refnaming a branch rather than a commit, asserting nothing reaches stdout (printPin'sFULL_SHA_REguard, which ci.yml feeds togit cat-file -e "$pin^{commit}"); with no name; and the inline--print-pin=<name>form.Why no
vitest.config.tschangevitest.config.tssets notest.include, so vitest 4.1.10's default**/*.{test,spec}.?(c|m)[jt]s?(x)already discoversscripts/*.test.ts. Verified rather than assumed:pnpm testwent from 26 to 27 test files with no config edit. Thesrc/**/*.tsglob in that file is the coverage include, so a test file outsidesrc/does not disturb it.tsconfig.jsonhasinclude: ["src"], so the new file is outside typecheck;oxlint .andoxfmt --check .do cover it.Verified
Review round 2 (
471c54b)Round 2 raised seven findings; five held up and were fixed, one was answered, one was deferred.
Fixed: the composite-action case now counts callers beside a real job-level caller instead of asserting only the zero-caller notice; the
--print-pinno-caller case holds a caller of a different reusable and asserts the mode-specific wording; a new case coversprintPin'sFULL_SHA_RErejection of a branch-valuedworkflows_ref;run()readssignalbeforeerrorand names the args in both, becausespawnSyncsets both on the timeout path so testingerrorfirst meant the signal was never named on exactly the path the cap exists for; andrun()now passes a hermeticenv(PATHonly) so an ambientNODE_OPTIONSor a future Node's deprecation warning cannot red the cases assertingstderris exactly"".Each new assertion was checked by MUTATION rather than by reading, and each reds exactly one case: replacing
printPin'sc.reusable === wantedfilter withcallers.slice(); deleting itsFULL_SHA_REguard; and wideningUSES_REgroup 1 to^(\s*(?:-\s+)?)so a step-level- uses:reads as a caller.One finding was answered, not fixed: a re-raise arguing the block-scalar decoys are rejected by an
isJobLevelUses/owningKey/JOB_ID_KEY_REpositional check rather than by the skip, so the skip stays unpinned. Those symbols do not exist in this repo (grepfinds none in either file),findCallerspushes everyUSES_REhit straight ontocallers, and the round-1 mutation still reproduces: deleting the block-scalar handling reds 3 cases. The fixtures and the comment are correct as written.One finding was corrected on its premise: the report that the composite-action case reds on every run read
printPin's zero-matchfail()as if it were the lint's. Lint mode's zero-caller branch isconsole.logon stdout with exit 0, and the pluralcallers foundis in the script. The case was green; it was strengthened for the separate, valid reason above.Review round 1 (
8e5570d)Cursor review found the block-scalar decoys could not reach the behaviour they claimed to pin, and that was confirmed by MUTATING the script rather than by reading it. Against
f852e7a's fixtures, deleting theBLOCK_SCALAR_REhandling fromfindCallersoutright — both theblockKeyIndentskip and the opener branch — left all 25 cases green. The decoys were written asecho "uses: …"arguments, and the caller regex anchorsusesas the first token after the indent, so they were rejected on spelling and the skip was never exercised.Rewritten as heredoc body lines, the same mutation now turns 3 cases red, and the suite is green against the unmodified script. Two further mutations are documented as not pinned, honestly rather than silently: flipping
BLOCK_SCALAR_REgroup 1 to exclude the-sequence marker, and deleting the whole-line#guard, both still leave 25 green — the first becausecursor-review:at column 2 ends the block under either column, the second becauseUSES_REandBLOCK_SCALAR_REeach already reject a leading#, making that guard redundant rather than untested.run()also now throws onspawnSync'serrorand on a non-nullsignal, and caps the child with spawnSync's owntimeout— it blocks the worker's event loop, so vitest's per-test timeout can never fire on a wedged child.Original
Every command below was run in this worktree. The suite was run on all three runtimes that matter: node 22.22.0 and 24.12.0 (the
Testjob's matrix) and node 26.4.0 (this machine's default) — 27 files / 645 tests passed on each.pnpm test— 27 files, 645 tests passed on node 22.22.0, 24.12.0 and 26.4.0 (26 files / 620 before this change).pnpm lint(oxlint .),pnpm format:check(oxfmt --check ., 86 files),pnpm typecheck(tsc --noEmit) — all clean, exit 0.node scripts/check-workflow-pins.mjsandnode scripts/check-public-repo-hygiene.mjs— both clean against the tree including the new fixture.git diffconfirmsscripts/check-workflow-pins.mjsis byte-identical to786762c; nothing under.github/and nothing invitest.config.tsis touched.Revert spot-checks
The acceptance asked for two; all three were done, each at the tip so the result is not masked by a later commit. (Reverting a commit in isolation on its own parent is misleading here: at
41fd901the missing-input allowlist was still opt-in, so six cases fail there for an unrelated reason and hide the delta.)41fd901—with:-child positional gatingworkflows_refas a sibling ofuses:; nested deeper than thewith:children6b02db7— walk back to the job key so the whole body is scannedwith:written above its job'suses:786762c— fail-closed missing input, quoted"uses":key, comment candidateswith:; quoted keyJudgment calls
matt/be-15270-resync-cursor-review-pinsrather thanmain. The plan offered opening againstmain; that is not viable — the script under test does not exist onmain, so the suite would fail there, and the diff would double-count #153's commits. Basing on #153's head kept this PR's original diff to exactly the two new files. #170's squash has since added more (see above).run(REAL_REPO)re-reds the same build theworkflow-pinsCI job already reds if someone lands a split pin. That duplication is deliberate and was asked for: it is what keeps the suite honest about the script's behaviour on a real caller rather than only on synthetic ones.786762calready shipped, and each was confirmed by running the real script rather than by asserting a premise.Residual
.github/workflows/ci.ymltriggers onpull_request: branches: [main]only, so while the base ismatt/be-15270-resync-cursor-review-pinstheTestmatrix,lint,typecheck,format:check,public-repo-hygieneandworkflow-pinsjobs are all skipped here; CodeRabbit likewise reportsreviews are disabled for this base branch. Those gates start running when GitHub retargets this PR tomainafter #153 merges. Until then the evidence for them is the local runs listed above — which is why the node matrix was reproduced locally on 22 and 24 rather than left to CI. Re-check the checks after retarget, before merging.uses:is misread as a caller — deferred, not fixed here. Reproduced against the real script: a composite-action step written- name:on one line anduses: Comfy-Org/github-workflows/actions/setup@<sha>on the next reachesfindCallersas a plain mapping key and reds the lint with "passes noworkflows_refinput". That contradicts the script header. The fixture cursor review asked for cannot be added here without either pinning the false positive or changing a lint that gates CI, and the fix needs a design call (require a.github/workflows/path, or tracksteps:context). Tracked as a follow-up; see the resolved thread onscripts/check-workflow-pins.test.ts.uses:was silently skipped — since fixed by #170. #170 (squash-merged into this branch) makes the scanner fail closed on a job-leveluses:it cannot read, with cases for the foldeduses: >-, flow-mapping job and continuation-line spellings. The original finding, for the record: Reproduced against the real script: a second job-level caller writtenuses: >-with the value on the next line is not parsed at all (BLOCK_SCALAR_REmatches the opener and swallows the value), so with one ordinary caller beside it the lint reports1 caller(s) checked, all pins agreeand exit 0 while a second caller on a different commit sits unread — the split-pin condition this lint exists to catch — and--print-pinreturns the first pin instead of refusing to choose. A test asserting today's output would pin the bug, and the honest fix (fail closed on a job-leveluses:the parser cannot read) changes a lint that gates CI, so it is tracked as a follow-up rather than done here. This is the false-negative twin of the dash-less-step false positive above; ayaml-parser rewrite would likely close both.with: { workflows_ref: … }written as a FLOW mapping is not parsed and reads as "input absent" — a deliberate loud-failure limit named in the script header. No case here covers it, so a future parser edit that silently started accepting it (or that changed how loudly it fails) would not be caught. Worth its own case.NO_WORKFLOWS_REFis untested. The allowlist that exempts a reusable from requiring aworkflows_refis empty today, so no fixture exercises the exempt path. A reusable added to it later has no test proving it is actually skipped..yaml-extension workflow files are untested.findCallersaccepts both.ymland.yaml; every fixture here is.yml.scripts/check-public-repo-hygiene.mjswould fail the build on one. (The fleet's automated follow-up notice comment does name the tickets it filed; that is tooling output about this PR, not part of the PR's content.) The origin that can be named is PR #153, whose four review rounds established every behaviour pinned above.Provenance
7599eee:pnpm test: 27 files, 654 tests passed.pnpm lint,pnpm format:checkandpnpm typecheckall exit 0 (local node only, not the 22/24 matrix).node scripts/check-workflow-pins.mjs: 1 caller, pins agree.node scripts/check-public-repo-hygiene.mjs: clean.ci.ymlparses as YAML, and everyrun:block passesbash -n.printPin's reusable filter and itsFULL_SHA_REguard each turns cases red. That evidence is from rounds before #170 changed the script and was not re-run at this head.main, because the script under test exists only on #153's branch.ci.ymlchanges into this PR. It is no longer test-only.$BUMP_NO_REASONto the stale-pin report's dedup key, so a bump PR whose failure reason changes is reported again.uses:read as a caller (still open), and a folded or flow-mappinguses:silently skipped (since fixed by #170, which fails closed on it).isJobLevelUses, which did not exist at471c54b. #170 later added a function of that name for its own fail-closed check, so that answer is accurate only for the round-2 head.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
uses:this scanner cannot read #174