Uh oh!
There was an error while loading. Please reload this page.
fix(pm): mark CI-measured-only gate families instead of listing them as runnable - #14280
Merged
Merged
Conversation
…as runnable `dispatch-gates` advertised `node scripts/pm/check-governed-queue-guard.mjs` among the families under "Local gates for this card (paste into the dispatch prompt)" and emitted it from `--commands`, whose caption promises one runnable command per line. That gate judges the merge-queue event payload and nothing else, so outside a workflow run its only possible outcome is EXIT=1 — every dev on a `.claude/agents/**` diff got 9 green and 1 structurally red on a clean tree, and a red that always fires trains its reader to discount reds. The classification is a SHAPE read off the gate's own source, never a list of names: limb 1, the family accesses `process.env.GITHUB_EVENT_PATH` (comments and self-test bodies masked); limb 2, no one can run it by name here (a direct workflow invocation, no root manifest script). Measured over the 200 discovered families: limb 1 alone selects 1, limb 2 alone 43, the conjunction exactly 1. The conjunction is the safe direction — this rule SUBTRACTS from `--commands`, and a wrong subtraction is silent while a miss is loud. The family is marked, never dropped: it keeps its own heading with its provenance in the human rendering, is flagged as `ciOnly` on its `--json` row, and the reconciliation carries the omission as its own term. `--tier` is untouched. Costs no new I/O — the payload dependence is a fifth answer off the single source read `discoverFamilies` already makes. ⛔ `check-governed-queue-guard.mjs` is not touched: its refusal is correct. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
baozhoutao
marked this pull request as ready for review
September 1, 2026 21:44
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#14004
dispatch-gatesadvertisednode scripts/pm/check-governed-queue-guard.mjsamong the families under "Local gates for this card (paste into the dispatch prompt)", and emitted it from--commands, whose caption promises one runnable command per line. That gate judges the merge-queue event payload and nothing else, so outside a workflow run its only possible outcome isEXIT=1. Every dev on a.claude/agents/**diff harvested 10 commands and got 9 green and 1 structurally red on a clean tree.The derivation now classifies such a family by SHAPE, marks it in the human rendering, flags it in
--json, and omits it from--commands.⛔
scripts/pm/check-governed-queue-guard.mjsis NOT touched. Its refusal is correct and deliberate — "could not look" must never exit 0 — and triage ruled option 2 out: 「不要改那个门禁。」 The defect was upstream of it, and that is where this lands.Premise re-check (fresh
origin/main, fa1eca3).claude/agents/os-dev.mdderives the queue-guard row among the local gatesEXIT=1grep -c 'GITHUB_EVENT_PATH|ciOnly|ci-only' scripts/pm/dispatch-gates.mjsis zeroPremise valid; the card is live.
The shape criterion — measured, which is the part triage left to this lane
Triage leaned to shape detection over a hand-maintained list and explicitly left the criterion's cost to be measured here. Measured on fa1eca3, over the 200 discovered families:
process.env.GITHUB_EVENT_PATH(dotted or bracketed, through a localenvalias too), with comments and self-test bodies maskedrun:family (never acheck:*npm script) whose file no root manifest script namesscripts/pm/check-governed-queue-guard.mjsCost of the derivation itself: zero new I/O.
discoverFamiliesalready reads every family file once for four other answers; the payload dependence is a fifth answer off that same read, so it cannot describe a different revision of the gate than the hints printed beside it.Three narrowings, each with the measurement that chose it:
--commands; a subtraction that fires wrongly is silent (a real gate quietly missing from the runnable list, every dispatch order still reading normal), while a miss is loud (the status quo). The same trade one screen up in that file —declaredInheritedPopulation— was decided the same direction by maintainer ruling on 2026-08-28. A gate with acheck:*name, or one that reads the payload with a git fallback, keeps its place in the list and keeps its loud red.GITHUB_EVENT_PATHalone, not the widerGITHUB_EVENT_NAME/GITHUB_BASE_REF/GITHUB_HEAD_REF/GITHUB_REF_NAMEfamily — those are routinely read WITH a local fallback, while the payload path is the run's input or nothing. Measured: exactly one tracked file in the repo names any of the five, so widening the set changes zero classifications. The narrowing is a statement about tomorrow's gate, not today's count.extractWatchHintsapplies. Today the masked and unmasked answers agree (one file names the variable and also accesses it), so the masking costs nothing — it is the difference between classifying a gate and classifying its docblock, in the direction where a false positive is silent.Not shipped, and the measurement that decided it: annotating the row with the gate's offline
--self-testentry point. The workflow does invokenode scripts/pm/check-governed-queue-guard.mjs --self-testatgoverned-surface-guard.yml:104, but that invocation is deliberately not a discovered family (extractCheckInvocationsskips a--self-testkey whose script already has acheck-basename, to avoid splitting one family into two), so surfacing it would need a second scan of the workflow text rather than data already in hand. Out of proportion to the row it decorates.Before / after, on a diff touching
.claude/agents/os-dev.mdBEFORE — 10 rows in the pasted block, the last one structurally red wherever a dev runs it:
--commandsemitted all ten.AFTER — 9 runnable rows in the pasted block; the family is still named, once, under its own heading past the blank line the published harvest stops at:
--commandsnow prints 9, and its stderr accounting names the omission out loud beside the two omissions that mode already declared (pending-changeset families, always-runs tail) — a quiet omission inside a fix for a quiet omission is how that defect reproduces itself one layer up.--jsonkeeps the family as a matched row and flags it (matched[].ciOnly.env), so a machine consumer READS the omission instead of inferring it from a missing row.--tieris structurally unaffected: its CLI branch never entersderive,discoverFamilies,commandsForor the reconciliation.Self-test
pnpm check:pm-dispatch-gates(the documented entry — it spawns the tool's--self-test) at4b6dca186:0 failing cases. 34 of the 1174 are new here.
New cases, in the halves the file separates:
check:*family outright, and limb 1 is pinned as REQUIRED (without it limb 2 alone would subtract 43 families).check:nul-bytesdoes not; and at least one directly-invoked gate that reads no payload does not either, so the rule discriminates rather than sweeping.--commandsomits the row and keeps the runnable one, and keeps it omitted when a change KIND names the same family (the other section it could arrive through); the reconciliation closes on that input and carries the omission as its own term rather than charging it to a repeat; a card whose ONLY match is CI-measured still says so at a total of zero rather than reading as "nothing matched"..claude/agents/os-dev.md— a CONTROL that the family is still derived at all (a rule that dropped it entirely would pass every case below); the real published harvest snippet, driven with realawk/sedover the real rendering, no longer yields the command;--commandsagrees with that harvest exactly; the human rendering still names the family with its workflow, provenance and reason;--jsonflags it and excludes it fromcommands; the stderr accounting says so.Gates run
Derived on the real changeset with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths — the script takes the change set off the merge base itself), plus the two the dispatch named. All run at4b6dca186, each exit code captured before any pipe:check:pm-dispatch-gatescheck:declared-population-livecheck:ratchet-remedy-authoritycheck:nul-bytes·check:agent-test-spelling·check:bash32-floor·check:cli-command-ids·check:cross-package-test-inputs·check:entry-guard·check:parse-guard·check:pnpm-filter-targets·check:watch-hint-literalcheck-ci-filter-parity·check-cross-package-test-inputs·check-self-test-wired·check-shard-attestationcheck-test-completenessturbo run testlog and the family names it with no argument; its own banner says this branch is unreachable in CI and is not a redAlso self-scanned for control bytes beyond the gate (
grep -naPover the edited file, exit 1 = none).The two
yaml-importing gates first returned exit 3 PREREQUISITE NOT MET in a fresh worktree; re-run afterpnpm installthey are green, and that first reading is recorded as NOT MEASURED rather than as a pass.Changeset route
skip-changeset. The diff is one file,scripts/pm/dispatch-gates.mjs— PM tooling that publishes nothing from any package, so there is no user-visible change to record. Label applied at PR-open time (never left for the Check Changeset step to redden first) and read back.Generated by Claude Code