6161 * IDIOM note at the top of `.github/workflows/lint.yml` carries that lane's
6262 * verbatim scope, and it is pointed at rather than restated here.
6363 * - INDEPENDENTLY REPRODUCIBLE. Anyone can audit a ceiling without running
64- * this script: `ceil($(wc -c < file) / 4)`. A tokenizer's count can only be
65- * checked by re-running the tokenizer.
64+ * this script: `ceil($(wc -c < file) / 4)` — on a prerelease tree, first
65+ * subtract the release stamp's prerelease bytes (the one exclusion below).
66+ * A tokenizer's count can only be checked by re-running the tokenizer.
6667 *
6768 * ~4 bytes per token is the standard rule of thumb for BPE over English prose
6869 * and markdown, which is what this catalog is. Its KNOWN limitation, stated
7374 * the catalog ever turns substantially non-Latin, the convention needs
7475 * revisiting rather than the ceilings.
7576 *
77+ * ONE EXCLUSION — the release stamp's prerelease segment (maintainer ruling,
78+ * route A of decision record 6138951701, verbatim: 「A 剥预发布后缀 (Recommended)」).
79+ * A published SKILL.md's frontmatter `metadata.version` is not authored text:
80+ * the root `version` script writes it from `@objectstack/spec`'s version
81+ * (`scripts/check-skill-compatibility-version.mjs --fix`, whose check reds on
82+ * any other value). Under Changesets pre mode that stamp grows from `"17.7.0"`
83+ * to `"18.0.0-next.0"` — 7 bytes no author wrote and no author can pay for by
84+ * deleting text — so a file at zero headroom went red on the Version Packages
85+ * PR alone. The semver prerelease segment of that ONE stamp (`-next.0`,
86+ * `-next.12`) is therefore not priced: the stamp counts at its stable width,
87+ * the width every ceiling was measured at. Nothing else moves — a
88+ * prerelease-shaped string anywhere else in a file, a stamp-shaped line in a
89+ * file the version script does not stamp, and every byte of a stable tree
90+ * (so `main`'s reading) are priced exactly as before. Where the stamp is, and
91+ * which files carry one, are read through that script's own
92+ * `extractMetadataVersion` and `stampedSkillPaths`, never a second parser here.
93+ * Hand audit on a prerelease tree: `ceil(($(wc -c < file) - N) / 4)`, where N
94+ * is the byte length of the segment after the stamp's patch number (`-next.0`
95+ * is 7, `-next.12` is 8); on a stable stamp N is 0.
96+ *
7697 * ⚠️ Counts produced here are comparable ONLY against other counts produced
7798 * here. They are not an estimate of any specific model's tokenizer, and the
7899 * ceilings below are pinned in this convention — changing the convention
@@ -172,6 +193,9 @@ import { isEntrypoint } from './invoked-as.mjs';
172193// see the boundary section in the header. #11705's ruling forbids a second
173194// mechanism, so this gate consults the register the governed-merge fork owns.
174195import { generatedExceptionFor } from './pm/check-governed-merges.mjs' ;
196+ // The stamp's ONE reader and the stamped set, from the script that writes the
197+ // stamp — see "ONE EXCLUSION" in the header.
198+ import { extractMetadataVersion , stampedSkillPaths } from './check-skill-compatibility-version.mjs' ;
175199
176200const HERE = resolve ( fileURLToPath ( import . meta. url ) , '..' ) ;
177201const REPO_ROOT = resolve ( HERE , '..' ) ;
@@ -595,6 +619,38 @@ export function countTokens(text) {
595619 return Math . ceil ( Buffer . byteLength ( text , 'utf8' ) / 4 ) ;
596620}
597621
622+ /** A semver version carrying a prerelease segment; group 1 is the segment. Build metadata never matches. */
623+ const STAMP_PRERELEASE_RE = / ^ \d + \. \d + \. \d + ( - [ 0 - 9 A - Z a - z ] [ 0 - 9 A - Z a - z . - ] * ) $ / ;
624+
625+ /**
626+ * The prerelease segment of a file's frontmatter `metadata.version` stamp
627+ * (`-next.0` of `18.0.0-next.0`), or `''` when the file has no stamp or a
628+ * stable one. Read with the stamp writer's own parser — see "ONE EXCLUSION".
629+ *
630+ * @param {string } text
631+ * @returns {string }
632+ */
633+ export function stampPrerelease ( text ) {
634+ const stamp = extractMetadataVersion ( text ) ;
635+ if ( ! stamp . ok ) return '' ;
636+ return STAMP_PRERELEASE_RE . exec ( stamp . value ) ?. [ 1 ] ?? '' ;
637+ }
638+
639+ /**
640+ * The count the gate judges: `countTokens`, less the stamp's prerelease
641+ * segment when the file is one the version script stamps. Every other file,
642+ * and every file on a stable tree, prices byte-for-byte as `countTokens`.
643+ *
644+ * @param {string } text
645+ * @param {{stamped?: boolean} } [options] is this file in `stampedSkillPaths()`?
646+ * @returns {number }
647+ */
648+ export function pricedTokens ( text , { stamped = false } = { } ) {
649+ if ( text . length === 0 ) return 0 ;
650+ const unpriced = stamped ? Buffer . byteLength ( stampPrerelease ( text ) , 'utf8' ) : 0 ;
651+ return Math . ceil ( ( Buffer . byteLength ( text , 'utf8' ) - unpriced ) / 4 ) ;
652+ }
653+
598654/**
599655 * Every file that ships inside a published skill — read off the filesystem,
600656 * never a hand list, walked RECURSIVELY (#12392).
@@ -749,6 +805,7 @@ export function reportLines(rows) {
749805
750806function run ( ) {
751807 const covered = [ ...new Set ( [ ...discoverBundleFiles ( ) , ...CEILINGS . keys ( ) ] ) ] . sort ( ) ;
808+ const stampedSet = new Set ( stampedSkillPaths ( ) ) ;
752809 const rows = [ ] ;
753810 let failed = 0 ;
754811
@@ -761,17 +818,24 @@ function run() {
761818 failed ++ ;
762819 continue ;
763820 }
764- const tokens = countTokens ( text ) ;
821+ const stamped = stampedSet . has ( rel ) ;
822+ const tokens = pricedTokens ( text , { stamped } ) ;
823+ const segment = stamped ? stampPrerelease ( text ) : '' ;
824+ // Empty on a stable tree, so `main`'s lines are byte-identical to before.
825+ const note = segment
826+ ? ` (the \`${ segment } \` prerelease segment of its metadata.version stamp, `
827+ + `${ Buffer . byteLength ( segment , 'utf8' ) } bytes, is not priced)`
828+ : '' ;
765829 const ceiling = CEILINGS . get ( rel ) ;
766830 const generated = generatorOwning ( rel ) ;
767831 rows . push ( { rel, tokens, ceiling, generated } ) ;
768832 const v = verdict ( rel , tokens , ceiling , generated ) ;
769833 if ( ! v . ok ) {
770834 failed ++ ;
771- console . error ( `✗ check-skills-token-ratchet: ${ v . msg } ` ) ;
835+ console . error ( `✗ check-skills-token-ratchet: ${ v . msg } ${ note } ` ) ;
772836 continue ;
773837 }
774- console . log ( `✓ check-skills-token-ratchet: ${ v . msg } ` ) ;
838+ console . log ( `✓ check-skills-token-ratchet: ${ v . msg } ${ note } ` ) ;
775839 }
776840
777841 const ratcheted = rows . filter ( ( r ) => ! r . generated ) . length ;
@@ -866,6 +930,17 @@ const SELF_TEST_BATTERIES = Object.freeze({
866930 'five ASCII bytes round up to two' : 1 ,
867931 'the unit is UTF-8 BYTES, not JS characters (a 3-byte CJK char is not free)' : 1 ,
868932 'empty text counts zero, which the verdict turns red' : 1 ,
933+ 'the stamp\'s prerelease segment is read off metadata.version' : 1 ,
934+ 'a zero-headroom SKILL.md stamped -next.0 is green: the stamp prices at stable width' : 1 ,
935+ 'priced whole, that same file is red (the row above is not vacuous)' : 1 ,
936+ 'one authored byte over a zero-headroom ceiling is red under a stable stamp' : 1 ,
937+ 'one authored byte over is red under a prerelease stamp too: the strip buys an author nothing' : 1 ,
938+ 'a multi-digit prerelease (-next.12) prices the same as a stable stamp' : 1 ,
939+ 'a stable stamp prices byte-for-byte as countTokens' : 1 ,
940+ 'every real bundle file without a prerelease stamp prices exactly as countTokens' : 1 ,
941+ 'a prerelease-shaped string outside the stamp is priced' : 1 ,
942+ 'a file the version script does not stamp is priced whole' : 1 ,
943+ 'the stamped set is the version script\'s: exactly the real SKILL.md files' : 1 ,
869944 'the catalog is discovered from disk' : 1 ,
870945 'every discovered file lives under a published skill directory' : 1 ,
871946 'a discovered file with no ceiling is RED' : 1 ,
@@ -920,7 +995,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
920995// the literal above, so the roster falls below this number; the table
921996// cross-check in the floor block is the other half, and names WHICH label
922997// collided.
923- const SELF_TEST_BATTERY_FLOOR = 64 ;
998+ const SELF_TEST_BATTERY_FLOOR = 75 ;
924999
9251000function selfTest ( ) {
9261001 const rel = 'skills/objectstack-ui/SKILL.md' ;
@@ -955,6 +1030,25 @@ function selfTest() {
9551030 // and the case below asks only that every one of them is priced.
9561031 const skillMdOnDisk = discoverBundleFiles ( ) . filter ( ( p ) => / ^ s k i l l s \/ [ ^ / ] + \/ S K I L L \. m d $ / . test ( p ) ) . length ;
9571032
1033+ // The release-stamp fixtures ("ONE EXCLUSION" in the header). Padded so the
1034+ // STABLE spelling is a whole number of tokens: a ceiling set at its count has
1035+ // zero headroom, so one more byte anywhere is red — the row that proves the
1036+ // padding is right is the "one authored byte over" pair below.
1037+ const stampHead = ( v ) => `---\nname: x\nmetadata:\n author: objectstack-ai\n version: "${ v } "\n---\n\n# body\n` ;
1038+ const stampPad = '.' . repeat ( ( 4 - ( Buffer . byteLength ( stampHead ( '18.0.0' ) , 'utf8' ) % 4 ) ) % 4 ) ;
1039+ const stampFixture = ( v , extra = '' ) => `${ stampHead ( v ) } ${ stampPad } ${ extra } ` ;
1040+ const stableStamp = stampFixture ( '18.0.0' ) ;
1041+ const next0Stamp = stampFixture ( '18.0.0-next.0' ) ;
1042+ const zeroHeadroom = countTokens ( stableStamp ) ;
1043+ const proseStamp = stampFixture ( '18.0.0' , 'pin 18.0.0-next.0 in prose\n' ) ;
1044+ const stampedOnDisk = new Set ( stampedSkillPaths ( ) ) ;
1045+ const repricedOnStableStamp = discovered . filter ( ( p ) => {
1046+ const text = readFileSync ( join ( REPO_ROOT , p ) , 'utf8' ) ;
1047+ const stamped = stampedOnDisk . has ( p ) ;
1048+ if ( stamped && stampPrerelease ( text ) !== '' ) return false ;
1049+ return pricedTokens ( text , { stamped } ) !== countTokens ( text ) ;
1050+ } ) ;
1051+
9581052 const cases = [
9591053 // ── ratchet semantics ────────────────────────────────────────────────
9601054 [ 'under the ceiling -> green' , verdict ( rel , 25000 , 25154 ) . ok , true ] ,
@@ -989,6 +1083,28 @@ function selfTest() {
9891083 [ 'the unit is UTF-8 BYTES, not JS characters (a 3-byte CJK char is not free)' , countTokens ( '中' ) , 1 ] ,
9901084 [ 'empty text counts zero, which the verdict turns red' , countTokens ( '' ) , 0 ] ,
9911085
1086+ // ── the release stamp's prerelease segment (ruling record 6138951701) ──
1087+ [ 'the stamp\'s prerelease segment is read off metadata.version' , stampPrerelease ( next0Stamp ) , '-next.0' ] ,
1088+ [ 'a zero-headroom SKILL.md stamped -next.0 is green: the stamp prices at stable width' ,
1089+ verdict ( rel , pricedTokens ( next0Stamp , { stamped : true } ) , zeroHeadroom ) . ok , true ] ,
1090+ [ 'priced whole, that same file is red (the row above is not vacuous)' ,
1091+ verdict ( rel , countTokens ( next0Stamp ) , zeroHeadroom ) . ok , false ] ,
1092+ [ 'one authored byte over a zero-headroom ceiling is red under a stable stamp' ,
1093+ verdict ( rel , pricedTokens ( stampFixture ( '18.0.0' , 'x' ) , { stamped : true } ) , zeroHeadroom ) . ok , false ] ,
1094+ [ 'one authored byte over is red under a prerelease stamp too: the strip buys an author nothing' ,
1095+ verdict ( rel , pricedTokens ( stampFixture ( '18.0.0-next.0' , 'x' ) , { stamped : true } ) , zeroHeadroom ) . ok , false ] ,
1096+ [ 'a multi-digit prerelease (-next.12) prices the same as a stable stamp' ,
1097+ pricedTokens ( stampFixture ( '18.0.0-next.12' ) , { stamped : true } ) , zeroHeadroom ] ,
1098+ [ 'a stable stamp prices byte-for-byte as countTokens' ,
1099+ pricedTokens ( stableStamp , { stamped : true } ) , countTokens ( stableStamp ) ] ,
1100+ [ 'every real bundle file without a prerelease stamp prices exactly as countTokens' , repricedOnStableStamp , [ ] ] ,
1101+ [ 'a prerelease-shaped string outside the stamp is priced' ,
1102+ pricedTokens ( proseStamp , { stamped : true } ) , countTokens ( proseStamp ) ] ,
1103+ [ 'a file the version script does not stamp is priced whole' ,
1104+ pricedTokens ( next0Stamp , { stamped : false } ) , countTokens ( next0Stamp ) ] ,
1105+ [ 'the stamped set is the version script\'s: exactly the real SKILL.md files' ,
1106+ [ ...stampedOnDisk ] , discovered . filter ( ( p ) => / ^ s k i l l s \/ [ ^ / ] + \/ S K I L L \. m d $ / . test ( p ) ) ] ,
1107+
9921108 // ── enumeration, never a hand list ───────────────────────────────────
9931109 [ 'the catalog is discovered from disk' , discovered . length > 0 , true ] ,
9941110 [ 'every discovered file lives under a published skill directory' ,
0 commit comments