Uh oh!
There was an error while loading. Please reload this page.
ci(pm): pin the governed-surface prose enumerations to GOVERNED_SURFACES - #9841
Merged
Conversation
os-warren
marked this pull request as ready for review
August 19, 2026 05:12
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 19, 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#9525
⛔ Governed surface — draft on purpose. This diff touches
AGENTS.md. Per Prime Directive #14 it is confirmed and merged by the maintainer, by hand: do not flip it ready, do not queue it, do not arm auto-merge.The gap
GOVERNED_SURFACESinscripts/pm/check-governed-merges.mjsis machine-read on every path decision. The sentences that tell a seat which surfaces are governed are prose, duplicated out of that register by hand — and nothing compared the two. The duplicate went stale twice in two days (#9395, #9511), and a human, not a gate, caught it both times.#9525 measured the failure precisely: dropping
skills/**back out of the directive — reproducing exactly the defect #9403 exists to fix — left the file's whole derived gate union green on all ten checks, with a positive control (an unrelated edit to the same file reddeningcheck:pm-skill-id-lint) proving the file is scanned. The gates read these files. None of them read the prefix list.The pin — the assertion inverted
The card names the trap in direction 1: "recognises a path-prefix-shaped literal in bilingual prose" is the intractability #9491 hit and cut. This does not re-attempt it. It inverts the assertion — the register is the thing enumerated, the prose is only searched:
globin the register appears verbatim as a backtick code span inside an anchored region.**) is a register entry. A retired prefix left behind, or one never in the register, reds here.The over-claim half is deliberately bounded to
**-shaped spans, and the script header says so rather than implying it: the register's twoexactentries are bare filenames, and these regions mention many other filenames in passing, so "every filename-shaped span must be governed" would be false on correct prose. Containment covers all five entries; over-claim covers the three glob-shaped ones. That boundary is the honest claim.Regions are delimited by two literal anchors that already exist in the file, so the pin costs the instruction surfaces zero lines. A missing anchor, an unreadable file, or an empty register is RED, never a skip — an empty register would make containment vacuously true, which is the exact false green this removes.
Two surfaces, not one
AGENTS.mdPrime Directive #14 is the card's site. The scan set also carries.claude/skills/pm-dispatch/SKILL.md's ACCEPT path-fork, which enumerates the same five surfaces and is read once per landing decision. Naming it in the register required no edit to that file — it already contains all five as code spans — so this is a pure additive assertion over a second live enumeration, not a widening of the diff. The card's direction 1 asks for "every scanned instruction surface that enumerates governed prefixes"; this is the second one that exists.Where the enumeration actually sits
In
AGENTS.mdthe five prefixes live inside a verbatim, untranslated maintainer quotation. Rewriting a quoted ruling is rewriting the ruling, so the region deliberately spans the whole directive rather than just the quoted line: when a sixth surface lands, it is named in the editable paragraph that follows. The gate's own failure text says this, and so does the directive now.Reverse verification
Three legs, direction predicted before running, each restored and re-run green afterwards. Nothing here resolves through a package
exportstodist/, so no rebuild sits between the mutation and the measurement.An initial leg-A attempt did not apply — the anchor string was wrong (the quotation uses a fullwidth
)with no space before+), and the gate returned exit 0. That reads exactly like "mutation applied, gate stayed green". It was caught only because the mutation helper asserts its anchor and exits 9; without that assertion this PR would have shipped a false green.skills/**from directive #14 — the #9403 defect verbatimmissingskills/**"; pm-dispatch correctly stayed greendocs/rfc/**code span to the directiveunknowndocs/rfc/**, which the register does not govern"GOVERNED_SURFACES, prose untouchedLeg C is why the CI step is unconditional — no
if:, no paths filter. The staleness it catches arrives via an edit to the register.Gates
Union derived from the real diff with
node scripts/pm/dispatch-gates.mjs(no paths — it takes the change set from the merge base itself), re-run at final HEAD79c71ba9d:check:pm-governed-prose✓ check-governed-prose self-test: 24 cases pass./✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md) and claim no others.check:cross-package-test-inputscheck:node-versioncheck:required-contextscheck:shard-attestationcheck:type-check-coveragecheck-type-check-coverage: OK — 64/77 workspace packages type-checkedcheck:workflow-status-functionscheck:nul-bytescheck:pm-skill-ratchetAGENTS.mdis 958 lines before and after (net zero), which keeps the ceiling #9837 proposes at 958/headroom 0 satisfiable whichever lands firstcheck:pm-skill-id-lintcheck:pm-governed-merges✓ check-governed-merges --self-test: 77 assertionscheck:pm-dispatch-gatescheck:type-check-debt--re-measure cannot run: 55 workspace dependenc(ies) … have no built type entry point on disk. It refuses rather than measuring a different world. This diff contains no TypeScript and no package change; CI builds the closure before this step.One consumer-side workaround, filed rather than smuggled
check-governed-merges.mjsruns its own--self-testat module scope with no entry-point guard, and this is its first real importer. Left alone, a failure thereprocess.exit(1)s during the import and the new gate would go red under another script's name, with its own 24 cases never reporting. The flag is withheld for the duration of the import, documented in place. That is a consumer-side accommodation for a producer-side defect, so it is filed rather than left implicit: #9839, as a sub-issue of #9757, which already owns this class and its "the tree spells this guard four different ways" cleanup. Delete the workaround when the guard lands.Premise correction — half this card was already fixed
The card's second in-scope item does not exist on current
main. It reported thatscripts/check-required-contexts.mjs'sADR maintainer approvalregistry row still described its subject as "the rule that adocs/adr/**diff may not merge without an APPROVED review" — one of five prefixes.git log -Sputs the removal in 86ea8df (#9523), the very PR the unlock scan cites as the #9495 landing. The row now reads as a retirement note with correct provenance and no prefix claim at all. Nothing to correct; no edit here.Findings filed
scripts/pm/check-governed-merges.mjsruns its--self-testat module scope — a second instance of #9757's class, now with a real importer that it can mask #9839 —check-governed-merges.mjsself-executes on import (sub-issue ofscripts/pm/dispatch-gates.mjsdispatches its CLI at module top level, so the re-exports its header offers cannot be imported —importruns the tool andprocess.exit(2)s the importer #9757)lint.ymlstep comment, outside this pin's reach by designNo changeset
CI tooling, a
package.jsonscript, a workflow step, and one clause ofAGENTS.md. Nothing published changes.Generated by Claude Code