Skip to content

ci(devx): a daily watchdog workflow gives check:platform-checklist a reporting channel - #16092

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-11730-platform-checklist-watchdog
Sep 6, 2026
Merged

ci(devx): a daily watchdog workflow gives check:platform-checklist a reporting channel#16092
os-steve merged 4 commits into
mainfrom
claude/issue-11730-platform-checklist-watchdog

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #11730

Ruled C′ on 2026-09-02 (verbatim 「其他同意」, director record 5511022650). One clause of that ruling was then re-decided: implementing "no pull_request trigger" literally reddened a tree-wide pin, the fork was put to the maintainer, and on 2026-09-06 the answer was 「A(荐)」 — filter the trigger rather than forbid it. Recorded on #11730 as 5556265772. The [Decision] title prefix is stale; this is an implementation card.

What lands

.github/workflows/platform-checklist-watchdog.yml — a daily schedule: plus workflow_dispatch: workflow that runs pnpm check:platform-checklist on main. On red it files, or refreshes, exactly one issue carrying the gate's own output verbatim; on green it files nothing, edits nothing and closes nothing.

It also carries a pull_request: trigger paths-filtered to that one workflow file and nothing else — the posture half-state-patrol.yml, release-coverage-patrol.yml and required-set-patrol.yml all keep, so a change to the patrol is exercised on a real runner before it merges. The filter is the whole of what makes that compatible with the standing decision: in the decision's own words in .github/workflows/lint.yml, "keeping it out of the per-PR path means an unrelated PR is never blocked by checklist drift". A trigger naming only this workflow fires on no unrelated pull request, so the decision's purpose is untouched and only its letter changed. ⛔ A pull_request run never writes to the board — it runs the gate, publishes to the run summary, and stops. The trigger block's own comment states that, and states which triggers are refused outright — an earlier revision of it claimed the opposite of the code four lines above and has been replaced.

scripts/check-platform-checklist-watchdog.mjs — the static pin, wired into lint.yml on every PR. Six clauses: the workflow exists and parses; it carries schedule: (with a real cron:) and workflow_dispatch:; it carries no merge_group: and no pull_request_target: trigger, refused outright because no filter makes them safe here; any pull_request: trigger is paths-filtered to this workflow alone; no board write is reachable from a pull_request run, judged on the step's own if:; and the gate is invoked as pnpm check:platform-checklist, never as an inlined copy of that alias's command.

Its --self-test carries 42 assertions across 8 floored batteries: a frozen SELF_TEST_BATTERIES roster of battery names with per-battery floors, the roster's own size pinned (deleting an entry silences a floor as effectively as zeroing it), registerCase() attribution to the most recently opened battery, the floor evaluated at the verdict site before the green line, and the reached-verdict handshake read at the --self-test dispatch. Without those, failures.length === 0 alone cannot tell "every case held" from "the cases never ran", and an early return would have printed 0 assertions and exited 0 — the one shape a PR about making an invisible red visible must not land. Measured on the branch, not asserted: node scripts/measure-self-test-floor.mjs --json reads ROSTER for this file (it read COUNT before), and --probe --only reads 0 DEFEATED, 1 HELD, 0 ACCIDENT. Every floor equals its measured count today.

docs/qa/platform-checklist/RUNNER.md — the ruled line naming the workflow as the channel that sees red. README.md gets the same correction where it said the gap was still open.

package.json — the check:platform-checklist alias now runs the gate's own --self-test leg. Not cosmetic: see "Second-order finding".

⛔ The standing maintainer decision — check:platform-checklist is not on the per-PR critical path — is preserved, by the paths filter rather than by absence.

The clause that was re-decided, and the measurement that forced it

scripts/pm/dispatch-gates.mjs pins tree-wide that zero discovered gate families are SCHEDULED-ONLY, because every patrol here carries a paths-filtered pull_request: trigger. A watchdog without that trigger made check:platform-checklist the first member. Measured, both legs, tree restored clean:

WITH the watchdog, no pull_request trigger : families=267 scheduledOnly=1 -> ["check:platform-checklist"]
WITHOUT the watchdog                       : families=266 scheduledOnly=0 -> []
WITH the watchdog, paths-filtered trigger  : families=267 scheduledOnly=0 -> []

pnpm check:pm-dispatch-gates, the required gate that reads it:

head verdict exit
eafd626 (trigger refused outright) ✗ dispatch-gates self-test: 3 of 1511 case(s) failed. 1
this head (trigger paths-filtered) ✓ dispatch-gates self-test: 1511 cases pass. 0

scripts/pm/dispatch-gates.mjs is not touched by this PR, and neither is its pin expectation.

The negative clauses are demonstrated, not asserted

Six ablations, each committed first, each mutation proven on disk by a count of the exact text before any reading was taken, each restored and the restoration proven by an empty git diff HEAD plus a git hash-object comparison against the HEAD blob.

On the narrowed clause, against the real workflow:

ablation reading
strip the paths: filter from the pull_request: trigger pin exit 1 — "fires on EVERY pull request", names the remedy; restore leg exit 0
widen the filter to name docs/qa/platform-checklist/** too pin exit 1 — "the filter may name this workflow and nothing else"; restore leg exit 0
drop github.event_name != 'pull_request' from the issue step's if: pin exit 1 — "a pull_request run could write to the board"; restore leg exit 0

⚠️ The third leg's first attempt was a void reading, and is reported rather than quietly re-run: its on-disk anchor expectation was wrong, so the harness printed MUTATION DID NOT LAND — reading VOID and refused to report a verdict. Re-run with a corrected anchor, it fires as above.

On the clauses carried over from the first round: adding a pull_request: trigger reddened the pin before it was narrowed; deleting the refused-trigger loop reddened the self-test (7 of 28 assertions) while leaving the live gate exit 0 — the reading that proves the self-test is the only instrument on that clause; deleting the workflow file reddened the pin.

The --self-test also drives each narrowed case both ways: unfiltered, types:-only, an empty paths list, a paths-ignore: complement, an extra path, a different path, and the bare on: [pull_request] sequence all fire; a comment mentioning pull_request does not, and a step that writes nothing needs no guard.

De-duplication — read off the precedent, and its lesson is negative

The C′ ruling names the Required-Set Patrol as the precedent for reporting by issue. Measured: it does not report by issue — it reports to the run summary plus an annotation and refuses a tracker outright in its own header. half-state-patrol.yml and release-coverage-patrol.yml do report by issue, but via one pre-minted pinned anchor from a repository variable, rewritten every run including on green. Neither is "opens an issue, or updates the existing open one".

The in-repo precedent that actually files-or-refreshes is merge-queue-triage.yml, and its anchor lookup is copied here with its measured lesson intact:

  • keyed on an identity label (platform-checklist-watchdog) that is never graded off — ⛔ not pm:queue or tooling, which are triage-state labels; keying on those makes the anchor invisible the moment a human triages it and files a fresh duplicate every day;
  • with a stable title naming no failing item, no count and no date;
  • and a plain-text body marker, ⛔ never an HTML comment, because this platform's body sanitizer eats short angle-bracket fragments and a de-dup key that can be silently swallowed files a duplicate a day.

A closed anchor is evidence and is not reopened: red again after the card was answered is a regression, so a new issue is filed naming the one it regressed from.

Verification

  • Gate family re-derived at the final commit with node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack — identical 59-command family to the previous round — then reconciled: 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN. Every exit code captured before any pipe.
  • 57 of 59 exit 0, including pnpm check:pm-dispatch-gates. Two exit 3 = PREREQUISITE NOT MET, i.e. NOT MEASURED and declared to CI: pnpm check:type-check-debt and pnpm check:dual-build-cjs-loads both demand a whole-tree pnpm build; this diff changes no package source, so neither verdict can move on it. check:doc-formula-expressions and check:dts-closure also returned 3 on a cold tree and both returned 0 after building their two-package closure under scripts/pm/os-verify-lock.sh.
  • pnpm check:platform-checklist itself: exit 0 on this tree — the watchdog would file nothing today, which is also the positive control that the pinned invocation actually runs.
  • turbo ls --affected against the merge base: 0 packages.
  • ESLint, narrowed and the narrowing proven: pnpm lint is eslint . --no-inline-config, and eslint.config.mjs states in its own prose that this repo "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file" — so this diff cannot move the verdict on any untouched file. Run over the changed JS files with --format json: 0 errors, 0 warnings.
  • Control characters: grep -naP over every changed file finds none.

Second-order finding, fixed here

Making CI run the gate at all reddened check:self-test-wired: scripts/check-platform-checklist.mjs ships a --self-test that no workflow executed. The remedy that gate names is wiring the flag, and a SELF_TEST_RUN_OTHERWISE row is explicitly "this finding written down somewhere quieter", so the alias carries the leg now. The gate's header argued a --self-test there "would execute NOWHERE" — the watchdog falsifies that, so the header is corrected rather than left reading as verified. The inline battery stays inline.

What only a real scheduled run exercises

Verified statically: the YAML parses, the triggers are present, absent and filtered as pinned, no board write is reachable from a pull_request run, the invoked package script exists, and the gate it names runs green here. ⛔ NOT verified and not claimable from this container: the anchor lookup against the live issues endpoint, label minting on first file, the retries: 3 delivery, and actions/github-script@v9 behaviour on a runner. The exit criterion the earlier ruling wrote down — one manual workflow_dispatch fire as a smoke test, verdict taken from GitHub output — is still owed and can only be taken after this merges. The new pull_request trigger means this PR's own successors get one leg of that for free: any later PR editing the watchdog will run it for real.


Generated by Claude Code

A GitHub Actions workflow on a daily schedule (plus workflow_dispatch) runs
`pnpm check:platform-checklist` on `main`. On red it files, or refreshes, one
issue carrying the gate's own output; on green it does nothing.

The gate stays OUT of per-PR CI, exactly as the standing maintainer decision in
lint.yml says — only the reporting channel changes.

`scripts/check-platform-checklist-watchdog.mjs` is the static pin, wired into
lint.yml on every PR: the workflow exists, fires on schedule + workflow_dispatch,
carries NO pull_request / pull_request_target / merge_group trigger, and invokes
the gate's package script rather than an inlined copy of its command. Every
clause, positive and negative, has a --self-test fixture that makes it fire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU9zBGbH2s2ZtxSyu963M3
… alias

`check:self-test-wired` reds the moment CI runs a script that ships a
`--self-test` no workflow executes — and the watchdog makes CI run this one.
Wiring the flag into the root alias is the remedy that gate names; a
SELF_TEST_RUN_OTHERWISE row would be the finding written down somewhere quieter.

The gate's header said a `--self-test` leg here "would execute NOWHERE". That
premise is falsified by the watchdog, so the header is corrected rather than
left to read as verified. The inline battery stays inline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU9zBGbH2s2ZtxSyu963M3
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026
@github-actions github-actions Bot added size/l ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation labels Sep 6, 2026
…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
@claude

claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Review of head 993d85a62 by the domain:devx @ objectstack seat (session session_01AhooRxUmvwYwcnQ5LATTB7): REWORK, two items — the verdict with line references is on the card, #11730 (the comment titled "REWORK — PR #16092 at head 993d85a62"). In one line each: (1) .github/workflows/platform-checklist-watchdog.yml:123-126 still says "NO pull_request: … here" four lines under the pull_request: trigger this commit added; (2) scripts/check-platform-checklist-watchdog.mjs's self-test has no battery roster/floor and no verdict handshake (the #13799 / #15657 shape every other scripts/** self-test on main carries). If the dev holding this branch is live, push the fix here; if nothing lands by 2026-09-06T03:11Z the seat continues on this branch with its own dev. Also owed on #11730: the os-dev-report for the 02:24Z push with the check:pm-dispatch-gates before/after.


Generated by Claude Code

…mment its own trigger falsified

Review found two things and both are correct.

1. The workflow carried a comment claiming NO pull_request and NO merge_group
   trigger, four lines under the pull_request trigger this branch added, and
   false about the pin besides. Replaced with the true statement.

2. The new gate's self-test decided success by `failures.length === 0` and
   printed an UNCOMPARED case count, and the dispatch discarded its completion —
   so an early return would have printed `0 assertions` and exited 0. Landing a
   new scripts/** gate in that shape re-opens two closed sets by one. Brought to
   the landed shape: a frozen SELF_TEST_BATTERIES roster of eight battery names
   with per-battery floors, the roster's own size pinned, registerCase()
   attribution to the most recently opened battery, the floor evaluated at the
   verdict site before the green line, and the reached-verdict handshake read at
   the --self-test dispatch.

Measured, not asserted: `measure-self-test-floor.mjs --json` now reads ROSTER
for this file (was COUNT), and `--probe --only` reads HELD (0 DEFEATED, 1 HELD,
0 ACCIDENT). Every floor equals its measured count; 42 assertions across 8
batteries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU9zBGbH2s2ZtxSyu963M3
@github-actions github-actions Bot added size/xl and removed size/l labels Sep 6, 2026
@os-steve
os-steve marked this pull request as ready for review September 6, 2026 03:12
@os-steve
os-steve added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 94ecb7e Sep 6, 2026
39 checks passed
@os-steve
os-steve deleted the claude/issue-11730-platform-checklist-watchdog branch September 6, 2026 03:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants