Filed unassigned by the domain:spec execution seat (session_016N6xmWt5hYm94ffVEwGH8x) as part of its shift-close hand-over report, mechanizable-item class. ⛔ Unassigned, no domain:*, no priority — routing and grading are the triage seat's.
The measurement
scripts/pm/check-governed-merges.mjs --test <paths> is the governance authority, and every dispatch instructs the dev and the seat to run it on the PR's exact final file list. Nothing states how that list is to be derived, and the two obvious derivations disagree whenever the branch is behind main.
Measured on PR #16997 at 2026-09-09T01:1xZ, head 98c651703, merge base bb7d91f19, origin/main at fe2b7554a:
git diff --stat origin/main 98c651703 # TWO-dot
7 files changed, 67 insertions(+), 881 deletions(-)
.changeset/value-domain-note-settings-door-repointed.md
docs/qa/platform-checklist/areas/integration-system.json
docs/qa/platform-checklist/areas/records-forms.json
packages/spec/scripts/generate-string-family-width.pin.test.ts
packages/spec/scripts/…-16394-character-width-dialect-parity.test.ts
packages/spec/liveness/field.json
scripts/check-changeset-no-major.mjs
git diff --stat origin/main...98c651703 # THREE-dot (the real PR)
2 files changed, 19 insertions(+), 7 deletions(-)
Five of those seven files are main's own newer commits, appearing as reversions because the branch has not taken them. The PR touches two files.
Why it is worth a card
The superset fails safe in one direction and unsafe in the other, and the unsafe direction is the one that costs:
- It can never hide a real hit — a superset only adds paths. So it will not let a governed PR through.
- ⚠️ But it can manufacture one. Any seat whose branch is behind a
main that has since touched docs/adr/**, .claude/**, skills/**, AGENTS.md or CLAUDE.md gets a GOVERNED verdict for a PR that touches none of them — and the standing rule is that a governed PR is maintainer-only: ⛔ never flip ready, never enqueue, never arm auto-merge. ⇒ a correct, ordinary PR gets parked, and the seat's reflex on a governed verdict is to escalate, not to re-derive the input.
skills/** is regenerated by gen:skill-refs and moves on ordinary packages/spec landings, so a branch a few hours old carrying that path in its two-dot diff is not a contrived case. This seat came within one command of taking the seven-file reading as the PR's file list.
⭐ The general shape: the gate is correct and its input is not. Nothing in the tool can detect it, because a path list is a path list — the tool cannot know the caller meant "what this PR changes" rather than "what differs from main".
Why the obvious fix is the mechanization
The derivation is not a judgement call — there is exactly one right answer (A...B, or equivalently git diff $(git merge-base A B) B) and the wrong one is a single character away. That is the profile of something a tool should compute rather than a rule prose should ask agents to remember.
Candidate shapes, ⛔ not a ruling:
--pr <n> — the script resolves the PR's file list itself (merge base, or the API's own changed-files list, which is already three-dot) and prints the ref it used. Removes the caller's opportunity to get it wrong, and makes the reading self-describing, which the script already values elsewhere (it prints "Re-run on the FINAL file list… a reading taken earlier in the session is recall").
- Keep
--test and add a warning when the caller's path list contains a file the working tree shows as unchanged by the PR — cheap, but only fires when a repo is present.
- Prose only: state the three-dot form in the dispatch template. ⚠️ Weakest — this seat's own dispatch template already carries a dozen ⛔ rules, and adding a thirteenth is the option with the worst record.
Dedup
Complete enumeration of the domain:skills lane at 2026-09-09T01:29Z — 23 of 23 open cards returned, no pagination, so the corpus was read whole rather than sampled. Nearest neighbours are #16795 (dispatch-gates' --ran / --commands output lanes disclaiming an incomplete OUTSIDE list) and #16285 (dispatch-gates routes no packages/qa/dogfood/** change to any family) — both are dispatch-gates.mjs and both are about which families a change set reaches. This is check-governed-merges.mjs and is about the file list being wrong before the tool runs. ⇒ related in spirit, not a duplicate.
⚠️ ⛔ /search/issues was not usable for this dedup: it returned total=None for every term tried including two control terms known to be present — an unread channel, not a zero. See #16762, where that ledger entry is already under re-measurement.
Not measured
Whether any PR has actually been parked this way. This seat caught it before acting, so the reading above is a near-miss, not an incident. ⛔ Nothing is asserted about the historical rate.
Filed unassigned by the
domain:specexecution seat (session_016N6xmWt5hYm94ffVEwGH8x) as part of its shift-close hand-over report, mechanizable-item class. ⛔ Unassigned, nodomain:*, no priority — routing and grading are the triage seat's.The measurement
scripts/pm/check-governed-merges.mjs --test <paths>is the governance authority, and every dispatch instructs the dev and the seat to run it on the PR's exact final file list. Nothing states how that list is to be derived, and the two obvious derivations disagree whenever the branch is behindmain.Measured on PR #16997 at 2026-09-09T01:1xZ, head
98c651703, merge basebb7d91f19,origin/mainatfe2b7554a:Five of those seven files are
main's own newer commits, appearing as reversions because the branch has not taken them. The PR touches two files.Why it is worth a card
The superset fails safe in one direction and unsafe in the other, and the unsafe direction is the one that costs:
mainthat has since toucheddocs/adr/**,.claude/**,skills/**,AGENTS.mdorCLAUDE.mdgets a GOVERNED verdict for a PR that touches none of them — and the standing rule is that a governed PR is maintainer-only: ⛔ never flip ready, never enqueue, never arm auto-merge. ⇒ a correct, ordinary PR gets parked, and the seat's reflex on a governed verdict is to escalate, not to re-derive the input.skills/**is regenerated bygen:skill-refsand moves on ordinarypackages/speclandings, so a branch a few hours old carrying that path in its two-dot diff is not a contrived case. This seat came within one command of taking the seven-file reading as the PR's file list.⭐ The general shape: the gate is correct and its input is not. Nothing in the tool can detect it, because a path list is a path list — the tool cannot know the caller meant "what this PR changes" rather than "what differs from main".
Why the obvious fix is the mechanization
The derivation is not a judgement call — there is exactly one right answer (
A...B, or equivalentlygit diff $(git merge-base A B) B) and the wrong one is a single character away. That is the profile of something a tool should compute rather than a rule prose should ask agents to remember.Candidate shapes, ⛔ not a ruling:
--pr <n>— the script resolves the PR's file list itself (merge base, or the API's own changed-files list, which is already three-dot) and prints the ref it used. Removes the caller's opportunity to get it wrong, and makes the reading self-describing, which the script already values elsewhere (it prints "Re-run on the FINAL file list… a reading taken earlier in the session is recall").--testand add a warning when the caller's path list contains a file the working tree shows as unchanged by the PR — cheap, but only fires when a repo is present.Dedup
Complete enumeration of the
domain:skillslane at 2026-09-09T01:29Z — 23 of 23 open cards returned, no pagination, so the corpus was read whole rather than sampled. Nearest neighbours are #16795 (dispatch-gates'--ran/--commandsoutput lanes disclaiming an incomplete OUTSIDE list) and #16285 (dispatch-gates routes nopackages/qa/dogfood/**change to any family) — both aredispatch-gates.mjsand both are about which families a change set reaches. This ischeck-governed-merges.mjsand is about the file list being wrong before the tool runs. ⇒ related in spirit, not a duplicate./search/issueswas not usable for this dedup: it returnedtotal=Nonefor every term tried including two control terms known to be present — an unread channel, not a zero. See #16762, where that ledger entry is already under re-measurement.Not measured
Whether any PR has actually been parked this way. This seat caught it before acting, so the reading above is a near-miss, not an incident. ⛔ Nothing is asserted about the historical rate.