mirror of
https://github.com/Comfy-Org/github-workflows.git
synced 2026-11-05 00:17:37 +00:00
[PR #302] [MERGED] ci(bump-callers): add WATCHED_EXEC to the five fleets whose asset directory outlives the scripts inside it #303
Labels
No labels
cursor-review-catalog-drift
groom
groom
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Comfy-Org/github-workflows#303
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/github-workflows/pull/302
Author: @mattmillerai
Created: 9/18/2026
Status: ✅ Merged
Merged: 9/18/2026
Merged by: @mattmillerai
Base:
main← Head:matt/be-15253-watched-exec-five-fleets📝 Commits (2)
1faebc1ci(bump-callers): add WATCHED_EXEC to the five fleets whose asset directory outlives the scripts inside it8f8b090test(bump-callers): machine-enforce WATCHED_EXEC, and correct three of its rationale notes📊 Changes
8 files changed (+467 additions, -14 deletions)
View changed files
📝
.github/bump-callers/README.md(+54 -14)📝
.github/bump-callers/tests/test_paths_contract.sh(+239 -0)📝
.github/workflows/bump-agents-md-callers.yml(+10 -0)📝
.github/workflows/bump-coderabbit-config-callers.yml(+29 -0)📝
.github/workflows/bump-cursor-review-callers.yml(+49 -0)📝
.github/workflows/bump-groom-callers.yml(+34 -0)📝
.github/workflows/bump-pr-size-callers.yml(+32 -0)📝
.github/workflows/test-bump-callers.yml(+20 -0)📄 Description
ELI-5
A bump fleet asks "is my watched surface still there?" by checking whether a directory still exists. But a directory outlives the files inside it — delete every script a consumer runs out of
.github/cursor-reviewand the directory is still there, becauseREADME.mdandtests/are. The fleet reads that as healthy, re-pins every enrolled caller to that SHA, and each consumer's reusable then hard-fails at run time with no signal from this side.WATCHED_EXECis the input that names the FILES instead, so a missing one takes the loud::warning::decommission path rather than bumping. Five fleets already set it; five named a directory and set none at all. This gives those five a list.What changed
WATCHED_EXECadded to five entrypoints:bump-agents-md-callers.yml,bump-coderabbit-config-callers.yml,bump-cursor-review-callers.yml,bump-groom-callers.yml,bump-pr-size-callers.yml. Each list was derived by reading what the matching reusable checks out fromworkflows_refand then loads or runs — not by listing what happens to be executable. Every file in each watched tree is now accounted for: listed, or documented in the entrypoint as deliberately absent.The test for inclusion is "absence breaks a pinned caller at run time", not "is it an executable" (the precedent
bump-pr-derisk-callers.ymlsets explicitly). So the groom briefs (finder.md/verifier.md/builder.md, eachopen()ed out of the pinned ref), the three cursor-review prompts, the vendoredschema.v2.jsonthe CodeRabbit checker resolves beside itself, the--require-hashesrequirements.txt, and.github/groom/package.json(the "Load the agent CLI pin" step fails closed on a missing manifest) all qualify.README.mdand anything undertests/do not.Three files live in a watched tree, are genuinely executed, and are deliberately left out —
catalog-drift.py,schema_drift.pyandwire-bot-identity.pyall run out of this repo's own checkout, never fromworkflows_ref. Listing one would turn its eventual retirement into a::warning::that freezes the whole fleet: a false decommission, the mirror of the false-healthy bump the input exists to stop..github/groom/key-broker.mjsis out for the adjacent reason:broker.mjssuperseded it and no step loads it. Each omission carries its rationale inline.The
scripts/check-pr-sizeentries are byte-identical between the cursor-review and pr-size entrypoints, which build the same tool from the same pin. That tree is the one that survives differently from the rest: it holds no README and notests/, so what keeps it resolving aftermain.go/size.go/go.modare deleted is precisely the*_test.gopair both filters deliberately exclude..github/bump-callers/README.mdis corrected in the two places that claimedWATCHED_EXECwas needed "Only bypr-riskandpr-derisktoday" — already wrong before this change (area-label, linear-ticket and public-repo-hygiene set it) and wrong about ten fleets after it. The replacement states the real rule (every fleet whoseWATCHED_ASSETSentry is a directory needs it; only the three that watch nothing beyondWATCHEDleave it unset) and writes out the three rules for composing one of these lists — including the false-decommission trap, and the fact that a path which stops resolving is now a red PR rather than a silent freeze.No behaviour of
preflight.shorbump-callers.shchanges, and no bump fleet'spaths:filter is touched. The onepaths:filter that does change istest-bump-callers.yml's own — see Now machine-enforced below.Now machine-enforced
These are hand-written literal paths and nothing in CI read them:
test_paths_contract.shparsed onlyWATCHED/WATCHED_ASSETS/WATCHED_PATHSPECS, andtest_preflight.shdrives synthetic fixtures without touching the entrypoints.WATCHED_EXECwas the last preflight input left to convention, and its failure mode is the worst shape to leave unguarded — a typo, or a rename applied to the tree but not to the list, makespreflight.shtake the decommission branch on every future run (one::warning::,proceed=false) behind a green run, so the fleet silently stops bumping and consumer pins drift. Raised in review; closed here rather than deferred.test_paths_contract.shnow asserts, for all thirteen fleets:WATCHED_EXECentry resolves to a TRACKED FILE at that commit.git ls-fileswith an exact match, not[[ -f ]]:preflight.shresolves each entry as a blob at the fetched tip, so an untracked or ignored path is absent as far as the probe is concerned — and exactness is what also rejects a directory, the shapepreflight.sherrors on at run time. This is what keeps the two byte-identicalscripts/check-pr-sizeblocks in step: a rename applied to one list leaves the other naming a path that cannot resolve, and that fleet fails.paths:filter, so it can only ever contribute a freeze — the commit deleting it starts no run of that fleet at all.WATCHED-only fleets keep the exemption, sincepreflight.shprobesWATCHEDunconditionally and a.ymlfile is its own probe.A check on the tree is only worth anything if it runs on the PR that breaks it, and a filter listing only the entrypoints would leave the commit that renames
.github/groom/scope.pyuntested — the guard would first speak on some unrelated later PR, long after the fleet froze. Sotest-bump-callers.ymlgrows.github/**andscripts/**: the whole trees, not an enumeration of the watched surfaces, because that enumeration is a roster and a roster copied into a filter drifts. The test then asserts that filter selects every path it reads, and that the file's two duplicatedpaths:lists are still identical, so the breadth assumption cannot go stale silently.Every new assertion is mutation-tested (a typo'd entry, a directory entry, a stray path, a dropped list, a narrowed filter, a drifted pair of lists — each fails loudly, and the suite is green with none applied). A self-test fixture pins
parse_exec_listagainstsplit_lines' comment rule, which diverges from theWATCHED_ASSETSparser beside it deliberately and in the opposite direction:WATCHED_EXECdrops a whole-line#(so a pasted filter's comments are fine) whileWATCHED_ASSETSrejects one.Rationale notes corrected
Three of the notes this PR added, or failed to add, said the wrong thing — all three raised in review:
pr-sizenamed the wrong mechanism. It claimed listing a*_test.go"would re-import the churn that exclusion removed". It would not:WATCHED_EXECis a presence-only decommission probe and takes no part in the staleness comparison (that isWATCHED_PATHSPECS), so an entry here can only ever read astip_gone— never turn a skip into a bump. The accurate reason is the first rule, absence breaks no pinned caller; the real cost of listing one is a false-decommission freeze the day a test is deleted. As written it invited a future maintainer to prune the:(exclude)line on the belief this input covers churn.coderabbit-configcarried no versioned-rename note. It now carries the onebump-pr-risk-callers.ymlandbump-pr-derisk-callers.ymlalready carry forrisk-map.v0.json:schema.v2.jsonis renamed by a v2→v3 migration (this repo already tracks it upstream viarefresh-coderabbit-schema.yml) andrequirements.txtdisappears the day the validator goes stdlib-only. Either reads here as a decommission.scripts/check-pr-sizeblock carried one either, thoughgo build .needs only some non-test source in the package — mergingsize.gointomain.gois a pure internal refactor that breaks no pinned caller yet would make the probe report absent. Both blocks now carry the note, and the tracked-file assertion above turns forgetting it into a red PR.Census
13 caller fleets. 10 watch an asset surface and now all set
WATCHED_EXEC(5 already did: area-label, linear-ticket, pr-risk, pr-derisk, public-repo-hygiene; 5 are fixed here). The 3 not fixed, measured and deliberate:bump-assign-reviewers-callers.yml,bump-auto-label-callers.ymlandbump-detect-unreviewed-merge-callers.ymlsetWATCHEDonly, have no asset directory, and are correctly exempt — there is no directory to outlive anything. (bump-cursor-cli-pin.ymlis not a caller fleet and does not match thebump-*-callers.ymlshape.)Verification
Beyond the repo's own suites, I exercised the real
WATCHED_*values — parsed straight out of the five shipped entrypoints — against a throwaway clone of the repo tree, reproducing the exact trigger shape the defect needs (the deleting commit IS the run's own commit, sofetched_tip == HEADand neither staleness comparison sees a change). For each of six deletions, and for each of five healthy trees: 28 assertions, 0 failures.check_agents_md.py,schema.v2.json,prompt-judge.md,size.go,finder.md,main.goeach deleted in turn → withoutWATCHED_EXECthe run emitsproceed=true(the defect, reproduced); with the shipped list it emitsproceed=falseand a::warning::naming the exact file.proceed=trueand no::error::, so no list introduces a false decommission or tripspreflight.sh's path/not-a-directory validation.The harness is a throwaway probe, not committed — the repo's
test_preflight.shalready covers the mechanism synthetically, and the contract test over the real lists is now in the suite (see Now machine-enforced).Judgment calls
.github/groom/package.jsonis listed even though it is read fromjob.workflow_sha, notworkflows_ref. That is deliberate ingroom.ymland unchanged here; for a SHA-pinned callerjob.workflow_sharesolves to the same commit this fleet pins, which is the commitWATCHED_EXECprobes. The step fails closed with an::error::on a missing manifest, so its absence genuinely breaks a pinned caller.slack-notify.sh,gate-unresolved.py), following the precedent inbump-pr-risk-callers.yml, which listsapply-risk-label.sh/publish-risk-surfaces.shon the same reasoning: a caller that enables the feature breaks without them.WATCHED, matching all five fleets that already set the input.Residual
preflight.shstill does not REQUIREWATCHED_EXECwhen aWATCHED_ASSETSentry resolves to a directory, so a fleet added tomorrow can reintroduce the same hole silently. That change must land after this one or every affected fleet reds at once, and it carries a constraint worth recording:preflight.sh's local asset probe deliberately uses-erather than-d, because a fleet may legitimately watch a single FILE — so the requirement has to be conditional on the entry actually resolving to a directory (mirror the existing[[ -d "$f" ]]idiom in theWATCHED_EXECvalidator), never unconditional. Worth its own change.:171it still says every entrypoint runspreflight.sh"exceptbump-pr-risk-callers.yml, which stays on its own inline guard" — pr-risk moved onto the shared script, and the same file says so 300 lines later. The note at:487still says "all eight entrypoints"; there are 13. Neither is theWATCHED_EXECclaim this change was scoped to correct, and both are independent of theenv:blocks here.paths:-exclusion work on the overlapping fleets is untouched (churn bumps from test-only commits, with its own forcedWATCHED_PATHSPECSmigration and a CI contract test). Neither fix subsumes the other.33245434399was not opened — it is the provenance of the finding, not an input to the fix, and the issue body reproduces its conclusions.Provenance
test_preflight.sh: 445 passed, 0 failed;test_paths_contract.sh: 140 passed, 0 failed (was 110; +30 for theWATCHED_EXECcontract, its parser self-test and the trigger-coverage check);test_bump_callers.sh: 437 passed, 0 failed;shellcheck -x .github/bump-callers/*.sh .github/bump-callers/tests/*.sh: clean;check_workflow_pins.py: OK, 11 workflows, 0 exempt;check_agents_md.py --root .: passed (2 pre-existing warnings, AGENTS.md untouched);yaml.safe_loadover the four edited workflow files: parse clean, bothpaths:lists identical, everyWATCHED_EXECblock scalar comment-free; real-value preflight harness: 28 passed, 0 failed; every new assertion mutation-tested to fail non-vacuously (six mutations across the five)WATCHED_EXEC-existence contract test is no longer deferred — it landed in review, together with thetest-bump-callers.ymlpaths:widening it needs to fire on the commit that would break it, and three corrected rationale notes. Still named under Residual rather than fixed here: thepreflight.shstructural close, the two adjacent stale README claims, and the siblingpaths:-exclusion work.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.