[PR #302] [MERGED] ci(bump-callers): add WATCHED_EXEC to the five fleets whose asset directory outlives the scripts inside it #303

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

📋 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)

  • 1faebc1 ci(bump-callers): add WATCHED_EXEC to the five fleets whose asset directory outlives the scripts inside it
  • 8f8b090 test(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-review and the directory is still there, because README.md and tests/ 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_EXEC is 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_EXEC added 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 from workflows_ref and 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.yml sets explicitly). So the groom briefs (finder.md / verifier.md / builder.md, each open()ed out of the pinned ref), the three cursor-review prompts, the vendored schema.v2.json the CodeRabbit checker resolves beside itself, the --require-hashes requirements.txt, and .github/groom/package.json (the "Load the agent CLI pin" step fails closed on a missing manifest) all qualify. README.md and anything under tests/ do not.

Three files live in a watched tree, are genuinely executed, and are deliberately left out — catalog-drift.py, schema_drift.py and wire-bot-identity.py all run out of this repo's own checkout, never from workflows_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.mjs is out for the adjacent reason: broker.mjs superseded it and no step loads it. Each omission carries its rationale inline.

The scripts/check-pr-size entries 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 no tests/, so what keeps it resolving after main.go / size.go / go.mod are deleted is precisely the *_test.go pair both filters deliberately exclude.

.github/bump-callers/README.md is corrected in the two places that claimed WATCHED_EXEC was needed "Only by pr-risk and pr-derisk today" — 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 whose WATCHED_ASSETS entry is a directory needs it; only the three that watch nothing beyond WATCHED leave 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.sh or bump-callers.sh changes, and no bump fleet's paths: filter is touched. The one paths: filter that does change is test-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.sh parsed only WATCHED / WATCHED_ASSETS / WATCHED_PATHSPECS, and test_preflight.sh drives synthetic fixtures without touching the entrypoints. WATCHED_EXEC was 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, makes preflight.sh take 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.sh now asserts, for all thirteen fleets:

  • every WATCHED_EXEC entry resolves to a TRACKED FILE at that commit. git ls-files with an exact match, not [[ -f ]]: preflight.sh resolves 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 shape preflight.sh errors on at run time. This is what keeps the two byte-identical scripts/check-pr-size blocks in step: a rename applied to one list leaves the other naming a path that cannot resolve, and that fleet fails.
  • every entry sits UNDER a watched surface. One that does not is never compared and never triggers the fleet's paths: filter, so it can only ever contribute a freeze — the commit deleting it starts no run of that fleet at all.
  • no fleet watching a DIRECTORY leaves the input unset — the rule this PR documents, now enforced rather than asserted in prose. The three WATCHED-only fleets keep the exemption, since preflight.sh probes WATCHED unconditionally and a .yml file 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.py untested — the guard would first speak on some unrelated later PR, long after the fleet froze. So test-bump-callers.yml grows .github/** and scripts/**: 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 duplicated paths: 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_list against split_lines' comment rule, which diverges from the WATCHED_ASSETS parser beside it deliberately and in the opposite direction: WATCHED_EXEC drops a whole-line # (so a pasted filter's comments are fine) while WATCHED_ASSETS rejects 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-size named the wrong mechanism. It claimed listing a *_test.go "would re-import the churn that exclusion removed". It would not: WATCHED_EXEC is a presence-only decommission probe and takes no part in the staleness comparison (that is WATCHED_PATHSPECS), so an entry here can only ever read as tip_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-config carried no versioned-rename note. It now carries the one bump-pr-risk-callers.yml and bump-pr-derisk-callers.yml already carry for risk-map.v0.json: schema.v2.json is renamed by a v2→v3 migration (this repo already tracks it upstream via refresh-coderabbit-schema.yml) and requirements.txt disappears the day the validator goes stdlib-only. Either reads here as a decommission.
  • Neither scripts/check-pr-size block carried one either, though go build . needs only some non-test source in the package — merging size.go into main.go is 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.yml and bump-detect-unreviewed-merge-callers.yml set WATCHED only, have no asset directory, and are correctly exempt — there is no directory to outlive anything. (bump-cursor-cli-pin.yml is not a caller fleet and does not match the bump-*-callers.yml shape.)

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, so fetched_tip == HEAD and 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.go each deleted in turn → without WATCHED_EXEC the run emits proceed=true (the defect, reproduced); with the shipped list it emits proceed=false and a ::warning:: naming the exact file.
  • Healthy tree, all five fleets → proceed=true and no ::error::, so no list introduces a false decommission or trips preflight.sh's path/not-a-directory validation.

The harness is a throwaway probe, not committed — the repo's test_preflight.sh already 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.json is listed even though it is read from job.workflow_sha, not workflows_ref. That is deliberate in groom.yml and unchanged here; for a SHA-pinned caller job.workflow_sha resolves to the same commit this fleet pins, which is the commit WATCHED_EXEC probes. The step fails closed with an ::error:: on a missing manifest, so its absence genuinely breaks a pinned caller.
  • Caller-gated files are listed (slack-notify.sh, gate-unresolved.py), following the precedent in bump-pr-risk-callers.yml, which lists apply-risk-label.sh / publish-risk-surfaces.sh on the same reasoning: a caller that enables the feature breaks without them.
  • Reusable workflow files are listed alongside WATCHED, matching all five fleets that already set the input.

Residual

  • The structural close is not in this PR, by design. preflight.sh still does not REQUIRE WATCHED_EXEC when a WATCHED_ASSETS entry 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 -e rather 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 the WATCHED_EXEC validator), never unconditional. Worth its own change.
  • Two adjacent stale claims in the same README were left as found. Around :171 it still says every entrypoint runs preflight.sh "except bump-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 :487 still says "all eight entrypoints"; there are 13. Neither is the WATCHED_EXEC claim this change was scoped to correct, and both are independent of the env: blocks here.
  • The sibling paths:-exclusion work on the overlapping fleets is untouched (churn bumps from test-only commits, with its own forced WATCHED_PATHSPECS migration and a CI contract test). Neither fix subsumes the other.
  • Artifacts exercised, for the record. The one named attachment — Comfy-Org/github-workflows#248 — was readable from here and was read; its census matches what I re-derived independently, and its "Fix" step 2 is the structural close named above. It is still OPEN and this PR does not close it, since step 2 remains. The linked Actions run 33245434399 was not opened — it is the provenance of the finding, not an input to the fix, and the issue body reproduces its conclusions.

Provenance

  • Authored by: agent-work loop
  • Verified: test_preflight.sh: 445 passed, 0 failed; test_paths_contract.sh: 140 passed, 0 failed (was 110; +30 for the WATCHED_EXEC contract, 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_load over the four edited workflow files: parse clean, both paths: lists identical, every WATCHED_EXEC block 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)
  • Deviations: all six acceptance criteria met. The WATCHED_EXEC-existence contract test is no longer deferred — it landed in review, together with the test-bump-callers.yml paths: 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: the preflight.sh structural close, the two adjacent stale README claims, and the sibling paths:-exclusion work.

🔄 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/302 **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-15253-watched-exec-five-fleets` --- ### 📝 Commits (2) - [`1faebc1`](https://github.com/Comfy-Org/github-workflows/commit/1faebc15ba8622b9fd5662df7421ba3ba5049ec2) ci(bump-callers): add WATCHED_EXEC to the five fleets whose asset directory outlives the scripts inside it - [`8f8b090`](https://github.com/Comfy-Org/github-workflows/commit/8f8b090ed02c0fe94f05b74171dff3bfd9bff32a) test(bump-callers): machine-enforce WATCHED_EXEC, and correct three of its rationale notes ### 📊 Changes **8 files changed** (+467 additions, -14 deletions) <details> <summary>View changed files</summary> 📝 `.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) </details> ### 📄 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-review` and the directory is still there, because `README.md` and `tests/` 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_EXEC` is 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_EXEC` added 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 from `workflows_ref` and 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.yml` sets explicitly). So the groom briefs (`finder.md` / `verifier.md` / `builder.md`, each `open()`ed out of the pinned ref), the three cursor-review prompts, the vendored `schema.v2.json` the CodeRabbit checker resolves beside itself, the `--require-hashes` `requirements.txt`, and `.github/groom/package.json` (the "Load the agent CLI pin" step fails **closed** on a missing manifest) all qualify. `README.md` and anything under `tests/` do not. Three files live in a watched tree, are genuinely executed, and are **deliberately left out** — `catalog-drift.py`, `schema_drift.py` and `wire-bot-identity.py` all run out of *this* repo's own checkout, never from `workflows_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.mjs` is out for the adjacent reason: `broker.mjs` superseded it and no step loads it. Each omission carries its rationale inline. The `scripts/check-pr-size` entries 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 no `tests/`, so what keeps it resolving after `main.go` / `size.go` / `go.mod` are deleted is precisely the `*_test.go` pair both filters deliberately exclude. `.github/bump-callers/README.md` is corrected in the two places that claimed `WATCHED_EXEC` was needed "Only by `pr-risk` and `pr-derisk` today" — 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 whose `WATCHED_ASSETS` entry is a directory needs it; only the three that watch nothing beyond `WATCHED` leave 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.sh` or `bump-callers.sh` changes, and no bump fleet's `paths:` filter is touched. The one `paths:` filter that does change is `test-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.sh` parsed only `WATCHED` / `WATCHED_ASSETS` / `WATCHED_PATHSPECS`, and `test_preflight.sh` drives synthetic fixtures without touching the entrypoints. `WATCHED_EXEC` was 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, makes `preflight.sh` take 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.sh` now asserts, for all thirteen fleets: - **every `WATCHED_EXEC` entry resolves to a TRACKED FILE** at that commit. `git ls-files` with an exact match, not `[[ -f ]]`: `preflight.sh` resolves 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 shape `preflight.sh` errors on at run time. This is what keeps the two byte-identical `scripts/check-pr-size` blocks in step: a rename applied to one list leaves the other naming a path that cannot resolve, and that fleet fails. - **every entry sits UNDER a watched surface.** One that does not is never compared and never triggers the fleet's `paths:` filter, so it can only ever contribute a freeze — the commit deleting it starts no run of that fleet at all. - **no fleet watching a DIRECTORY leaves the input unset** — the rule this PR documents, now enforced rather than asserted in prose. The three `WATCHED`-only fleets keep the exemption, since `preflight.sh` probes `WATCHED` unconditionally and a `.yml` file 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.py` untested — the guard would first speak on some unrelated later PR, long after the fleet froze. So `test-bump-callers.yml` grows `.github/**` and `scripts/**`: 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 duplicated `paths:` 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_list` against `split_lines`' comment rule, which diverges from the `WATCHED_ASSETS` parser beside it deliberately and in the *opposite* direction: `WATCHED_EXEC` drops a whole-line `#` (so a pasted filter's comments are fine) while `WATCHED_ASSETS` rejects 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-size` named the wrong mechanism.** It claimed listing a `*_test.go` "would re-import the churn that exclusion removed". It would not: `WATCHED_EXEC` is a presence-only decommission probe and takes no part in the staleness comparison (that is `WATCHED_PATHSPECS`), so an entry here can only ever read as `tip_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-config` carried no versioned-rename note.** It now carries the one `bump-pr-risk-callers.yml` and `bump-pr-derisk-callers.yml` already carry for `risk-map.v0.json`: `schema.v2.json` is renamed by a v2→v3 migration (this repo already tracks it upstream via `refresh-coderabbit-schema.yml`) and `requirements.txt` disappears the day the validator goes stdlib-only. Either reads here as a decommission. - **Neither `scripts/check-pr-size` block carried one either**, though `go build .` needs only *some* non-test source in the package — merging `size.go` into `main.go` is 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.yml` and `bump-detect-unreviewed-merge-callers.yml` set `WATCHED` only, have no asset directory, and are correctly exempt — there is no directory to outlive anything. (`bump-cursor-cli-pin.yml` is not a caller fleet and does not match the `bump-*-callers.yml` shape.) ## 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, so `fetched_tip == HEAD` and 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.go` each deleted in turn → **without** `WATCHED_EXEC` the run emits `proceed=true` (the defect, reproduced); **with** the shipped list it emits `proceed=false` and a `::warning::` naming the exact file. - Healthy tree, all five fleets → `proceed=true` and no `::error::`, so no list introduces a false decommission or trips `preflight.sh`'s path/not-a-directory validation. The harness is a throwaway probe, not committed — the repo's `test_preflight.sh` already 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.json` is listed even though it is read from `job.workflow_sha`, not `workflows_ref`.** That is deliberate in `groom.yml` and unchanged here; for a SHA-pinned caller `job.workflow_sha` resolves to the same commit this fleet pins, which is the commit `WATCHED_EXEC` probes. The step fails closed with an `::error::` on a missing manifest, so its absence genuinely breaks a pinned caller. - **Caller-gated files are listed** (`slack-notify.sh`, `gate-unresolved.py`), following the precedent in `bump-pr-risk-callers.yml`, which lists `apply-risk-label.sh` / `publish-risk-surfaces.sh` on the same reasoning: a caller that enables the feature breaks without them. - **Reusable workflow files are listed alongside `WATCHED`**, matching all five fleets that already set the input. ## Residual - **The structural close is not in this PR, by design.** `preflight.sh` still does not REQUIRE `WATCHED_EXEC` when a `WATCHED_ASSETS` entry 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 `-e` rather 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 the `WATCHED_EXEC` validator), never unconditional. Worth its own change. - **Two adjacent stale claims in the same README were left as found.** Around `:171` it still says every entrypoint runs `preflight.sh` "except `bump-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 `:487` still says "all eight entrypoints"; there are 13. Neither is the `WATCHED_EXEC` claim this change was scoped to correct, and both are independent of the `env:` blocks here. - **The sibling `paths:`-exclusion work on the overlapping fleets is untouched** (churn bumps from test-only commits, with its own forced `WATCHED_PATHSPECS` migration and a CI contract test). Neither fix subsumes the other. - **Artifacts exercised, for the record.** The one named attachment — [Comfy-Org/github-workflows#248](https://github.com/Comfy-Org/github-workflows/issues/248) — was readable from here and was read; its census matches what I re-derived independently, and its "Fix" step 2 is the structural close named above. It is still OPEN and this PR does not close it, since step 2 remains. The linked Actions run `33245434399` was not opened — it is the provenance of the finding, not an input to the fix, and the issue body reproduces its conclusions. ## Provenance - **Authored by:** agent-work loop - **Verified:** `test_preflight.sh`: 445 passed, 0 failed; `test_paths_contract.sh`: 140 passed, 0 failed (was 110; +30 for the `WATCHED_EXEC` contract, 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_load` over the four edited workflow files: parse clean, both `paths:` lists identical, every `WATCHED_EXEC` block 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) - **Deviations:** all six acceptance criteria met. The `WATCHED_EXEC`-existence contract test is no longer deferred — it landed in review, together with the `test-bump-callers.yml` `paths:` 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: the `preflight.sh` structural close, the two adjacent stale README claims, and the sibling `paths:`-exclusion work. --- <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:14 +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#303
No description provided.