openspec-sdd-e2e-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +63 -0
  2. package/bin/sdd-e2e-kit.mjs +53 -0
  3. package/kit/.codex/skills/feature-to-e2e/SKILL.md +188 -0
  4. package/kit/.codex/skills/feature-to-e2e/agents/openai.yaml +4 -0
  5. package/kit/.codex/skills/openspec-apply-change/SKILL.md +180 -0
  6. package/kit/.codex/skills/openspec-archive-change/SKILL.md +157 -0
  7. package/kit/.codex/skills/openspec-continue-change/SKILL.md +136 -0
  8. package/kit/.codex/skills/openspec-explore/SKILL.md +292 -0
  9. package/kit/.codex/skills/openspec-full-spec-discovery/SKILL.md +356 -0
  10. package/kit/.codex/skills/openspec-full-spec-discovery/references/backlog-row-to-main-spec.md +447 -0
  11. package/kit/.codex/skills/openspec-new-change/SKILL.md +92 -0
  12. package/kit/.codex/skills/openspec-propose/SKILL.md +132 -0
  13. package/kit/.codex/skills/spec-to-gherkin/SKILL.md +686 -0
  14. package/kit/SDD_E2E_FLOW.md +268 -0
  15. package/kit/manifest.json +78 -0
  16. package/kit/openspec/config.yaml +18 -0
  17. package/kit/openspec/schemas/sdd-e2e/schema.yaml +128 -0
  18. package/kit/openspec/schemas/sdd-e2e/templates/acceptance-coverage.md +9 -0
  19. package/kit/openspec/schemas/sdd-e2e/templates/design.md +29 -0
  20. package/kit/openspec/schemas/sdd-e2e/templates/feature.feature +13 -0
  21. package/kit/openspec/schemas/sdd-e2e/templates/proposal.md +23 -0
  22. package/kit/openspec/schemas/sdd-e2e/templates/spec.md +21 -0
  23. package/kit/openspec/schemas/sdd-e2e/templates/tasks.md +16 -0
  24. package/kit/openspec/schemas/sdd-e2e/templates/test-cases.md +35 -0
  25. package/kit/openspec/schemas/sdd-e2e.yaml +160 -0
  26. package/kit/openspec/sdd-e2e-flow.md +290 -0
  27. package/kit/openspec/sdd-e2e-maintenance.md +98 -0
  28. package/kit/scripts/sdd/check-report.mjs +34 -0
  29. package/kit/scripts/sdd/lib.mjs +290 -0
  30. package/kit/scripts/sdd/lint-features.mjs +60 -0
  31. package/kit/scripts/sdd/lint-tasks.mjs +41 -0
  32. package/kit/scripts/sdd/self-test.mjs +185 -0
  33. package/kit/scripts/sdd/summarize-acceptance.mjs +41 -0
  34. package/package.json +19 -0
  35. package/src/check.mjs +86 -0
  36. package/src/diff.mjs +101 -0
  37. package/src/install.mjs +159 -0
  38. package/src/lib.mjs +221 -0
@@ -0,0 +1,686 @@
1
+ ---
2
+ name: spec-to-gherkin
3
+ description: 基于 SDD Spec 扩写前端 Gherkin 测试用例。将 spec 中的主流程描述扩展为覆盖状态机、交互反馈、竞态、跨视图同步等前端维度的完整测试规格,并为 Scenario 标注 phase 与 validation 标签,便于后续按开发切片生成小型 E2E。输出 Cucumber 兼容的 .feature 文件。
4
+ license: MIT
5
+ metadata:
6
+ author: ZSY
7
+ version: '1.4'
8
+ ---
9
+
10
+ # Skill:基于 SDD Spec 的前端 Gherkin 测试用例扩写
11
+
12
+ ## 目标
13
+
14
+ 将 SDD spec 中偏主流程的功能描述,**扩写**为覆盖前端特有风险面的完整 Gherkin 测试用例。输出 Cucumber 可直接解析执行的 `.feature` 文件。
15
+
16
+ **核心价值是「扩写」而非「翻译」**:
17
+
18
+ - Spec 中已有的 Scenario 仅作为主流程基线,不应简单重编码为 Gherkin
19
+ - 本 skill 的主要产出是 spec 未覆盖但前端需要验证的新场景(状态机、竞态、回显、降级等)
20
+ - 如果最终输出中超过 50% 的场景是 spec 原文的直接改写,说明扩写力度不足
21
+
22
+ ---
23
+
24
+ ## 输入
25
+
26
+ 用户提供以下内容之一或组合:
27
+
28
+ 1. SDD spec 文件路径(自动读取)
29
+ 2. spec 片段(直接粘贴)
30
+ 3. 指定要扩写的 Requirement 名称
31
+
32
+ ---
33
+
34
+ ## 处理流程
35
+
36
+ ### Step 1:解析 spec
37
+
38
+ 从 spec 中识别:
39
+
40
+ - 页面/组件结构
41
+ - 用户交互动作
42
+ - 接口依赖(请求/响应)
43
+ - 业务对象与状态
44
+ - 条件渲染逻辑
45
+ - 数据流向(输入 → 本地状态 → 接口 → UI 反馈)
46
+
47
+ ### Step 2:抽取业务规则
48
+
49
+ 不直接写用例,先列出规则清单。格式:
50
+
51
+ ```
52
+ Rule 1:[条件] 时,系统应 [行为]
53
+ Rule 2:[条件] 时,系统不应 [行为]
54
+ ```
55
+
56
+ ### Step 3:按前端维度扩写场景
57
+
58
+ 每个 Requirement 从以下维度检查(按适用性裁剪,非全部强制):
59
+
60
+ | 维度 | 关注点 | 典型场景 |
61
+ | ---------- | --------------------------------------- | ------------------------------------------------------------------- |
62
+ | 主流程 | 理想条件下操作成功 | 正常提交、正常加载 |
63
+ | 输入校验 | 空值/非法/超长 + 校验时机 | 提交时校验、失焦校验 |
64
+ | UI 状态机 | loading → 成功 → 空态 → 错误态 → 禁用态 | 加载中展示骨架屏、接口失败展示错误提示 |
65
+ | 条件渲染 | 不同数据/角色下的可见性与可操作性 | 默认方案不可删除、线上版全部禁用 |
66
+ | 交互反馈 | 操作后的即时反馈 | message 提示、按钮状态变化、列表刷新 |
67
+ | 数据回显 | 编辑页进入时已有数据正确填充 | 每个表单字段独立验证回显值、选中项是否正确、开关/Radio 状态是否匹配 |
68
+ | 防重复交互 | 按钮 loading 防连点、debounce | 发布按钮 loading 期间不可再次点击 |
69
+ | 竞态与时序 | 快速切换时旧请求不覆盖新数据 | 连续切换 tab、快速翻页、版本切换时旧版本响应晚到 |
70
+ | 跨视图同步 | A 页操作后 B 页反映最新状态 | 配置页发布后列表页刷新 |
71
+ | 降级展示 | 数据缺失时的兜底渲染 | null 值、空数组、关联实体被删除/状态异常时的展示 |
72
+
73
+ **裁剪规则**:
74
+
75
+ - 如果某个维度在当前 Requirement 中确实没有适用场景,直接跳过
76
+ - **最低扩写要求**:每个 Requirement 必须从至少 3 个非主流程维度产出场景;如果不足 3 个,需要重新审视是否遗漏了前端风险面
77
+
78
+ ### Step 3.5:交互入口完整性检查
79
+
80
+ 当 spec 或业务规则提到同一能力可通过多个入口触发时,必须把入口作为覆盖维度显式检查,不能只用一个主入口代表全部入口。
81
+
82
+ 常见入口:
83
+
84
+ - 鼠标点击主按钮,如“发送”“保存”“发布”
85
+ - 键盘提交,如 Enter、快捷键、组合键
86
+ - 菜单、下拉、右键动作
87
+ - 拖拽、批量选择、开关切换
88
+ - 自动触发,如失焦保存、滚动加载、定时刷新
89
+
90
+ 规则:
91
+
92
+ - 如果不同入口共享同一业务效果,但有独立的可失败风险,例如按钮 disabled 与 Enter guard 不同,P0/P1 场景必须分别覆盖。
93
+ - 如果 spec 明确写到某个入口,例如“Enter 发送不应被阻止”,必须生成对应 Scenario,不能只在另一个场景中隐含。
94
+ - 如果入口差异只影响视觉或极端快捷操作,可降为 P2 或记录为 test-cases 中的非自动化说明,但不能静默忽略。
95
+ - 覆盖矩阵的“维度”列应写出入口差异,例如 `键盘提交`、`按钮提交`、`快捷键提交`。
96
+
97
+ 示例:
98
+
99
+ ```gherkin
100
+ @phase:message-send @validation:mock-e2e @req:chat-message-send @p0 @state-machine
101
+ Scenario: pending 时点击发送按钮提交第二条消息
102
+ Given 第一条消息的 answer 请求仍处于 pending 状态
103
+ When 用户输入买家消息"第二条问题"
104
+ And 用户点击发送按钮
105
+ Then 系统发送买家消息"第二条问题"
106
+
107
+ @phase:message-send @validation:mock-e2e @req:chat-message-send @p0 @state-machine @keyboard-submit
108
+ Scenario: pending 时通过 Enter 提交第二条消息
109
+ Given 第一条消息的 answer 请求仍处于 pending 状态
110
+ When 用户输入买家消息"第二条问题"
111
+ And 用户按 Enter 发送
112
+ Then 系统发送买家消息"第二条问题"
113
+ ```
114
+
115
+ ### Step 4:标记 Spec Gap
116
+
117
+ 对 spec 未明确定义的行为,不自行假设,标记为 Spec Gap:
118
+
119
+ ```
120
+ Spec Gap:[具体问题],需产品确认。
121
+ ```
122
+
123
+ **只标记影响实现决策的 gap**,显而易见的行业惯例不需要标记。
124
+
125
+ **常见 Gap 类型清单**(按此清单主动检查,避免遗漏):
126
+
127
+ | 类型 | 示例 |
128
+ | --------------------- | -------------------------------------------------------- |
129
+ | 边界值未定义 | 名称最大长度、列表最大条数、文件大小上限 |
130
+ | 错误提示文案未明确 | 接口失败时展示什么文案、是 toast 还是行内提示 |
131
+ | 状态判断来源未指定 | "存在未发布变更"由前端 diff 还是后端字段决定 |
132
+ | 竞态处理策略未说明 | 快速切换时是取消旧请求还是忽略旧响应 |
133
+ | 冲突/异常时的交互细节 | 冲突提示文案谁提供(前端构造 vs 后端返回)、用户如何修正 |
134
+ | 时序/频率约束 | debounce 时间、轮询间隔、重试策略 |
135
+
136
+ ### Step 4.5:Spec Gap 处理策略
137
+
138
+ 如果 Step 4 中识别出 Spec Gap:
139
+
140
+ **交互模式(推荐)**:将 Gap 列表呈现给用户并请求确认后再继续生成。
141
+
142
+ **自主模式**:如果用户未响应或明确要求"先生成再确认",按以下规则处理:
143
+
144
+ 1. **不影响 E2E 可执行性的 Gap**(如文案措辞、具体字符长度限制)→ 标记为待确认,仍生成对应场景但在 scenario 标签中加 `@gap-pending`
145
+ 2. **影响 E2E 可执行性的 Gap**(如某个异常分支的完整交互流程未定义)→ 不生成对应场景,仅在覆盖矩阵中标记为「待定 - 缺 spec 约束」
146
+
147
+ **规则**:
148
+
149
+ - 如果无 Spec Gap,直接进入 Step 5
150
+ - Gap 处理结果记录在输出的 `test-cases.md` 顶部(标注"已确认"或"待确认")
151
+ - 待确认的 Gap 不应阻止其他场景的生成
152
+ - 使用 `@gap-pending` 标签标记受 Gap 影响的场景,便于后续筛选
153
+
154
+ ### Step 5:生成覆盖矩阵
155
+
156
+ 先输出矩阵,再写 Gherkin:
157
+
158
+ | 规则 | 维度 | 优先级 | Phase | Validation | 前置条件 | 用户行为 | 预期结果 |
159
+ | ---- | ---- | ------ | ----- | ---------- | -------- | -------- | -------- |
160
+
161
+ ### Step 5.5:生成 Phase / Validation 标注
162
+
163
+ 每个写入 `.feature` 的 Scenario 必须标注:
164
+
165
+ - `@phase:<phase-id>`:候选开发切片。`phase-id` 使用 kebab-case,表达业务垂直切片,不表达技术层级。
166
+ - `@validation:<type>`:推荐验证方式。可选值:`mock-e2e`、`real-smoke`、`component`、`unit`、`api`、`manual`。
167
+ - `@req:<id>`:对应 spec Requirement 或 capability。没有稳定 Requirement ID 时使用 capability 名称,例如 `@req:skill-plan-list`。
168
+ - `@p0` / `@p1` / `@p2`:验收优先级。P0 不允许使用 `@gap-pending`。
169
+
170
+ Validation 默认选择规则:
171
+
172
+ - 主流程、happy path、只读展示、导航入口、基础筛选/搜索等核心主链路,默认优先使用 `@validation:real-smoke`。
173
+ - 写操作成功路径默认优先评估 `real-smoke`,但必须先中断并主动询问用户是否允许使用真实后端接口、可使用哪些测试数据、以及 cleanup/rollback 方式;只有用户明确确认后才标记为 `real-smoke`。
174
+ - 如果用户拒绝、未确认、或无法提供安全测试数据/清理方案,将该写操作成功路径标记为 `mock-e2e`,并在 `test-cases.md` 记录问询结果和降级原因;不得静默执行真实写操作,也不得默认假设用户同意。
175
+ - 失败、异常、接口延迟、竞态、边界值、空态、条件渲染依赖特定数据、权限/第三方依赖等场景,默认使用 `@validation:mock-e2e`。
176
+ - 如果核心主链路被标记为 `mock-e2e`,必须在 `test-cases.md` 说明原因,例如真实环境缺少稳定测试数据、操作不可安全回滚、需要固定业务实体或依赖不可控权限。
177
+ - `real-smoke` 场景不得依赖 mock 固定数据;如果真实环境无法保证具体业务实体存在,应把断言写成结构性/业务性断言,或在 `test-cases.md` 标记 blocker/测试数据要求。
178
+
179
+ 如果当前 change 使用 `schema: sdd-e2e`,这些标签属于项目级 OpenSpec artifact 契约;Feature 级标签不能替代 Scenario 级追踪标签。
180
+
181
+ Phase 是候选归属,不要求在生成 Gherkin 时完全冻结;后续生成 `tasks.md` 时可以调整,但必须保持 feature 标签和 tasks 分组使用同一套 phase id。
182
+
183
+ Phase 拆分原则:
184
+
185
+ - 按用户可感知的垂直行为拆分,不按 API / Store / Component 技术层拆分。
186
+ - 一个 Scenario 只归属一个 phase。
187
+ - 同一 phase 的 `mock-e2e` Scenario 应尽量能共享一套 fixture,并能独立于其他 phase 运行。
188
+ - 核心主链路默认优先使用 `real-smoke`;`real-smoke` 只用于真实后端入口和核心主链路,不覆盖大量异常分支。
189
+ - 如果目标业务接口被 mock,只能标记为 `mock-e2e`,或在项目 schema 允许时使用更精确的 hybrid/real-ui 类型,不能标成纯 `real-smoke`。
190
+ - `manual` 只用于无法稳定自动化、依赖外部权限/验证码/第三方控制台或需求尚未确认的场景。
191
+ - `@validation` 必须使用 `openspec/schemas/sdd-e2e.yaml` 中允许的类型。
192
+
193
+ 推荐 phase 示例:
194
+
195
+ ```
196
+ @phase:plan-list
197
+ @phase:plan-filter
198
+ @phase:create-plan
199
+ @phase:plan-config
200
+ @phase:publish-flow
201
+ @phase:knowledge-template
202
+ ```
203
+
204
+ ### Step 6:生成 Gherkin(Cucumber 兼容)
205
+
206
+ 输出为 `.feature` 文件,遵循 Cucumber 解析要求。
207
+
208
+ ---
209
+
210
+ ## Cucumber 兼容规范
211
+
212
+ ### 文件格式
213
+
214
+ - 文件扩展名:`.feature`
215
+ - 每个文件对应一个 Feature(一个 Requirement 对应一个 .feature 文件)
216
+ - 关键字使用英文(Feature / Rule / Scenario / Given / When / Then / And / But)
217
+ - 场景描述和 Step 文本使用中文(与 spec 语言一致)
218
+
219
+ ### 文件结构模板
220
+
221
+ ```gherkin
222
+ @plan-config
223
+ Feature: 方案配置页 - 方案名称编辑
224
+
225
+ Background:
226
+ Given 用户已登录系统
227
+ And 当前技能下存在至少一个方案
228
+
229
+ Rule: 方案名称校验
230
+
231
+ @phase:publish-flow @validation:mock-e2e @req:skill-plan-config @p0 @form-validation
232
+ Scenario: 发布时名称为空
233
+ Given 用户处于新建方案配置页
234
+ And 方案名称为空
235
+ When 用户点击发布上线
236
+ Then 系统阻止发布
237
+ And 提示"方案名称不能为空"
238
+
239
+ @phase:publish-flow @validation:mock-e2e @req:skill-plan-config @p0 @form-validation
240
+ Scenario Outline: 发布时名称校验失败
241
+ Given 用户处于新建方案配置页
242
+ And 方案名称为"<名称>"
243
+ When 用户点击发布上线
244
+ Then 系统阻止发布
245
+ And 提示"<错误信息>"
246
+
247
+ Examples:
248
+ | 名称 | 错误信息 |
249
+ | | 方案名称不能为空 |
250
+ |   | 方案名称不能为空 |
251
+ | 已存在方案 | 方案名称已存在 |
252
+ ```
253
+
254
+ ### Step 参数化原则
255
+
256
+ Step 文本应支持参数提取,便于 step definition 复用:
257
+
258
+ ```gherkin
259
+ # 好 - 参数化,可复用 step definition
260
+ Given 用户处于"新建"方案配置页
261
+ Then 提示"方案名称不能为空"
262
+ Then 发布按钮状态为"禁用"
263
+
264
+ # 差 - 每个场景一条独立 step,无法复用
265
+ Given 用户处于新建方案配置页且名称为空且已选择店铺
266
+ ```
267
+
268
+ 参数使用双引号包裹,Cucumber 会自动提取为 step definition 参数。
269
+
270
+ ### Background 使用规则
271
+
272
+ - 同一 Feature 内所有场景共享的前置条件放入 Background
273
+ - 只放稳定的通用前置条件(如登录状态、基础数据存在)
274
+ - 不放特定场景才需要的条件
275
+
276
+ ### 标签规范
277
+
278
+ 标签放在 `Scenario:` 或 `Feature:` 行的上方:
279
+
280
+ ```gherkin
281
+ @p0 @happy
282
+ Scenario: 正常发布无冲突
283
+ ```
284
+
285
+ Feature 级别标签对该文件内所有场景生效:
286
+
287
+ ```gherkin
288
+ @plan-config
289
+ Feature: 方案配置页
290
+ ```
291
+
292
+ ---
293
+
294
+ ## 优先级标准
295
+
296
+ ```
297
+ P0:用户无法完成核心任务(主流程断裂、数据丢失、死循环请求)
298
+ P1:用户体验受损但可绕过(错误提示缺失、状态不同步需刷新、回显错误)
299
+ P2:极端场景或视觉细节(超长文本截断、快速连续操作、空态文案)
300
+ ```
301
+
302
+ ---
303
+
304
+ ## 标签体系
305
+
306
+ ```
307
+ @p0 @p1 @p2 — 优先级
308
+ @phase:<phase-id> — 开发/验证切片,kebab-case,例如 @phase:plan-list
309
+ @validation:<type> — 验证方式:mock-e2e / real-smoke / component / unit / api / manual
310
+ @req:<id> — 对应 spec Requirement 或 capability
311
+ @happy @negative — 正向/反向
312
+ @state-machine — UI 状态流转
313
+ @conditional-render — 条件渲染
314
+ @feedback — 交互反馈
315
+ @form-validation — 表单校验
316
+ @data-echo — 数据回显
317
+ @anti-duplicate — 防重复交互
318
+ @race-condition — 竞态与时序
319
+ @cross-view-sync — 跨视图同步
320
+ @degradation — 降级展示
321
+ ```
322
+
323
+ ---
324
+
325
+ ## Gherkin 编写规范
326
+
327
+ ### 原则
328
+
329
+ 1. 声明式业务语言,避免 UI 操作脚本(不写「点击蓝色按钮」「等待 3 秒」)
330
+ 2. 每个场景只验证一个核心规则
331
+ 3. `Then` 必须包含可验证的业务结果(状态变化、数据变化、UI 反馈)
332
+ 4. `Given` 描述前置状态,不描述操作步骤
333
+ 5. Step 文本使用中文,与 spec 语言一致
334
+ 6. Step 文本保持简洁,参数用双引号包裹
335
+ 7. 相似数据组合优先使用 `Scenario Outline` + `Examples` 而非重复场景
336
+ 8. `.feature` 只描述业务可观察行为,不写接口路径、HTTP 方法、请求体字段、数据库字段等实现细节
337
+ 9. 每个 Scenario 必须包含 `@phase:*`、`@validation:*`、`@req:*` 和 `@p0|@p1|@p2` 标签;Feature 级标签不能替代 Scenario 级追踪标签
338
+
339
+ ### 系统条件前置规则(mock 时序)
340
+
341
+ **系统事件**(加载失败、保存失败、请求超时、接口返回冲突等)必须以 Given 前置条件形式声明,**不能**作为 When 之后的 And。
342
+
343
+ 原因:E2E 测试中 mock 必须在触发请求的操作之前注册,"When 点击 And 失败" 的写法会导致 mock 来不及拦截请求。
344
+
345
+ ```gherkin
346
+ # GOOD - 系统条件作为 Given 前置
347
+ Scenario: 新建发布遇到接口失败
348
+ Given 用户位于技能"客服接待"的新建方案配置页
349
+ And 下次创建方案请求将失败
350
+ When 用户点击"发布上线"
351
+ Then 系统展示错误信息
352
+ And 页面保留本地输入
353
+
354
+ # GOOD - 竞态条件作为 Given 前置
355
+ Scenario: 线上版加载失败时不进入混合状态
356
+ Given 用户位于方案"售前方案"的草稿版配置页
357
+ And 下次线上版配置加载将失败
358
+ When 用户点击"线上版"
359
+ Then 系统展示错误信息
360
+ And 用户仍可切回"草稿版"
361
+
362
+ # BAD - 系统事件跟在操作后面(mock 来不及注册)
363
+ Scenario: 新建发布遇到接口失败
364
+ Given 用户位于技能"客服接待"的新建方案配置页
365
+ When 用户点击"发布上线"
366
+ And 创建方案失败 ← 请求已经发出了,mock 拦截不到
367
+ Then 系统展示错误信息
368
+ ```
369
+
370
+ **判断标准**:如果一个 And step 描述的是"系统/网络/接口层面的预设条件"而非"用户操作",它必须放在 Given 中。
371
+
372
+ **系统条件 step 命名模式**:`下次{操作}请求将{结果}`
373
+
374
+ - "下次创建方案请求将失败"
375
+ - "下次创建方案请求将返回店铺归属冲突"
376
+ - "下次线上版配置加载将失败"
377
+ - "下次草稿保存请求将失败"
378
+
379
+ **生成后硬性校验**:
380
+
381
+ - 不得出现 `When ...接口返回失败/冲突/超时`
382
+ - 不得在第一个 `When` 之后出现描述系统/网络/接口预设的 `And ...失败/冲突/超时/延迟返回`
383
+ - 发现上述写法时,必须重写为 `Given 下次{操作}请求将{结果}`,不能把问题留给 `feature-to-e2e` 处理
384
+
385
+ ### 测试数据声明规则
386
+
387
+ 1. When/Then 中引用的每个业务实体(方案、店铺、技能),**必须**在 Given 或 Background 中先声明其存在和状态
388
+ 2. Given 声明业务现实("系统中存在技能 X"、"技能 X 下存在方案 Y"),不声明技术实现(不写接口返回什么、不写 mock、不写数据库)
389
+ 3. 名称保持具体可读("售前方案"、"上海店"),不用抽象代号("方案 A"、"店铺 1")
390
+
391
+ ```gherkin
392
+ # GOOD - 实体声明 + 系统条件前置
393
+ Scenario: 配置详情加载失败时保留返回入口
394
+ Given 技能"客服接待"下存在方案"售前方案"
395
+ And 下次方案详情加载将失败
396
+ When 用户进入方案"售前方案"的配置页
397
+ Then 配置页结束 loading 状态
398
+ And 返回方案列表入口保持可用
399
+
400
+ # BAD - "售前方案"从天而降,没有 Given 声明它的存在
401
+ Scenario: 配置详情加载失败时保留返回入口
402
+ Given 用户处于方案"售前方案"的配置页
403
+ When 方案详情加载失败
404
+ Then 返回入口保持可用
405
+ ```
406
+
407
+ ### 推荐写法
408
+
409
+ ```gherkin
410
+ @plan-config
411
+ Feature: 方案配置页
412
+
413
+ Background:
414
+ Given 用户已登录系统
415
+
416
+ Rule: UI 状态流转
417
+
418
+ @p0 @state-machine
419
+ Scenario: 方案详情加载中展示 loading 态
420
+ Given 下次方案详情加载将延迟返回
421
+ When 用户从方案列表点击编辑进入配置页
422
+ Then 页面展示 loading 状态覆盖内容区
423
+
424
+ Rule: 竞态安全
425
+
426
+ @p1 @race-condition
427
+ Scenario: 快速切换技能时旧请求不覆盖新数据
428
+ Given 用户处于技能"A"的方案列表
429
+ When 用户快速连续切换到技能"B"再切换到技能"C"
430
+ Then 最终展示技能"C"的方案列表数据
431
+ And 不展示技能"A"或技能"B"的过期数据
432
+
433
+ Rule: 跨视图状态同步
434
+
435
+ @p1 @cross-view-sync
436
+ Scenario: 新建方案发布后列表同步更新
437
+ Given 用户在新建方案配置页完成配置
438
+ When 用户点击发布上线且创建成功
439
+ Then 返回方案列表页
440
+ And 列表中展示刚创建的方案
441
+
442
+ Rule: 数据回显
443
+
444
+ @p0 @data-echo
445
+ Scenario: 编辑已有方案时表单正确回显
446
+ Given 已有方案包含已保存的配置数据
447
+ When 用户从列表进入该方案配置页
448
+ Then 方案名称回显为已保存的值
449
+ And 关联店铺回显为已保存的选择
450
+ And 表单字段回显为已保存的配置
451
+
452
+ Rule: 降级展示
453
+
454
+ @p1 @degradation
455
+ Scenario: 关联店铺状态异常时仍正确展示名称
456
+ Given 方案关联的某店铺状态为"异常"
457
+ When 用户进入该方案配置页
458
+ Then 该店铺仍以名称形式展示在已选列表中
459
+ And 不展示为原始 ID
460
+ ```
461
+
462
+ ### 避免写法
463
+
464
+ ```gherkin
465
+ # BAD - UI 操作脚本,绑定实现细节
466
+ Scenario: 点击按钮失败
467
+ Given 用户打开页面
468
+ When 用户点击蓝色按钮
469
+ And 等待3秒
470
+ Then 页面显示红色提示
471
+
472
+ # BAD - 多个规则混在一个场景中
473
+ Scenario: 完整操作流程
474
+ Given 用户登录
475
+ When 用户填写表单并选择店铺并点击发布
476
+ Then 发布成功且列表刷新且名称正确且状态正确
477
+
478
+ # BAD - 泄露接口实现细节(字段名、参数值、HTTP 状态码)
479
+ Scenario: 启用方案
480
+ When 用户确认启用
481
+ Then 系统调用接口并传入 enabled 为"true"
482
+ And 请求体包含 scope_ids、configurations、extra_configurations
483
+
484
+ # GOOD - 用业务语言描述同样的意图
485
+ Scenario: 启用方案
486
+ When 用户确认启用方案
487
+ Then 方案状态变为"已启用"
488
+ ```
489
+
490
+ **实现细节泄露的判断标准**:如果更换了后端接口实现(字段改名、参数结构调整),这个 Step 是否需要修改?如果需要,说明它绑定了实现而非业务。
491
+
492
+ ### 接口细节隔离规则
493
+
494
+ Gherkin 场景不得把接口调用本身作为业务预期。以下内容不得出现在 `.feature` 的 Step 文本中:
495
+
496
+ - HTTP 方法和路径:`GET /api/...`、`POST /...`、`PUT /...`、`DELETE /...`
497
+ - 请求体/响应体字段:`request body`、`scope_ids`、`configurations`、`enabled=true`
498
+ - HTTP 状态码或后端错误码作为断言核心:`409`、`code=0`
499
+ - “系统请求某接口”“系统不请求某接口”这类实现级断言
500
+
501
+ 如果某个接口行为对 E2E 自动化很重要,应这样处理:
502
+
503
+ 1. 在 `.feature` 中用业务语言描述结果,例如 `Then 方案状态变为"已启用"`、`Then 页面保留本地输入`
504
+ 2. 在 `test-cases.md` 的覆盖矩阵或“E2E 实现提示”中记录接口/字段/拦截建议,供 `feature-to-e2e` 建立 endpoint map
505
+ 3. 如果缺少可用 endpoint map,不在 `.feature` 中补接口路径占位
506
+
507
+ ---
508
+
509
+ ## 输出格式
510
+
511
+ 固定输出四部分。即使用户只要求生成 `.feature`,也必须同步生成或更新 `test-cases.md`,否则本 skill 的产物不完整:
512
+
513
+ ```
514
+ 1. 业务规则清单(写入 test-cases.md)
515
+ 2. Spec Gap 列表(写入 test-cases.md;如无则标注"无")
516
+ 3. 测试覆盖矩阵(写入 test-cases.md,包含 Phase 与 Validation 列)
517
+ 4. Phase 验证摘要(写入 test-cases.md,按 phase 汇总 Scenario 数和验证方式)
518
+ 5. Feature 文件索引与生成质量摘要(写入 test-cases.md)
519
+ 6. Gherkin .feature 文件(每个 Requirement 一个文件)
520
+ ```
521
+
522
+ `test-cases.md` 必须包含:
523
+
524
+ - 输入来源
525
+ - 补充约束/待确认 Gap
526
+ - 业务规则清单
527
+ - 覆盖矩阵
528
+ - Phase 验证摘要:每个 phase 包含哪些 Scenario、推荐验证方式、是否需要独立 fixture
529
+ - Feature 文件索引
530
+ - 生成质量摘要:Scenario 数、唯一 step pattern 数、pattern/Scenario 比值、是否存在 mock 时序违规、是否存在接口细节泄露、是否所有 Scenario 具备 `@phase/@validation/@req/@p0|@p1|@p2` 标签、是否存在 P2 写入 feature 的例外说明
531
+ - 如果当前 change 使用 `schema: sdd-e2e`,生成后必须能通过 `pnpm sdd:lint-features <change>`
532
+
533
+ ---
534
+
535
+ ## Step 复用词表约束
536
+
537
+ **目标**:生成的 step 文本应当高度可复用,避免每个 Scenario 产生大量一次性 step。下游 E2E 测试中,每个 step pattern 对应一个 step definition——pattern 越少,维护成本越低。
538
+
539
+ ### 复用策略
540
+
541
+ 1. **参数化优先**:用 `{string}` 参数覆盖变化部分,固定模式句型
542
+
543
+ ```gherkin
544
+ # GOOD - 一个 pattern 覆盖所有类似场景
545
+ Given 用户位于方案{string}的草稿版配置页
546
+ When 用户点击{string}
547
+ Then 系统提示{string}
548
+ Then {string}处于禁用状态
549
+
550
+ # BAD - 每个场景独立措辞,无法复用 definition
551
+ Given 用户打开了售前方案的草稿编辑页面
552
+ Given 用户正在编辑售前方案的草稿
553
+ Given 用户进入到售前方案草稿版页面
554
+ ```
555
+
556
+ 2. **固定句型表**:以下为推荐的标准 step 句型,同一语义必须使用同一句型
557
+
558
+ | 语义 | 标准句型 | 反例(不要用) |
559
+ | ------------ | --------------------------------------------- | ------------------------------ |
560
+ | 进入页面 | `用户位于方案{string}的草稿版配置页` | 用户打开了...、用户进入到... |
561
+ | 进入新建 | `用户位于技能{string}的新建方案配置页` | 用户在新建方案页面 |
562
+ | 点击按钮 | `用户点击{string}` | 用户触发发布、用户执行发布操作 |
563
+ | 修改字段 | `用户将{field}修改为{string}` | 用户输入了...、用户编辑... |
564
+ | 系统提示 | `系统提示{string}` | 弹出提示...、展示提示框... |
565
+ | 阻止操作 | `系统阻止{action}` | 系统不允许...、操作被拒绝 |
566
+ | 展示/不展示 | `{area}展示{target}` / `{area}不展示{target}` | 页面显示了...、可以看到... |
567
+ | 禁用状态 | `{component}处于禁用状态` | 按钮灰掉了、不可点击 |
568
+ | 恢复可用 | `{component}恢复可编辑` | 变回可以操作的状态 |
569
+ | 系统条件前置 | `下次{action}请求将{result}` | 接口会返回失败 |
570
+
571
+ 3. **归一化检查**:生成完所有 feature 后,扫描去重。如果归一化后的唯一 step pattern 数量超过 Scenario 数量的 3 倍,说明词表过于发散,必须重写合并后再交付。
572
+
573
+ **归一化口径**:
574
+
575
+ - 双引号内容统一替换为 `"{string}"`
576
+ - `<Examples>` 占位统一替换为 `<param>`
577
+ - 数字、页码、ID、URL、接口路径等可变值统一替换为 `{value}`
578
+ - `Scenario Outline` 按 1 个 Scenario 计数,不按 Examples 行数展开
579
+
580
+ ### 衡量标准
581
+
582
+ - **理想**:唯一 step pattern 数量 ≤ Scenario 总数 × 2
583
+ - **可接受**:唯一 step pattern 数量 ≤ Scenario 总数 × 3
584
+ - **必须重写**:唯一 step pattern 数量 > Scenario 总数 × 3
585
+
586
+ ---
587
+
588
+ ## 输出量控制
589
+
590
+ - 每个 Requirement 产出上限 15-20 个场景
591
+ - P0 必写完整 Gherkin,P1 按需写,P2 仅列入矩阵
592
+ - 默认不把 P2 场景写入 `.feature`;除非用户明确要求“包含 P2”或该 P2 是验证已知高风险缺陷的唯一场景。例外必须写入 `test-cases.md` 的生成质量摘要。
593
+ - 如果用户指定了特定维度(如"只关注竞态"),仅产出该维度
594
+ - 相似输入组合使用 `场景大纲` 合并,不逐条展开
595
+
596
+ ---
597
+
598
+ ## 输出路径
599
+
600
+ ```
601
+ specs/{spec-name}/features/
602
+ ├── {requirement-1}.feature
603
+ ├── {requirement-2}.feature
604
+ └── ...
605
+ ```
606
+
607
+ 与原 spec 文件同层级,便于关联追溯。
608
+
609
+ ---
610
+
611
+ ## 执行步骤
612
+
613
+ 1. 读取用户指定的 spec 文件
614
+ 2. 如果用户未指定具体 Requirement,询问要扩写哪个(或全部)
615
+ 3. 按上述流程逐个 Requirement 处理
616
+ 4. 业务规则清单、Spec Gap、覆盖矩阵输出到 `specs/{spec-name}/test-cases.md`
617
+ 5. Phase 验证摘要输出到 `specs/{spec-name}/test-cases.md`
618
+ 6. Gherkin 输出到 `specs/{spec-name}/features/{requirement-name}.feature`
619
+ 7. 所有 `.feature` 文件使用英文关键字(Feature / Scenario / Given / When / Then / And),Step 文本使用中文
620
+ 8. 生成后执行独立 Review 门禁;门禁未通过时先重写产物,不把不合格 feature 交给用户
621
+ 9. Review 通过后,将最终质量摘要写入 `test-cases.md`
622
+
623
+ ## 多 Agent Review 门禁
624
+
625
+ 每完成一个 spec 的 feature 生成或重生成后,必须进行独立只读 review。
626
+
627
+ **优先方式**:如果运行环境支持子 agent,主 agent 必须启动一个 reviewer 子 agent 执行质量门禁检查。
628
+
629
+ **降级方式**:如果运行环境不支持子 agent,主 agent 必须按同一清单自检,并在最终回复中说明“未使用子 agent,已按同一门禁自检”。
630
+
631
+ ### Reviewer 职责
632
+
633
+ Reviewer 只读检查,不直接修改文件。Reviewer 必须输出结构化结果:
634
+
635
+ ```
636
+ Review Result: PASS | FAIL
637
+
638
+ Failed Gates:
639
+ - [门禁名称] 文件路径:行号 - 问题说明 - 建议修正
640
+
641
+ Metrics:
642
+ - Requirement 数
643
+ - Feature 文件数
644
+ - Scenario 数
645
+ - 唯一 step pattern 数
646
+ - pattern/Scenario 比值
647
+ - phase 数与 validation 类型分布
648
+ - @gap-pending 场景数
649
+ ```
650
+
651
+ ### Reviewer 检查清单
652
+
653
+ Reviewer 必须检查:
654
+
655
+ 1. 产物完整性:是否存在 `test-cases.md` 和所有 Requirement 对应 `.feature`
656
+ 2. Requirement 覆盖:每个 Requirement 是否至少有一个 Feature;跳过项是否在覆盖矩阵说明
657
+ 3. Mock 时序:系统/网络/接口预设是否全部为 Given 前置
658
+ 4. 接口细节隔离:`.feature` 是否泄露 HTTP 方法、接口路径、请求体字段、后端错误码断言
659
+ 5. Step 复用:唯一 step pattern 数是否 ≤ Scenario 数 × 3
660
+ 6. P2 输出边界:P2 是否默认只进矩阵;写入 `.feature` 的 P2 是否有例外理由
661
+ 7. Spec Gap 标记:待确认但仍生成的场景是否带 `@gap-pending`
662
+ 8. Cucumber 兼容性:英文关键字、标签位置、Background/Rule/Scenario 结构是否可解析
663
+ 9. Phase/Validation 标记:每个 Scenario 是否有 `@phase:*`、`@validation:*`、`@req:*`;同一 phase id 是否在 `test-cases.md` 的 Phase 验证摘要中出现
664
+
665
+ ### 主 Agent 处理规则
666
+
667
+ - Reviewer 返回 `PASS`:主 agent 可交付,并将 Metrics 写入 `test-cases.md`
668
+ - Reviewer 返回 `FAIL`:主 agent 必须修正失败项,再重新执行 Review 门禁
669
+ - 如果连续两轮 Review 仍失败,主 agent 必须停止声称“可进入 E2E”,并向用户列出剩余阻塞项
670
+ - 主 agent 不得把 reviewer 的问题仅作为备注保留;除非用户明确要求“先看草稿”,否则必须先修正
671
+
672
+ ## 生成后质量门禁
673
+
674
+ 每次生成或重生成 feature 后,必须由 reviewer 或主 agent 自检以下门禁,并在 `test-cases.md` 记录结果:
675
+
676
+ | 门禁 | 通过标准 | 未通过处理 |
677
+ | ---------------- | ----------------------------------------------------------------- | ------------------------------------------- |
678
+ | 产物完整性 | 存在 `test-cases.md` 和所有 Requirement 对应 `.feature` | 补齐缺失产物 |
679
+ | Requirement 覆盖 | 每个 Requirement 至少对应 1 个 Feature | 补 Feature 或在矩阵中说明跳过原因 |
680
+ | Mock 时序 | 系统/网络/接口预设均为 Given 前置 | 重写相关 Scenario |
681
+ | 接口细节隔离 | `.feature` 不包含 HTTP 方法、接口路径、请求体字段、后端错误码断言 | 改为业务语言,技术细节移入矩阵/E2E 实现提示 |
682
+ | Step 复用 | 唯一 step pattern 数 ≤ Scenario 数 × 3 | 合并句型、改用参数化或 Scenario Outline |
683
+ | P2 输出边界 | P2 默认仅在矩阵中,不写入 `.feature` | 移出 feature,或记录用户明确要求/例外理由 |
684
+ | Spec Gap 标记 | 待确认且仍生成的场景带 `@gap-pending` | 补标签或移入矩阵待定项 |
685
+
686
+ 如果任一门禁失败,最终回复必须明确说明失败项;除非用户要求“先看草稿”,否则不得声称 feature 已可进入 E2E 生成。