Skip to content

ci(pm): pin the governed-surface prose enumerations to GOVERNED_SURFACES - #9841

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-9525-governed-enum-pin
Aug 19, 2026
Merged

ci(pm): pin the governed-surface prose enumerations to GOVERNED_SURFACES#9841
os-warren merged 1 commit into
mainfrom
claude/issue-9525-governed-enum-pin

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

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_SURFACES in scripts/pm/check-governed-merges.mjs is 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 reddening check: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:

  1. Containment — every glob in the register appears verbatim as a backtick code span inside an anchored region.
  2. No over-claim — every code span shaped like a governed-surface glob (a path ending in **) 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 two exact entries 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.md Prime 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.md the 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 exports to dist/, 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.

legmutationpredictedobserved
Adrop skills/** from directive #14 — the #9403 defect verbatimred, missingred — "AGENTS.md:176-189 … does not name skills/**"; pm-dispatch correctly stayed green
Badd a docs/rfc/** code span to the directivered, unknownred — "… claims docs/rfc/**, which the register does not govern"
Cadd a sixth surface to GOVERNED_SURFACES, prose untouchedred on both surfacesred on both — this is the real drift mechanism: the register grows in a PR that touches neither prose file

Leg 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 HEAD 79c71ba9d:

gateverdict line
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-inputspass
check:node-versionpass
check:required-contextspass
check:shard-attestationpass
check:type-check-coveragecheck-type-check-coverage: OK — 64/77 workspace packages type-checked
check:workflow-status-functionspass
check:nul-bytespass
check:pm-skill-ratchetpass — AGENTS.md is 958 lines before and after (net zero), which keeps the ceiling #9837 proposes at 958/headroom 0 satisfiable whichever lands first
check:pm-skill-id-lintpass
check:pm-governed-merges✓ check-governed-merges --self-test: 77 assertions
check:pm-dispatch-gatespass
check:type-check-debtred for a stated environment prerequisite, not for this diff: --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.mjs runs its own --self-test at module scope with no entry-point guard, and this is its first real importer. Left alone, a failure there process.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 that scripts/check-required-contexts.mjs's ADR maintainer approval registry row still described its subject as "the rule that a docs/adr/** diff may not merge without an APPROVED review" — one of five prefixes. git log -S puts 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

No changeset

CI tooling, a package.json script, a workflow step, and one clause of AGENTS.md. Nothing published changes.


Generated by Claude Code

@os-warrenos-warren added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 19, 2026 — with Claude
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file labels Aug 19, 2026
@os-warren
os-warren marked this pull request as ready for review August 19, 2026 05:12
@os-warren
os-warren added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit f76423dAug 19, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-9525-governed-enum-pin branch August 19, 2026 05:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cddependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding: nothing ties AGENTS.md's governed-surface enumeration to GOVERNED_SURFACES — #9491's pin covers check-run names, not path prefixes

2 participants

@os-warren@claude