Skip to content

feat(scripts): --fix derives skill body versions; release guard and token ratchet follow - #31745

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-54kzzv
Nov 9, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-54kzzv

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #31115 (item 3, the last; items 1 and 2 landed in #31432).

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: check:skill-compatibility 现在也检查技能正文里写死的版本号(如 bootstrap.md 起步 package.json 里的 5 个 "@objectstack/…": "18.3.0"),发版时由 --fix 自动改写;新导出 bodyPinnedPaths() 列出会被改写的文件,release.yml 的发版后守卫据此放行;token 棘轮不再计价这些由脚本写入的版本值。
  • 为什么改: 正文版本号以前靠手改,已经落后过一次;按裁决 B(「31115 … 同意」)把它变成派生值,并堵住 18.9→18.10 这类变宽导致 Version Packages PR 被棘轮打红的坑(bootstrap.md 余量只有 1)。
  • 风险与回滚: 只改两个检查脚本和 release.yml 的一个守卫步骤,不动运行时代码和技能文本;在本地模拟了一次升到 18.10.0-next.12 的发版:--fix 改写 12 个文件,守卫零意外路径,棘轮保持绿。回滚即 revert 本 PR。
  • 先看: .github/workflows/release.yml(新增 BODY_LIST,第六个过滤器,空列表合法);scripts/check-skills-token-ratchet.mjs(bodyPinValues 与 BODY_PIN_REPIN,bootstrap.md 行 1351→1343);scripts/check-skill-compatibility-version.mjs(bodyPinnedPaths、applyFix)。
  • 两分钟验证: 运行 pnpm check:skill-compatibility 和 node scripts/check-skills-token-ratchet.mjs --self-test,应分别看到「6 body version(s) in 71 skill markdown file(s)」和「104 cases pass」。
  • 需要你做的: 这是受管面 PR(.github/workflows/release.yml),需要一位受管审批人批准(APPROVED review)。

What changes

  • scripts/check-skill-compatibility-version.mjs.
    • The check now judges every body version under skills/**. A version must equal its workspace package's version, or sit in a sentence that reads as history (since, removed in, until, …). An install pin is always judged.
    • --fix rewrites body versions in the same pass as the frontmatter. applyFix writes each file once, and a range (^/~) is kept.
    • New export bodyPinnedPaths(): the files whose body the writer owns. The writer is asked with a probe value, so a file stays listed after the pass made it current. A history-only file is never listed. A moved skills directory throws; a catalog with no pins answers [].
    • The reader, the history rule and deriveBodyVersions come from the first item 3 commit, now called.
  • .github/workflows/release.yml, "Validate the post-version tree".
    • BODY_LIST is resolved from bodyPinnedPaths() and applied as a sixth whole-line grep -vxF -f filter.
    • Unlike the four lists above it, an empty list is legitimate and admits nothing; a failed resolve still fails the step.
    • The content half already runs check:skill-compatibility, which now judges the bodies.
  • scripts/check-skills-token-ratchet.mjs: the second exclusion.
    • Body pin VALUES the writer rewrites are not priced. bodyPinValues() reads them off deriveBodyVersions with a probe, so there is no second parser.
    • The package name, quotes, range, and every history version stay priced.
    • pricedTokens takes bodyPinned, and run() reads bodyPinnedPaths().
    • bootstrap.md's row is re-pinned 1351 → 1343, the same 8 its count fell (five 18.3.0 values are 30 bytes, 5398 → 5368), so its headroom stays 1. This is recorded as BODY_PIN_REPIN.
    • The header gains the ruling and a hand audit (grep | sed | tr | wc) that the self-test reproduces on the live file.

Why

The maintainer ruled B on the item 3 decision (「31115 … 同意」, 2026-11-09; triage record 6730018726). --fix rewrites the install-shape body pins at version time; the written set is declared to the release guard; and the ratchet stops pricing writer-rewritten body pin values, generalising the frontmatter stamp-line rule. Without the third part, a width change at release reds bootstrap.md on the Version Packages PR.

Tests

Self-tests, scans and a simulated release
  • pnpm check:skill-compatibility: the self-test passes 22 check cases and 22 --fix derivation cases (floor 30 → 44). The new cases: the written set equals stampedSkillPaths() then bodyPinnedPaths() on an all-stale fixture; a history-only file is neither listed nor written; the guard's grep -vxF -f admits a body write only through the body list, and an empty list admits nothing; the export throws on a moved tree and answers [] with no pins; on the live tree the writer's list equals the reader's. The scan reads "6 body version(s) in 71 skill markdown file(s)", and --fix on main is a no-op.
  • node scripts/check-skills-token-ratchet.mjs --self-test: 104 cases pass (floor 97 → 104). The new cases: exactly the owned value bytes are unpriced, never a range, a name or a history version; any pin width from 17.7.0 to 18.10.0-next.12 moves no count; one authored byte over a zero-headroom row is red at every width; on the live file the writer's bytes equal the reader's; the header's hand audit reproduces the live count; and the re-pin record. The scan is green, with bootstrap.md at 1342 / 1343.
  • Simulated release (restored afterwards). The five pinned packages were set to 18.10.0-next.12 and --fix was run.
    • It rewrote 11 SKILL.md stamps and the five bootstrap.md pins (12 files), and the check stayed green.
    • With the body list, the guard filters left 0 unexpected paths. Without it, bootstrap.md was unexpected.
    • The ratchet stayed green at 1342 / 1343 (75 unpriced bytes). Priced whole, the file would read 1361 against the old 1351.

Acceptance notes

  • Card item 3: body versions are judged and derived. The --fix writes are declared to the release guard, and the ratchet does not price them, as ruled.
  • Files outside the original claim surface, by ruling: .github/workflows/release.yml and scripts/check-skills-token-ratchet.mjs. docs: teach os mcp connect and the use-runtime deploy target in the remaining pages #31335 was already on main when this branch was cut, so no merge of main was needed.
  • skills/** readings: no file under skills/** changes. bootstrap.md stays at 190 → 190 lines, and the bundle (all SKILL.md) at 4592 → 4592 lines.
  • Not judged: a version that names a package the workspace lacks is listed by bodyPinnedPaths() as writable. The check reds it, so no green tree carries one.

Verification

  • Head 1415775e (merge base f2abd4767).
  • node scripts/pm/dispatch-gates.mjs --ran: 55 derived, 55 run, 0 NOT-MEASURED, 0 UNRUN, and all 55 exit 0 at this head. --tier: no path-derived mandate, so this was built on the default tier.
  • Left to this PR's check-runs: the artifact-roster and wide-population families, the path-scheduled CI jobs, the type-check lanes and the always-runs tail that dispatch-gates prints outside the derived total. The release lane itself runs only on a Version Packages pass; the simulated release under Tests stands in for it.
  • Deviations: none from the ruling. The branch was restarted from main after feat(scripts): skills link/anchor gate and three liveness-exhaustive sections #31432 merged, and the held item 3 commit was re-applied from its patch, so history was not rewritten.

Generated by Claude Code

…l body names

Pure functions and self-test rows only: extractBodyVersions finds the
install-shape pins and the prose versions in every skill markdown body,
judgeBodyVersions holds each to its package's workspace version unless
its sentence reads as history, and deriveBodyVersions is the --fix
derivation that rewrites the non-history ones. Kept apart from
deriveFrontmatter, which the token ratchet asks for the one stamp line.
Not yet called from the gate's run or its --fix leg.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jU4PLtyyimMz72z285
…ease guard and unpriced by the token ratchet

Ruling B on the item 3 decision: the version pass now rewrites the install
pins a skill body names, the way it restamps the frontmatter.

- check-skill-compatibility-version: the check judges every body version,
  and --fix writes them. A new export, bodyPinnedPaths(), lists the files
  whose body the writer owns, asked of the writer with a probe.
- release.yml: the post-version guard resolves bodyPinnedPaths() as a sixth
  whole-line filter. An empty list is a real answer there.
- check-skills-token-ratchet: the body pin values the writer rewrites are not
  priced. This generalises the stamp-line rule. bootstrap.md's row is
  re-pinned 1351 -> 1343, the same 8 its count fell, so its headroom does not
  move (BODY_PIN_REPIN).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jU4PLtyyimMz72z285
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · zhuangjianguo · session_01QsR2jT34CXs5fCn3BDirZw

Contract-review-claim: session_01QsR2jT34CXs5fCn3BDirZw · head 1415775 · 2026-11-09T01:56Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · zhuangjianguo · session_01QsR2jT34CXs5fCn3BDirZw

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1415775e6f78a8133a697304e67a8ef03ba6b5a1
Local-runs: probe — the ratchet self-test on the head worktree is the one measurement of the re-pin against the live bootstrap.md (writer bytes = reader bytes, hand audit = priced count) that CI's green does not print.
Probe detail: node scripts/check-skills-token-ratchet.mjs --self-test in a detached worktree at the head, no install: exit 0, "104 cases pass", all seven body-pin rows ✓. Everything else was read, not run (the hand audit below is wc -c / grep over the tree, the header's own formula applied by hand).
Scope: scripts/check-skill-compatibility-version.mjs (claim) + .github/workflows/release.yml, scripts/check-skills-token-ratchet.mjs (by ruling B, triage record 6730018726)

Inputs read: card #31115 (body; comments 6706623953 triage, 6708443569 ruling A on item 1, 6718198041 claim, 6718224824 decision owed with the dev's four-option analysis, 6718947788 and 6719355267 items 1–2 report and landing, 6730018726 ruling B of record for item 3, 6730391223 os-dev-report for this PR), PR #31745 body and file list (3 files, +626 −46, every patch present), git diff f2abd4767 1415775e against the live tree at the head (worktree), the seat's project files issue-31115/ (item3-needs-decision.md, item3.patch, pr-body-item3.md, pr-body.md), and the check-runs on the head twice. At the second read: 58 check-runs, 54 concluded — 42 success (Governed Surface Queue Guard, PR-body guards, Check PR Size, Type Check · source gates — the lane that runs pnpm check:skill-compatibility at lint.yml:5402 and the ratchet self-test + scan at lint.yml:5490–5491 — Type Check · workspace, every Lint · lane, Test Core (2/8), all 16 Test UI shards), 11 expected skips, and ONE red: Check Changeset, red because the PR carries no skip-changeset label (labels at read time: ci/cd, size/l; the step's own log: "Check Changeset is not one of the required contexts"). 4 still in_progress (Build & E2E UI, Lint UI, Test Core (1/8), Type Check UI), none of which exercises the three changed files. "Every check green" is the landing condition, not this record's; the seat re-reads it before landing, and ② names what makes Check Changeset green.

① Derived judgments

  • Surface = claim + ruling, nothing more. Three files: scripts/check-skill-compatibility-version.mjs is in the claim (6718198041: "body versions + --fix"); .github/workflows/release.yml and scripts/check-skills-token-ratchet.mjs are the two the ruling names (6730018726 "Two files are outside the original surface"). No skills/** file, no package.json, no lint.yml line, no changeset (bootstrap.md 190 → 190 lines, 5398 bytes, unchanged on the head). The ruling's "merge main after docs: teach os mcp connect and the use-runtime deploy target in the remaining pages #31335 lands" is satisfied by construction: the branch was cut from f2abd476, which already carries docs: teach os mcp connect and the use-runtime deploy target in the remaining pages #31335 (the ratchet's CEILINGS hunk this PR edits is one row, :1275, and the diff applies cleanly to that basis). Right.
  • Tier H by path. release.yml is in WORKFLOW_TIER_H_FILES (check-governed-merges.mjs:1631, "publishes the packages"), so the split reader cannot lower it; Governed Surface Queue Guard concluded success and the PR body says a governed approver's APPROVED review is owed. Right.
  • Accept set: the sixth filter widens only what --fix legitimately writes. BODY_LIST (release.yml:746) is resolved from bodyPinnedPaths() (compat:814), which is the WRITER asked with a probe (BODY_PROBE, :791): a file is listed iff deriveBodyVersions (:469) would move at least one byte of it for some version, i.e. it carries a non-history body version. The version pass writes a file iff deriveBodyVersions with the real workspace moves a byte of it — a subset of the listed set (the probe also counts an already-current value, which is the point: the guard runs AFTER the pass, when every value is current; and a value naming a package the workspace lacks, which --fix leaves alone and the check reds at compat:434–438, so no green tree carries one). Nothing else is newly admitted: the filter is grep -vxF -f (whole-line, fixed-string, :763), the same mechanism as the four lists above it, so no path outside the list can match, and admission is whole-file at the same granularity as SKILL_LIST. Right.
  • Empty-list case is fail-safe. bodyPinnedPaths() THROWS on a tree with no skill markdown (compat:816–823, "a moved directory") → the node -e resolve exits non-zero → exit 1 (release.yml:750–751). It returns [] only on a catalog with markdown and no pins → an empty BODY_LIST → grep -vxF -f <empty> matches nothing, -v passes every line, so nothing is admitted: the body write that would then be unexpected IS reported. The deliberate absence of the [ ! -s ] check the other four lists carry is therefore correct, and the comment at :742 says why. If the extractor regressed to list nothing, bootstrap.md would surface as unexpected — the failure direction is red. Pinned: 'a body-pin write is admitted by the bodyPinnedPaths() filter alone, and an empty body list admits nothing' (compat:1511–1539, real grep via spawnSync). Right.
  • Ratchet: the unpriced rule reaches only writer-rewritten values, with no general escape hatch. pricedTokens(text, {bodyPinned}) (:1463) subtracts bodyPinValues(text).bytes only when run() found the file in bodyPinnedPaths() (:1665). bodyPinValues (:1408) hands the file to the SAME writer with a probe and prices back exactly the bytes it replaced (old line − new line + probes × probe bytes), throwing if the writer changed the line count. So the unpriced bytes are the version VALUES only; the package name, quotes, ^/~, the key, every history version and every other byte stay priced — pinned by 'the unpriced body bytes are exactly the version values the writer owns, never a range, name or history version' (count 2, bytes 12 on a fixture with a ^ pin and a Removed in … 16.0.0 sentence) and, on the live file, by 'on every real body-pinned file the unpriced bytes are exactly the non-history versions the reader finds'. An author cannot buy headroom: each extra pin costs ~30 priced bytes to shed ≤ 15, and a pin naming a non-existent package reds check:skill-compatibility in the same lane. Right.
  • Width. stampWidths (:1946) is 17.7.0, 18.0.0-next.0, 18.9.0, 18.10.0, 18.10.0-next.12, 100.0.0; 'a body pin change of any width moves no count (18.3.0 -> 18.10.0-next.12)' asserts one priced count across the whole set, and 'one authored byte over a zero-headroom body-pinned ceiling is red at every pin width' holds the ceiling tight at every width. The width trap the ruling names (18.9 → 18.10, -next) is closed and pinned. Right.
  • The re-pin 1351 → 1343 is measured and legitimate under the ratchet's own conventions. Hand audit with the header's own formula (:135–138) on the head's bootstrap.md: 5398 bytes, five 18.3.0 values = 30 bytes, ceil(5398/4) = 1350 priced whole, ceil(5368/4) = 1342 with the values out. Old row 1351 at count 1350 = headroom 1; new row 1343 at count 1342 = headroom 1 — lowered by exactly the 8 the count fell, as BODY_PIN_REPIN (:356, basis f2abd476, pins 5, value 18.3.0, tokens 8) records and as 'the body-pin re-pin is recorded' pins. This is the STAMP_LINE_REPIN precedent ("re-pinned, never loosened") applied verbatim. Probe: 104 cases pass on the head, including 'the header's hand audit reproduces every real body-pinned count'. Right.
  • The history rule and the writer agree. HISTORY_RE (:383) decides both judgeBodyVersions (prose only — an install pin is "always judged", :446) and deriveBodyVersions (a history prose version is left alone, :472–476). The catalog carries six body versions: five install pins in bootstrap.md and one prose version at skills/objectstack-ui/rules/record-page.md:44 ("were removed in @objectstack/spec 18.3.0") — read on the head, removed in matches, so that file is history-only, not listed, not written, priced whole. The PR body's "6 body version(s) in 71 skill markdown file(s)" and the one-file BODY_PIN_REPIN agree with the tree. Right.
  • Public surface. New exports from the compat script: extractBodyVersions, judgeBodyVersions, deriveBodyVersions, readSkillMarkdown, bodyPinnedPaths; applyFix gains a defaulted fourth parameter and returns { files, markdown, written } (one new key; the self-test is its only consumer). From the ratchet: bodyPinValues, BODY_PIN_REPIN; pricedTokens gains an optional bodyPinned (default preserves every caller). Repo scripts, not a published package. Right.

② Semver level

The diff touches two repo scripts and one workflow; no packages/**, no skills/**, no published entry point, no changeset edited or added. Clause-②: no, Metadata-protocol: none on the PR body; check-changeset-no-major concluded "no major bump … a non-protocol change at patch". Correct level. The posture is skip-changeset — route 2 of the Check Changeset message (this PR releases nothing; the route-0 discriminator does not apply because no .changeset/*.md row is M or D) — but the label is not on the PR, which is the one red on the head. The dev's writes: 2 kept labels to the seat, per the dispatch order ("comments, labels and bodies only via scripts/pm write tools"). Landing item L1: the seat applies skip-changeset through scripts/pm before landing; the gate re-reads labels live and goes green without a push. No grade on the diff.

③ Boundary flags

  • B1 (Check Changeset red, label missing). Named in ② as L1. Not a defect of the diff; a seat write owed before landing. Answered.
  • B2 (dev flag: the branch was restarted from main after feat(scripts): skills link/anchor gate and three liveness-exhaustive sections #31432 and item3.patch re-applied as 79468c3). Read: 79468c32 is the reader/history rule/deriveBodyVersions and 1415775e the --fix/guard/ratchet half; both are on the branch, no history rewrite, and item3.patch in the project files matches the first commit's substance. Answered: no effect on the record.
  • B3 (--fix and the unpriced set reach non-history PROSE versions, not only install pins). The card (item 3) and the ruling's first clause say "--fix rewrites the install-shape body pins"; the diff's writer (:469) also rewrites a prose version that does not read as history, and the ratchet then unprices it. This is a reasoned extension, not a breach: the check's contract ("equal or history") makes every non-history prose version a statement of the CURRENT version, so at version time a check-only prose version would red check:skill-compatibility in release.yml's content half (:815) — the same "check-only is not an option" fact that forced the decision. Today the catalog carries zero such values (the one prose version is history), so nothing on this tree changes hands. For the maintainer's eye, no grade: at a release, a sentence such as "Install @objectstack/spec 18.3.0" in any skill body is rewritten by the bot, as the metadata.version stamp is. If that is unwanted, the narrowing is one line (shape === 'install' in to() at :476) and the check's prose arm would need to accept "equal to ANY past release" — a different rule from the card's.
  • B4 (self-test fragility, fail-closed). 'the header's hand audit reproduces every real body-pinned count' (:2010–2011) requires every live body-pinned file to carry ONLY install pins (allInstall), because the header's grep | sed formula reads install pins alone. The day an author writes a legitimate non-history prose version into a body-pinned file, the ratchet SELF-TEST reds (not the gate), with a message that names the file but not the cause. Red direction, same convention as the ratchet's other live-tree pins (skillMdOnDisk, notTheStamp). Nit N1 below.
  • B5 (platform tier). The dev-report says dispatch-gates --tier derived no mandate for these paths and the default tier was used; the earlier PR's clause-① mandate was for skills/**, which this PR does not touch. Answered.

④ Design judgment

release.yml (:739–764), sentence by sentence against the ruling's second clause ("a new written-set export is declared to release.yml's post-version guard"). The export is bodyPinnedPaths(), declared where the other four rewriters' sets are, resolved the same way (node --input-type=module -e 'import …' from the repo root — the module's entry-point guard, isEntrypoint, already serves stampedSkillPaths on :728 so the import is side-effect-free), filtered the same way (whole-line fixed match), and judged by the content gate that already runs (run_gate pnpm check:skill-compatibility, :815, which now judges bodies). The comment header is edited from "Five filters … four declared lists" to "Six … five" and is true. The one deliberate difference from its neighbours (no empty-file refusal) is argued in the comment and holds under ① ("Empty-list case"). Holds. A note on granularity, pre-existing and shared with SKILL_LIST: admission is per FILE, so a second version-time writer that later learns to touch bootstrap.md would be admitted by this list without a new declaration. Not this diff's to fix; worth remembering when the next rewriter arrives.

check-skill-compatibility-version.mjs, the body section (:349–506) and the writer (:790–872).

  • Reader. Two shapes, both anchored on @objectstack/<pkg> and a full x.y.z[-pre]: an N.x pin, a bare 18.3.0, a table cell | 18.3.0 | are not read (pinned: 'frontmatter pins and the stamp are not body versions'). Frontmatter is skipped by frontmatterEnd; fenced code is deliberately NOT skipped (the starter package.json is the target). The sentence a prose match sits in is rebuilt across wrapped lines (sentenceAt, :405–415) and HISTORY_RE is tested on that sentence, so "removed in" one line above still counts and "one sentence away" does not — both pinned. Holds.
  • Judge. Equal → green; prose + history → green; install pin is always judged; unknown package → red with its own message; the red names file:line, both versions and the --fix command. Holds.
  • Writer. deriveBodyVersions rewrites every non-history version to its package's version and keeps ^/~; a second pass is a no-op; it never touches the frontmatter — all pinned. applyFix (:839) derives frontmatter first, body second on the derived text, writes each file ONCE, and hands the check the derived text for both halves. Holds. One divergence between judge and writer, contrived enough to be a nit: the history set (:472) is keyed line:pkg:version, so on a single line carrying BOTH a prose history mention and an install pin of the same package at the same version (Since … removed in @objectstack/spec 18.3.0: "@objectstack/spec": "18.3.0"), the install pin is not rewritten although the judge says an install pin is always judged. Nit N2.
  • Export. bodyPinnedPaths throws on no markdown, [] on no pins, lists by the writer's probe; 'bodyPinnedPaths() on the live tree is exactly the files the reader finds a non-history version in' cross-checks writer against reader on the real catalog. The self-test's written-set case is now stampedSkillPaths() then bodyPinnedPaths() in walk order against what applyFix wrote on an all-stale fixture with a pinned file and a history-only file, and asserts the body is green afterwards and the history file untouched. Holds.
  • Floor. SELF_TEST_BATTERY_FLOOR 30 → 44 with 14 new rows, each declared in SELF_TEST_BATTERIES and each registered by the driving loop. Holds.

check-skills-token-ratchet.mjs: the second exclusion (:116–138 header; :356; :1408–1475; :1665–1690; the seven self-test rows). The header's rule is now stated as ONE rule with TWO exclusions ("bytes the version script writes are not priced"), which is exactly the ruling's "generalising the frontmatter stamp-line rule"; the first exclusion's text is kept and its "Nothing else moves" sentence is amended to point at the second. The writer-asked-with-a-probe pattern is reused rather than a second parser, so the ratchet cannot disagree with --fix about which bytes are derived. The report line names the unpriced byte count on a body-pinned row, as it does for the stamp. Holds. Two nits on the self-test, neither a contract effect:

  • N1 (B4) — 'the header's hand audit reproduces every real body-pinned count' conflates "the formula reproduces the count" with "every owned version is an install pin". When that assumption breaks the self-test reds for a reason the message does not name. Fix: split it — keep pinHandAuditMisses over the allInstall files only, and add a row asserting the current fact (realBodyPinned.every(allInstall)) with a label that says so.
  • N2 — deriveBodyVersions's same-line history key (compat:472, :476) can shield an install pin; the judge never does. Fix: compute the history set from PROSE matches only (it already is) and skip the history.has test inside the install-pin replacer.
  • N3 — repinMisses (:1984–1986) still calls pricedTokens(text) with no options for a non-stamped file and so prices bootstrap.md whole; its comment "every other file must read exactly as it did" is no longer what run() does for that file. The new row 'on every real body-pinned file …' carries the body-pinned case, so nothing is unguarded; the older row's comment is now narrower than its words. A one-line comment fix.

The simulated release in the PR body (five packages set to 18.10.0-next.12, 12 files rewritten, 0 unexpected with the list and 1 without, ratchet 1342 / 1343, restored) is the only exercise of the release lane this PR can have before a Version Packages PR; it is consistent with every reading above and with the probe.

Verdict reasoning. The surface is the claim plus exactly the two files the ruling names. The guard's sixth filter admits only what the writer owns, whole-line, and fails red on a throw, on an empty list and on a regressed extractor. The ratchet unprices only the bytes the same writer replaces, pinned on fixtures and on the live file, and the re-pin is the stamp-line precedent applied with the arithmetic right (5398 → 5368, 1350 → 1342, headroom 1 → 1). Width 18.9 → 18.10 and -next are pinned on both sides. Semver and changeset posture are right in the diff; the skip-changeset label is a seat write owed before landing (L1). The three nits are self-test prose and a contrived writer edge, none of which lets a wrong value land green or a right one land red on a real tree. No must-fix.

Implemented-by: claude/project-thread-54kzzv
Reviewed-by: session_01QsR2jT34CXs5fCn3BDirZw

VERDICT: PASS

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 9, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · zhuangjianguo · session_01QsR2jT34CXs5fCn3BDirZw

维护者速读

  • 改了什么:发版时 --fix 除了改 frontmatter,也会改写 bootstrap.md 正文里的安装版本号(install-shape 正文钉)。release.yml 发版后的守卫新增一个过滤项 BODY_LIST,由 bodyPinnedPaths() 给出,只放行这些被改写的文件。token ratchet 不再计这些由写入方改写的版本号字节,bootstrap.md 的上限按实测从 1351 降到 1343,余量还是 1。
  • 为什么改:这是 skills gates: a link/anchor walk over skills/**, three hand-written tables as liveness-exhaustive sections, and spec versions in skill bodies checked — the blind spots behind the 2026-11-02 skills review #31115 第 3 项,你裁定 B。现在版本号一旦变宽(18.9 → 18.10,或者带 -next),Version Packages PR 就会被 ratchet 拦红。这次改动把这个坑堵上。
  • 风险与回滚:只动 3 个文件(release.yml 加两个根脚本),不发布任何包(skip-changeset,标签按你的「加」打上了)。守卫在列表为空时什么都不放行;脚本抛错就 exit 1。回滚即 revert 本 PR。
  • 先看:release.yml L746 的 BODY_LIST;check-skill-compatibility-version.mjs 的 bodyPinnedPaths;check-skills-token-ratchet.mjs 的 bodyPinValues。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs --self-test 退出 0(104 条);node scripts/check-skill-compatibility-version.mjs --self-test 退出 0。
  • 需要你做的:批准并入队(Tier H,因为改了 release.yml)。复审有两点请你知悉,都不用改代码:B3,--fix 和不计价的范围也覆盖正文里「非历史」的版本号文字,不只安装钉,今天树上没有这类文字;B4,某个文件哪天出现这种文字时,ratchet 自测有一条会先报红(是失败即关,不会放过)。
  • 席位意见:ACCEPT。隔离复审首轮 PASS(6730515562),无必改项。

席位核对

Head-sha: 1415775e6f78a8133a697304e67a8ef03ba6b5a1

  1. Shape: draft, base main, body carries Fixes #31115 (the last item; items 1–2 landed in feat(scripts): skills link/anchor gate and three liveness-exhaustive sections #31432), skip-changeset (applied by the seat on the maintainer's 「加」 in the project thread). 3 files, +626/−46. Tier H by .github/workflows/release.yml.
  2. Premise measured: on main, --fix stamps frontmatter only and the ratchet prices every body byte, so a width bump reds the Version Packages PR. The dev's release simulation at 18.10.0-next.12: --fix wrote 12 files, the guard found 0 unexpected paths (1 without BODY_LIST), and the ratchet stayed green.
  3. Gates: contract review at CONTRACT_REVIEW_TIER: PASS 6730515562 on this head. Dev gates: 55 derived, 55 run, all exit 0. Check-runs on this head: 46 success, 16 skipped; Check Changeset re-ran as skipped after the label, and its earlier failure is superseded. K = 4.
  4. Accepted deviations: none. Nits N2 (a same-line history key can shield an install pin from the writer; the judge still judges it) and N3 (the repinMisses comment is narrower than its words) are left as they stand.
  5. Out of scope / Skills-docs: B3 and B4 go to the maintainer above. If either is to be narrowed, it is a follow-up card. Metadata-protocol: none, so no Skills-docs: line is owed.
  6. Landing: Tier H. pm:awaiting-merge plus reviewers os-zhuang and hotlong. The PR stays draft until an authorized approval or a maintainer enqueue.

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 9, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review November 9, 2026 02:42
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit 6dfc7fa Nov 9, 2026
68 of 69 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-54kzzv branch November 9, 2026 03:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants