Uh oh!
There was an error while loading. Please reload this page.
Price the whole hand-authored skills bundle, not just SKILL.md - #12423
Merged
Conversation
…LL.md The published-skills token ratchet enumerated only `skills/<name>/SKILL.md` — 11 files of the 49 that ship. The other 37% (`rules/**`, `evals/**`, the hand-authored `references/*.md`) carried no ceiling, so an author could add thousands of tokens of `rules/*.md` and truthfully report a `+0` package delta against the only number the gate produced. The population now walks every published skill directory recursively. Generator-owned outputs (`references/_index.md`, the react-blocks pair) are excluded from RATCHETING but still measured and printed, because a ceiling on a generated file reds on regeneration — the wrong actor. Membership is decided by `generatedExceptionFor()` from `scripts/pm/check-governed-merges.mjs`, the register the #11705 ruling created for exactly this question, rather than by a second definition of "generator-owned" that could drift from it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JANH3y7qe3MD8aLaLXci8N
os-steve
marked this pull request as ready for review
August 26, 2026 02:26
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 26, 2026
This was referenced Aug 26, 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#12392
The published-skills token ratchet enumerated only
skills/<name>/SKILL.md— 11 of the 49 files that ship — so an author could add thousands of tokens ofrules/*.mdand truthfully report a+0package delta against the only number the gate produced. The gate's own header read as though it priced the published bundle as a category, and its## Enumeration, never a hand listsection actively invited the reader to believe nothing inskills/could land unpriced.Implements the scope settled in this card's grading comment (2026-08-26).
What changed
One file:
scripts/check-skills-token-ratchet.mjs.discoverSkillFiles→discoverBundleFiles, which walks every published skill directory recursively instead of naming one file per directory. A file inside an existing skill is now as impossible to land unpriced as a whole new skill was before.CEILINGSrows, each initialized at its measured value onc026b0d2d(recorded asCEILING_BASIS.bundleExtension). Every newly-priced file starts with exactly zero headroom, so this is a ratchet installation, not a budget grant.The boundary, and the one definition of "generator-owned"
A ceiling on a generated file reds on a legitimate regeneration — a spec author moves an indexed headline, the generator faithfully rewrites the index, and the gate stops them. That is the wrong actor: they did not write the bytes and cannot pay by deleting them.
Membership is decided by
generatedExceptionFor()imported fromscripts/pm/check-governed-merges.mjs— not by a second definition here. That register exists by the #11705 ruling (2026-08-25), which is explicit on the shape: "The exemption is enumerated from the generator, never a hand-copied path list … Extend that registry; ⛔ do not author a second mechanism." Two definitions of "generator-owned" that can drift is the failure this avoids.The gate header now states the boundary in full — three populations (priced / excluded-and-why / structurally outside), the generator named as the reason for the exclusion, and the reuse's known limit stated rather than hidden: this gate runs in the pre-build lint group with no toolchain, so it can take only the register's cheap half (the narrowing match), not its expensive half (recompute + byte-compare). That residual risk is closed by a tripwire: the excluded set is pinned by name in
--self-test, so a hand-authored file matching a generated spelling cannot slip out of pricing without reddening the gate's own self-test.Whole-tree token readings (this change IS the measurement)
Convention
ceil(utf8 bytes / 4), measured onc026b0d2d:SKILL.mdrules/**,evals/**, authoredreferences/**_index.md×9, react-blocks ×2)Ceiling total moves 117,857 → 170,527.
skills/README.md(1,926 tok) is outside the population: it is the catalog's front page, not inside any skill, and itself carries a generated block frombuild-skill-docs.ts.No
skills/**content is touched by this PR. It prices that surface; it does not edit it.Positive control — the gate FIRING before it passes
Run with the new enumeration and the old 11-row ceiling map, before adding any rows:
Exit 1, 27 reds naming exactly the unpriced authored files, while all 11 generator-owned files came back green and measured. After adding the rows: exit 0.
Reverse verification — three mutations, each proven on disk and restored
The self-test grew 40 → 64 cases. Each leg confirmed the mutation landed (blob hash differs from
HEAD) and the restore was clean (git checkout HEAD -- <path>, blob back toHEAD,git diff HEADempty):nullfor everything_index.md/react-blocks paths reported as carrying no ceilingthe boundary does not swallow the population(got false, want true)The third is the vacuity direction the card required: a boundary predicate that silently prices nothing fails loudly rather than reporting a clean bundle while measuring none of it.
Gates
Derived from the actual diff via
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-supplied paths; it read the change set itself). All run at final commita51236a4, exit codes captured before any pipe: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-ci-filter-parity·check-cross-package-test-inputs· the gate itself · the gate's--self-test— all green.Plus four the derivation classes as path-unreachable but that I could see were implicated, run anyway:
check:ratchet-remedy-authority(still reads this gate's remedy asmarked, 155 scripts swept) ·bare-root-worklist --self-test·check:nul-bytes·check-governed-merges --self-test(the register this gate now imports) — all green.No changeset:
scripts/-only, matching the convention in recentscripts/-only merges (851b2d8e5,0b10068c3,c8bf50f5d,81f8b7ed4,9646cdbd1— none carry one).skip-changesetapplied.Out-of-scope finding filed
#12422 — landing this PR moves the token-ratchet row's population from 11 files to 49, which makes two other files' descriptions of it stale: the
SKILLS_DIR skillsTRIAGE row'swhyinscripts/pm/bare-root-worklist.mjs, and #12369's measured table (which lists this gate's precise live spelling asskills/*/SKILL.md, 11/11). Not fixed here: theTRIAGEmap is shrink-only and #12369 is an openneeds-user-decisioncard holding the adjudication for that exact row among 16 others, so rewriting one of them mid-decision would be adjudicating a slice of the card the maintainer is holding.Generated by Claude Code