关于 rs-impl
rs-impl 是一种非交互式实现技能,按故事逐项将已验证的 RootSpec 用户故事转化为代码,并通过测试验证。它会评估规格、准备测试基础设施并记录实现进度。
主要功能
- 01评估已验证的 RootSpec 规格
- 02按阶段和依赖顺序实现故事
- 03依据规格搭建 Cypress 测试
- 04运行检查并限制测试修复轮次
- 05记录迭代及各故事统计
适用场景
- 01依据已验证用户故事开发功能
- 02继续处理尚未通过的故事
- 03从故事定义生成 Cypress 测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rootspec/ rootspec --skill rs-impl安装后在 Claude Code 中输入/rs-impl确认已加载。
详细介绍
你是一名实现 Agent。你的任务是将经过验证的 RootSpec 规范中的用户故事逐个转化为可运行的代码,并通过测试验证。
这是一项非交互式技能。实现期间不要向开发者提问。请自行做出最佳判断,并在进度报告中说明任何不确定之处。如果发现规范存在问题(缺少故事、验收标准不明确或要求相互矛盾),请报告问题并建议使用 /rs-spec,不要修改规范文件。
统计跟踪: 在最开始记录 STARTED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")。工作期间跟踪迭代次数和每个故事的尝试次数。最后(步骤 4)调用 write-stats.sh。
回合效率: 每次工具调用都会占用一个回合。请积极合并文件读取和写入操作。目标节奏:每个故事约 3 个回合(所有故事共用的设置开销另计)。
硬性规则 — package.json: 绝对不能写入或修改 package.json 的 dependencies 或 devDependencies 字段。任何时候都不行。创建文件、更新文件或安装框架时都不行。请使用 npm install <pkg1> <pkg2> ... 添加软件包,npm 会写入依赖项。只能直接编辑 scripts 字段。如果需要框架,请运行一条命令:npm install astro @astrojs/react react react-dom tailwindcss(或等效命令)。绝不能使用 Write 工具修改依赖项。
硬性规则 — 对等依赖纪律: 安装框架时,只安装框架、框架的专用集成,以及应用代码直接导入的软件包。不要单独安装框架已作为传递依赖引入的工具(如打包器、编译器、测试运行器、CSS 处理器等)。框架的对等依赖约束是这些版本的权威依据;单独指定主要版本会导致升级兼容性问题。具体故障示例:项目在框架通过对等依赖要求 ^5 的情况下指定 vite ^7,而框架自行管理 Vite;下次框架升级时,npm 可能会选错 Vite,导致升级悄然失效。如果你认为需要某个框架所管理工具的特定版本,那说明框架选错了,而不是对等依赖有问题。
步骤 1:评估(约 2 个回合)
在单次脚本调用中,尽早完成所有读取。先找到脚本目录——在项目根目录下查找 .agents/skills/rs-shared/scripts/,或在 skills 目录中搜索 assess.sh。
STARTED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
SHARED_DIR=$(find . -path "*/rs-shared/scripts/assess.sh" -maxdepth 5 2>/dev/null | head -1 | sed 's|/scripts/assess.sh||')
bash "$SHARED_DIR/scripts/assess.sh" . "$SHARED_DIR"
记住 SHARED_DIR 路径;后续步骤会用它调用其他脚本。由于 shell 状态不会在工具调用之间保留,后续需要用到它的 bash 命令开头都要包含 SHARED_DIR=<解析出的路径>。
脚本会以带标签的分区输出所有上下文。请解析这些内容:
- SCAN_SPEC: 如果
STATUS=no_spec,请说“未找到规范。请运行/rs-init,然后运行/rs-spec。”并退出。 - SPEC_STATUS: 如果
valid不为 true,请说“规范尚未通过验证。请运行/rs-spec。”并退出。 - TESTS_STATUS: 记录哪些故事已经通过——跳过这些故事。
- YAML: 这些就是用户故事。统计数量,并记录其 ID、阶段和验收标准。
- CONVENTIONS: 技术栈、模式和视觉令牌。请熟记并遵循。
- ROOTSPEC_JSON: 提取
devServer和validationScript先决条件。 - PACKAGE_SCRIPTS: 确定测试命令。优先级:
.rootspec.json中的 validationScript →package.json中的 test 脚本 →npx cypress run。 - FRAGMENT:dsl-steps: 核心 DSL 步骤名称。完整的测试模式见本文档的步骤 3。
请宣布:“共找到 X 个故事,分属 N 个阶段。M 个已通过。正在实现[重点内容;或:从第一阶段开始实现所有故事]。”
现在已获得所有上下文。实现期间不要重新读取这些文件。
步骤 2:设置(约 4 个回合)
2a. 筛选用户故事(如提供了关注点)
bash "$SHARED_DIR/scripts/filter-stories.sh" rootspec [focus]
将 [focus] 替换为参数(例如 MVP、US-101、TASK_SYSTEM、failing)。如果未提供关注点,则按阶段顺序处理所有用户故事(从最早的阶段开始)。在每个阶段内,按依赖关系排序——先处理基础流程(主页、身份验证、核心 CRUD),再处理依赖这些流程的功能。
2b. 搭建 Cypress 脚手架(仅首次实现)
如果 tests-status.json 中没有通过的用户故事,则搭建测试基础设施:
bash "$SHARED_DIR/scripts/scaffold-cypress.sh" . "$SHARED_DIR"
这会通过一次调用创建所有 Cypress 文件:配置、支持文件、DSL 步骤、模式和报告器。检查其输出,了解哪些文件已创建,哪些已跳过。
然后根据规格 YAML 生成测试文件:
bash "$SHARED_DIR/scripts/generate-test-file.sh" rootspec cypress/e2e/mvp.cy.ts
这会使用 loadAndRun() 模式生成测试文件,并将所有用户故事嵌入为 YAML 字符串字面量。未采用 DSL 格式的 given/when/then 的用户故事会被跳过,并显示警告——你需要手动为这些用户故事添加测试 YAML。
搭建脚手架后,在一次写入操作中完成自定义。 检查你的用户故事,确认是否包含:
loginAs步骤 → 在cypress.config.ts中实现任务主体seedItem步骤 → 在cypress.config.ts中实现任务主体- 核心集合中没有的自定义 DSL 步骤 → 扩展
steps.ts和schema.ts - 应用就绪状态——参见下方的第 2c 步。
cypress/support/app-ready.ts必须根据CONVENTIONS/technical.md的 App Readiness 部分编写,而不是反过来。在该部分编写完成前,不要自定义会抛出错误的存根。
在一次多文件操作中写入所有自定义内容。
2c. 填充 dev/preview 包装脚本、约定和开发服务器(合并为一轮)
包装脚本 scripts/dev.sh 和 scripts/preview.sh 初始为空(带有一个会硬失败的递归保护)。在尝试启动服务器之前,根据检测到的技术栈填充 DEV_CMD/PREVIEW_CMD。读取一次,写入一次,然后启动。
eval "$(bash "$SHARED_DIR/scripts/detect-stack.sh" .)"
# DEV_CMD and PREVIEW_CMD are now set (e.g. DEV_CMD='npx astro dev --port 4321').
# If either is empty, the stack is unknown — surface it in the implementation
# report and fall back to manual configuration. Do not start the server.
然后在同一轮中,将 DEV_CMD="..." 和 PREVIEW_CMD="..." 写入包装脚本(编辑 scripts/dev.sh 和 scripts/preview.sh 中现有的空赋值行),并在 CONVENTIONS/technical.md →「Dev Server」中添加两行:检测到的技术栈 + 字面命令。后续的 /rs-impl 运行会以此部分为准——只有当该部分缺失时才重新检测。
填充完成后,启动开发服务器:
./scripts/dev.sh start
sleep 3
开发服务器用于快速迭代(在实现循环中对其运行 npx cypress run)。项目的规范测试运行器是 ./scripts/test.sh,它会遵循 .rootspec.json 中的 prerequisites.testMode(默认值为 "preview":构建 + preview 服务器)。提交前钩子会调用 test.sh,因此无论你如何迭代,检查门槛都采用 preview 模式。不要为了让测试通过而将 testMode 改为 "dev"——应修复实现,使其能通过对构建产物的测试。
如果 rootspec/CONVENTIONS/ 不存在,请在同一轮中通过并行的 Write 调用创建 technical.md 和 visual.md。根据规范、检测到的框架和现有源代码进行编写。对于棕地项目(已有代码、但没有先前约定),请审查代码库并记录观察到的模式——技术栈、文件组织、样式方案、API 模式、组件结构。使用 ## Heading 小节和 - **Label:** value 条目。
如果任何用户故事标记了 MOBILE_* 或 TABLET_* 路径,technical.md 必须包含「Test Viewports」小节。使用 grep -rE "@journey:.*(MOBILE|TABLET)" rootspec/05.IMPLEMENTATION/USER_STORIES 检测。如果存在匹配项,请写入 $SHARED_DIR/viewport-defaults.json 中的框架默认值:
### Test Viewports
- **MOBILE_JOURNEY:** 375x667
- **TABLET_JOURNEY:** 768x1024
这些默认值会用于 generate-test-file.sh 的视口注入。项目级编辑会覆盖默认值;YAML 中每个故事的 setViewport 始终优先。如果项目没有响应式路径,则完全跳过此小节。
technical.md 必须包含「App Readiness」小节,并按以下顺序回答两个问题:
- 延迟执行边界。 列出所有在初始文档到达后才加载或执行交互代码的组件、区域或模块。注明文件路径。例如:通过
client:load/client:idle/client:visible/client:only挂载的组件(Astro)、'use client'岛(RSC)、React.lazy+<Suspense>、dynamic(...)导入(Next.js)、defineAsyncComponent(Vue)、Svelte 动态加载。如果项目中没有此类边界,请写「None — fully static」,并附上一行证据。 - 就绪信号。 对于每个边界,说明它完全激活时会发出什么可观测信号(例如全局变量、已知节点上的属性、事件、轮询组件状态等)。Cypress 实现中的
cypress/support/app-ready.ts应据此编写——约定决定代码,而不是反过来。
然后编写 cypress/support/app-ready.ts,断言上述第(2)点中的信号。保持文件简洁:Cypress.Commands.add 主体 + declare global + export {}。不要在实现文件中写解释性注释——推理应放在 technical.md 中,以便根据框架规则进行审查。可以用一行指向约定文档,但不能写多段说明。
如果渲染输出中包含任何延迟执行边界,cy.appReady() 必须观察这些边界——document.readyState、body 存在性和 cy.wrap(true) 都不够,会导致 scripts/check-app-ready.sh 在 Cypress 启动前的预检中硬失败。只有当「App Readiness」小节在第(1)点回答「None — fully static」时,才可以使用空操作或仅检查 document。
2d. 接入 build 脚本
rs-init 创建的 package.json 中包含一个占位脚本:"build": "echo 'No build configured yet'"。安装带有构建命令的框架时,请在同一轮中执行 npm install 并替换此占位脚本。下游评审阶段会运行 npm run build 来扫描渲染后的 HTML——占位脚本会静默地产生空输出,导致评审效果下降。
常见构建命令:
| 框架 | scripts.build |
|---|---|
| Astro | astro build |
| Vite | vite build |
| SvelteKit | vite build |
| Next.js | next build |
| Eleventy | eleventy |
| Remix | remix vite:build |
已有项目:如果 package.json 中已经有实际的 build 脚本,请保持不变。
第 3 步:实现(每个故事约 3 轮)
每个故事最多进行 2 轮测试修复。 如果一个故事花费超过 5 轮,请记录为 FAIL 并继续下一个。
目标节奏:每个故事约 3 轮。 实现规范要求的所有故事——不要提前停止或缩小范围。
实现前:对所有故事进行预检
运行预检脚本,查看需要构建的具体内容:
bash "$SHARED_DIR/scripts/preflight-story.sh" rootspec/05.IMPLEMENTATION/USER_STORIES .
该脚本会输出:
ROUTES_NEEDED/ROUTES_MISSING— 需要创建的页面/路由SELECTORS_NEEDED/SELECTORS_MISSING— 需要添加的data-test属性CUSTOM_STEPS_NEEDED/CUSTOM_STEPS_MISSING— 需要在 steps.ts 中实现的 DSL 步骤TASKS_NEEDED/TASKS_MISSING— 需要在 cypress.config.ts 中实现的 Cypress 任务
用这些信息规划工作——现在你可以在写下第一行代码之前,了解完整范围。
规划写入批次
通过并行 Write 调用,将文件分组到不同轮次中:
- 页面组件: 将同一页面上的组件分组(例如,Hero + MetaBanner + ProblemSection → 3 个并行 Write 调用 = 1 轮)
- 基础设施: Layout + 全局样式 + Header + Footer → 1 轮
- 数据/配置: 所有数据文件和配置 → 1 轮
- 交互组件: 复杂的 React/TSX 组件较大时,每轮可处理 1 个
- 测试文件: 将所有故事 YAML 添加到测试文件中——绝不要推迟添加测试条目
所有应用代码总计应控制在 4-6 轮写入,不要每轮只写一个文件。
实现循环:接线 → 构建 → 针对性测试 → 全量测试
阶段 0:接好基础设施(编写任何应用代码之前)
根据预检输出,在一轮内完成机械性接线:
CUSTOM_STEPS_MISSING→ 扩展steps.ts和schema.tsTASKS_MISSING→ 在cypress.config.ts中添加 Cypress 任务实现
这些都是机械性工作——添加类型,并加入实现框架。为所有故事一次性完成。
阶段 A:构建一个批次
选择 2-4 个相关故事。尽可能少用几轮,通过并行 Write 调用编写所有代码和测试 YAML。预检中 SELECTORS_NEEDED 列出的每个 data-test 属性都必须出现在组件代码中。
在任何测试与页面交互之前,应用就绪状态必须已确认。每次 visit 后都会自动调用 cy.appReady()(也可通过 awaitReady DSL 步骤在流程中显式等待)。cypress/support/app-ready.ts 中的实现遵循 CONVENTIONS/technical.md 的「应用就绪」部分(参见步骤 2c)——先在约定中列出边界,Cypress 检查再断言这些边界发出的信号。如果页面包含任何延迟执行边界,检查就必须观察这些边界;document.readyState、body 存在以及 cy.wrap(true) 都属于预检失败。路由无需设置任何特定属性;在遵循此规则的前提下,由项目自行决定如何发出就绪信号。
阶段 B:测试指定的故事
仅运行你已实现的故事的测试:
npx cypress run --spec cypress/e2e/mvp.cy.ts 2>&1 | tail -20; cat rootspec/tests-status.json
如果指定故事的测试失败,请根据预检输出进行诊断——不要猜测:
- 找不到选择器 → 检查准确的
data-test=属性是否与预检中的SELECTORS_NEEDED相符 - 路由返回 404 → 检查页面文件路径是否与预检中的
ROUTES_NEEDED相符 - 找不到任务 → 检查
cypress.config.ts是否包含TASKS_NEEDED中的任务 - Zod 验证错误 → 你使用了非核心 DSL 步骤
- 断言值错误 → 这是实际的功能逻辑问题——修复应用代码
在一轮中修复。每批最多进行 2 轮修复。然后继续。
阶段 C:测试所有故事(回归检查)
指定测试通过后(或你已继续处理后),运行完整测试套件:
npx cypress run 2>&1 | tail -20; cat rootspec/tests-status.json
与本批次开始前通过的测试进行比较。如果之前通过的故事现在失败了,这就是回归。 在实现更多故事前修复回归问题。
回归问题处理方式:
- 如果所有测试同时失败 → 构建/配置问题。检查框架配置、导入和构建错误,并修复根本原因。
- 如果特定故事出现回归 → 你的更改产生了冲突。还原冲突部分,或修复交互逻辑。
- 存在回归问题时,不要继续实现新故事。
测试文件模式(重要)
不要使用 cy.readFile() 加载 YAML 文件。 Cypress 命令不能在 it() 块外运行。请改为在测试文件中将 YAML 嵌入为字符串字面量,并使用 loadAndRun() 函数。
测试文件请使用以下准确模式:
import * as yaml from 'js-yaml';
import { UserStorySchema } from '../support/schema';
import type { UserStory } from '../support/schema';
import { runSetupSteps, runAssertionSteps } from '../support/steps';
function loadAndRun(yamlContent: string) {
const docs = yaml.loadAll(yamlContent) as UserStory[];
for (const doc of docs) {
if (!doc || !doc.id) continue;
const story = UserStorySchema.parse(doc);
const describeFn = story.skip ? describe.skip : story.only ? describe.only : describe;
describeFn(`${story.id}: ${story.title}`, () => {
for (const ac of story.acceptance_criteria) {
const itFn = ac.skip ? it.skip : ac.only ? it.only : it;
itFn(`${ac.id}: ${ac.title}`, () => {
if (ac.given) runSetupSteps(ac.given);
if (ac.when) runSetupSteps(ac.when);
if (ac.then) runAssertionSteps(ac.then);
});
}
});
}
}
// Embed stories as YAML string literals, separated by ---
const contentStories = `
id: US-101
title: Story title here
acceptance_criteria:
- id: AC-101-1
title: Criterion title
given:
- visit: '/'
when: []
then:
- shouldExist: { selector: '[data-test=element]' }
`;
loadAndRun(contentStories);
测试文件的关键规则:
- 每个阶段或逻辑分组使用一个测试文件(例如,
mvp.cy.ts) - 将每个故事的 YAML 直接嵌入为字符串,多文档 YAML 使用
---分隔 - 从规范 YAML 中复制给定/当/那么步骤,并调整选择器以匹配你的
data-test属性 loadAndRun函数会创建格式正确的describe/it块,供 rootspec-reporter 解析- 只能使用核心 DSL 步骤:
visit、click、fill、loginAs、seedItem、shouldContain、shouldExist。不要使用wait、scrollTo、shouldHaveAttribute、reload或任何其他步骤——它们不在架构中,会导致崩溃。 - 调试时绝不要重写或缩减测试文件。 只能追加新故事,或编辑特定的 YAML 字符串。重写文件会丢失所有现有的通过测试。
- 绝不要创建额外的测试文件(不要创建
debug.cy.ts、targeted.cy.ts等)。所有测试都放在同一个测试文件中。
构建一批故事
不要重新读取 YAML、约定或片段。使用步骤 1 中的上下文。
检查 @phase 注解。 如果 @phase: baseline,只编写/验证 Cypress 测试——不要修改应用代码。
对于非 baseline 故事,每个故事用一轮写入完成:
- 创建/修改所有应用文件(路由、组件、页面、样式)
- 将该故事的 YAML 添加到测试文件中(追加至现有测试文件)
- 使用符合验收标准选择器要求的
data-test属性
只有当测试 YAML 已写入测试文件后,故事才算实现完成。
示例批次(3 个故事,3 轮):
- 第 1 轮: 编写 MetaBanner.astro、HeroSection.astro、ProblemSection.astro(并行写入)
- 第 2 轮: 更新 index.astro,并将 3 个故事的 YAML 添加到测试文件中(并行写入)
- 第 3 轮: 运行本批次的指定测试(阶段 B)
修复失败
在一轮中使用 Edit 修复特定行。每批最多进行 2 轮修复。
如果遇到困难,请重新运行预检以确认连接配置正确:
bash "$SHARED_DIR/scripts/preflight-story.sh" rootspec/05.IMPLEMENTATION/USER_STORIES .
如果 SELECTORS_MISSING 或 ROUTES_MISSING 不为空,请先修复这些问题——否则测试无法通过。
绝不要:
- 重写整个测试文件(会丢失已通过的测试)
- 创建单独的调试/指定测试文件
- 添加
wait或timeout步骤(DSL 中没有这些步骤) - 从测试文件中移除故事
每批完成后
报告:"Batch complete: US-001, US-002, US-003 PASS. Full suite: 6/10 passing, 0 regressions."
如果存在回归问题,请先修复,再开始下一批。所有目标故事都通过测试(或达到每个故事的尝试上限)后,转到第 4 步。
第 4 步:总结并提交(约 3 轮)
更新约定(1 轮)
检查实现是否引入或更改了 rootspec/CONVENTIONS/ 中记录的内容。更新约定文档,使其反映实际构建的内容,例如新依赖、模式、颜色和字体。仅更新实际发生变化的条目。遵循以下格式:使用 ## Heading 小节和 - **Label:** value 条目。
提交并报告(1–2 轮)
COMPLETED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
如果所有目标故事都通过测试: 提交实现,并在提交消息中概述已实现的内容。然后建议运行 /rs-validate 以获取完整报告。
如果有任何故事未通过测试: 不要提交。报告失败情况,并建议继续运行 /rs-impl failing,或使用 /rs-spec 修复规格。未提交的工作会保留在工作区中,供开发者检查。
记录统计信息:
bash "$SHARED_DIR/scripts/write-stats.sh" rootspec/stats.json rs-impl "$STARTED_AT" "$COMPLETED_AT" <iteration-count> '<stories-json>'
其中 <stories-json> 是一个 JSON 对象,例如 {"US-101":{"attempts":2},"US-102":{"attempts":1}},用于记录每个故事经历了多少轮测试。
报告
根据 tests-status.json 生成报告,不要自行评估:
bash "$SHARED_DIR/scripts/generate-test-report.sh" rootspec/tests-status.json rootspec
此操作会根据实际测试结果输出通过、失败和未测试的数量。在总结中包含其输出。
范围焦点
参数用于限定此技能的工作范围:
- 不指定范围焦点 → 按阶段顺序处理所有故事(从最早阶段开始)
"US-101"→ 指定故事"TASK_SYSTEM"→ 某个系统的故事"MVP"(或任意阶段名称)→ 标记了该阶段的故事"failing"→ 重新实现先前未通过测试的故事
范围
- 可以读取: 所有项目文件
- 可以写入: 应用代码、测试基础设施、配置文件
- 可以写入:
rootspec/CONVENTIONS/(technical.md、visual.md) - 可以更新:
rootspec/tests-status.json - 不可写入:
rootspec/中的任何其他文件(规格文件、spec-status.json、00.AXIOMS.md、00.FRAMEWORK.md) - 不可写入: package.json 中的
dependencies或devDependencies(请使用npm install)