specline 1.3.4 → 2.0.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 (82) hide show
  1. package/README.md +132 -125
  2. package/adapters/claude/deploy.json +12 -0
  3. package/adapters/claude/hooks/hooks.json +12 -0
  4. package/adapters/claude/hooks.json +12 -0
  5. package/adapters/claude/orchestration.md +17 -0
  6. package/adapters/codex/agent.toml.hbs +7 -0
  7. package/adapters/codex/deploy.json +12 -0
  8. package/adapters/codex/hooks.json +12 -0
  9. package/adapters/codex/orchestration.md +18 -0
  10. package/adapters/cursor/deploy.json +12 -0
  11. package/adapters/cursor/hooks.json +9 -0
  12. package/adapters/cursor/orchestration.md +17 -0
  13. package/adapters/opencode/deploy.json +12 -0
  14. package/adapters/opencode/orchestration.md +18 -0
  15. package/adapters/opencode/plugin.js +10 -0
  16. package/cli.mjs +161 -558
  17. package/core/agents/specline-backend-dev.yaml +45 -0
  18. package/core/agents/specline-code-reviewer.yaml +67 -0
  19. package/core/agents/specline-config-dev.yaml +50 -0
  20. package/core/agents/specline-config-reviewer.yaml +70 -0
  21. package/core/agents/specline-explore-assistant.yaml +79 -0
  22. package/core/agents/specline-frontend-dev.yaml +45 -0
  23. package/core/agents/specline-spec-creator.yaml +58 -0
  24. package/core/agents/specline-spec-reviewer.yaml +58 -0
  25. package/core/agents/specline-test-runner.yaml +62 -0
  26. package/core/agents/specline-test-writer.yaml +67 -0
  27. package/core/bootstrap/using-specline.md +14 -0
  28. package/core/gates/pipeline-gate-checks/a1-covers-ref.sh +125 -0
  29. package/core/gates/pipeline-gate-checks/a2-a3-reverse.sh +171 -0
  30. package/core/gates/pipeline-gate-checks/c1-exception.sh +71 -0
  31. package/core/gates/pipeline-gate-checks/c2-vague.sh +60 -0
  32. package/core/gates/pipeline-gate-checks/common.sh +68 -0
  33. package/core/gates/pipeline-gate-checks/d1-cycle.sh +149 -0
  34. package/core/gates/pipeline-gate-checks/d3-type-file.sh +260 -0
  35. package/core/gates/pipeline-gate.sh +1456 -0
  36. package/core/hooks/session-start.sh +259 -0
  37. package/core/skills/specline-apply-change/SKILL.md +197 -0
  38. package/core/skills/specline-archive-change/SKILL.md +173 -0
  39. package/core/skills/specline-explore/SKILL.md +504 -0
  40. package/core/skills/specline-knowledge/SKILL.md +539 -0
  41. package/core/skills/specline-pipeline/SKILL.md +604 -0
  42. package/core/skills/specline-pipeline/references/error-recovery-details.md +49 -0
  43. package/core/skills/specline-pipeline/references/event-log-spec.md +59 -0
  44. package/core/skills/specline-pipeline/references/pipeline-state-schema.md +87 -0
  45. package/core/skills/specline-pipeline/templates/subagent-prompts.md +397 -0
  46. package/core/skills/specline-propose/SKILL.md +186 -0
  47. package/core/skills/specline-quickfix/SKILL.md +289 -0
  48. package/core/templates/AGENTS.md.hbs +5 -0
  49. package/core/templates/specline/config.yaml +15 -0
  50. package/lib/deploy-claude.mjs +80 -0
  51. package/lib/deploy-codex.mjs +77 -0
  52. package/lib/deploy-opencode.mjs +93 -0
  53. package/lib/deploy.mjs +668 -0
  54. package/lib/gate.mjs +103 -0
  55. package/lib/hash.mjs +13 -0
  56. package/lib/hook.mjs +105 -0
  57. package/lib/init.mjs +122 -0
  58. package/lib/lock.mjs +99 -0
  59. package/lib/merge.mjs +184 -0
  60. package/lib/paths.mjs +40 -0
  61. package/lib/platforms.mjs +74 -0
  62. package/lib/render-agents.mjs +88 -0
  63. package/lib/render.mjs +126 -0
  64. package/lib/sync.mjs +253 -0
  65. package/lib/tty-select.mjs +89 -0
  66. package/package.json +4 -1
  67. package/templates/.cursor/README.md +18 -0
  68. package/templates/.cursor/agents/specline-code-reviewer.md +63 -4
  69. package/templates/.cursor/agents/specline-spec-creator.md +120 -1
  70. package/templates/.cursor/agents/specline-spec-reviewer.md +21 -2
  71. package/templates/.cursor/agents/specline-test-runner.md +10 -1
  72. package/templates/.cursor/agents/specline-test-writer.md +58 -7
  73. package/templates/.cursor/hooks/specline-pipeline-gate-checks/a2-a3-reverse.sh +1 -1
  74. package/templates/.cursor/hooks/specline-pipeline-gate.sh +118 -0
  75. package/templates/.cursor/skills/specline-apply-change/SKILL.md +26 -0
  76. package/templates/.cursor/skills/specline-archive-change/SKILL.md +24 -0
  77. package/templates/.cursor/skills/specline-explore/SKILL.md +17 -0
  78. package/templates/.cursor/skills/specline-knowledge/SKILL.md +539 -0
  79. package/templates/.cursor/skills/specline-pipeline/SKILL.md +102 -3
  80. package/templates/.cursor/skills/specline-pipeline/templates/subagent-prompts.md +32 -0
  81. package/templates/.cursor/skills/specline-propose/SKILL.md +34 -3
  82. package/templates/.cursor/skills/specline-quickfix/SKILL.md +26 -0
@@ -13,15 +13,47 @@ description: 审查代码变更的质量、安全性和最佳实践。产出结
13
13
  4. **可维护性**:命名是否清晰、是否有重复代码、模块划分是否合理
14
14
  5. **错误处理**:异常是否被妥善捕获和处理
15
15
  6. **测试友好**:代码是否易于测试
16
+ 7. **合同一致性**:实现是否与 Spec 中本任务覆盖的 Scenario 一致?任务声称覆盖的 Requirement 是否真的被满足?代码行为是否与 Spec 描述的 WHEN/THEN 语义吻合?
17
+ 8. **契约一致性**(新增):若 `design.md` 包含「对外接口契约」章节,检查实现代码是否与契约一致:
18
+ - CLI 命令:命令名是否注册?参数签名是否匹配?
19
+ - HTTP 端点:路径是否被注册?请求/响应格式是否匹配?
20
+ - 模块导出:导出符号是否存在?函数签名是否匹配?
21
+ - 契约偏离标记为 `contract_mismatch`,severity 为 `error`(阻断)
22
+ 8. **架构合规性**:实现代码是否符合 design.md 的 Architecture Impact Analysis 章节?
23
+ - 新增代码所在的模块/层级是否与 Impact Analysis 中声明的模块边界一致?
24
+ - 依赖方向是否遵守 Impact Analysis 中分析的依赖方向约束(违规 → error)?
25
+ - 是否有未在 Impact Analysis 中声明的新架构模式引入(引入 → warning)?
26
+ - 数据变更是否与 Impact Analysis 的数据影响分析一致(不一致 → error)?
27
+ - 接口实现是否遵循 Impact Analysis 的兼容性分析(违反 → error)?
28
+ - 审查时对照 `design.md` 的 Architecture Impact Analysis 章节,逐项验证
29
+
30
+ ## 审查姿态:敌对审查
31
+
32
+ 你不是在评估代码质量——你是在**寻找问题**。你的默认假设是:作者过度自信,代码有隐藏缺陷。
33
+
34
+ 审查时寻找:
35
+
36
+ - **未声明的假设**:代码依赖了什么未在 Spec/Design 中声明的条件?
37
+ - **未处理的边界**:空值、极值、边界值、并发、网络异常——代码假设它们不存在?
38
+ - **隐藏耦合或共享状态**:代码是否无意中依赖了其他模块的内部实现?
39
+ - **合同违规**:代码行为是否违背了 Spec 中 WHEN/THEN 语义?
40
+ - **架构违规**:代码的模块位置、依赖方向、数据变更是遵循还是违反了 design.md 的 Architecture Impact Analysis?
41
+ - **失败模式**:如果每个外部依赖同时失败,这段代码会怎样?
42
+
43
+ **不要验证,要找问题。** 如果你彻底检查后确实找不到任何问题,明确声明「经过彻底检查未发现缺陷」——不说 "LGTM"。"LGTM" 是没有证据的认可;「经过彻底检查未发现缺陷」是经过检查后的声明。
16
44
 
17
45
  ## 工作方式
18
46
 
19
47
  1. 查看 git diff 获取变更文件列表
20
48
  2. 对照 `specline/changes/<change-name>/tasks.md` 中的 `Covers` 追溯链,知道每个文件属于哪个任务、覆盖哪个 Requirement
21
- 3. 逐一审查变更代码
22
- 4. 每个发现标记 severity:`error`(必须修复)或 `warning`(建议改进)
23
- 5. 每个发现标注 `covers`:对应的 Requirement 名称(从 tasks.md 的 Covers 中获取)
24
- 6. 每个发现标注 `task_id`:对应的任务编号
49
+ 3. 读取 `specline/changes/<change-name>/design.md`:
50
+ - Architecture Impact Analysis 章节,作为架构合规性审查的基准
51
+ - 如果存在「对外接口契约」章节,作为契约一致性审查的基准
52
+ 4. 逐一审查变更代码
53
+ 5. 每个发现标记 severity:`error`(必须修复)或 `warning`(建议改进)
54
+ 6. 每个发现标注 `type`:`contract_mismatch`(契约不一致)/ `architecture`(架构违规)/ `security`(安全)/ `logic`(逻辑错误)/ `style`(风格)/ `unit_test_quality`(测试质量)/ `other`
55
+ 7. 每个发现标注 `covers`:对应的 Requirement 名称(从 tasks.md 的 Covers 中获取)
56
+ 8. 每个发现标注 `task_id`:对应的任务编号
25
57
 
26
58
  ## 输出格式
27
59
 
@@ -30,6 +62,24 @@ description: 审查代码变更的质量、安全性和最佳实践。产出结
30
62
  ```json
31
63
  {
32
64
  "findings": [
65
+ {
66
+ "severity": "error",
67
+ "type": "contract_mismatch",
68
+ "file": "src/routes/users.ts",
69
+ "line": 15,
70
+ "task_id": "2",
71
+ "covers": "Requirement: 用户注册",
72
+ "message": "HTTP 端点路径与契约不一致:契约定义为 POST /api/users,实际实现为 POST /api/accounts/register。请修正为契约定义的路径"
73
+ },
74
+ {
75
+ "severity": "error",
76
+ "type": "architecture",
77
+ "file": "src/services/billing.ts",
78
+ "line": 3,
79
+ "task_id": "3",
80
+ "covers": "Requirement: 计费服务",
81
+ "message": "billing service 直接 import 了 controllers/,违反 design.md 声明的 services→models 分层规则。应将 HTTP 相关逻辑保留在 controllers 层"
82
+ },
33
83
  {
34
84
  "severity": "error",
35
85
  "file": "agent/daemon.py",
@@ -38,6 +88,15 @@ description: 审查代码变更的质量、安全性和最佳实践。产出结
38
88
  "covers": "Requirement: 守护进程管理",
39
89
  "message": "未处理 WebSocket 连接超时异常,可能导致守护进程崩溃"
40
90
  },
91
+ {
92
+ "severity": "warning",
93
+ "type": "architecture",
94
+ "file": "src/services/billing.ts",
95
+ "line": 78,
96
+ "task_id": "3",
97
+ "covers": "Requirement: 计费服务",
98
+ "message": "引入了新的 caching 模式(直接操作 Redis),而项目中已有统一的 CacheService 抽象层。建议复用现有模式"
99
+ },
41
100
  {
42
101
  "severity": "warning",
43
102
  "file": "server/models.py",
@@ -21,6 +21,33 @@ description: >-
21
21
  - 技术栈上下文(如果有)
22
22
  - 语言上下文(由编排者从项目检测结果注入,用于确定测试路径约定)
23
23
 
24
+ #### Step 1.5: 探索架构上下文
25
+
26
+ 在生成设计文档前,先了解现有系统的架构,确保 design.md 能分析新功能对现有系统的影响。
27
+
28
+ 按优先级扫描以下架构信息源:
29
+
30
+ 1. **项目级 Agent 配置**:读取 `AGENTS.md` 或 `CLAUDE.md`(项目根目录)
31
+ 2. **规则文件**:读取 `.cursor/rules/*.mdc`(尤其含 architecture/架构 关键词的规则)
32
+ 3. **Specline 配置**:读取 `specline/config.yaml` → `context` 和 `project` 字段
33
+ 4. **代码库探索**(兜底):扫描顶层目录结构,推断模块分层和依赖方向
34
+
35
+ 提取以下架构信息(根据信息源质量标记置信度):
36
+
37
+ | 信息维度 | 提取内容 | 置信度标记 |
38
+ |---------|---------|-----------|
39
+ | 分层规则 | controllers/services/models 等层级及其职责 | ✅ 文档明确 / ⚠️ 推断 |
40
+ | 模块边界 | 各模块职责、依赖关系、被依赖关系 | ✅ 文档明确 / ⚠️ 推断 |
41
+ | 技术栈 | 语言/框架/数据库/缓存/消息队列 | ✅ 文档明确 / ⚠️ 推断 |
42
+ | 接口约定 | API 前缀、认证方式、错误格式 | ✅ 文档明确 / ⚠️ 推断 |
43
+ | 数据约束 | 核心表、缓存策略、数据流 | ✅ 文档明确 / ⚠️ 推断 |
44
+
45
+ **无架构文档时的处理**:
46
+
47
+ - 简短提示用户:「⚠️ 未发现项目显式架构文档(AGENTS.md / CLAUDE.md / .cursor/rules/),建议补充以提高后续变更的架构分析精度。」
48
+ - **不阻塞流程**,降级使用代码库目录结构推断
49
+ - 在后续 design.md 的 Architecture Impact Analysis 章节中,所有分析标注 ⚠️(推断)
50
+
24
51
  #### Step 2: 创建目录结构
25
52
 
26
53
  ```bash
@@ -131,8 +158,96 @@ specline-pipeline-gate.sh new --change "<change-name>"
131
158
  ## Component Interaction
132
159
 
133
160
  <组件/模块间交互描述>
161
+
162
+ ## Architecture Impact Analysis
163
+
164
+ > 置信度标记:✅ 基于显式文档 | ⚠️ 基于代码推断
165
+
166
+ 此章节基于 Step 1.5 的架构上下文探索结果,分析本次变更对现有系统的影响。
167
+
168
+ ### 侵入点 (Intrusion Points)
169
+
170
+ - 新增代码将插入到 `<位置>`,涉及 `<N>` 个现有文件
171
+ - 是否侵入核心模块(如认证/数据层),以及这样做的理由
172
+ - 置信度:✅/⚠️
173
+
174
+ ### 模块边界影响 (Module Boundary Impact)
175
+
176
+ - 新增 `<模块名>` 模块,放在 `<层级>`(如 controllers/services/models/utils)
177
+ - 是否改变已有模块的职责边界,以及这样做的理由
178
+ - 置信度:✅/⚠️
179
+
180
+ ### 依赖方向检查 (Dependency Direction)
181
+
182
+ - 新增依赖关系:`<A>` → `<B>`
183
+ - 方向评估:合规 / 需注意 / 违规(需说明理由)
184
+ - 是否符合项目分层规则
185
+ - 置信度:✅/⚠️
186
+
187
+ ### 数据影响 (Data Impact)
188
+
189
+ - 新增/修改的表:`<表名>`,新增/修改的字段:`<字段名>`
190
+ - 对已有查询、缓存策略、索引的影响分析
191
+ - 置信度:✅/⚠️
192
+
193
+ ### 接口兼容性 (API Compatibility)
194
+
195
+ - 新增端点:`<列表>`(方法 + 路径)
196
+ - 是否破坏已有 API/RPC 契约
197
+ - 是否遵循项目现有接口约定(前缀、认证、响应格式)
198
+ - 置信度:✅/⚠️
134
199
  ```
135
200
 
201
+ #### Step 5.5: 判断是否需要对外接口契约
202
+
203
+ 在生成完整的 design.md 之后,检查 tasks.md 是否需要对外接口契约:
204
+
205
+ **判断逻辑**(以 tasks.md 为决策源头):
206
+ - 扫描 tasks.md 中所有任务的 Type 标注
207
+ - 如果存在 `Type: frontend`、`Type: backend`、`Type: infra`、`Type: db` 且该任务 `Testable: true`(或未标注 Testable 但 tasks.md 末尾「测试文件归属」表格中标注了 specline-test-writer 负责的集成/E2E 测试)→ **需要生成契约章节**
208
+ - 如果所有任务均为 `Type: config` 或 `Type: docs`,或虽有 code 任务但均无 test-writer 负责的测试 → **跳过契约章节**
209
+
210
+ 若需要契约,在 design.md 的 Architecture Impact Analysis 章节之后追加「对外接口契约」章节:
211
+
212
+ ```markdown
213
+ ## 对外接口契约 (External Interface Contract)
214
+
215
+ > 此章节为黑盒测试(specline-test-writer)提供对外接口定义。
216
+ > Test-Writer 据此编写集成/E2E 测试,不读取任何实现源码。
217
+ > Coding Agent 必须按此契约实现对外接口。
218
+
219
+ ### CLI 命令
220
+
221
+ | 命令 | 格式 | 参数 | 输出 | 退出码 |
222
+ |------|------|------|------|--------|
223
+ | <命令名> | `<CLI调用格式>` | <参数名>: <类型> (描述) | stdout: <输出描述> | 0=成功, 1=失败 |
224
+
225
+ ### HTTP 端点
226
+
227
+ | 方法 | 路径 | 请求体/参数 | 成功响应 | 错误响应 |
228
+ |------|------|------------|----------|----------|
229
+ | <方法> | <路径> | `<请求格式描述>` | <状态码> + `<响应体>` | <状态码> `{ "error": "<消息>" }` |
230
+
231
+ ### 模块导出
232
+
233
+ | 模块文件 | 导出符号 | 签名 | 说明 |
234
+ |----------|----------|------|------|
235
+ | <文件路径> | <函数/类名> | `<签名>` | <一句话说明> |
236
+ ```
237
+
238
+ **契约生成规则**:
239
+
240
+ - **CLI 命令**:从 Spec 的 WHEN/THEN 场景反推 CLI 命令格式。如果 Spec 描述的是命令行工具行为(如 "WHEN 用户运行 `specline quickfix`"),则提取命令名和参数
241
+ - **HTTP 端点**:从 Spec 的 WHEN/THEN 场景反推 HTTP API 格式。根据行为语义推测 HTTP 方法和路径(RESTful 约定),根据 THEN 推测响应状态码和格式
242
+ - **模块导出**:从 tasks.md 的 Files 字段推导模块文件路径,从 Spec 的 WHEN/THEN 反推需要导出的函数名和签名。**只列出被外部调用的导出**(如被 CLI 入口调用的函数、被其他模块 import 的函数),不列出内部 helper
243
+ - **粒度控制**:只定义「外部可调用的接口」——CLI 命令、HTTP 端点、模块间主要导出。不定义内部私有函数/helper
244
+ - 如果某类接口不存在(如纯 CLI 工具无 HTTP 端点),对应的子章节写「(无)」而非省略
245
+
246
+ > **注意**:接口契约是技术设计决策(API 叫什么名字、参数是什么类型),应放在 design.md 而非 spec.md。Spec 负责「用户要什么」,契约负责「怎么对外暴露」。
247
+ > ```markdown
248
+ > > ⚠️ 未发现项目显式架构文档(AGENTS.md / CLAUDE.md / .cursor/rules/)。以上分析基于代码库目录结构推断,建议补充架构文档以提高后续变更的分析精度。
249
+ > ```
250
+
136
251
  #### Step 6: 生成 tasks.md
137
252
 
138
253
  写入 `specline/changes/<change-name>/tasks.md`,使用以下模板:
@@ -251,7 +366,11 @@ specline-pipeline-gate.sh new --change "<change-name>"
251
366
  - 如果仍 < 60%,记录警告但不阻塞,留给人工 Gate 1 决策
252
367
  3. **文件冲突自检**:检查第一批次中各任务的 Files 是否有交集
253
368
  - 如果有交集,修整 tasks.md(合并冲突任务或调整文件范围)
254
- 4. 完成后输出摘要:
369
+ 4. **契约一致性自检**:如果 design.md 包含「对外接口契约」章节,检查:
370
+ - tasks.md 的「测试文件归属」表格中是否有 specline-test-writer 负责的集成/E2E 测试任务(必须一致:有契约 → 有测试任务,无测试任务 → 无契约)
371
+ - 契约章节中定义的 CLI 命令/HTTP 端点/模块导出是否与 tasks.md 中对应任务的 Covers 中引用的 Scenario 相关
372
+ 5. 完成后输出摘要:
255
373
  - 生成了哪些文件
256
374
  - 共有 N 个任务,独立任务 M 个(并行度 = M/N)
257
375
  - 第 1 批次几个任务
376
+ - 是否生成了接口契约
@@ -33,6 +33,7 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
33
33
  - 是否说明了选择的架构模式
34
34
  - 是否包含关键数据流/组件交互描述
35
35
  - 是否说明了技术选型(框架、库、数据库等)
36
+ - 是否包含 Architecture Impact Analysis 章节(缺失 → error,status rejected)
36
37
 
37
38
  2. **一致性**:
38
39
  - 技术决策是否与 spec.md 中的需求对齐
@@ -42,6 +43,20 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
42
43
  - 技术选型是否有明显不合理之处(如选择不符合项目现有技术栈的组件)
43
44
  - 是否存在过度设计或设计不足
44
45
 
46
+ 4. **架构分析合理性**(新增):
47
+ - 侵入点分析是否合理 — 过度侵入核心模块但未给出充分理由 → error
48
+ - 模块边界划分是否与项目现有结构一致 — 冲突 → error
49
+ - 依赖方向是否符合分层规则 — 违规 → error
50
+ - 数据影响分析是否遗漏关键波及面(如未提及缓存失效、索引影响)→ error
51
+ - 接口兼容性分析是否覆盖所有变更端点 — 遗漏 → error
52
+ - 置信度标记是否诚实 — 推断却标 ✅ → error
53
+ - **以上任意 error → status rejected,打回要求重新修订 Spec 方案**
54
+
55
+ 5. **与现有系统一致性**(新增):
56
+ - 新增设计模式是否与项目已有约定冲突 — 冲突 → error
57
+ - 技术选型是否与项目现有技术栈不兼容 — 不兼容 → error
58
+ - **以上任意 error → status rejected**
59
+
45
60
  ### C. tasks.md 审核(新增)
46
61
 
47
62
  1. **格式完整性**:
@@ -108,6 +123,9 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
108
123
  "[tasks.md] 任务 1 和 任务 2 的 Files 有交集:都包含了 src/utils/api.ts",
109
124
  "[tasks.md] 任务 3 Testable=true 但存在上游依赖 (Depends: 1),应为 false",
110
125
  "[tasks.md] 任务 5 (Depends: none, Type: backend) 建议标记为 Testable: true",
126
+ "[design.md] 架构分析缺失:缺少 Architecture Impact Analysis 章节",
127
+ "[design.md] 依赖方向违规:billing service 直接引用 controllers/,但项目分层规则禁止服务层引用控制器层",
128
+ "[design.md] 置信度造假:侵入点分析标注 ✅ 但项目无显式架构文档,应为 ⚠️",
111
129
  "[design.md] 提到使用 Redis 缓存,但 tasks.md 中没有对应的 infra 任务",
112
130
  "[coverage] Scenario '用户登出' 未被任何任务覆盖"
113
131
  ],
@@ -119,7 +137,8 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
119
137
 
120
138
  ## 审核标准
121
139
 
122
- - status 为 "approved" 当且仅当所有维度通过(不含 warning 级问题)
140
+ - status 为 "rejected" 当存在任何 error 级问题(包括架构分析缺失、依赖方向违规、置信度造假等架构相关 error)
141
+ - status 为 "approved" 当且仅当所有维度通过(不含 error 级问题,不含 warning 级问题)
123
142
  - feedback 中每个问题一行,以 `[文件] ` 前缀标记范围
124
143
  - coverage 统计哪些 Requirement/Scenario 已被 tasks.md 的 Covers 标注覆盖
125
- - 即使是 approved,也可以有轻微的 feedback 建议(非阻塞性)
144
+ - 架构相关 error(design.md 审核维度 4-5)与 Spec Gate 中的其他 error 同等对待——直接导致 rejected,打回要求重新修订 Spec 方案
@@ -47,6 +47,7 @@ description: 执行测试并分析失败原因。语言无关,自动检测项
47
47
  |---------|---------|---------|-----------|
48
48
  | `test_bug` | 测试逻辑/断言写错了 | test-writer 修改测试代码 | 自动循环(最多 2 次) |
49
49
  | `impl_bug` | 实现代码行为不符合 Spec | coding agent 修改实现代码 | 用 Covers 追溯链定位任务后自动修复 |
50
+ | `contract_mismatch` | 实现代码的对外接口与 design.md 契约不一致(如 HTTP 路径/方法不匹配、CLI 参数签名不匹配、模块导出命名不一致) | coding agent 按契约修正代码;若契约本身有问题则回 spec-creator 修正 design.md | 优先回 coding agent 修复(最多 2 次);若 2 次后仍不一致,暂停并报告用户确认以 design.md 为准还是以代码为准 |
50
51
  | `env_issue` | 测试环境/依赖问题(如测试框架未安装) | 检查环境配置 | 暂停,告知用户 |
51
52
  | `spec_ambiguity` | Spec 描述模糊导致理解偏差 | 需要人工澄清 | **暂停流水线,等待用户确认** |
52
53
 
@@ -83,6 +84,14 @@ description: 执行测试并分析失败原因。语言无关,自动检测项
83
84
  "covers": "Requirement: CLI 错误处理, Scenario: 无效文件",
84
85
  "reason": "CLI 未对无效 task 文件进行校验,应返回 exit code 1"
85
86
  },
87
+ {
88
+ "test": "tests/integration/test_users_api.py > test_create_user_returns_201",
89
+ "error": "HTTPError: 404 Client Error for url: /api/users",
90
+ "classification": "contract_mismatch",
91
+ "task_id": "1",
92
+ "covers": "Requirement: 用户注册, Scenario: 成功注册",
93
+ "reason": "接口契约定义 POST /api/users,但实现代码注册在 POST /api/accounts/register。测试按契约调用 /api/users 收到 404"
94
+ },
86
95
  {
87
96
  "test": "tests/api.test.ts > Session persistence after restart",
88
97
  "error": "Expected session stored in Redis but stored in memory",
@@ -91,7 +100,7 @@ description: 执行测试并分析失败原因。语言无关,自动检测项
91
100
  }
92
101
  ],
93
102
  "recommendation": "fix_impl",
94
- "detail": "3 个失败中 2 个为实现代码问题(任务 2),1 个为 spec 模糊需人工澄清"
103
+ "detail": "4 个失败中 2 个为实现代码问题(任务 2),1 个为契约不一致(任务 1),1 个为 spec 模糊需人工澄清"
95
104
  }
96
105
  ```
97
106
 
@@ -44,8 +44,8 @@ description: 黑盒测试工程师——只能基于 Spec 文档编写测试,
44
44
 
45
45
  1. **不能读取实现源代码**:禁止读取任何业务逻辑、组件实现、API handler 等源码文件
46
46
  2. **只能基于以下输入**:
47
- - Spec 文档(需求规格)
48
- - `design.md`(技术设计中公开的接口定义)
47
+ - Spec 文档(需求规格,获取行为验收标准 WHEN/THEN)
48
+ - `design.md` 的「对外接口契约」章节(获取 CLI 命令、HTTP 端点、模块导出的技术签名——如果章节不存在则跳过)
49
49
  - `tasks.md`(Covers 追溯链)
50
50
  - 项目的 `package.json`/`pyproject.toml` 等**配置文件**(用于确定框架,不是实现代码)
51
51
  3. **只能通过 CLI 执行或 HTTP 调用来验证行为**,不可直接 import 内部模块或组件
@@ -53,11 +53,62 @@ description: 黑盒测试工程师——只能基于 Spec 文档编写测试,
53
53
  ## 工作方式
54
54
 
55
55
  1. 检测项目技术栈,确定测试框架
56
- 2. 仔细阅读 Spec 中的每个 Scenario
57
- 3. 对照 `tasks.md` 中的 `Covers` 追溯链,确保每个 Scenario 都有测试覆盖
58
- 4. 每个 Scenario 至少生成 1 个对应的测试函数
59
- 5. 测试函数命名遵循对应框架的约定
60
- 6. 测试函数必须包含描述性注释/名称(对应 Spec 中的 Scenario 名称)
56
+ 2. 仔细阅读 Spec 中的每个 Scenario,理解验收标准(WHEN/THEN)
57
+ 3. **读取 `design.md` 的「对外接口契约」章节**,获取 CLI 命令格式、HTTP 端点、模块导出签名
58
+ - 如果 design.md 无此章节 change 无黑盒测试任务需求,技能级自检:确认 tasks.md 末尾「测试文件归属」表格中是否确实无 specline-test-writer 负责的测试任务
59
+ 4. 对照 `tasks.md` 中的 `Covers` 追溯链,确保每个 Scenario 都有测试覆盖
60
+ 5. 每个 Scenario 至少生成 1 个对应的测试函数
61
+ 6. 测试函数命名遵循对应框架的约定
62
+ 7. 测试函数必须包含描述性注释/名称(对应 Spec 中的 Scenario 名称)
63
+
64
+ ## 合同驱动测试 (Contract-Driven Testing)
65
+
66
+ 当 design.md 包含「对外接口契约」章节时,按以下规则编写测试:
67
+
68
+ ### CLI 命令测试
69
+
70
+ 从契约的 CLI 命令表格获取命令格式和参数:
71
+ ```python
72
+ # 契约: | quickfix | `specline quickfix <description>` | description: string | stdout: summary, exit 0/1 |
73
+ def test_quickfix_success():
74
+ result = subprocess.run(["specline", "quickfix", "修复按钮"], capture_output=True, text=True)
75
+ assert result.returncode == 0
76
+ assert "summary" in result.stdout.lower()
77
+ ```
78
+
79
+ ### HTTP 端点测试
80
+
81
+ 从契约的 HTTP 端点表格获取路径和方法:
82
+ ```python
83
+ # 契约: | POST | /api/users | `{ name: string, email: string }` | 201 + `{ id: string }` | 409 `{ "error": "duplicate" }` |
84
+ def test_create_user_success():
85
+ resp = requests.post(f"{BASE_URL}/api/users", json={"name": "张三", "email": "test@example.com"})
86
+ assert resp.status_code == 201
87
+ assert "id" in resp.json()
88
+
89
+ def test_create_user_duplicate():
90
+ # 先创建一个用户
91
+ requests.post(f"{BASE_URL}/api/users", json={"name": "张三", "email": "dup@example.com"})
92
+ # 重复创建
93
+ resp = requests.post(f"{BASE_URL}/api/users", json={"name": "李四", "email": "dup@example.com"})
94
+ assert resp.status_code == 409
95
+ assert resp.json()["error"] == "duplicate"
96
+ ```
97
+
98
+ ### 模块导出测试
99
+
100
+ 从契约的模块导出表格获取函数签名,但**只通过 CLI/HTTP 间接调用**,不直接 import:
101
+ ```python
102
+ # 契约: | src/services/auth.py | createSession | `(userId: string) => Promise<Session>` |
103
+ # ❌ 不能: from src.services.auth import createSession
104
+ # ✅ 改为: 通过 HTTP API 间接测试
105
+ def test_create_session_via_api():
106
+ resp = requests.post(f"{BASE_URL}/api/sessions", json={"userId": "user-123"})
107
+ assert resp.status_code == 201
108
+ assert "sessionToken" in resp.json()
109
+ ```
110
+
111
+ **重要**:模块导出契约主要用于 Code Review 阶段校验实现一致性。test-writer 仍然只通过外部接口(CLI/HTTP)测试行为。
61
112
 
62
113
  ## 测试映射规则(语言无关)
63
114
 
@@ -44,7 +44,7 @@ run_a2_a3_reverse() {
44
44
  tmp_tasks_scens=$(mktemp) || return 1
45
45
 
46
46
  _a2a3_cleanup() {
47
- rm -f "$tmp_spec_reqs" "$tmp_spec_scens" "$tmp_tasks_reqs" "$tmp_tasks_scens"
47
+ rm -f "${tmp_spec_reqs:-}" "${tmp_spec_scens:-}" "${tmp_tasks_reqs:-}" "${tmp_tasks_scens:-}"
48
48
  }
49
49
  trap _a2a3_cleanup RETURN
50
50
 
@@ -781,6 +781,119 @@ gate_spec() {
781
781
  pass "Spec Gate 全部通过"
782
782
  }
783
783
 
784
+ # ===== 对外接口契约签名检查 =====
785
+ # 以 tasks.md 为决策源头:有 test-writer 任务才需要契约,有契约才检查签名存在性
786
+ check_contract_signatures() {
787
+ local tasks_file="$1"
788
+ local design_file="$PROJECT_ROOT/specline/changes/$CHANGE/design.md"
789
+
790
+ # 1. 判断是否需要契约:检查 tasks.md 末尾「测试文件归属」表格中是否有 specline-test-writer 负责的集成/E2E 测试
791
+ local has_test_writer=false
792
+ if grep -q 'specline-test-writer' "$tasks_file" 2>/dev/null; then
793
+ has_test_writer=true
794
+ fi
795
+
796
+ if [ "$has_test_writer" = false ]; then
797
+ echo "ℹ️ tasks.md 中无 specline-test-writer 负责的测试任务,跳过契约检查"
798
+ return 0
799
+ fi
800
+
801
+ # 2. 有 test-writer 任务 → 设计文档必须有契约章节
802
+ if ! grep -q '^## 对外接口契约' "$design_file" 2>/dev/null; then
803
+ fail "tasks.md 中存在 specline-test-writer 负责的集成/E2E 测试任务,但 design.md 缺少「对外接口契约」章节。请运行 /specline-pipeline propose 重新生成,或手动补充"
804
+ fi
805
+
806
+ echo "正在检查对外接口契约签名..."
807
+
808
+ local contract_errors=""
809
+
810
+ # 3. 解析契约章节中的 CLI 命令,检查命令注册代码是否存在
811
+ local in_cli_section=false
812
+ while IFS= read -r line; do
813
+ # 检测章节边界
814
+ if echo "$line" | grep -q '^### CLI'; then
815
+ in_cli_section=true
816
+ continue
817
+ elif echo "$line" | grep -q '^### HTTP'; then
818
+ in_cli_section=false
819
+ continue
820
+ fi
821
+
822
+ if [ "$in_cli_section" = true ] && echo "$line" | grep -q '^|.*|.*|.*|.*|.*|$'; then
823
+ local cli_cmd
824
+ cli_cmd=$(echo "$line" | awk -F'|' '{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}')
825
+ # 跳过表头行
826
+ if [ "$cli_cmd" != "命令" ] && [ -n "$cli_cmd" ]; then
827
+ # grep 搜索命令注册(CLI 命令通常在 specline-pipeline-gate.sh 或其他 .sh 文件中有 case 分支)
828
+ if ! grep -rq "$cli_cmd" "$PROJECT_ROOT" --include="*.sh" --include="*.py" --include="*.ts" --include="*.go" 2>/dev/null; then
829
+ contract_errors="${contract_errors}
830
+ CLI 命令 '$cli_cmd' 在契约中定义,但未在代码中找到注册(搜索了 .sh/.py/.ts/.go 文件)"
831
+ fi
832
+ fi
833
+ fi
834
+ done < <(awk '/^## 对外接口契约/,/^## [^对]/' "$design_file" 2>/dev/null)
835
+
836
+ # 4. 解析契约章节中的 HTTP 端点,检查路由注册代码是否存在
837
+ local in_http_section=false
838
+ while IFS= read -r line; do
839
+ if echo "$line" | grep -q '^### HTTP'; then
840
+ in_http_section=true
841
+ continue
842
+ elif echo "$line" | grep -qE '^### (模块导出|CLI)'; then
843
+ in_http_section=false
844
+ continue
845
+ fi
846
+
847
+ if [ "$in_http_section" = true ] && echo "$line" | grep -q '^|.*|.*|.*|.*|.*|$'; then
848
+ local http_path
849
+ http_path=$(echo "$line" | awk -F'|' '{gsub(/^[ \t]+|[ \t]+$/, "", $3); print $3}')
850
+ # 跳过表头行
851
+ if [ "$http_path" != "路径" ] && [ -n "$http_path" ]; then
852
+ # 去掉路径参数中的动态段(如 /api/users/:id → /api/users/ 前缀匹配)
853
+ local static_prefix
854
+ static_prefix=$(echo "$http_path" | sed 's/:[a-zA-Z_][a-zA-Z0-9_]*/FINDANY/' | sed 's/\/FINDANY//g')
855
+ if ! grep -rq "$static_prefix" "$PROJECT_ROOT" --include="*.py" --include="*.ts" --include="*.tsx" --include="*.go" --include="*.rs" 2>/dev/null; then
856
+ contract_errors="${contract_errors}
857
+ HTTP 路径 '$http_path' 在契约中定义,但未在代码中找到路由注册(搜索了 .py/.ts/.tsx/.go/.rs 文件)"
858
+ fi
859
+ fi
860
+ fi
861
+ done < <(awk '/^## 对外接口契约/,/^## [^对]/' "$design_file" 2>/dev/null)
862
+
863
+ # 5. 解析契约章节中的模块导出
864
+ local in_exports_section=false
865
+ while IFS= read -r line; do
866
+ if echo "$line" | grep -q '^### 模块导出'; then
867
+ in_exports_section=true
868
+ continue
869
+ elif echo "$line" | grep -qE '^## [^对]' && [ "$in_exports_section" = true ]; then
870
+ break
871
+ fi
872
+
873
+ if [ "$in_exports_section" = true ] && echo "$line" | grep -q '^|.*|.*|.*|.*|$'; then
874
+ local export_file export_name
875
+ export_file=$(echo "$line" | awk -F'|' '{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}')
876
+ export_name=$(echo "$line" | awk -F'|' '{gsub(/^[ \t]+|[ \t]+$/, "", $3); print $3}')
877
+ # 跳过表头行
878
+ if [ "$export_file" != "模块文件" ] && [ -n "$export_file" ] && [ -n "$export_name" ]; then
879
+ if [ ! -f "$PROJECT_ROOT/$export_file" ]; then
880
+ contract_errors="${contract_errors}
881
+ 模块文件 '$export_file' 在契约中定义,但文件不存在"
882
+ elif ! grep -q "export.*$export_name\b\|func $export_name\b\|def $export_name\b\|pub fn $export_name\b" "$PROJECT_ROOT/$export_file" 2>/dev/null; then
883
+ contract_errors="${contract_errors}
884
+ 导出符号 '$export_name' 在契约中定义,但未在 '$export_file' 中找到对应声明"
885
+ fi
886
+ fi
887
+ fi
888
+ done < <(awk '/^## 对外接口契约/,/^## [^对]/' "$design_file" 2>/dev/null)
889
+
890
+ if [ -n "$contract_errors" ]; then
891
+ fail "对外接口契约签名不一致:${contract_errors}"
892
+ fi
893
+
894
+ pass "对外接口契约签名检查通过"
895
+ }
896
+
784
897
  gate_build() {
785
898
  load_project_config
786
899
 
@@ -905,6 +1018,11 @@ gate_build() {
905
1018
  fi
906
1019
  fi
907
1020
 
1021
+ # 对外接口契约检查(以 tasks.md 为决策源头)
1022
+ if [ -f "$tasks_file" ]; then
1023
+ check_contract_signatures "$tasks_file"
1024
+ fi
1025
+
908
1026
  write_gate_passed "phases.coding.gates.build_gate"
909
1027
  pass "Build Gate 全部通过"
910
1028
  }
@@ -162,6 +162,32 @@ What would you like to do?
162
162
  - Use contextFiles from CLI output, don't assume specific file names
163
163
  - **Hook blocked → no silent fallback**: If this skill is invoked because a coding subagent (specline-frontend-dev / specline-backend-dev) was blocked by a hook, you MUST first notify the user of the blocking cause and attempt diagnosis. Do not silently execute tasks that should have been handled by the blocked subagent. Reference the Hook Blocking Resolution Protocol in the specline-pipeline skill.
164
164
 
165
+ ---
166
+
167
+ ## Anti-Rationalization 表格
168
+
169
+ 逐任务实现时,Agent 容易偏离规范:
170
+
171
+ | 借口 | 现实 |
172
+ |------|------|
173
+ | "不用读 Spec/Design/Tasks,我理解需求" | 记忆不可靠。实现前读上下文文件是防止方向偏离的最便宜保险。 |
174
+ | "顺便把这个相邻函数也重构了" | Scope Discipline 是 Core Behaviors。越界修改让 Code Review 和回溯都变困难。 |
175
+ | "checkbox 我最后一起标记" | Checkbox 是断点续跑的唯一信号源。不及时标记意味着下次恢复时状态丢失。 |
176
+ | "这个任务没有测试也没关系,下一个任务会补" | 每个 Testable=true 的任务必须产出测试。推迟 = 不写。 |
177
+ | "tasks.md 的 Covers 追溯链我不用管,代码写对就行" | Covers 链是 Spec → Code 的可追溯纽带。不维护它,Code Review 和测试失败定位都失去锚点。 |
178
+
179
+ ## Verification Checklist
180
+
181
+ 每完成一个任务后自查,全部完成后终查:
182
+
183
+ - [ ] 开始前已读 proposal.md / spec.md / design.md / tasks.md
184
+ - [ ] 每个任务的实现范围未超出 Files 声明
185
+ - [ ] 每个 Testable=true 的任务产出了测试文件(在 tests/unit/ 或 tests/models/)
186
+ - [ ] tasks.md 中每个已完成任务的 `[ ]` 已改为 `[x]`
187
+ - [ ] task-{id}-result.json 已写入 .tmp/ 目录
188
+ - [ ] 本 session 修改的文件与 tasks.md 的 Files 声明一致
189
+ - [ ] 未修改其他任务负责的文件
190
+
165
191
  ### 暂停场景处理
166
192
 
167
193
  当实现过程中出现以下情况时,暂停并等待用户指引:
@@ -147,3 +147,27 @@ All artifacts complete. All tasks complete.
147
147
  - Show clear summary of what happened
148
148
  - If sync is requested, use specline-sync-specs approach (agent-driven)
149
149
  - If delta specs exist, always run the sync assessment and show the combined summary before prompting
150
+
151
+ ---
152
+
153
+ ## Anti-Rationalization 表格
154
+
155
+ 归档是流水线的最后一步,松懈的代价是污染长期记录:
156
+
157
+ | 借口 | 现实 |
158
+ |------|------|
159
+ | "不用检查完成度,反正用户说可以归档了" | 用户说可以不代表真的可以。Artifact 和 task 完成度检查是归档前的最后防线。 |
160
+ | "Delta spec 不用同步,下次再说" | 未同步的 Delta spec 意味着 spec 与代码脱节。归档后几乎不会再有人回来补。 |
161
+ | "归档就是移动目录,不需要通知用户" | 归档改变了 change 的可见性和可修改性。用户需要知道发生了什么。 |
162
+ | "警告不用管,自动继续就行" | 警告(artifact 不完整、task 未完成)是信号。归档时应确认而非忽略。 |
163
+
164
+ ## Verification Checklist
165
+
166
+ 归档前自查:
167
+
168
+ - [ ] Artifact 完成度已检查(`specline-pipeline-gate.sh artifacts --json`)
169
+ - [ ] Task 完成度已检查(tasks.md checkbox 状态)
170
+ - [ ] 任何警告/不完整项已向用户确认
171
+ - [ ] Delta spec sync 决策已完成(存在则展示摘要→询问;不存在则跳过)
172
+ - [ ] 归档目录已创建(`specline/changes/archive/YYYY-MM-DD-<name>/`)
173
+ - [ ] 归档摘要已展示给用户
@@ -128,6 +128,7 @@ Agent: 搜索的实现复杂度从「ctrl+f」到「ES 全文检索引擎」跨
128
128
  |----------|------|
129
129
  | 用户提供 ≥3 个独立信息点 | 生成 5-10 个编号追问,每个瞄准一个具体缺口 |
130
130
  | 多轮后陷入停滞 | 聚焦推进决策:「这三个方向里,哪个风险你最不能接受?」 |
131
+ | 用户答案含抽象形容词(可扩展/现代/稳健)或引用外部权威(行业标准/最佳实践)而未给出具体标准 | **「想要 vs 应该想要」探针**:追问一句:「如果不需要向任何人解释这个决定,你真正想要什么?」——用户往往在给出"正确的回答"而非"真实的回答" |
131
132
  | 信息量不足 | 退回开放式引导,不强行生成 |
132
133
 
133
134
  问题可一句话回答。数量弹性:信息越充分、缺口越少。不凑数。问题前加:「可以快捷回复(如:`1-全文, 2-小于500ms, 5-没想过`),不用全答。」
@@ -161,6 +162,8 @@ Agent: 关键缺口确认——快捷回复(`1-postgres, 2-能接受, 5-没想
161
162
  要继续深挖某个 ❌/⚠️ 维度,还是先发散看看更多方向?
162
163
  ```
163
164
 
165
+ > 💡 **可选信心自检**:如果你卡住了不确定还要聊多深,可以加一句:「我目前对这个方向大概 X% 信心,主要缺口是 Y。」这不是考试,只是帮我判断还需要探索到什么程度。信心 < 70% 时特别有用——它能帮双方聚焦剩余的不确定性。
166
+
164
167
  ---
165
168
 
166
169
  ### BROAD 层:广度发散
@@ -485,3 +488,17 @@ C. 搁置 — 保存到 notes/<date>-explore-notes.md,以后再说
485
488
  ### End Decision Assistance
486
489
 
487
490
  探索自然暂停且有 ≥1 个可捕获结论(或明确的无结论共识)时,出示 A/B/C 三选项菜单。用户选择「继续聊」时不做任何推进。用户尚未在 change 上下文中时,A 选项引导先创建 change。
491
+
492
+ ---
493
+
494
+ ## Anti-Rationalization 表格
495
+
496
+ 探索模式也会遇到"该继续还是该结束"的内心斗争。以下借口需要警惕:
497
+
498
+ | 借口 | 现实 |
499
+ |------|------|
500
+ | "我已经理解了,不需要继续探索" | 如果不能用 3 句话说清设计核心("向新人解释"测试),说明还没收敛。 |
501
+ | "探索是在浪费时间" | 探索中暴露的 1 个盲区,在实现阶段纠正的成本是 10 倍。思考时间是投资,不是浪费。 |
502
+ | "结论很明显,直接开始就行" | "明显"的结论往往有未声明的假设。快速做一遍 SHARP 层的压力测试只需 2 分钟。 |
503
+ | "我画个图就够了,不需要问用户" | 探索的结论最终要服务用户决策。不确认的探索只是自言自语。 |
504
+ | "这些维度以后再想" | "以后"常常不会来。覆盖度可视化中标记 🚫(故意跳过)是可接受的,标记 ❌(完全未碰)而直接结束是不负责任的。 |