Carve generator-owned skills/** outputs out of the governed-merge fork - #12274
Draft
os-zhuang wants to merge 1 commit into
Draft
Carve generator-owned skills/** outputs out of the governed-merge fork#12274os-zhuang wants to merge 1 commit into
skills/** outputs out of the governed-merge fork#12274os-zhuang wants to merge 1 commit into
Conversation
Implements the maintainer's 2026-08-25 ruling on #11705 (option A) by EXTENDING the provenance-aware register PR #11078 built in `scripts/pm/check-governed-merges.mjs` — no second mechanism. A row is a GENERATOR, not a path exemption. A hit lifts only when both halves are the generator's own answer on the tree under test: it declared the path among its outputs (`--generated-manifest`, written from the same map the write disposition uses), and its own `--check` reported no drift. Hand-authored `skills/**` content is not on the register at all. - `packages/spec/scripts/lib/generated-output.ts`: the shared sink declares its output set on demand, so the register never restates a path list. - `scripts/pm/check-governed-merges.mjs`: two ruled rows (gen:skill-refs, gen:react-blocks), the shared recompute driver, per-row co-edit fences. - `scripts/pm/check-governed-queue-guard.mjs`: consumes the shared membership test and driver instead of its own copy. - `.claude/skills/pm-dispatch/references/state-machine.md`: one-line note. Closes #11705
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
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 free
to 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.
Closes #11705
Implements the maintainer's ruling by extending the provenance-aware register PR #11078 built in
scripts/pm/check-governed-merges.mjs— no second mechanism, no hand-copied path list, no blind path exemption.The ruling (issue #11705, comment 5406727512, maintainer 2026-08-25)
What a row is now
A register row is a generator, not a path. A hit lifts only when both halves are the generator's own answer, recomputed on the tree under test:
packages/spec/scripts/lib/generated-output.ts), which now declares its output set on demand:--generated-manifest=<file>writes the repo-relative paths of everything a real run emits, from the sameemittedmap the write disposition reads. The register consults that list and never restates it.--check— "the real run minus the writes" by the sink's construction — must report no drift. One hand-edited byte reddens it.A row's
candidateregexp is a narrowing gate only: matching it earns a path the question, never the answer. Too narrow ⇒ a real regeneration stays governed (heavy, safe); too wide ⇒ the manifest refuses it (safe). No spelling of it lifts anything on its own — the same property the #11084 co-edit fence has, pinned in both directions.Measured, on this tree
The #11685 shape now lifts (
--test, exit 0):Ablation — one hand-edited byte in the real generated file flips it back to governed. Run from a committed state, with a
trap … EXIT INT TERMrestore; the mutation was confirmed on disk before the reading was taken (anchor## Core schemascount 1 → 0, injected marker count 0 → 1,git diff --numstat=1 1), and the restore confirmed after (1 → back, injected 0, cleangit status):The ruled limit holds — hand-authored
skills/**content is untouched:--testinputskills/objectstack-ui/SKILL.mdskills/objectstack-upgrade/references/_index.mdreact-blocks.md+react-blocks.contract.json_index.md+packages/spec/scripts/build-skill-references.tsDoes the exemption reach the #12102 merge-group guard?
check-governed-queue-guard.mjsalready shared the predicate (testVerdict,governedPathsIn,applyGeneratedExceptions) but held its own copy of the recompute, and derived its own membership set frome.path— which readsundefinedfor a row that matches by candidate instead. That silent miss is now impossible: membership is the register'sgeneratedExceptionForand the recompute is itsrecomputeProvenanceFor, both imported. A future row reaches both tools with nothing to change in the guard. Its lift path had zero self-test coverage; it now has six cases.--check, andgoverned-surface-guard.ymldeliberately installs no dependencies ("the script is dependency-free"). There the rows fail closed: a spec PR carrying its regenerated_index.mdis still governed at merge-group time and needs the approving review, while the seat-side--test— the predicate the measured incident actually turned on — lifts it in a dev container. Closing that gap means giving the guard job a dependency install, a cost on every queue build that no ruling covers, so it is filed, not taken (see the report on #11705). The no-toolchain environment is itself pinned as a self-test case in both scripts, so the fail-closed answer there is a tested behaviour, not an accident.Ratchet:
state-machine.md43 → 44, and why it was not paid in placeThe dispatch priced the one-line note as pay-in-place. It could not be: the file holds 34 content lines over 3,467 bytes, and every bullet is already packed to its own minimum against the 120-byte cap (measured per bullet — the only bullets spanning >1 line each exceed 120 bytes on their own). The only in-place payment available was deleting an existing ruled clause. The raise is instead backed by this card's own ruling, which orders the note ("the skills seat opens the charter card to land the exemption in
check-governed-merges.mjsplus the one-line state-machine doc note") and is quoted in the ratchet comment beside the number, per that file's convention. The note carries no issue number, per that document's own rule.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(27 matched families), re-derived after the final commit — unchanged. Run at100accdaa, exit codes captured before any pipe:pnpm check:pm-governed-merges→✓ check-governed-merges --self-test: 159 assertions(was 129 — 30 new)node scripts/pm/check-governed-queue-guard.mjs --self-test→✓ check-governed-queue-guard self-test: 86 cases pass(was 80 — 6 new)pnpm check:pm-skill-ratchet→✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/state-machine.md is 44 lines (ceiling 44; headroom 0).pnpm --filter @objectstack/spec run check:skill-refs→✅ 9 generated files in sync with packages/specpnpm --filter @objectstack/spec run check:react-blocks→✅ 2 generated files in sync with packages/specpnpm check:nul-bytes→✓ check-nul-bytes --self-test: 75 assertions over a temp git repo (real scan() path)check:agent-test-spelling,check:doc-authoring,check:skill-frame-sync,check:pm-skill-id-lint,check:entry-guard,check:parse-guard,check:pnpm-filter-targets,check:cross-package-test-inputs,check-ci-filter-parity,check:merge-driver,check:published-files,check:slot-lookup,check:test-source-alias,check:type-source-resolution,check:scripts-typecheck(spec),check:doc-formula-expressions(after building its@objectstack/formula+@objectstack/lintclosure),check-plugin-teardown-shape,check:empty-state,check:liveness,check:strictness-ledger,check:variant-docs,docs-audit/check-affected-docs,docs-audit/check-drift-comment.Declared narrowing — one family of the 27 not run:
node scripts/check-dev-prereqs.mjsexits 1 in a fresh worktree because 63 of 67 workspace packages have nodist/on disk. Its own output classifies that as "1 unmet precondition, not a list of problems" withFix: pnpm build— a whole-workspace build, which CI owns. It measured nothing about this diff either way.An ablation proves the new self-test cases can fail rather than passing vacuously: neutering the manifest-membership branch (
if (!run.outputs.includes(path))→if (false), injection confirmed on disk) turned the suite red on11705-case-C-a-path-the-generator-never-declared-stays-governed; restoring returned it to green.Not a changeset
No user-visible change:
scripts/pm/**is seat tooling,packages/spec/scripts/**is generator-side and outside the package's publishedfilesallowlist, and.claude/**is the instruction tree.skip-changesetlabel applied and read back.Draft, human merge —
.claude/**and the governed register itself are governed surface. Review requested fromos-zhuang.Generated by Claude Code
Generated by Claude Code