specline 1.3.0 → 1.3.2

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.
@@ -9,40 +9,11 @@ description: >-
9
9
 
10
10
  ---
11
11
 
12
- ## Layer 0: Session 绑定与 Pipeline 切换
12
+ ## Layer 0: Session 绑定
13
13
 
14
- ### 概述
15
-
16
- 每个 Cursor 会话通过 `session_id` **显式绑定**到特定的 Pipeline(通过 `/specline-pipeline --change <name>` 命令)。Hook 脚本通过查 `specline/.pipeline-sessions.json` 表获得确定性映射。
17
-
18
- ### 启动时行为
19
-
20
- `sessionStart` Hook (`specline-session-start.sh`) 自动处理:
21
-
22
- 1. **已绑定且有效** → 直接使用,注入正确的阶段约束
23
- 2. **无绑定** → 透明放行(`echo '{}'`),不自动扫描活跃 pipeline,不注入任何上下文
24
- 3. **过期绑定清理** → `bound_at` 超过 7 天的绑定自动删除
25
- 4. **脏数据清理** → 绑定指向已归档/不存在的 Pipeline 时自动删除
26
-
27
- > **重要**:sessionStart 不再自动扫描和绑定活跃 pipeline。要绑定到一个 Pipeline,需要显式执行 `/specline-pipeline --change <name>`。
28
-
29
- ### 用户要求切换 Pipeline 时
30
-
31
- 当用户在对话中说「帮我处理 \<other-change\>」:
32
-
33
- 1. 检查当前 Pipeline 是否在安全切换点(Gate 之后、批次之间)
34
- 2. 如果当前在 CODING 批次中间 → 提示「请先完成当前批次」
35
- 3. 否则执行切换:
36
-
37
- ```bash
38
- .cursor/hooks/specline-pipeline-gate.sh bind <session_id> <other_change>
39
- ```
40
-
41
- 绑定后,下一个 Hook 调用立即生效。
42
-
43
- ### Pipeline 归档时自动解绑
44
-
45
- `gate archive --execute` 会自动清理所有绑定到该 Pipeline 的 session 记录,无需手工操作。
14
+ Session 通过 `specline-pipeline-gate.sh bind <session_id> <change>` 绑定到 Pipeline。
15
+ 绑定后 sessionStart Hook 自动注入阶段上下文,归档时自动解绑。
16
+ 切换 Pipeline 需在安全切换点(Gate 之后、批次之间)执行。
46
17
 
47
18
  ---
48
19
 
@@ -97,18 +68,7 @@ description: >-
97
68
 
98
69
  ### Quickfix vs Pipeline 边界判断
99
70
 
100
- | 维度 | Quickfix (`/specline-quickfix`) | Pipeline (`/specline-pipeline`) |
101
- |------|-------------------------------|-------------------------------|
102
- | 文件改动数 | 1-3 个 | 4+ 个 |
103
- | 关注点 | 单一关注点 | 多关注点/跨模块 |
104
- | 架构变更 | 无新架构/新组件 | 需要新组件/新 API |
105
- | 测试 | 不需要新测试 | 需要写新测试 |
106
- | 典型场景 | 修 bug、改配置、文档微调 | 新增功能、重构 |
107
- | 产出 | summary.md + files-changed.json | proposal/design/tasks/specs + 全部测试 |
108
- | 人工确认 | 0 个 | 3 个 |
109
- | 耗时 | 1-3 分钟 | 10-30 分钟 |
110
-
111
- **使用建议**:如果不确定,优先用 quickfix。如果需要更严格的流程保证,用 pipeline。
71
+ **Quickfix vs Pipeline**:小改动(1-3 文件、单一关注点)使用 `/specline-quickfix`,多功能/跨模块改动使用 `/specline-pipeline`。详细对比见 `specline-quickfix` Skill。
112
72
 
113
73
  ---
114
74
 
@@ -175,13 +135,7 @@ specline-spec-reviewer 审核三份文件:
175
135
  .cursor/hooks/specline-pipeline-gate.sh spec --change "<name>"
176
136
  ```
177
137
 
178
- 校验内容:
179
- - ✓ `proposal.md` 存在
180
- - ✓ `design.md` 存在
181
- - ✓ `tasks.md` 存在,且每个任务含 `Type:`、`Depends:`、`Covers:`、`Files:` 标注
182
- - ✓ 每个 Requirement 至少被 1 个 task 的 `Covers:` 引用(通过 spec-review.json 的 coverage 字段)
183
- - ✓ 第 1 批次任务的 `Files` 集合互不重叠
184
- - ✓ 至少 1 个任务 `Depends: (none)`
138
+ 校验内容:`proposal.md` + `design.md` + `tasks.md` 存在且每个任务标注完整(Type/Depends/Covers/Files),每个 Requirement 至少被 1 个 task 引用,第 1 批次 Files 无重叠,至少 1 个任务 Depends: (none)。
185
139
 
186
140
  exit code 0 = 通过,写入 passed。exit code != 0 = 失败,读取 stderr 展示给用户。
187
141
 
@@ -191,46 +145,7 @@ exit code 0 = 通过,写入 passed。exit code != 0 = 失败,读取 stderr
191
145
 
192
146
  Spec Gate 通过后,使用 `AskUserQuestion` 工具请求确认。展示内容包括:需求提案摘要、功能需求列表、任务拆解概览(含并行组)。
193
147
 
194
- > **用户交互规范**:所有需要用户做选择的交互,必须使用 `AskUserQuestion` 工具而非自由文本询问。结构化问题能让 Cursor 在单次请求中完成交互,避免额外轮次。
195
-
196
- AskUserQuestion 使用模式:
197
-
198
- ```javascript
199
- AskUserQuestion({
200
- title: "简洁标题",
201
- questions: [{
202
- id: "unique_id",
203
- prompt: "问题描述",
204
- options: [
205
- { id: "option_a", label: "选项 A 的描述" },
206
- { id: "option_b", label: "选项 B 的描述" }
207
- ],
208
- allow_multiple: false // 单选;需要多选时设为 true
209
- }]
210
- })
211
- ```
212
-
213
- Human Gate 1 具体交互:
214
-
215
- ```javascript
216
- AskUserQuestion({
217
- title: "确认 Spec 和任务规划",
218
- questions: [{
219
- id: "spec_confirm",
220
- prompt: "specline-spec-creator 已生成 spec / design / tasks 文件并通过格式校验。请确认:\n" +
221
- "1. 需求描述是否准确?\n" +
222
- "2. 任务拆解是否合理?独立任务是否足够?\n" +
223
- "3. 验收场景是否覆盖核心路径和异常路径?",
224
- options: [
225
- { id: "approve", label: "确认通过,继续编码阶段" },
226
- { id: "reject", label: "不通过,手动修改后重新审核" }
227
- ]
228
- }]
229
- })
230
- ```
231
-
232
- - `approve` → 写入 `human_gate_1.passed = true`,进入 Phase 2
233
- - `reject` → 等待用户修改 spec/tasks 文件后,回到 Step 3 重新审核
148
+ Human Gate 1 具体交互:使用 `AskUserQuestion`,title="确认 Spec 和任务规划",选项:`approve`(确认通过,继续编码)/ `reject`(不通过,手动修改后重新审核)。
234
149
 
235
150
  ### Phase 2: CODING
236
151
 
@@ -256,30 +171,7 @@ Track A 和 Track B 同时启动,互不阻塞。test-writer 在 Coding 全部
256
171
 
257
172
  **6a. 启动 specline-test-writer(与 Coding 阶段 Agent 同时启动)**:
258
173
 
259
- ```javascript
260
- // 在派发 coding agent 的同时,启动 specline-test-writer
261
- Task({
262
- subagent_type: "specline-test-writer",
263
- description: `编写测试: ${changeName}`,
264
- prompt: `
265
- 你收到一个测试编写任务。请基于 Spec 编写所有测试用例。
266
-
267
- ## 上下文文件(只读参考)
268
- - Spec: specline/changes/${changeName}/specs/*/spec.md
269
- - Tasks: specline/changes/${changeName}/tasks.md
270
-
271
- ## 关键约束
272
- 1. 你是黑盒测试工程师,不能读取实现源代码
273
- 2. 先检测项目的测试框架(读配置文件),按项目实际语言和框架编写测试
274
- 3. 每个 Scenario 至少生成 1 个对应的测试函数(命名遵循框架约定)
275
- 4. 基于 Covers 追溯链,确保每个 Scenario 都有测试
276
- 5. 只编写 tests/integration/** 和 tests/e2e/** 目录下的测试,不得编写 tests/unit/** 和 tests/models/** 下的测试(单元测试由 coding agent 的 TDD 流程负责)
277
-
278
- ## 产出报告
279
- 完成后在 specline/changes/${changeName}/.tmp/test-code-result.json 写入状态(含 test_framework / language / test_dir / scenarios_covered 等字段)
280
- `
281
- })
282
- ```
174
+ 使用 Task 工具,subagent_type="specline-test-writer",prompt 中包含 changeName/Spec/Tasks 路径和黑盒约束。specline-test-writer 只编写 tests/integration/** 和 tests/e2e/** 下的测试,产出 test-code-result.json。
283
175
 
284
176
  **6b. 解析 tasks.md,构建任务 DAG**:
285
177
 
@@ -287,58 +179,15 @@ Task({
287
179
 
288
180
  > **断点续跑时的任务状态恢复**:解析 tasks.md 时同时读取每个任务的 checkbox 状态(`[x]` 表示已完成,`[ ]` 表示未完成)。已完成的任务在 DAG 中标记为 `status: "completed"`,后续批次派发时自动跳过。
289
181
 
290
- 解析算法(jq + grep):
291
-
292
- ```bash
293
- TASKS_FILE="specline/changes/<name>/tasks.md"
294
-
295
- # 提取每个任务的核心元数据
296
- # 期望输出格式:TASK_NUM|TYPE|DEPS|COVERS|FILES|TESTABLE
297
- rg -N --no-line-number '^## \d+\.|^- \*\*Type\*\*:|^- \*\*Depends\*\*:|^- \*\*Covers\*\*:|^- \*\*Files\*\*:|^- \*\*Testable\*\*:' "$TASKS_FILE" | while read -r line; do
298
- # 解析并按批次分组
299
- ...
300
- done
301
-
302
- # 构建任务列表写入状态文件(含 Files 用于冲突检测,Covers 用于追溯,Testable 用于 TDD 判定)
303
- # Testable 缺失时默认为 false(向后兼容)
304
- jq --argjson tasks '[
305
- {"id":"1","type":"backend","deps":[],"batch":1,"status":"pending","testable":true,"covers":"Requirement: 数据模型","files":["server/models.py"]},
306
- {"id":"2","type":"frontend","deps":[],"batch":1,"status":"pending","testable":false,"covers":"Requirement: 登录页面","files":["src/components/Login.tsx"]}
307
- ]' '.phases.coding.tasks = $tasks' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
308
- ```
309
-
310
- 批次划分规则:
311
- - 批次 1:所有 `Depends: (none)` 的任务
312
- - 批次 N:所有依赖仅限于 1..N-1 批次内已完成任务的任务
182
+ 解析算法:
183
+ 1. 用 grep 提取每个任务的 `Type`、`Depends`、`Covers`、`Files`、`Testable` 元数据
184
+ 2. 批次划分:批次 1 = 所有 `Depends: (none)` 的任务,批次 N = 依赖仅限于 1..N-1 批次已完成任务的任务
185
+ 3. 用 jq 构建任务列表写入状态文件,Testable 缺失默认为 false(向后兼容)
186
+ 4. 断点续跑时同步 tasks.md 的 `[x]`/`[ ]` checkbox 状态到状态文件
313
187
 
314
188
  **6c. 文件冲突检测(每批次派发前)**:
315
189
 
316
- 在派发每批任务之前,将当前批次所有任务的 `Files` 按路径前缀分为三类进行冲突检测:
317
-
318
- **文件类型分类规则**:
319
-
320
- | 文件类型 | 路径前缀 | 编写者 | 说明 |
321
- |---------|---------|--------|------|
322
- | `implementation` | 不以 `tests/` 开头 | coding agent | 实现代码、配置文件、文档等 |
323
- | `unit_test` | `tests/unit/` 或 `tests/models/` | coding agent(TDD) | 白盒单元测试 |
324
- | `other_test` | `tests/integration/` 或 `tests/e2e/` | test-writer | 黑盒集成/E2E 测试 |
325
-
326
- **冲突判定逻辑**:
327
-
328
- | 场景 | 判定 |
329
- |------|------|
330
- | 同类型文件重叠(均为 `implementation`、`unit_test` 或 `other_test`) | **冲突** |
331
- | `unit_test` 与 `other_test` 重叠 | **不冲突**(目录隔离保证互不干扰) |
332
- | `implementation` 与任何测试文件重叠 | **不冲突**(实现和测试天然分离) |
333
-
334
- ```bash
335
- # 伪代码:基于文件类型的三类冲突检测算法
336
- # 1. 读取当前批次所有任务的 files 数组
337
- # 2. 对每个文件按路径前缀分类:implementation / unit_test / other_test
338
- # 3. 在同类型文件集合内检查是否有交集
339
- # 4. 如果同类型 files 有交集 → 标记冲突任务对,暂停并报告用户
340
- # 5. 跨类型重叠不标记为冲突
341
- ```
190
+ 将当前批次所有任务的 `Files` 按路径前缀分为三类(`implementation` / `unit_test` / `other_test`),同类型文件重叠视为冲突。跨类型重叠不冲突(测试目录与实现目录天然隔离)。
342
191
 
343
192
  #### Step 7: 按批次并发派发 Coding Agent
344
193
 
@@ -357,169 +206,10 @@ Type: docs → subagent_type: "specline-config-dev"(Markdown 文档、Skil
357
206
 
358
207
  每个任务启动一个独立的子 Agent:
359
208
 
360
- ```javascript
361
- for (const task of currentBatchTasks) {
362
- // 根据 Type 选择对应的 agent 类型
363
- let agentType;
364
- switch (task.type) {
365
- case "frontend": agentType = "specline-frontend-dev"; break;
366
- case "backend": case "infra": case "db": agentType = "specline-backend-dev"; break;
367
- case "config": case "docs": agentType = "specline-config-dev"; break;
368
- default: agentType = "specline-backend-dev";
369
- }
370
-
371
- // 根据 Testable 和 Type 构造 Prompt
372
- let prompt;
373
- if (task.testable === true) {
374
- // === TDD prompt(Testable: true) ===
375
- prompt = `
376
- 你收到一个编码任务(Type: ${task.type}, Testable: true),请按 TDD(测试驱动开发)方式实现本任务范围内的代码。
377
-
378
- ## 上下文文件(只读参考)
379
- - Spec: specline/changes/${changeName}/specs/${capability}/spec.md
380
- - Design: specline/changes/${changeName}/design.md
381
- - Tasks: specline/changes/${changeName}/tasks.md
382
-
383
- ## 当前任务(只实现这个)
384
- 任务 ID: ${task.id}
385
- 覆盖需求: ${task.covers}
386
- 预期文件: ${task.files}
387
-
388
- 从 tasks.md 中提取的任务 ${task.id} 的完整描述:
389
- ---
390
- ${task.content}
391
- ---
392
-
393
- ## TDD 约束(RED-GREEN-REFACTOR)
394
-
395
- 你必须按以下 TDD 循环编写代码:
396
-
397
- ### RED 阶段
398
- 1. 分析 Spec 中本任务覆盖的 Scenario,提取需要测试的逻辑单元
399
- 2. 在 tests/unit/<module>/test_<feature>.{ext} 下编写测试文件
400
- 3. 每个 Scenario 至少 1 个测试函数/方法
401
- 4. 覆盖:Happy Path + 边界条件(空值、极值、边界值)+ 异常路径(错误输入、异常状态)
402
- 5. 运行测试,确认全部 FAIL(RED)
403
-
404
- ### GREEN 阶段
405
- 6. 编写最小实现代码,只使当前测试通过
406
- 7. 不编写测试未覆盖的逻辑
407
- 8. 运行测试,确认全部 PASS(GREEN)
408
-
409
- ### REFACTOR 阶段
410
- 9. 重构实现代码改善结构(提取方法、消除重复、优化命名)
411
- 10. 运行测试,确保持续 PASS
412
- 11. 如果需要,补充缺失的边界条件测试
413
-
414
- ## 关键约束
415
- 1. 只修改本任务 Files 范围内的文件
416
- 2. 不修改其他任务负责的文件
417
- 3. 与已完成任务的接口约定必须遵守(参考已生成的接口/类型定义文件)
418
- 4. 确认过 design.md 中的技术决策后再动手
419
- 5. 测试文件只能写入 tests/unit/ 或 tests/models/ 目录
420
- 6. 不得修改 tests/integration/ 或 tests/e2e/ 目录下的文件(属于 test-writer)
421
- 7. **完成后必须将 tasks.md 中本任务的 \`[ ]\` 改为 \`[x]\`**(方便断点续跑识别进度)
422
-
423
- ## 产出报告
424
- 完成后在 specline/changes/${changeName}/.tmp/task-${task.id}-result.json 写入:
425
- {
426
- "task_id": "${task.id}",
427
- "type": "${task.type}",
428
- "testable": true,
429
- "covers": "${task.covers}",
430
- "status": "completed",
431
- "files_changed": [...],
432
- "test_files": ["tests/unit/...", ...],
433
- "tests_passed": true,
434
- "summary": "..."
435
- }
436
- `;
437
- } else if (["frontend", "backend", "infra", "db"].includes(task.type)) {
438
- // === 标准编码 prompt(Testable: false,有代码逻辑) ===
439
- prompt = `
440
- 你收到一个编码任务(Type: ${task.type}, Testable: false),请只实现本任务范围内的代码。
441
-
442
- ## 上下文文件(只读参考)
443
- - Spec: specline/changes/${changeName}/specs/${capability}/spec.md
444
- - Design: specline/changes/${changeName}/design.md
445
- - Tasks: specline/changes/${changeName}/tasks.md
446
-
447
- ## 当前任务(只实现这个)
448
- 任务 ID: ${task.id}
449
- 覆盖需求: ${task.covers}
450
- 预期文件: ${task.files}
451
-
452
- 从 tasks.md 中提取的任务 ${task.id} 的完整描述:
453
- ---
454
- ${task.content}
455
- ---
456
-
457
- ## 约束
458
- 1. 只修改本任务 Files 范围内的文件
459
- 2. 不修改其他任务负责的文件
460
- 3. 与已完成任务的接口约定必须遵守(参考已生成的接口/类型定义文件)
461
- 4. 确认过 design.md 中的技术决策后再动手
462
- 5. **完成后必须将 tasks.md 中本任务的 \`[ ]\` 改为 \`[x]\`**(方便断点续跑识别进度)
463
-
464
- ## 产出报告
465
- 完成后在 specline/changes/${changeName}/.tmp/task-${task.id}-result.json 写入:
466
- {
467
- "task_id": "${task.id}",
468
- "type": "${task.type}",
469
- "testable": false,
470
- "covers": "${task.covers}",
471
- "status": "completed",
472
- "files_changed": [...],
473
- "summary": "..."
474
- }
475
- `;
476
- } else {
477
- // === 配置/文档 prompt(Type: config/docs) ===
478
- prompt = `
479
- 你收到一个编码任务(Type: ${task.type}),请只实现本任务范围内的代码。
480
-
481
- ## 上下文文件(只读参考)
482
- - Spec: specline/changes/${changeName}/specs/${capability}/spec.md
483
- - Design: specline/changes/${changeName}/design.md
484
- - Tasks: specline/changes/${changeName}/tasks.md
485
-
486
- ## 当前任务(只实现这个)
487
- 任务 ID: ${task.id}
488
- 覆盖需求: ${task.covers}
489
- 预期文件: ${task.files}
490
-
491
- 从 tasks.md 中提取的任务 ${task.id} 的完整描述:
492
- ---
493
- ${task.content}
494
- ---
495
-
496
- ## 约束
497
- 1. 只修改本任务 Files 范围内的文件
498
- 2. 不修改其他任务负责的文件
499
- 3. 与已完成任务的接口约定必须遵守(参考已生成的接口/类型定义文件)
500
- 4. 确认过 design.md 中的技术决策后再动手
501
- 5. **完成后必须将 tasks.md 中本任务的 \`[ ]\` 改为 \`[x]\`**(方便断点续跑识别进度)
502
-
503
- ## 产出报告
504
- 完成后在 specline/changes/${changeName}/.tmp/task-${task.id}-result.json 写入:
505
- {
506
- "task_id": "${task.id}",
507
- "type": "${task.type}",
508
- "covers": "${task.covers}",
509
- "status": "completed",
510
- "files_changed": [...],
511
- "summary": "..."
512
- }
513
- `;
514
- }
515
-
516
- Task({
517
- subagent_type: agentType,
518
- description: `实现任务 ${task.id}: ${task.title} [${task.type}]${task.testable ? ' (TDD)' : ''}`,
519
- prompt: prompt
520
- })
521
- }
522
- ```
209
+ 根据 task.type 和 task.testable 从 `templates/subagent-prompts.md` 中选择对应模板,填充变量后作为子 Agent prompt。模板选择规则:
210
+ - testable=true Template 1 (TDD)
211
+ - testable=false frontend/backend/infra/db Template 2 (Standard)
212
+ - config/docs → Template 3 (Config/Docs)
523
213
 
524
214
  **7b. 等待当前批次所有 Agent 完成后**:
525
215
  1. 验证每个 Agent 的产出报告(`specline/changes/<name>/.tmp/task-<id>-result.json`)
@@ -599,24 +289,7 @@ code-review.json 中 unit test 相关的 finding 标注 `type` 为 `"unit_test_q
599
289
 
600
290
  > **策略判断**:读取 `specline/config.yaml` → `pipeline.human_gate_policy`。若为 `minimal` 或 `none` → 跳过此 HG,直接写入 `human_gate_2.passed = true`,进入 Phase 4。
601
291
 
602
- 仅当 code-review.json 中 warnings > 0 且 errors = 0 时,使用 `AskUserQuestion`:
603
-
604
- ```javascript
605
- AskUserQuestion({
606
- title: "代码审查复核",
607
- questions: [{
608
- id: "review_check",
609
- prompt: "代码审查发现 " + warning_count + " 个警告,0 个错误。是否需要人工复核?",
610
- options: [
611
- { id: "skip", label: "无需复核,自动继续测试阶段" },
612
- { id: "review", label: "需要人工复核" }
613
- ]
614
- }]
615
- })
616
- ```
617
-
618
- - `skip`(默认)→ 自动继续
619
- - `review` → 展示警告详情,等待人工处理
292
+ 仅当 code-review.json 中 warnings > 0 且 errors = 0 时,使用 `AskUserQuestion` 询问是否人工复核(`skip` 自动继续 / `review` 展示警告详情)。
620
293
 
621
294
  ### Phase 4: TEST
622
295
 
@@ -657,24 +330,7 @@ exit code 全 0 = 通过,进入 Phase 5。失败处理见 [Layer 3: 测试失
657
330
 
658
331
  > **策略判断**:读取 `specline/config.yaml` → `pipeline.human_gate_policy`。若为 `none` → 跳过此 HG,直接写入 `human_gate_3.passed = true`,执行归档。`minimal` 策略下 HG3 保留人工确认。
659
332
 
660
- 全部测试通过后,使用 `AskUserQuestion` 请求归档确认:
661
-
662
- ```javascript
663
- AskUserQuestion({
664
- title: "归档确认",
665
- questions: [{
666
- id: "archive_confirm",
667
- prompt: "全部测试通过。变更摘要:新增 " + new_files + " 个文件,修改 " + modified_files + " 个文件。是否归档此变更?",
668
- options: [
669
- { id: "archive", label: "确认归档" },
670
- { id: "cancel", label: "暂不归档" }
671
- ]
672
- }]
673
- })
674
- ```
675
-
676
- - `archive` → 执行归档
677
- - `cancel` → 暂停流水线,保留状态文件待后续继续
333
+ 全部测试通过后,使用 `AskUserQuestion` 请求归档确认(`archive` 执行归档 / `cancel` 暂停流水线)。
678
334
 
679
335
  #### Step 15: 归档
680
336
 
@@ -687,147 +343,51 @@ specline-pipeline-gate.sh archive --execute --change "<name>"
687
343
 
688
344
  ---
689
345
 
690
- ## Layer 3: 异常与恢复
691
-
692
- ### Build Gate 失败处理
693
-
694
- ⚠️ Build Gate 失败时,分析失败原因并定位到具体任务:
695
-
696
- **8a. 单个任务构建失败** → 回对应 coding agent 修复(最多 2 次循环)
697
-
698
- **8b. 接口不兼容** → 计算影响范围,只重置受影响的下游任务:
699
-
700
- ```bash
701
- # 影响范围分析:基于 tasks.md 的 Depends 关系,计算受影响的下游任务
702
- # 例如:Task 1 的 API 签名改了 → Task 3 (Depends: 1)、Task 5 (Depends: 1,3) 需要重跑
703
- # Task 2 无依赖关系 → 不受影响,保持 completed
704
-
705
- AFFECTED_TASK_IDS=("3" "5") # 从 DAG 计算得出
706
-
707
- for tid in "${AFFECTED_TASK_IDS[@]}"; do
708
- jq --arg tid "$tid" '
709
- .phases.coding.tasks |= map(
710
- if .id == $tid then .status = "pending" | .completed_at = null else . end
711
- )
712
- ' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
713
- done
714
- ```
715
-
716
- 影响范围算法:
717
- 1. 找到被修改的任务 ID 集合 M
718
- 2. 遍历所有任务,如果某任务的 Depends 列表中包含 M 中任一 ID,则加入受影响集合
719
- 3. 递归执行第 2 步直到不再扩展
720
-
721
- **8c. Build Gate 重置**:
722
-
723
- ```bash
724
- jq '.phases.coding.gates.build_gate.passed = null' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
725
- ```
726
-
727
- 修复后**只重跑受影响的任务**(按原批次顺序),未受影响的任务保持 completed 状态。
728
-
729
- ### 测试失败处理
730
-
731
- ⚠️ 测试失败根据失败文件所在的目录区分处理路径:
732
-
733
- #### 单元测试失败处理
734
-
735
- 失败文件在 `tests/unit/` 或 `tests/models/` 目录下:
346
+ ### 统一失败处理流程
736
347
 
737
- - 利用 `Covers` 追溯链定位到具体 coding 任务
738
- - 回对应 coding agent 修复实现代码或测试代码(最多 2 次循环)
739
- - `spec_ambiguity`(Spec 模糊)→ **不自动循环修复**,暂停流水线并展示模糊点给用户
740
-
741
- > **策略判断**:若 `pipeline.human_gate_policy` 为 `minimal` 或 `none` → 不暂停,将 spec_ambiguity 降级为 WARNING 记录到事件日志并继续下一阶段。
742
-
743
- Gate 重置(仅重置 test_unit_gate):
744
-
745
- ```bash
746
- jq '.phases.test.sub_phases.unit.gates.test_unit_gate.passed = null' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
747
- ```
748
-
749
- #### 集成/E2E 测试失败处理
750
-
751
- 失败文件在 `tests/integration/` 或 `tests/e2e/` 目录下:
752
-
753
- - specline-test-runner 分析原因:
754
- - **测试代码问题** → specline-test-writer 自修(最多 2 次)
755
- - **实现代码问题** → 利用 `Covers` 追溯链定位到具体任务,回对应 coding agent 修复 → **使用影响范围算法精确重置受影响任务的 Gate**
756
- - **`spec_ambiguity`**(Spec 模糊)→ **不自动循环修复**,暂停流水线并展示模糊点给用户
757
-
758
- > **策略判断**:若 `pipeline.human_gate_policy` 为 `minimal` 或 `none` → 不暂停,将 spec_ambiguity 降级为 WARNING 记录到事件日志并继续下一阶段。
759
- - 循环最多 2 次
760
-
761
- Gate 重置:
762
-
763
- ```bash
764
- jq '
765
- .phases.test.sub_phases.integration.gates.test_integration_gate.passed = null |
766
- .phases.test.sub_phases.e2e.gates.test_e2e_gate.passed = null
767
- ' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
768
- ```
769
-
770
- #### 优先级规则
771
-
772
- 当单元测试和集成/E2E 测试同时失败时:**优先修复单元测试**(先执行 coding agent 修复循环),单元测试通过后再处理集成/E2E 测试失败。
773
-
774
- 代码修复后 Gate 全部重置(所有测试类型):
775
-
776
- ```bash
777
- jq '
778
- .phases.test.sub_phases.unit.gates.test_unit_gate.passed = null |
779
- .phases.test.sub_phases.integration.gates.test_integration_gate.passed = null |
780
- .phases.test.sub_phases.e2e.gates.test_e2e_gate.passed = null
781
- ' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
782
- ```
348
+ 所有 Gate 失败均按以下管道处理:
783
349
 
784
- ### Hook 阻断处理规范
350
+ 1. **诊断**:分析 stderr/exit code 确定失败类型(Build/Test/Hook
351
+ 2. **定位**:基于 Covers 追溯链定位到具体任务(Build 失败额外用 Depends 影响范围算法计算下游任务)
352
+ 3. **修复**:回对应 agent 修复(最多 2 次循环)
353
+ 4. **Gate 重置**:`jq '...gate.passed = null' "$STATE_FILE"`
354
+ 5. **特殊规则**:
355
+ - `spec_ambiguity` → 暂停流水线展示模糊点(minimal/none 策略下降级为 WARNING)
356
+ - 接口不兼容 → 只重置受影响的下游任务(保留未受影响任务的状态)
357
+ - Hook 阻断 → 先诊断原因 → 与用户沟通(AskUserQuestion)→ 修复后重试 → 绝不静默降级
358
+ - 测试失败优先级:单元测试优先于集成/E2E
785
359
 
786
- ⚠️ 当任何子 Agent 被 `subagentStart` hook 阻止时,**编排者绝对不允许静默降级为自己直接执行**。必须按以下流程处理:
787
-
788
- **Step 1: 识别阻断原因**
360
+ ---
789
361
 
790
- 根据阻断信息判断原因类型,按优先级诊断:
362
+ ## Layer 3: 异常与恢复
791
363
 
792
- | 原因类型 | 典型症状 | 诊断命令 |
793
- |---------|---------|---------|
794
- | 脚本缺少执行权限 | "Permission denied" 或脚本执行失败 | `ls -la .cursor/hooks/specline-agent-guard.sh` |
795
- | `jq` 未安装 | jq 相关错误 | `which jq` |
796
- | Agent 名称不在白名单 | "子Agent类型 'xxx' 不在允许列表中" | 检查 `specline-agent-guard.sh` 中 `ALLOWED_AGENTS` 变量 |
797
- | hooks.json 缺失或不完整 | hook 未触发或配置错误 | 检查 `.cursor/hooks.json` 文件是否存在且内容完整 |
364
+ ### Build 失败差异化处理
798
365
 
799
- **Step 2: 与用户沟通**
366
+ - 单个任务构建失败 → 回对应 coding agent 修复(最多 2 次循环)
367
+ - 接口不兼容 → 使用影响范围算法(找到修改任务集合 M → 遍历 Depends → 递归扩展),只重置受影响的下游任务(未受影响任务保持 completed)
368
+ - Gate 重置:`jq '.phases.coding.gates.build_gate.passed = null' "$STATE_FILE"`
369
+ - 修复后只重跑受影响的任务(按原批次顺序)
800
370
 
801
- ```javascript
802
- AskUserQuestion({
803
- title: "Hook 阻断 - 子 Agent 启动失败",
804
- questions: [{
805
- id: "hook_fix",
806
- prompt: `子 Agent **${agentName}** 被 hook 阻止,诊断结果:
371
+ ### 测试失败差异化处理
807
372
 
808
- **阻断原因**:${diagnosis}
809
- **影响**:${impact_description}
373
+ - **单元测试失败**(`tests/unit/` / `tests/models/`):利用 Covers 追溯链定位 → 回 coding agent 修复(最多 2 次)
374
+ - Gate 重置:`jq '.phases.test.sub_phases.unit.gates.test_unit_gate.passed = null' "$STATE_FILE"`
375
+ - **集成/E2E 测试失败**(`tests/integration/` / `tests/e2e/`):先判断是测试代码还是实现代码问题
376
+ - 测试代码问题 → specline-test-writer 自修(最多 2 次)
377
+ - 实现代码问题 → Covers 追溯定位 → 回 coding agent 修复 → 用影响范围算法重置受影响 Gate
378
+ - Gate 重置:`.phases.test.sub_phases.integration.gates.test_integration_gate.passed = null | .phases.test.sub_phases.e2e.gates.test_e2e_gate.passed = null`
379
+ - **优先级**:单元测试失败优先修复;代码修复后所有测试 Gate 全部重置
810
380
 
811
- **建议修复操作**:${fix_commands}
381
+ ### Hook 阻断差异化处理
812
382
 
813
- 请选择处理方式:`,
814
- options: [
815
- { id: "auto_fix", label: "自动修复(执行上述修复命令)" },
816
- { id: "manual_fix", label: "我手动修复后通知你重试" },
817
- { id: "skip_agent", label: "跳过此 Agent,由编排者直接执行(不推荐)" }
818
- ]
819
- }]
820
- })
821
- ```
383
+ ⚠️ 编排者绝对不允许静默降级为自己直接执行。处理流程:
822
384
 
823
- **Step 3: 执行修复并重试**
824
- - `auto_fix` 执行修复命令(如 `chmod +x .cursor/hooks/*.sh`、安装 `jq` 等),修复完成后立即重试启动子 Agent
825
- - `manual_fix`等待用户确认修复完成,然后重试启动子 Agent
826
- - `skip_agent`**仅当用户明确选择时才降级为编排者直接执行**,并必须在事件日志中记录降级原因
385
+ 1. **识别原因**:判断权限/依赖/白名单/hooks.json 等问题
386
+ 2. **与用户沟通**:使用 AskUserQuestion 展示诊断结果和修复选项(auto_fix / manual_fix / skip_agent)
387
+ 3. **执行修复并重试**:auto_fix → 执行修复命令;manual_fix → 等待用户;skip_agent → 仅用户明确选择时降级(记录到事件日志)
388
+ 4. **验证修复结果**:重试子 Agent,失败则重新诊断 再次沟通(最多 2 次循环)→ 仍失败则暂停并报告用户
827
389
 
828
- **Step 4: 验证修复结果** — 修复后重新启动子 Agent,如果仍然被阻止:重新诊断 → 再次沟通(最多循环 2 次)→ 暂停流水线并报告用户,不得自行降级
829
-
830
- 此规范适用于**所有 hook 阻断场景**(`subagentStart`、`beforeShellExecution` 等)。
390
+ 此规范适用于所有 hook 阻断场景(`subagentStart`、`beforeShellExecution` 等)。
831
391
 
832
392
  ### 断点续跑流程
833
393
 
@@ -948,112 +508,10 @@ AskUserQuestion({
948
508
 
949
509
  ---
950
510
 
951
- ## Layer 4: 附录
952
-
953
- > ℹ️ 初次阅读可跳过 — 以下为完整参考信息,首次阅读可略过
954
-
955
- ### 附录 A: .pipeline-state.json 完整 Schema
956
-
957
- ```json
958
- {
959
- "version": 1,
960
- "change_name": "<name>",
961
- "created_at": "<ISO8601>",
962
- "updated_at": "<ISO8601>",
963
- "current_phase": "spec",
964
- "current_step": "specline-spec-creator",
965
- "phases": {
966
- "spec": { "status": "in_progress", "retry_count": 0, "sub_phases": {}, "gates": { "spec_gate": { "passed": null }, "human_gate_1": { "passed": null } } },
967
- "coding": { "status": "pending", "tasks": [], "sub_phases": {}, "gates": { "build_gate": { "passed": null } } },
968
- "code_review": { "status": "pending", "retry_count": 0, "gates": { "lint_gate": { "passed": null }, "human_gate_2": { "passed": null } } },
969
- "test": { "status": "pending", "framework": null, "sub_phases": { "unit": { "status": "pending", "gates": { "test_unit_gate": { "passed": null } } }, "integration": { "status": "pending", "gates": { "test_integration_gate": { "passed": null } } }, "e2e": { "status": "pending", "gates": { "test_e2e_gate": { "passed": null } } } } },
970
- "archive": { "status": "pending", "gates": { "human_gate_3": { "passed": null }, "archive_gate": { "passed": null } } }
971
- }
972
- }
973
- ```
974
-
975
- ### 附录 B: 状态写入规则
976
-
977
- > ℹ️ 初次阅读可跳过
978
-
979
- 所有状态写入由 Gate 脚本或 Skill 编排逻辑完成,**不使用 LLM 写入状态**:
980
-
981
- - Gate 脚本通过后自动写入 `gate.passed = true`
982
- - 子 Agent 完成后 Skill 写入 `completed_at`
983
- - 代码修复后 Skill 重置相关 gates 为 null
984
-
985
- ### 附录 C: 结构化事件日志
986
-
987
- > ℹ️ 初次阅读可跳过
988
-
989
- 每个关键事件追加写入 `specline/changes/<name>/pipeline-events.jsonl`(JSON Lines 格式,每行一个事件):
990
-
991
- ```json
992
- {"ts":"...","event":"pipeline_start","change":"<name>"}
993
- {"ts":"...","event":"phase_transition","from":"spec","to":"coding"}
994
- {"ts":"...","event":"agent_start","agent":"specline-spec-creator","task":null}
995
- {"ts":"...","event":"agent_done","agent":"specline-spec-creator","result":"completed"}
996
- {"ts":"...","event":"agent_start","agent":"specline-frontend-dev","task":"1","type":"frontend"}
997
- {"ts":"...","event":"agent_done","agent":"specline-frontend-dev","task":"1","result":"completed","files_changed":["..."],"duration_ms":45200}
998
- {"ts":"...","event":"gate_run","phase":"build","exit_code":0,"passed":true}
999
- {"ts":"...","event":"gate_run","phase":"lint","exit_code":1,"passed":false,"stderr":"..."}
1000
- {"ts":"...","event":"conflict_detected","tasks":["1","2"],"overlap_files":["src/utils/api.ts"]}
1001
- {"ts":"...","event":"retry","phase":"coding","task":"3","attempt":2,"reason":"build_failure"}
1002
- {"ts":"...","event":"pipeline_pause","reason":"human_gate_1"}
1003
- {"ts":"...","event":"pipeline_resume","from_phase":"coding"}
1004
- {"ts":"...","event":"pipeline_complete","change":"<name>"}
1005
- ```
1006
-
1007
- **写入原则**:
1008
- - 每个事件一行,JSON 对象结尾无逗号
1009
- - 任何编排动作(启动 agent、运行 gate、状态转换)都写入事件日志
1010
- - Gate 脚本不写事件日志(Gate 是无状态的),仅编排层写入
1011
- - 事件日志用于人工排查问题和统计分析,不影响流水线决策
1012
-
1013
- ### 附录 D: Hook 约束体系
1014
-
1015
- > ℹ️ 初次阅读可跳过
1016
-
1017
- Specline 通过 Cursor Hooks 提供了三层自动约束,确保在长对话中 Agent 不偏离流水线逻辑:
1018
-
1019
- ```
1020
- sessionStart → specline-session-start.sh
1021
- 新会话启动时检测活跃 pipeline,自动注入阶段上下文到 Agent 系统提示
1022
-
1023
- preToolUse → specline-phase-guard.sh
1024
- 操作前检查:SPEC 阶段拦截代码编辑、阶段不匹配的子Agent 启动
1025
-
1026
- postToolUse → specline-reminder.sh
1027
- 关键操作后注入提醒:更新 tasks.md checkbox、运行 Gate 脚本
1028
- ```
1029
-
1030
- #### 对你(编排者)的影响
1031
-
1032
- 1. **总是先检查** - preToolUse 会阻止不匹配当前阶段的操作,所以你在行动前自然会考虑阶段
1033
- 2. **被提醒下一步** - postToolUse 在子Agent完成后提醒你更新 checkbox 和运行 Gate
1034
- 3. **非流水线会话无影响** - 所有 Hook 的第一步检查「是否有活跃 pipeline」,无则透明放行
1035
-
1036
- #### 约束策略
1037
-
1038
- | 场景 | 策略 | 原因 |
1039
- |------|------|------|
1040
- | SPEC 阶段编辑代码文件 | **硬拦截 (deny)** | 明确违规 |
1041
- | SPEC 阶段启动编码 Agent | **硬拦截 (deny)** | 阶段不匹配 |
1042
- | CODING 阶段直接编辑代码 | **软提醒 (postToolUse)** | Hook 无法区分编排者和子Agent的 Write |
1043
- | 子Agent完成后忘记 Gate | **软提醒 (postToolUse)** | 操作后注入下一步提醒 |
1044
-
1045
- > 注意:CODING 阶段 Orchestrator 直接编辑代码文件不会被 Hook 硬拦截(因为子Agent 也需要 Write 权限),但 SKILL 指令和 sessionStart 注入的上下文会持续提醒你「编码应通过子 Agent」。如果你发现自己想直接编辑代码,停一下,改用 Task 工具。
1046
-
1047
- ### 附录 E: 关键约束速查表
511
+ ## Layer 4: 参考文档
1048
512
 
1049
- > ℹ️ 初次阅读可跳过
513
+ > 以下文档为完整参考信息,根据需要查阅:
1050
514
 
1051
- | # | 约束 | 说明 |
1052
- |---|------|------|
1053
- | 1 | **不做判断,只做编排** | 不评估代码质量、需求好坏、测试覆盖——这些由子 Agent 和 Gate 脚本负责 |
1054
- | 2 | **所有门禁通过 Gate 脚本** | 调用 `specline-pipeline-gate.sh`,不要自己写 grep/检查逻辑 |
1055
- | 3 | **状态文件是唯一真相源** | 所有决策基于 `.pipeline-state.json` 的当前值 |
1056
- | 4 | **人工确认点必须尊重策略** | 根据 `pipeline.human_gate_policy` 配置决定是否暂停,不要无条件跳过或强制暂停 human_gate |
1057
- | 5 | **测试 Agent 必须黑盒** | 不给 specline-test-writer 传递源代码文件路径 |
1058
- | 6 | **Hook 阻断绝不静默降级** | 子 Agent 被 hook 阻止时,必须先诊断、沟通、修复后重试 |
1059
- | 7 | **接受 Hook 约束** | preToolUse/postToolUse/sessionStart Hook 会自动校验和提醒,不要试图绕过 |
515
+ - State Schema 详见 `references/pipeline-state-schema.md`
516
+ - Event Log → 详见 `references/event-log-spec.md`
517
+ - Hook & Constraints 详见 `references/error-recovery-details.md`