Repository navigation
Commit 6dfc7fa
feat(scripts): --fix derives skill body versions; release guard and token ratchet follow (#31745)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbPjpDtuAfMiZPGY3g5SZw34)_
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
<details><summary>Self-tests, scans and a simulated release</summary>
- `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.
</details>
## 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`. #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 #31432 merged, and the held item 3 commit was re-applied from its
patch, so history was not rewritten.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jU4PLtyyimMz72z285)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 1f30be1 commit 6dfc7fa
3 files changed
Lines changed: 626 additions & 46 deletions
File tree
- .github/workflows
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
736 | 736 | | |
737 | 737 | | |
738 | 738 | | |
739 | | - | |
740 | | - | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
741 | 756 | | |
742 | 757 | | |
743 | 758 | | |
744 | 759 | | |
745 | 760 | | |
746 | 761 | | |
747 | | - | |
| 762 | + | |
| 763 | + | |
748 | 764 | | |
749 | 765 | | |
750 | 766 | | |
| |||
0 commit comments