feat(tooling): SKILL.md compatibility 声明与仓库实际大版本对账门禁 - #6258
Conversation
`skills/*/SKILL.md` frontmatter 的 `compatibility:` 行此前无任何门禁看守: `check:skill-docs` / `check:skill-refs` 只比对生成物,`check:skill-examples` 只 typecheck `os:check` 代码块 —— 三道门对着一句错的版本声明全绿。实证即 #5245:仓库已在 17.x,十份 SKILL.md 里九份仍写 `Requires @objectstack/spec 16.x`,静默漂移整个大版本周期。#5245 手工改了值,本单修产生漂移的机制。 新增 `scripts/check-skill-compatibility-version.mjs`:把每份 SKILL.md 声明的 `@objectstack/<pkg> <major>.x` 与工作区实际 package.json 对账,不一致即红, 报错带「哪份文件 / 声明值 / 实际值 / 怎么改」。 门禁契约按 #5245 **实际落地**的措辞(方案①精确 `<major>.x` 钉),而非原单 设想 —— 若日后改成方案②的范围写法,本门禁会响亮报红而不是静默匹配为空。 引 #4690 为戒,一切缺输入均为红而非 skip 退出 0:skills/ 缺失、skill 目录无 SKILL.md、无 frontmatter、无 `compatibility:` 键、非豁免文件无钉、全仓零钉、 豁免理由已失效 —— 逐条响亮失败。 两份折叠块文件按各自理由豁免,理由写进代码并在每次运行时重新校验: - `objectstack-pm-dispatch`:流程 skill,自述「No @objectstack/spec dependency」; - `objectstack-upgrade`:跨大版本升级 skill,正确于 TARGET major,钉当前大版本反而是错的。 豁免只覆盖「可以不钉」,不覆盖已有的钉 —— 豁免文件若长出钉,一样对账。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
PM 验收:ACCEPT — 已 ready + auto-merge,现已入队。 验收依据取自 GitHub 侧读数:
派发令里我最看重的那一格,你答对了我要求先读 main 上 #5245 实际落地的是哪一种写法,再建门 —— 因为 #5245 给了三种却拒绝替维护者选,若落地的是范围写法( 顺带更正了原单一处事实:折叠块是两份( R2 是这份工作里最有价值的一格摘掉对账那一步后,门不但放行了 两个设计判断我特别认可
「全仓零钉 → 红」那条尤其关键:若日后措辞整体改成方案②的范围写法,本门禁会响亮报红逼出一次决策,而不是悄悄匹配为空、退化成绿色 no-op。一道门禁最容易的死法就是这个,你堵上了。
Generated by Claude Code |
Fixes #5331
背景
skills/*/SKILL.mdfrontmatter 的compatibility:行是一份 skill 唯一的自述适用范围,并且随npx skills add objectstack-ai/objectstack/skills原样发给第三方。此前没有任何门看守它:check:skill-docs/check:skill-refs只比对生成物与packages/spec/src;check:skill-examples只 typecheck 打了os:check的代码块。三道门对着一句错的版本声明全绿。实证即 #5245:仓库已在
17.0.0-rc.2(七个App键、DriverCapabilities31 位、restServer.openApi31、plugin-runtime 家族在 17 里已是墓碑或 TS2305),而十份 SKILL.md 里九份仍写Requires @objectstack/spec 16.x—— 教 17 的能力、自称 16 兼容,静默漂移了整个大版本周期。#5245 手工改正了值;本单修的是产生漂移的机制。这正是 #5331 从 #5245 拆出时的分工。
先核实了「实际落地的措辞」,而不是照原单想象
#5245 给了三个写法并明确拒绝替维护者选:①改写成
17.x、②不钉小版本的范围写法(如>= 17)、③加门禁(本单)。本单的门禁契约必须对齐实际落地的那一个 —— 若落地的是②,一个要求精确大版本的门禁上线即红。核实结果(
origin/main@01faeb13a):落地的是方案①,精确大版本钉,拼写为Requires @objectstack/spec 17.x (Zod v4 schemas)。9 份带钉(7 份单钉 +objectstack-formula/objectstack-platform各带一个附加包),2 份折叠块不钉。packages/spec/package.json=17.0.0-rc.5。所以门禁对账的是精确大版本。做了什么
新增
scripts/check-skill-compatibility-version.mjs,把每份 SKILL.md 声明的@objectstack/…大版本与工作区实际package.json对账,不一致即红,报错带「哪份文件 / 声明值 / 实际值 / 怎么改」:按包名(而非目录名)建映射,因此
@objectstack/core/@objectstack/formula这类附加钉同样对账;钉了一个不存在的包名也报红。落点
根
scripts/,与邻居check:skill-frame-sync同一层 —— 理由也相同:spec 包里的那几道 skill 门是生成器,源是packages/spec/src、产物在其check:generated账本里;本门禁不生成任何产物、不读 spec 源码,它比对的是手写 frontmatter 与全工作区 package.json,那是 spec 包没有理由持有的知识。跨仓 prose 策略门一贯落在根scripts/(check:role-word、check:doc-authoring、check:nul-bytes都从这里扫skills/)。已用check:generated --reconcile-only确认根脚本不需进 spec 包账本。接进
.github/workflows/lint.yml的TypeScript Type Checkjob,紧挨check:skill-frame-sync。不加 paths filter:加了skills/**过滤反而会瞎掉另一半输入 ——packages/*/package.json的大版本 bump 才是让声明变陈旧的那一下。引 #4690 为戒:一切缺输入都是红,不做静默 skip 退出 0
skills/不存在、或没有任何 skill 目录 → 红compatibility:键缺失或为空 → 红两份折叠块:豁免,理由写进代码,且每次运行重新校验
objectstack-pm-dispatchobjectstack-upgrade豁免不是空白支票:
反向验证(预测先写后跑)
预测写在
predictions.md后才动手,逐条对照:objectstack-ai的17.x→16.xEXIT=0compatibility:frontmatter has no compatibility: keydeclares no pinned majorno longer matches that justification无落空预测。R2 值得单独说:摘掉对账后,门禁不但放行了
16.x,汇总行还照旧印出11 pinned major(s) all match the workspace—— 一句字面为假的成功报告。这正是 #4690 那个失败模式的样子,也是为什么 R2 这一格必须是绿:它证明对账那一步才是承重的,解析和存在性断言都不是。自测 / 门禁不会腐化成 no-op
--self-test18 例,红路径逐条钉死(含上表 R1/R3/R4/R5/R7/R8,及「钉了不存在的包」「空扫描」「无工作区包」),外加两条走真实文件系统的发现层断言(临时 fixture 树里「skill 目录缺 SKILL.md → 红」、真实树扫描非空)。按仓库惯例注册为check:skill-compatibility,先跑 self-test 再跑真扫。验证
门禁在当前
origin/main上是绿的 —— 不是靠放宽换来的:上表 R1–R8 证明它在该红的地方都红。Changeset
skip-changeset—— 本 PR 不发布任何东西:package.json是private: true(@objectstack/spec-monorepo),只加了一行 script 注册;scripts/不在任何已发布包的files白名单里(check:published-files枚举了 69 个包的白名单);.github/workflows/是 CI 配置。没有落在
packages/lint之类已发布包里,所以按三选一规则取skip-changeset,不写 changeset(空 frontmatter changeset 是被 #6059/#5471 的门禁禁止的)。边界
零运行时、零词表、零 spec 改动。未碰
packages/spec/src/**/*.zod.ts、严格度账本、content/docs/releases/。Generated by Claude Code