Skip to content

fix(skills-ratchet): price the release stamp's prerelease segment at stable width - #23187

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23176-ratchet-prerelease-stamp
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23176-ratchet-prerelease-stamp

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23176
Clause-②: no (an internal repo gate; no published accept set or public surface)

The maintainer ruling this lands on

This PR moves the skills token ratchet in the direction its own refusal marks ⛔ MAINTAINER-ONLY: "The other direction lands only in a PR whose body quotes a maintainer ruling authorizing it." The maintainer chose route A on decision record 6138951701 (comment 6138951701 on #23176). The choice, verbatim:

「A 剥预发布后缀 (Recommended)」

Route A as that decision record defines it, verbatim:

门禁计数前,把 frontmatter version: 值里的预发布后缀(-next.N)剥掉,按正式版宽度计 | scripts/check-skills-token-ratchet.mjs 加自测行;非受管面,普通合并队列 | … | 预发布树上少计 7 字节;main 的读数不变,上限一个不动

What changed

One file: scripts/check-skills-token-ratchet.mjs (+122 / -6).

  • pricedTokens(text, { stamped }) is now the count the gate judges. It is countTokens minus the semver prerelease segment of the frontmatter metadata.version stamp, and the minus applies only to files the version script stamps. countTokens and TOKEN_CONVENTION are unchanged.
  • stampPrerelease(text) reads the stamp with extractMetadataVersion from scripts/check-skill-compatibility-version.mjs, the script whose --fix writes the stamp. The stamped set is that script's stampedSkillPaths(). There is no second parser. A version carrying build metadata matches nothing, so nothing is excluded for it.
  • The output says when it excludes something. The file's line gains one sentence: "The -next.0 prerelease segment of its metadata.version stamp (7 bytes) is not priced." On a stable tree that note is empty, so main's output does not change.
  • Header. A new "ONE EXCLUSION" paragraph quotes the ruling and states the boundary. The "INDEPENDENTLY REPRODUCIBLE" bullet now gives the hand audit for a prerelease tree: ceil((bytes - N) / 4), where bytes is the file's wc -c and N is the byte length of the segment after the stamp's patch number (-next.0 is 7, -next.12 is 8, a stable stamp is 0). The rest of the counting convention is unchanged.
  • Not touched: no CEILINGS row (not one number changed); no skills/**, .github/workflows/** or other governed path; nothing on changeset-release/main / chore: version packages (next) #21988.

Why the fix belongs in this gate and not in the producer. The root version script runs check-skill-compatibility-version.mjs --fix after changeset version, and that writes the stamp from @objectstack/spec's version. The same script's check reds on any other value. So 18.0.0-next.0 is the correct stamp, and the defect was this gate counting it as authored text. An author cannot use the exclusion to buy room, for two reasons: only that one segment is excluded, and its length is fixed by a stamp another gate holds equal to the spec version.

Measurements (head f06e7f636)

tree script objectstack-automation/SKILL.md objectstack-data/SKILL.md gate exit
main e540e7ffb before this PR 5784 / 5785 6128 / 6128 0
main + this PR after 5784 / 5785 6128 / 6128 0. Full output byte-identical to before (135 lines, diff empty)
#21988 63eb607ad1 (the card's tree) the tree's own 5786 / 5785 ✗ 6129 / 6128 ✗ 1, matching the card
#21988 63eb607ad1 this PR's script overlaid 5784 / 5785 6128 / 6128 0. Step 37 (--self-test, then the gate) both exit 0, 76 cases pass
#21988 8826e954d (the PR's current head) the tree's own 5786 / 5785 ✗ 6129 / 6128 ✗ 1
#21988 8826e954d this PR's script overlaid 5784 / 5785 6128 / 6128 0. Step 37 both exit 0. The per-file token list is identical to main's
  • The overlaid copy is byte-identical to this PR's blob (48e6aadbb) on both trees. The two modules it imports are byte-identical to main's on 8826e954d.
  • Authored growth still reds. I appended +4 bytes to objectstack-data/SKILL.md, the smallest growth that crosses its ceiling on main, since that file carries 3 bytes of sub-token slack. That gives 6129 / 6128 and exit 1, both on a scratch copy of main (stamp 17.7.0) and on 8826e954d (stamp 18.0.0-next.0). The file was restored afterwards and the hash compared.
  • A two-digit counter is handled. Re-stamping that file 18.0.0-next.12 on 8826e954d gives 6128 / 6128, green, with an 8-byte segment excluded.

Self-test: 11 rows added (65 to 76 cases; roster floor 64 to 75)

  • the stamp's prerelease segment is read off metadata.version
  • a zero-headroom SKILL.md stamped -next.0 is green: the stamp prices at stable width
  • priced whole, that same file is red (the row above is not vacuous)
  • one authored byte over a zero-headroom ceiling is red under a stable stamp
  • one authored byte over is red under a prerelease stamp too: the strip buys an author nothing
  • a multi-digit prerelease (-next.12) prices the same as a stable stamp
  • a stable stamp prices byte-for-byte as countTokens
  • every real bundle file without a prerelease stamp prices exactly as countTokens
  • a prerelease-shaped string outside the stamp is priced
  • a file the version script does not stamp is priced whole
  • the stamped set is the version script's: exactly the real SKILL.md files

The fixtures are padded so the stable spelling has zero byte slack. The "one authored byte over" pair is what proves the padding is right.

Gates

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 31 commands at f06e7f636. All 31 exit 0, and --ran reconciles 31 derived, 31 run, 0 NOT-MEASURED. That includes pnpm check:pm-dispatch-gates (the fast tier, 410.9 s), pnpm check:nul-bytes, pnpm check:entry-guard and both ratchet commands.
  • pnpm check:ratchet-remedy-authority: OK. 275 scripts swept: 14 mark the expanding remedy, 5 turn it down, 256 hand out none. That is the same classification as on base e540e7ffb. The control corpus, which pins this gate as marked, passes set-equality both ways. The change adds no loosening remedy.
  • Lint, narrowed and evidenced: eslint --no-inline-config --format json scripts/check-skills-token-ratchet.mjs linted 1 file with 0 errors and 0 warnings. --print-config for the file shows parserOptions {ecmaVersion, sourceType} and no project/projectService. Type-aware linting is off, so this diff cannot change any untouched file's verdict. CI runs the full pnpm lint.

Changeset

None; skip-changeset. The root package is private: true, and scripts/ is in no package's files[].

Acceptance notes

  • TOKEN_CONVENTION (the price-tag header string) is deliberately unchanged. It names the convention the ceilings are pinned in. The exclusion is named on the line of each file it touches, and only on a tree where it applies.
  • On the older 63eb607ad1 tree, one generator-owned file (objectstack-automation/references/_index.md) reads 816 against main's 886. That is a difference between the two bases. The file is not ratcheted and is out of scope here; on the current head 8826e954d the list matches main.

Generated by Claude Code

…stable width

The skills token ratchet counted the `metadata.version` stamp that the
version script writes into every published SKILL.md as authored text.
Under Changesets pre mode the stamp grows from "17.7.0" to
"18.0.0-next.0" (+7 bytes), so objectstack-automation (1 token of
headroom) and objectstack-data (0) went red on the Version Packages PR
with no author edit to pay for.

The gate now prices that one stamp at its stable width: the semver
prerelease segment of `metadata.version` is not counted, for the files
the version script stamps. The stamp is read with
check-skill-compatibility-version's own extractMetadataVersion and the
stamped set is its stampedSkillPaths(). Nothing else moves: no ceiling
changes, a stable tree's reading is byte-identical, and authored growth
under either stamp is still red. Eleven self-test rows pin it; the
header's reproducibility note gives the hand audit for a prerelease tree.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants