关于 rs-update
rs-update 用于升级 RootSpec 项目并对齐当前框架规则,面向项目维护者。它提供交互式审查流程,也支持事务式 CI 模式。
主要功能
- 01分析框架版本差距
- 02交互式制定升级计划
- 03事务式 CI 升级
- 04更新前置条件与配置
- 05协调框架规则
适用场景
- 01应用升级前审查 RootSpec 项目
- 02在 CI 流水线中自动执行安全升级
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rootspec/ rootspec --skill rs-update安装后在 Claude Code 中输入/rs-update确认已加载。
附带文件UPDATE.md
详细介绍
你正在将一个 RootSpec 项目升级到最新框架版本。首先,告诉开发者你接下来要做什么:
“我会检查你的项目是否符合最新框架版本,并升级所需内容——框架文件、前置条件和配置。”
模式
此技能以以下两种模式之一运行:
- 交互模式(默认)——展示计划,等待确认,并与开发者逐项处理违规情况。适用于有人参与的运行场景。
- CI 模式——无需提示,自动应用计划;针对每项违规采用“推荐”路径;整个运行过程具有事务性(所有预检均通过,或者不修改任何文件)。适用于 CI 流水线中的
claude -p调用、计划重建或任何无人值守场景。
可通过以下任一信号检测 CI 模式:
if [[ "${CI_FOCUS:-}" == "ci" || -n "${ROOTSPEC_CI:-}" || "${CI:-}" == "true" ]]; then
CI_MODE="true"
else
CI_MODE="false"
fi
(此处的 CI_FOCUS 表示将字面字符串 ci 作为此技能的 focus 参数传入——/rs-update ci。)大多数 CI 运行器会自动设置 CI=true;希望在本地使用 CI 模式的用户可以导出 ROOTSPEC_CI=1。
当 CI_MODE=true 时,请遵循本文中的 CI 行为说明,并跳过所有确认提示。否则,请按所述交互流程操作。
第 1 步:检测差异
运行扫描和差异分析脚本:
bash "$(dirname "$0")/../rs-shared/scripts/scan-spec.sh" .
bash "$(dirname "$0")/../rs-shared/scripts/gap-analysis.sh" .
如果这些路径无法解析,请在技能目录中搜索 gap-analysis.sh 来查找脚本。
根据输出:
GAP=no_project——未找到.rootspec.json。告知开发者:“未找到 RootSpec 项目。请先运行/rs-init。”然后退出。GAP=none——已使用最新框架版本。如果RULE_VIOLATIONS为空,告知开发者:“项目使用的是版本 X.Y.Z,且符合当前框架规则。无需更新。”然后退出。如果RULE_VIOLATIONS不为空,则项目早于一项或多项框架规则——跳过第 2 至第 4 步,转到第 5 步(协调框架规则)。GAP=patch|minor|major——需要升级。继续。(升级后仍需执行第 5 步中的协调。)
第 2 步:展示变更内容
阅读更新指南:
skills/rs-update/UPDATE.md
相对于此技能文件查找指南:"$(dirname "$0")/UPDATE.md"
提取 PROJECT_VERSION(不含)和 BUNDLED_VERSION(含)之间的所有版本章节。展示:
- 版本差异(例如,“6.2.1 → 6.2.3”)
- 各版本变更摘要
- 任何手动操作说明
- 是否存在破坏性变更
第 3 步:展示升级计划
根据差异分析输出,展示清晰的计划:
Upgrade plan (6.2.1 → 6.2.3):
Will update:
- rootspec/00.FRAMEWORK.md (replace with vX.Y.Z)
- rootspec/00.AXIOMS.md (replace with latest)
- .rootspec.json version field
Will create (new prerequisites):
- [list from NEW_PREREQUISITES, if any]
Needs reconciliation (changed prerequisites):
- [list from CHANGED_PREREQUISITES, if any]
Will NOT touch:
- Spec files (01.PHILOSOPHY.md through 05.IMPLEMENTATION/)
- CONVENTIONS/ (owned by /rs-impl)
[If HAS_BREAKING=true]
⚠ Breaking changes detected — see details above.
**交互模式:**等待开发者确认。开发者可以排除部分项目(例如,“跳过 dev.sh”)。
CI 模式:跳过确认。在应用任何内容之前,先运行下文所述的事务性预检。如果预检失败,则使用结构化报告中止整个运行;否则应用完整计划。
事务性预检(仅限 CI)
写入任何文件之前,验证每项计划操作。如果任何检查失败,则输出中止报告(步骤 6 的 CI 格式)并以 1 退出——不执行任何部分操作。
对于 RULE_VIOLATIONS 中的每个条目,运行其预检:
| 违规项 | 预检 | 失败时 → |
|---|---|---|
baseUrl_has_path | 对所有 visit: 引用进行分类;相对路径(不以 / 开头)需要人工判断 | 中止 |
testmode_implicit_dev | 无——自动选择“保留当前行为” | 通过 |
previewServer_missing | 无——只会增加内容 | 通过 |
legacy_body_ready | cypress/support/app-ready.ts 存在,且不包含 'cy.appReady() is not implemented',并且 check-app-ready.sh 对其检查通过 | 中止 |
shallow_app_ready | 无——框架规则禁止自动重写 | 中止 |
dev_cmd_recursive | detect-stack.sh 返回非空的 DEV_CMD(如果 preview 也递归,则还需返回 PREVIEW_CMD) | 未检测到时中止 |
viewport_missing | 无——文件编辑具有幂等性;绝不删除测试文件 | 通过 |
中止消息必须包含违规项标记、检查内容、失败原因,以及开发者在重新运行前需要执行的操作。具体错误模板:
-
legacy_body_ready预检失败:无法应用 legacy_body_ready 迁移:cy.appReady() 尚未实现 (或实现过于浅显)。请按照 CONVENTIONS/technical.md → App Readiness 的要求实现 cypress/support/app-ready.ts,然后重新运行 /rs-update ci。
将 safeVisit 连接到会抛出错误的 cy.appReady() 属于已知的错误状态, 我们拒绝发布这种状态。
-
shallow_app_ready预检失败:逐字转达check-app-ready.sh的错误,并说明 CI 模式不会自动重写——请按照CONVENTIONS/technical.md→ App Readiness 的要求实现,然后重新运行。 -
dev_cmd_recursive预检失败(未检测到框架):无法协调 dev_cmd_recursive:detect-stack.sh 未找到任何框架 (没有 astro/vite/next/nuxt 等配置或 devDependency)。请在 scripts/dev.sh / scripts/preview.sh 中手动设置 DEV_CMD 和 PREVIEW_CMD,然后重新运行。
如果任何预检失败:不写入任何内容,输出中止报告,并以 1 退出。
步骤 4:执行升级
对于已确认计划中的每个项目:
框架文件
00.FRAMEWORK.md— 从../rs-shared/00.FRAMEWORK.md(相对于此技能)读取,并写入{specDirectory}/00.FRAMEWORK.md。00.AXIOMS.md— 从../rs-shared/00.AXIOMS.md读取,并写入{specDirectory}/00.AXIOMS.md。
配置
.rootspec.json— 读取当前文件,仅将version字段更新为 BUNDLED_VERSION。保留所有其他字段(specDirectory、prerequisites)。
新增前置条件
- 对于
NEW_PREREQUISITES中的每个条目,检查项目中是否已存在对应文件/配置。如果不存在,则使用与/rs-init相同的逻辑创建。读取../rs-shared/fragments/prerequisites.md,了解创建说明。
已变更的前置条件
-
对于
CHANGED_PREREQUISITES中的每个条目:该前置条件已存在,但要求已更新。向开发者说明发生了什么变化以及原因(来自 UPDATE.md)。不要自动覆盖——开发者的副本可能包含项目特定的自定义内容。对于模板脚本(例如
scripts/dev.sh):显示项目副本与捆绑模板之间的差异。由开发者决定如何协调。对于配置接线(例如
cypress.config.ts):说明所需变更,并让开发者确认后再应用。
规格状态
- 运行
write-spec-status.sh,更新 spec-status.json 中的版本:
bash "$(dirname "$0")/../rs-shared/scripts/write-spec-status.sh" {specDirectory}
如果检测到重大变更(HAS_BREAKING=true):将 false 作为第二个参数传入,以将规格标记为无效,并告知开发者重新运行 /rs-spec。
bash "$(dirname "$0")/../rs-shared/scripts/write-spec-status.sh" {specDirectory} false
步骤 5:协调框架规则
当 gap-analysis 中的 RULE_VIOLATIONS 非空时运行。每个标记都表示某个项目状态早于一项框架规则。
交互模式: 对每个违规项,展示修复方案并在应用前征求确认——这些操作都不会自动重写用户代码。CI: 根据下表中的策略自动应用推荐方案;每项都已在步骤 3 的预检中验证。
CI 策略摘要
| 违规项 | CI 行为 |
|---|---|
baseUrl_has_path | 自动从 baseUrl 中移除路径;自动为未添加前缀的访问引用添加前缀(相对引用已在预检中中止)。 |
testmode_implicit_dev | 自动选择 “保留当前行为” —— 添加 "testMode": "dev"。此方案较为保守,因为切换到 preview 可能导致行为意外改变。之后如有需要,可由人工切换。 |
previewServer_missing | 自动添加 "previewServer": "./scripts/preview.sh";如果模板缺失,则复制随附的模板。 |
legacy_body_ready | 始终执行完整迁移。 预检已确认 cy.appReady() 已实现。 |
shallow_app_ready | 绝不自动重写 —— 预检已中止运行。(此行仅作提醒;存在此违规项时,CI 永远不会进入 Apply 阶段。) |
dev_cmd_recursive | 自动通过 detect-stack.sh 填充 DEV_CMD/PREVIEW_CMD,并用随附模板覆盖包装脚本(以确保发布递归防护)。预检已确认检测成功。 |
viewport_missing | 自动扩展 cypress/support/schema.ts 和 steps.ts,加入 setViewport 和几何断言(可幂等执行);如果 CONVENTIONS/technical.md 中缺少 Test Viewports 章节,则自动添加。绝不删除现有的 .cy.ts 文件;输出 WARN=cypress_e2e_needs_regen,以便开发者知道需要重新生成。 |
baseUrl_has_path
项目的 cypress.config.ts 中包含 baseUrl: 'http://host:port/some/path'。框架现在要求 baseUrl 仅包含主机和端口;部署路径应放在 visit: 目标和 cy.visit() 调用中。两者拼接后会产生类似 /some/path/some/path/ 的 404 错误。
向开发者展示:
- 当前的 baseUrl 行,以及需要移除的路径(例如
/some/path)。 - 在
cypress/和rootspec/05.IMPLEMENTATION/USER_STORIES/中搜索visit:和cy.visit(引用的结果。 - 对访问引用进行分类:
- 已添加前缀(以该路径开头)——保持不变;移除 baseUrl 中的路径后即可正常工作。
- 未添加前缀(以
/开头,但不是以该路径开头)——询问是否要在前面添加该路径。 - 相对路径(不以
/开头)——标记为需要人工检查;相对路径在新旧两种约定下都会失效。
交互模式: 仅应用开发者确认的修改。完成后:重新运行 /rs-validate 进行确认。
CI: 自动移除路径;自动为未添加前缀的访问引用添加路径前缀;相对引用已在预检中中止。
testmode_implicit_dev
项目的 scripts/test.sh 会启动开发服务器,但 .rootspec.json 中没有 prerequisites.testMode。旧项目会默认隐式使用开发模式进行端到端测试;框架现在默认使用预览模式(构建产物 + 预览服务器),以减少不稳定因素。
提供两种方案:
- 保留当前行为 — 在
.rootspec.json的prerequisites中添加"testMode": "dev"。无需其他更改。项目会按开发者的明确选择继续以开发模式运行端到端测试。 - 切换到预览模式默认配置(推荐) — 从捆绑包中复制
scripts/preview.sh(参见下文的previewServer_missing),将scripts/test.sh重写为支持testMode的模板(参见bootstrap-init.sh),并在.rootspec.json中添加"testMode": "preview"和"previewServer": "./scripts/preview.sh"。提交前验证npm run build可以正常运行。
如果开发者选择“切换”,请提醒他们项目可能需要实现应用就绪检测(参见 framework-rules.md 中的 App Readiness)——预览模式会暴露开发模式下被掩盖的水合时序问题。
**CI:**自动选择“保留当前行为”(添加 "testMode": "dev")。切换到预览模式可能会改变运行时行为;CI 的任务是安全升级,而不是更改行为默认值。之后人工可以再切换。
legacy_body_ready
项目的 cypress/support/steps.ts 会在每次访问后等待 <body data-ready="true">(7.6.0 之前的约定)。框架现在要求使用 cy.appReady()——由项目在 cypress/support/app-ready.ts 中定义的就绪检测。页面级就绪检测无法观察各个岛屿的水合情况;对于包含异步岛屿的技术栈,测试可能在页面就绪后通过,然后却点击到尚不可交互的 DOM。
提供两种方案:
- 仅添加存根(最小改动) — 使用会抛出错误的默认实现生成
cypress/support/app-ready.ts,并在cypress/support/e2e.ts中添加import './app-ready';。暂时保留steps.ts不变。开发者之后可以按故事逐步采用cy.appReady()。现有测试会继续使用页面级等待。 - 完整迁移(推荐) — 执行与“仅添加存根”相同的操作,并重写
steps.ts中的safeVisit:将cy.get('body').should('have.attr', 'data-ready', 'true')这一行替换为cy.appReady()。将awaitReady步骤添加到runSetupSteps和架构定义中。随后,开发者必须实现cy.appReady()(对于静态站点,可使用一行空操作实现;对于水合过程较多的站点,则需要进行实际检查)。现有设置了body[data-ready]的页面不受影响——可以继续这样做,也可以停止这样做,框架不再依赖它。
如果选择完整迁移,请提醒开发者,第一次运行 npx cypress run 会因“未实现 cy.appReady()”而失败——这是约定开始生效,并非回归问题。
**CI:**始终执行完整迁移。预检已确认 cy.appReady() 已实现(不是会抛出错误的存根,也未在延迟执行边界处进行浅层检查)。将 safeVisit 接入会抛出错误的存根是已知的错误状态,预检会拒绝该状态,而不会悄然发布。
shallow_app_ready
项目的 cypress/support/app-ready.ts 会在文档级别信号(document.readyState、body 存在、cy.wrap(true) 等)出现时解析,但项目挂载了延迟执行边界——也就是交互代码会在初始文档到达 之后 才运行的组件或模块(Astro client:*、RSC 'use client'、React.lazy/Suspense、Next.js dynamic()、Vue defineAsyncComponent 等)。浅层检查会在这些边界可交互之前触发;测试时而通过,时而在首次点击尚未完成水合的组件时失败。这与 7.6.0 合约原本要防止的问题相同,只是发生在更底层。
向开发者展示:
- 当前
cypress/support/app-ready.ts的内容,以及它匹配的浅层模式。 - 在项目中找到的延迟执行标记(文件路径 + 匹配到的指令/导入)。
- 所需修复:重写
cy.appReady(),使其等待这些边界完全激活时发出的信号(框架或应用在水合后设置的全局变量、已知节点上的属性、轮询组件状态、事件等)。在CONVENTIONS/technical.md→ App Readiness 中记录所选机制,包括:(a) 第 2 步中列出的边界;(b) 每个边界激活时发出的信号。
不要自动重写 app-ready.ts——具体选择取决于应用实际使用的水合机制。先引导开发者完成 conventions 章节,再据此实现。
如果项目还缺少 scripts/check-app-ready.sh,请从随附的 rs-shared/scripts/ 中复制,以便今后的测试运行都受到相同规则的检查。重写后,运行 ./scripts/check-app-ready.sh . 确认检查通过。
**CI:**绝不自动重写。预检会以原样输出 check-app-ready.sh 的错误,并中止整个运行。按照 CONVENTIONS/technical.md → App Readiness 实现后,重新运行。
dev_cmd_recursive
项目的 scripts/dev.sh 和/或 scripts/preview.sh 将 DEV_CMD/PREVIEW_CMD 设置成递归模式(npm run dev、./scripts/dev.sh 等效命令),而且 package.json 通过包装脚本调用其 dev/preview 脚本。一旦有人输入 npm run dev,就会陷入无限循环。有些项目中用于掩盖此问题的绕过方式(package.json 直接调用框架二进制文件,跳过包装脚本)会破坏包装脚本对端口的处理和单实例保证。
运行 detect-stack.sh 以确定框架二进制命令:
eval "$(bash "$(dirname "$0")/../rs-shared/scripts/detect-stack.sh" .)"
echo "Detected: $STACK"
echo "DEV_CMD=$DEV_CMD"
echo "PREVIEW_CMD=$PREVIEW_CMD"
detect-stack.sh 会优先使用 .rootspec.json prerequisites.detected 中捕获的原始配置(项目引导前的 package.json 脚本),然后回退到检查框架配置文件是否存在,最后检查 package.json 中的 devDependency。
**交互模式:**向开发者展示检测到的 DEV_CMD/PREVIEW_CMD 值并征求确认。开发者可能希望修改这些值(例如,添加 --host 0.0.0.0 或更改端口)。仅应用开发者确认的修改。如果 STACK=unknown,请引导开发者手动识别框架二进制文件。
**CI:**如果 DEV_CMD 非空,则自动应用(预检已完成验证)。如果 STACK=unknown,则中止运行(预检失败)。
应用阶段:
- 使用
../rs-shared/scripts/dev.sh中随附的模板覆盖scripts/dev.sh(此模板包含新的递归保护)。 - 使用
../rs-shared/scripts/preview.sh中随附的模板覆盖scripts/preview.sh。 - 将包装脚本中的空行
DEV_CMD=""替换为DEV_CMD="<detected command>"。对PREVIEW_CMD=""执行相同操作。 - 确认
package.json中的dev/preview脚本通过包装脚本运行(触发此违规时,它们应该已经如此)。如果不是,则重写这些脚本。 - 在
CONVENTIONS/technical.md→ Dev Server 中记录所选命令。
viewport_missing
项目至少有一个标记为 @journey: MOBILE_* 或 @journey: TABLET_* 的用户故事,已生成 cypress/e2e/*.cy.ts 文件,并且这些测试文件中都不包含 cy.viewport(。7.8.0 之前的生成器不支持视口感知——无论用户旅程如何,这些测试都会以 Cypress 的 1000×660 默认视口运行。“在 375px 宽度下没有水平滚动”的验收标准在桌面宽度的视口下会空通过。
应用阶段(交互模式和 CI 模式相同):
-
扩展
cypress/support/schema.ts— 将setViewport添加到 Step 联合类型中(如果缺少shouldHaveNoOverflowX/shouldFitViewport,也一并添加)。此操作幂等;跳过已存在的行。 -
扩展
cypress/support/steps.ts— 在runSetupSteps中添加else if ('setViewport' in s) cy.viewport(s.setViewport.width, s.setViewport.height);;在runAssertionSteps中添加几何断言分支。此操作幂等。 -
将 Test Viewports 添加到
CONVENTIONS/technical.md— 如果缺少该部分,则在 Testing 下追加,并使用../rs-shared/viewport-defaults.json中的框架默认值:### Test Viewports - **MOBILE_JOURNEY:** 375x667 - **TABLET_JOURNEY:** 768x1024 -
告知开发者,现有的
cypress/e2e/*.cy.ts文件是在支持视口感知之前生成的,需要重新生成:面向移动端/平板端用户旅程故事的现有测试是在支持视口感知之前生成的。请删除受影响的
cypress/e2e/*.cy.ts文件,并重新运行/rs-impl,以便注入setViewport后重新生成。(我们不会自动删除用户拥有的测试文件。)
交互模式: 确认后再编辑每个文件;最后询问是否重新生成测试文件。
CI 模式: 自动应用步骤 1–3(幂等文件编辑,属于框架自有机制),跳过重新生成步骤(CI 不会删除用户文件),并在结构化报告中包含 WARN=cypress_e2e_needs_regen,以便下一次由开发者主导的运行处理此问题。
previewServer_missing
项目的 .rootspec.json 缺少 previewServer 条目。如果正在通过“切换”路径处理 testmode_implicit_dev,则在同一步中修复此问题。否则,添加指向 ./scripts/preview.sh 的条目(如果缺少,则复制捆绑的模板),以记录此先决条件——即使项目仍使用 dev 模式,也能从可用的预览基础设施中受益。
报告
对于每个违规项,在第 6 步的报告中说明已协调的内容(或应开发者要求跳过的内容)。
第 6 步:报告
交互模式
总结已完成的工作:
Update complete (6.2.1 → 6.2.3).
Updated:
- rootspec/00.FRAMEWORK.md
- rootspec/00.AXIOMS.md
- .rootspec.json version
Created:
- [any new prerequisites]
Flagged for review:
- [any changed prerequisites with reconciliation notes]
Next steps:
- [Re-run /rs-impl to update conventions if needed]
- [Any manual instructions from UPDATE.md]
CI 模式
输出结构化的 key=value 报告,以便 CI 脚本进行检索:
RESULT=success
APPLIED=baseUrl_has_path,previewServer_missing,legacy_body_ready
SKIPPED=
ABORTED_ON=
WARN=
EXIT=0
当 viewport_missing 位于 APPLIED 中时,还要输出 WARN=cypress_e2e_needs_regen,以便 CI 脚本得知这些测试文件需要由下一次开发者主导的 /rs-impl 删除并重新生成。多个警告以逗号分隔。
中止时:
RESULT=aborted
APPLIED=
SKIPPED=
ABORTED_ON=shallow_app_ready
EXIT=1
字段含义:
RESULT—success(所有违规项均已解决或不存在)或aborted(一个或多个预检失败;未应用任何更改——事务性约定)。APPLIED— 本次运行中已协调的违规项标记,以逗号分隔。SKIPPED— 因开发者(在交互模式下)拒绝而未应用的标记;在 CI 中始终为空。ABORTED_ON— 预检失败的违规项标记,以逗号分隔。成功时为空。EXIT—0表示成功,1表示中止。技能以此代码退出。
将报告输出到标准输出,并在前后换行,以便 CI 通过管道将其传给解析器。
重点
如果开发者传入参数:
"ci"→ 激活 CI 模式(参见 Mode 部分):无需确认,针对每个违规项采用建议路径,执行事务性预检,并输出结构化 key=value 报告。等同于设置ROOTSPEC_CI=1。- 版本号(例如
"6.2.2")→ 仅显示该特定版本的变更 "check"→ 仅运行差距分析,不执行任何更改(试运行)"prerequisites"→ 仅处理先决条件更新(跳过框架文件)
范围
- 可以读取: 所有项目文件、所有捆绑的技能文件
- 可以写入:
{specDirectory}/00.FRAMEWORK.md、{specDirectory}/00.AXIOMS.md、{specDirectory}/spec-status.json、.rootspec.json、新的前置文件 - 不应写入: 规范文件(01-05)、应用程序源代码、现有测试文件
- 不应覆盖: 未经开发者确认的现有前置文件