feat(skills): 新增 objectstack-upgrade 升级剧本 —— AI 一键升级客户元数据项目 (#6111) - #6193
Merged
Conversation
面向客户元数据项目的 v16 → v17 升级 agent 剧本,骑在 ADR-0087 D2 conversions 之上,不替代确定性转换。三层边界按维护者拍板落地: - **机械层(只调用,不重实现)**:`os migrate meta --from N` 重放转换链; 存量行由 `applyConversionsToStoredItem` 在 rehydration 时重放。剧本写清 「该命令不改写源文件、只写 --out」这条最容易踩的事实,以及 `--stored` 与跨 17 时留给运维的两条 data migration。 - **语义残余层(AI 的活)**:三类残余(意图选择 / 调用已退役 API 的自定义 代码 / 仓内散文),四个指令源的**实测可达性**表 —— 两张 ADR-0087 登记表 与生成的 upgrade-guide **不在 npm 包内**(`files` 只含 `src/**/*.zod.ts`), 消费侧的投影是 `spec-changes.json` 与链自身的 `--json`; `retiredKey()` 处方经 `json-schema/**` 与 `*.zod.ts` 双通道可 grep。 给出「自己定 vs 问业主」的判据(各三/四条,可检验)。 - **验收层**:typed + parse-gated + `validate` 绿 + 人类可读 diff 报告模板。 实测钉出一条关键更正:`validate` 绿**不等于**链已跑完 —— `field-required-notnull-explicit` 属 migration-chain-only,无墓碑拦截, 故补「从目标 major 重放必须 0 变更」为并列判据。 基线按激活批复钉在 `origin/main@80f7dc6`(17.0.0-rc.5),正文记录读数并给出 刷新规则,rc.5 之后的登记增量作为显式 delta,不推翻已写部分。 目录名 `objectstack-upgrade` 随家族 `objectstack-<domain>` 命名;`domain: process` (同 pm-dispatch,只带 SKILL.md)。仅发布侧,不做 `.claude/` 内部对偶 —— 退役侧对偶 `spec-property-retirement` 作用于本仓 spec,本单作用于客户项目。 packages/spec 唯一改动是 `build-skill-docs.ts` 的 `DISPLAY` 目录登记表: 该表是「列出 skills 的索引」,缺项时 `check:skill-docs` 直接 exit 1。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
CI 的 `check:role-word` 红:剧本的 `os:check` 块用了 1 次 `role`。该处是 `AgentSchema` 自己的**必填键** —— `defineAgent` 缺它编不过(同一处 fixture 也踩过),不是 ADR-0090 D3 所指的 role 概念,属门自身报错文案点名的 「genuine boundary」。同族先例:`skills/objectstack-ai/SKILL.md` 的 `role: 'Help Desk Assistant…'` 已在基线里(5 次)。 按门的处方 `--update` 落基线,delta 精确为一条新条目 (`skills/objectstack-upgrade/SKILL.md: 1`),其余文件计数均未变动 —— 未顺带向下棘轮任何别的文件,以免掩盖其它漂移。 漏检原因记录:首轮 `check:role-word` 在 `git add` 之前跑,而该门与 `check:nul-bytes` 一样只扫 tracked 文件,故新文件不在扫描面内、门形式绿。 本轮全部门均在 staged 状态下重跑。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
hotlong
marked this pull request as ready for review
August 7, 2026 10:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6111
新增发布侧升级剧本
skills/objectstack-upgrade/(domain: process,673 行,随 pm-dispatch 家族只带SKILL.md)。剧本骑在 ADR-0087 D2 conversions 之上,不替代确定性转换,也不新增任何 CLI 命令 / 脚本 / 门。三层如何落地
机械层 —— 只调用,AI 不重实现
剧本把
os migrate meta当作既有底座来说明与编排,并把最容易踩的事实写在最显眼处:conversionId归属的 diff,唯一落盘的是--out的 JSON 快照。AST 改写 TS config 有损(丢注释、看不见来自 import 的值),所以「拿--out当 oracle 去 diff,而不是当成品提交」是剧本的硬要求。applyConversionsToStoredItem在 rehydration 时无条件重放(含includeRetired),所以 ⛔ 从不手改sys_metadata;要落盘则--stored(默认只读预览)/--stored --apply。files-to-references/value-shapes)按名带进报告 —— 「没人被告知的门就没人守」。--apply/--yes/--force/--type/--database-url是 stored-only,误用会被拒收而非忽略;MigrationFloorError的含义也写清了。语义残余层 —— AI 的活
四个指令源,按实测可达性列表(这是本 PR 对报单描述的一处更正):
@objectstack/spec的files白名单只含src/**/*.zod.ts,所以 ADR-0087 的两张登记表源码(src/migrations/registry.ts/src/conversions/registry.ts)与生成的 upgrade-guide 都不在 npm 包内,把客户 agent 指过去会扑空。消费侧真正可达的是:os migrate meta --from 16 --json的.specChangesnode_modules/@objectstack/spec/spec-changes.jsonretiredKey()处方(可 grep)node_modules/@objectstack/spec/json-schema/**与src/**/*.zod.tsnode_modules/@objectstack/spec/CHANGELOG.mdtsc输出残余分三类:R1 意图选择(退役键无无损目标)、R2 自定义代码调用已退役 API、R3 仓内散文。R1 以被退役的
transform为完整示例,给出三个真实去向(导入映射 transform / ETL 步骤 / 删除且承认变换从未发生 —— 第三条常常才是事实)。「自己定 vs 问业主」判据(剧本的核心段):三条全中才自决(处方只有唯一目标 + 证据在客户仓里 grep 得到 + 判错会被门拦下);任一命中即问人(两个以上真实目标且属业务表态 / 改动不触发测试失败却对外可见如鉴权与行可见性 / 能力要重新声明到新位置故选错是静默丢能力 / 客户仓里根本没有可判依据)。并规定问的形式(逐条给站点 + 逐字处方 + 选项代价 + 推荐 + 验证方式),以及等待期间以
AWAITING DECISION挂账、不阻塞机械层。验收层
typed(
tsc,墓碑把退役键定为never)+ parse-gated(墓碑当场拒收并给处方)+os validate绿 + 人类可读 diff 报告模板(五节:机械改动 / 已决残余 / 未决 / 每部署待跑 / 未改动及原因)。实测钉出的一处关键更正:
validate绿 不等于 链已跑完。field-required-notnull-explicit是 migration-chain-only(默认翻转而非改名,loader 故意不自动应用,也没有墓碑拦截),只带这种形状的项目validate全绿而链仍有活。故剧本把**「从目标 major 重放必须报 Nothing to migrate」**升为与validate并列的判据 —— 只看validate的报告对这一整类是盲的。剧本另要求先跑一次升级前validate作为对照,以区分「升级残余」与「项目既有 lint 欠账」。基线读数(
origin/main@80f7dc6= 17.0.0-rc.5)按激活批复钉死,正文单列一节,并给出刷新命令与「读数与安装不符时以安装为准、增量记进报告而非改写钉住的数字」的规则,使 rc.5 之后的登记增量成为显式 delta。
@objectstack/spec版本17.0.0-rc.5(protocol17.0.0)*.zod.ts里的retiredKey()墓碑json-schema/里去重后的[REMOVED]处方RETIRED_KEYS_BY_MAJOR[17](3 条,同一次退役:属性声明一次、被两个.extend()继承,故登记三次):data/ExternalFieldMapping:transformintegration/ConnectorFieldMapping:transformshared/FieldMapping:transformRETIRED_DEFS_BY_MAJOR[17](1 条):shared/FieldMappingTransform剧本同时说明为什么这两张表短而上面的计数不短:两表只记录「在建表后的精确键门下正式登记过的退役」,并非全部退役的回填;问「这次退役有没有被正式登记」查表,问「我要改什么」查 conversions 与墓碑 —— 升级问的是后者。
Walkthrough(真跑,非叙述)
临时目录里构造 v16 形状的客户项目(形状逐条抄自真实
CONVERSIONS_BY_MAJOR[17]的fixture.before;process家族不随附 fixture,故不入库),用packages/cli/bin/run-dev.js实跑。1 · 机械层 ——
os migrate meta --from 16 --step(exit 0)--json读数:applied 7 · todos 29 · schemaValid true,且specChanges.converted = 45/migrated = 29—— 与上面基线表逐字吻合(基线数字由 CLI 实跑复核,非抄源码)。2 · 验收层 —— 升级前 vs 升级后
升级前
os validate(exit 1,5 处 protocol 报错,每条自带处方,节选):把 7 处机械改动 port 进源、两条 R1 残余各作决定后,升级后
os validate(exit 0):os migrate meta --from 17(exit 0):诚实记录两处:(a) 首轮
schemaValid: false,查因是我的 fixture 缺 agent 的label/role/instructions—— 转换 fixture 是逐条转换级的,拼起来不自动构成合法 stack;补齐后为 true,该教训已写进剧本。(b) 本 fixture 未触发两条 data-migration 提示,因为它没有 media / reference / structured-JSON 字段 —— 这正是pendingDataMigrations「只在客户元数据确实声明了该字段类时才提示」的设计,不是缺陷,故未在 PR 里假称跑出了该段。反向验证(方向先声明,再运行)
A. 墓碑真的会开火 —— 预期方向:migrate 之后把退役键放回去,parse 必须失败且报错即处方。实测即上面的
validateexit 1 五连,处方逐字与剧本引用的一致(非 "unrecognized key",非静默)。B.
validate绿 ≠ 链已跑完 —— 预期方向:构造只含required: true、不含任何墓碑键的探针,validate应绿而链仍有活。实测:两个方向都按预声明的方向成立,B 直接改写了剧本的验收判据(见上)。
C. 门自己开了两次火,都是真红:
check:skill-examples首轮红,点名我os:check块里的defineAgent缺三个必填键 —— 与 fixture 踩的是同一个坑。check:role-word红(第二个 commit 修)。漏检原因值得记下来:该门与check:nul-bytes一样只扫 tracked 文件,而我首轮是在git add之前跑的,新文件根本不在扫描面内 —— 门形式绿、实则没看过我的文件。第二轮起全部门均在 staged 状态下重跑。两次都说明这些门对新 skill 是真的在跑,不是形式绿。
门读数(第二个 commit,全部 staged 状态下重跑)
check:nul-bytesgrep -naP无命中check:doc-authoringcheck:role-wordskills/objectstack-upgrade/SKILL.md: 1,见下)check:skill-frame-synccheck:skill-docsskills/README.md+content/docs/ai/skills-reference.mdx已重生成)check:skill-refsprocessskill 无需SKILL_MAP条目)check:skill-examplescheck:upgrade-guide/check:spec-changes/check:generatedcheck:published-files/check:adr-anchors/check:docs-audit-scope298f802)role-word基线的一条新增,及为什么它是对的剧本的
os:check块用了 1 次role:role: 'Front-line support triage'。该处是AgentSchema自己的必填键 ——defineAgent缺它编不过(check:skill-examples已经替我证明过一次),不是 ADR-0090 D3 所指的 role 概念,属门自身报错文案点名的「genuine boundary」。同族先例:skills/objectstack-ai/SKILL.md的role: 'Help Desk Assistant…'早已在基线里(5 次)。按门的处方--update落基线,delta 精确为一条新条目,其余文件计数均未变动 —— 未顺带向下棘轮任何别的文件,以免掩盖其它漂移。范围与一处必要的例外
packages/spec的唯一改动是scripts/build-skill-docs.ts里DISPLAY目录登记表加一行。该表就是派发词允许的「列出 skills 的索引/登记文件」:脚本显式做 catalog ⇄ DISPLAY 双向对账,新 skill 目录没有 DISPLAY 条目时check:skill-docs直接exit 1(✗ SKILL.md without DISPLAY entry),不改则门必红。未触碰 spec schema、退役登记表、content/docs/releases/、digest/console 脚本,也未改任何既有 skill。未做
.claude/内部对偶(派发词的默认):退役侧对偶spec-property-retirement作用于本仓 spec 故内部;本单作用于客户项目,受众是第三方 agent,只应发布。changeset:走 route 2 ——
skills/不经 npm 包发布,本 PR 无 user-visible 的包变更,故打skip-changeset标签,不写空 changeset。标签实测被 Auto Label 覆盖过一次(#5533 那个 race),已按并集重写并读回确认:documentation/size/l/tooling/skip-changeset;Check Changeset现为 skipped。