Skip to content

Commit 993d85a

Browse files
committed
ci(devx): the watchdog's pull_request trigger is paths-filtered, and the pin narrows to match
Maintainer ruling of 2026-09-06, answering the A/B/C fork this PR opened: A. The workflow gains a `pull_request:` trigger filtered to its own file and nothing else — the posture every patrol in this repo keeps, and the one `scripts/pm/dispatch-gates.mjs` pins tree-wide. It fires on no unrelated pull request, so the standing decision's purpose ("an unrelated PR is never blocked by checklist drift") is untouched; only its letter changed. The pin narrows rather than drops its negative half: `merge_group` and `pull_request_target` stay refused outright, and a `pull_request` trigger must carry a `paths:` filter naming this workflow and nothing else. Unfiltered, an empty list, a `paths-ignore:` complement, an extra path and a different path each fire with their own message. A second clause follows from the first: no board write may be reachable from a pull_request run, judged on the step's own `if:`. 42 self-test assertions, up from 28. Measured before the edit: `check:pm-dispatch-gates` went from `3 of 1511 case(s) failed` to `1511 cases pass`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PU9zBGbH2s2ZtxSyu963M3
1 parent eafd626 commit 993d85a

3 files changed

Lines changed: 270 additions & 59 deletions

File tree

.github/workflows/lint.yml

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,18 +2329,20 @@ jobs:
23292329
# `.github/workflows/platform-checklist-watchdog.yml` runs the gate on `main`
23302330
# daily and files or refreshes one issue when it is red.
23312331

2332-
# ⛔ …and the step below is NOT that wiring. `check:platform-checklist` still
2333-
# never runs on a pull request. What runs here is the STATIC PIN over the
2334-
# watchdog workflow: it asserts the file exists, fires on `schedule:` +
2335-
# `workflow_dispatch:`, carries NO `pull_request:` / `pull_request_target:` /
2336-
# `merge_group:` trigger, and invokes the gate through its package script
2337-
# rather than an inlined copy of its command. Two file reads, no socket.
2338-
#
2339-
# It has to live HERE rather than inside the watchdog. The watchdog runs on no
2340-
# pull request by construction — that absence is the very thing being pinned —
2341-
# so it cannot judge the PR that edits it, and a PR DELETING it would silence
2342-
# any check that lived inside it. This job is the only place where the absence
2343-
# of that file is observable before it merges.
2332+
# ⛔ …and the step below is NOT that wiring. What runs here is the STATIC PIN
2333+
# over the watchdog workflow: it asserts the file exists, fires on
2334+
# `schedule:` + `workflow_dispatch:`, carries NO `merge_group:` /
2335+
# `pull_request_target:` trigger, that any `pull_request:` trigger is
2336+
# `paths`-filtered to the watchdog file ALONE, that no board write is
2337+
# reachable from a pull_request run, and that the gate is invoked through its
2338+
# package script rather than an inlined copy. Two file reads, no socket.
2339+
#
2340+
# The watchdog's own `pull_request:` trigger names only itself, so it fires on
2341+
# no unrelated PR — that filter is what keeps the decision above intact, and
2342+
# pinning it is most of this step's job. It has to live HERE rather than
2343+
# inside the watchdog for the other half: a PR DELETING the watchdog would
2344+
# silence any check that lived inside it, and this job is the only place where
2345+
# the absence of that file is observable before it merges.
23442346
#
23452347
# Invoked as `node scripts/…` rather than a `pnpm check:*` alias: see the GATE
23462348
# INVOCATION IDIOM note at the top of this file. The `--self-test` leg runs

.github/workflows/platform-checklist-watchdog.yml

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,22 @@ name: Platform-Checklist Watchdog
1111
# by checklist drift. ⛔ This workflow does not touch that. Only the REPORTING
1212
# CHANNEL changes — which is the whole of what was ruled.
1313
#
14-
# That is why this file carries `schedule:` and `workflow_dispatch:` and ⛔ NO
15-
# `pull_request:` and NO `merge_group:` trigger, unlike every other patrol in
16-
# this repo (`half-state-patrol.yml`, `release-coverage-patrol.yml` and
17-
# `required-set-patrol.yml` all carry a paths-filtered `pull_request:` so a
18-
# change to the patrol is exercised before it merges). Copying that row here
19-
# would put `check:platform-checklist` on a PR's critical path through the back
20-
# door and contradict the decision this workflow exists to preserve. The cost is
21-
# stated rather than hidden: a change to THIS FILE is not proven on a real
22-
# runner before it merges, so the first scheduled run after such a change is the
23-
# proof. `scripts/check-platform-checklist-watchdog.mjs` runs on every PR and
24-
# holds the shape statically — including the absence of those two triggers — so
25-
# what a PR loses is the live rehearsal, not the shape.
14+
# So this file carries `schedule:`, `workflow_dispatch:`, and a `pull_request:`
15+
# trigger `paths`-filtered to THIS FILE AND NOTHING ELSE — the posture
16+
# `half-state-patrol.yml`, `release-coverage-patrol.yml` and
17+
# `required-set-patrol.yml` all keep, so a change to the patrol is exercised
18+
# before it merges. ⛔ NO `merge_group:` and ⛔ NO `pull_request_target:`, ever.
19+
#
20+
# The filter is what makes that trigger compatible with the standing decision
21+
# rather than a back door through it. The decision's own words, in
22+
# `.github/workflows/lint.yml`: "keeping it out of the per-PR path means an
23+
# unrelated PR is never blocked by checklist drift." A trigger that fires only
24+
# on a pull request editing this workflow blocks no unrelated pull request at
25+
# all — it preserves the decision's purpose while changing its letter, which is
26+
# exactly what the maintainer ruled on 2026-09-06.
27+
#
28+
# ⛔ A `pull_request` run NEVER writes to the board. It runs the gate, publishes
29+
# the output to the run summary, and stops there.
2630
#
2731
# ## Why a workflow, and not "a seat should run it"
2832
#
@@ -95,6 +99,27 @@ on:
9599
- cron: '51 2 * * *'
96100
# The manual fire, and the smoke test this card exits on.
97101
workflow_dispatch: {}
102+
# ⛔ PATHS-FILTERED TO THIS ONE FILE, and that filter is the whole reason this
103+
# trigger is allowed to exist. The maintainer's standing decision, in its own
104+
# words in `.github/workflows/lint.yml`: "keeping it out of the per-PR path
105+
# means an unrelated PR is never blocked by checklist drift." A trigger that
106+
# fires ONLY on a pull request editing this workflow blocks no unrelated PR at
107+
# all, so it preserves that decision's purpose while changing its letter — the
108+
# posture every patrol in this repo already keeps, and the one
109+
# `scripts/pm/dispatch-gates.mjs` pins tree-wide.
110+
#
111+
# ⛔ Do NOT widen this list. A second path here would put the checklist gate on
112+
# the critical path of pull requests that have nothing to do with it, which IS
113+
# the thing the decision forbids;
114+
# `scripts/check-platform-checklist-watchdog.mjs` refuses any widening and its
115+
# self-test proves that refusal fires.
116+
#
117+
# ⛔ And a pull_request run NEVER writes to the board — see the `if:` on the
118+
# issue step. It proves the transport, the invocation and the rendering on a
119+
# real runner, and nothing else.
120+
pull_request:
121+
paths:
122+
- '.github/workflows/platform-checklist-watchdog.yml'
98123
# ⛔ NO `pull_request:` and ⛔ NO `merge_group:` here, deliberately — see the
99124
# header. `scripts/check-platform-checklist-watchdog.mjs` fails when either
100125
# appears, and its self-test proves that refusal fires rather than asserting
@@ -185,6 +210,10 @@ jobs:
185210
{
186211
echo "### Platform-checklist watchdog — gate exit ${{ steps.gate.outputs.exit_code }}"
187212
echo
213+
if [ "${{ github.event_name }}" = "pull_request" ]; then
214+
echo "_Board write skipped: a pull_request run proves the sweep without filing or refreshing anything._"
215+
echo
216+
fi
188217
echo '```'
189218
cat "$RUNNER_TEMP/gate.out" 2>/dev/null || echo '(no stdout captured)'
190219
cat "$RUNNER_TEMP/gate.err" 2>/dev/null || true
@@ -194,7 +223,14 @@ jobs:
194223
- name: File or refresh the watchdog issue
195224
# RED only. On green this step does not run, and nothing else in this
196225
# job writes anything — that is the ruled behaviour.
197-
if: steps.gate.outputs.exit_code != '0'
226+
#
227+
# ⛔ And NEVER on a `pull_request` run, whatever the gate said. A pull
228+
# request proves the transport, the invocation and the rendering on a
229+
# real runner; it must not touch the board. That is the posture every
230+
# patrol in this repo keeps, and
231+
# `scripts/check-platform-checklist-watchdog.mjs` refuses a board write
232+
# whose `if:` lacks this guard.
233+
if: steps.gate.outputs.exit_code != '0' && github.event_name != 'pull_request'
198234
uses: actions/github-script@v9
199235
env:
200236
GATE_EXIT: ${{ steps.gate.outputs.exit_code }}

0 commit comments

Comments
 (0)