specline 1.1.3 → 1.2.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.
- package/README.md +95 -16
- package/package.json +1 -1
- package/templates/.cursor/agents/specline-spec-reviewer.md +11 -1
- package/templates/.cursor/commands/specline-quickfix.md +24 -0
- package/templates/.cursor/hooks/specline-pipeline-gate.sh +125 -13
- package/templates/.cursor/hooks/specline-session-start.sh +5 -62
- package/templates/.cursor/skills/specline-pipeline/SKILL.md +225 -52
- package/templates/.cursor/skills/specline-propose/SKILL.md +4 -1
- package/templates/.cursor/skills/specline-quickfix/SKILL.md +239 -0
package/README.md
CHANGED
|
@@ -8,8 +8,16 @@
|
|
|
8
8
|
/specline-pipeline "实现用户登录功能"
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
修 bug、改配置、文档微调?用轻量模式:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/specline-quickfix "修复登录按钮样式"
|
|
15
|
+
```
|
|
16
|
+
|
|
11
17
|
## 它能做什么
|
|
12
18
|
|
|
19
|
+
**完整流水线**(新功能、重构):
|
|
20
|
+
|
|
13
21
|
```
|
|
14
22
|
自然语言需求 → Spec → 审核 → 编码 → 审查 → 测试 → 归档
|
|
15
23
|
↑ ↑ ↑ ↑ ↑ ↑
|
|
@@ -18,12 +26,20 @@
|
|
|
18
26
|
并行 reviewer E2E
|
|
19
27
|
```
|
|
20
28
|
|
|
29
|
+
**轻量修复**(修 bug、改配置、文档微调):
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/ specline-quickfix "描述" → 理解代码 → 直接编辑 → Lint+自审 → 现有单测 → 轻量归档
|
|
33
|
+
0 个子 Agent 0 个人工确认 0 个 state 文件
|
|
34
|
+
```
|
|
35
|
+
|
|
21
36
|
每个阶段都经过 **确定性门禁校验** —— 用 `grep`、`jq`、编译器退出码、测试退出码判断通过与否。**质量判断零 LLM 参与**。
|
|
22
37
|
|
|
23
38
|
## 核心特性
|
|
24
39
|
|
|
25
40
|
- **需求驱动**:自然语言 → 结构化规格文档(Requirements + Scenarios + WHEN/THEN)
|
|
26
41
|
- **并行编码**:自动按前端/后端/config 拆分任务,同批次并发派发 Coding Agent
|
|
42
|
+
- **TDD 白盒测试**:无依赖任务自动启用 TDD 模式(先写单测 → 确认失败 → 最小实现 → 重构),与黑盒 test-writer 并行协作
|
|
27
43
|
- **确定性门禁**:每个阶段用 Shell 脚本的退出码判定是否通过,不做模糊判断
|
|
28
44
|
- **黑盒测试**:测试 Agent 只看 Spec 文档,不能读取任何实现源码
|
|
29
45
|
- **断点续跑**:随时中断,下次从最后一个可信门禁自动恢复(tasks.md 的 `[x]`/`[ ]` 标记进度)
|
|
@@ -59,8 +75,8 @@ specline sync --dry-run # 预览变更
|
|
|
59
75
|
my-project/
|
|
60
76
|
├── .cursor/
|
|
61
77
|
│ ├── agents/ ← 9 个 Specline Agent 定义
|
|
62
|
-
│ ├── commands/ ←
|
|
63
|
-
│ ├── skills/ ←
|
|
78
|
+
│ ├── commands/ ← 3 个 Slash 命令入口
|
|
79
|
+
│ ├── skills/ ← 6 个 Skill 指令
|
|
64
80
|
│ ├── hooks/ ← 7 个 Gate/Hook 脚本
|
|
65
81
|
│ └── hooks.json ← Cursor Hook 配置
|
|
66
82
|
├── specline/ ← 运行时目录
|
|
@@ -77,13 +93,36 @@ my-project/
|
|
|
77
93
|
/specline-pipeline "添加 JWT 用户认证"
|
|
78
94
|
```
|
|
79
95
|
|
|
96
|
+
小改动用快速模式:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
/specline-quickfix "修改按钮颜色"
|
|
100
|
+
```
|
|
101
|
+
|
|
80
102
|
开始编码前先探索思路:
|
|
81
103
|
|
|
82
104
|
```
|
|
83
105
|
/specline-explore
|
|
84
106
|
```
|
|
85
107
|
|
|
86
|
-
##
|
|
108
|
+
## 工作流选择
|
|
109
|
+
|
|
110
|
+
Specline 提供两种工作流,按变更规模选择:
|
|
111
|
+
|
|
112
|
+
| 维度 | Quickfix (`/specline-quickfix`) | Pipeline (`/specline-pipeline`) |
|
|
113
|
+
|------|-------------------------------|-------------------------------|
|
|
114
|
+
| 文件改动数 | 1-3 个 | 4+ 个 |
|
|
115
|
+
| 关注点 | 单一关注点 | 多关注点/跨模块 |
|
|
116
|
+
| 架构变更 | 无新架构/新组件 | 需要新组件/新 API |
|
|
117
|
+
| 测试 | 不需要新测试 | 需要写新测试 |
|
|
118
|
+
| 典型场景 | 修 bug、改配置、文档微调 | 新增功能、重构 |
|
|
119
|
+
| 产出 | summary.md + files-changed.json | proposal/design/tasks/specs + 全部测试 |
|
|
120
|
+
| 人工确认 | 0 个 | 3 个 |
|
|
121
|
+
| 耗时 | 1-3 分钟 | 10-30 分钟 |
|
|
122
|
+
|
|
123
|
+
**使用建议**:如果不确定,优先用 quickfix。如果需要更严格的流程保证,用 pipeline。
|
|
124
|
+
|
|
125
|
+
## 完整流水线阶段
|
|
87
126
|
|
|
88
127
|
```
|
|
89
128
|
PHASE 1: SPEC(规格)
|
|
@@ -91,15 +130,16 @@ PHASE 1: SPEC(规格)
|
|
|
91
130
|
├── proposal.md — 需求提案(What/Why/Scope)
|
|
92
131
|
├── specs/*/spec.md — 功能规格(Requirements/Scenarios/WHEN-THEN)
|
|
93
132
|
├── design.md — 技术设计(架构/数据流/决策)
|
|
94
|
-
└── tasks.md — 任务清单(Type/Depends/Covers/Files + [ ] 进度标记)
|
|
133
|
+
└── tasks.md — 任务清单(Type/Depends/Covers/Testable/Files + [ ] 进度标记)
|
|
95
134
|
→ specline-spec-reviewer 审核
|
|
96
135
|
→ Gate: grep + jq 格式校验
|
|
97
136
|
→ 🟡 人工确认 Spec 和任务规划
|
|
98
137
|
|
|
99
138
|
PHASE 2: CODING(编码)
|
|
100
139
|
解析 tasks.md → 按依赖 DAG 分层 → 同批次前后端/config Agent 并发
|
|
140
|
+
无依赖 + 可测试任务 → 自动启用 TDD 模式(RED-GREEN-REFACTOR)
|
|
101
141
|
每完成一个任务,[ ] 自动标记为 [x]
|
|
102
|
-
→ Gate: 编译检查(tsc --noEmit / python -m compileall)
|
|
142
|
+
→ Gate: 编译检查(tsc --noEmit / python -m compileall) + 单元测试文件存在性检查
|
|
103
143
|
|
|
104
144
|
PHASE 3: REVIEW(审查)
|
|
105
145
|
specline-code-reviewer + specline-config-reviewer 分别审查代码和配置/文档
|
|
@@ -116,16 +156,55 @@ PHASE 5: ARCHIVE(归档)
|
|
|
116
156
|
→ delta specs 合并到主规格目录
|
|
117
157
|
→ 按日期归档到 specline/changes/archive/
|
|
118
158
|
✅ 完成
|
|
159
|
+
|
|
160
|
+
### TDD 白盒测试
|
|
161
|
+
|
|
162
|
+
Pipeline 采用「两层测试分离」架构:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
Coding Agent(白盒 TDD) Test-Writer(黑盒)
|
|
166
|
+
───────────────────────── ─────────────────
|
|
167
|
+
产出: tests/unit/** 产出: tests/integration/**
|
|
168
|
+
tests/models/** tests/e2e/**
|
|
169
|
+
测试: 单个函数的输入输出 测试: 跨模块的用户行为
|
|
170
|
+
边界条件、异常路径 API 端到端契约
|
|
171
|
+
Spec Scenario 全覆盖
|
|
172
|
+
触发: 编码时同步产出 触发: Phase 2 与 Coding 并行启动
|
|
173
|
+
先写测试 → 确认失败 → 写实现 只读 Spec,不读源码
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
tasks.md 中 `Testable: true` 的任务自动启用 TDD 模式(完整 RED-GREEN-REFACTOR 循环),`Testable: false` 的任务保持原有流程。两个测试域严格目录隔离,冲突检测自动识别越界。
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 轻量修复流程
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
PHASE 1: UNDERSTAND(理解)
|
|
183
|
+
读取相关代码 → 理解上下文 → 意图模糊时 AskUserQuestion 确认
|
|
184
|
+
|
|
185
|
+
PHASE 2: IMPLEMENT(实现)
|
|
186
|
+
直接 Write/StrReplace 编辑 1-3 个文件
|
|
187
|
+
不需要 Spec 文档、DAG 构建、批次调度
|
|
188
|
+
|
|
189
|
+
PHASE 3: REVIEW(审查)
|
|
190
|
+
ReadLints 检查 + 自动修复(最多 2 次)→ Agent 自审逻辑正确性
|
|
191
|
+
|
|
192
|
+
PHASE 4: TEST(测试)
|
|
193
|
+
仅运行项目已有单元测试,无测试则跳过
|
|
194
|
+
失败自动修复最多 2 次
|
|
195
|
+
|
|
196
|
+
PHASE 5: ARCHIVE(归档)
|
|
197
|
+
生成 summary.md + files-changed.json → 询问是否 git commit
|
|
119
198
|
```
|
|
120
199
|
|
|
121
200
|
## 架构
|
|
122
201
|
|
|
123
202
|
```
|
|
124
|
-
/specline-pipeline ←
|
|
125
|
-
│
|
|
126
|
-
▼
|
|
127
|
-
specline-pipeline SKILL ←
|
|
128
|
-
│
|
|
203
|
+
/specline-pipeline ← 完整流水线(大功能) /specline-quickfix ← 轻量修复(小改动)
|
|
204
|
+
│ │
|
|
205
|
+
▼ ▼
|
|
206
|
+
specline-pipeline SKILL ← 编排层 编排者直接执行(无子 Agent)
|
|
207
|
+
│ Read → Write → ReadLints → Shell → 归档
|
|
129
208
|
┌───┼──────────────────┬──────────────────────┐
|
|
130
209
|
▼ ▼ ▼ ▼
|
|
131
210
|
9 个子 Agent specline-pipeline- Cursor Hooks
|
|
@@ -149,13 +228,13 @@ specline-pipeline SKILL ← 编排层(读状态、派发 Agent、调 Gate)
|
|
|
149
228
|
|-------|------|
|
|
150
229
|
| `specline-spec-creator` | 根据自然语言需求,基于内联模板直接生成 proposal/design/tasks/spec 四个文件 |
|
|
151
230
|
| `specline-spec-reviewer` | 审核规格的完整性、一致性和覆盖度 |
|
|
152
|
-
| `specline-frontend-dev` | UI
|
|
153
|
-
| `specline-backend-dev` | API
|
|
231
|
+
| `specline-frontend-dev` | UI 组件、页面、样式、交互逻辑(单个任务级别,Testable 任务启用 TDD) |
|
|
232
|
+
| `specline-backend-dev` | API 端点、数据模型、业务逻辑(单个任务级别,Testable 任务启用 TDD) |
|
|
154
233
|
| `specline-config-dev` | Shell 脚本、配置文件(JSON/YAML)、Markdown 文档(处理 Type: config/docs 任务) |
|
|
155
234
|
| `specline-code-reviewer` | 前端/后端代码质量、安全性、可维护性审查 |
|
|
156
235
|
| `specline-config-reviewer` | Shell 脚本安全性、配置文件语法和一致性、Markdown 文档结构审查 |
|
|
157
|
-
| `specline-test-writer` | 黑盒测试编写——只能看 Spec
|
|
158
|
-
| `specline-test-runner` | 执行测试并分类失败原因(测试问题/代码问题/Spec
|
|
236
|
+
| `specline-test-writer` | 黑盒测试编写——只能看 Spec 不能读源码,仅写集成测试(tests/integration/)和 E2E 测试 |
|
|
237
|
+
| `specline-test-runner` | 执行测试并分类失败原因(测试问题/代码问题/Spec 模糊),区分单元测试(回 coding agent)和集成/E2E 测试(回 test-writer) |
|
|
159
238
|
|
|
160
239
|
## 确定性门禁
|
|
161
240
|
|
|
@@ -163,8 +242,8 @@ specline-pipeline SKILL ← 编排层(读状态、派发 Agent、调 Gate)
|
|
|
163
242
|
|
|
164
243
|
| 门禁 | 检查内容 |
|
|
165
244
|
|------|---------|
|
|
166
|
-
| Spec | `grep` 检查 Purpose/Requirements/Scenarios 章节完整性、WHEN/THEN
|
|
167
|
-
| Build | `tsc --noEmit` / `python -m compileall` 编译检查 |
|
|
245
|
+
| Spec | `grep` 检查 Purpose/Requirements/Scenarios 章节完整性、WHEN/THEN 配对、Testable 字段格式与一致性 |
|
|
246
|
+
| Build | `tsc --noEmit` / `python -m compileall` 编译检查 + Testable 任务单元测试文件存在性与语法检查 |
|
|
168
247
|
| Lint | `ruff` / `eslint` 退出码 + code-review.json 中 error 数量 |
|
|
169
248
|
| Test | 测试框架退出码 + 覆盖率阈值 |
|
|
170
249
|
| Archive | 归档目录结构 + 必要文件完整性 |
|
package/package.json
CHANGED
|
@@ -49,6 +49,7 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
|
|
|
49
49
|
- 每个任务含 `Depends:` 标注
|
|
50
50
|
- 每个任务含 `Covers:` 标注(链接到具体的 Requirement 和 Scenario)
|
|
51
51
|
- 每个任务含 `Files:` 标注(非空,列出预期文件)
|
|
52
|
+
- 每个任务含 `Testable:` 标注(值在 true/false 范围内,可选但建议标注)
|
|
52
53
|
|
|
53
54
|
2. **独立性**:
|
|
54
55
|
- `Depends: (none)` 的任务占比 ≥ 50%(否则标记为 warning)
|
|
@@ -63,6 +64,12 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
|
|
|
63
64
|
- backend 类型的任务应涉及 API/模型/逻辑
|
|
64
65
|
- 没有 fullstack 类型(前端和后端必须拆开)
|
|
65
66
|
|
|
67
|
+
5. **Testable 合理性**:
|
|
68
|
+
- `Testable: true` 的任务必须满足:Depends: (none) + Type ≠ config/docs + 有可拆分的独立逻辑单元
|
|
69
|
+
- `Testable: false` 的任务如果同时满足 Depends: (none) + Type ≠ config/docs + 有独立逻辑单元 → warning(建议标记为 Testable: true)
|
|
70
|
+
- 有上游依赖的任务 Testable 必须为 false
|
|
71
|
+
- Type 为 config/docs 的任务 Testable 必须为 false
|
|
72
|
+
|
|
66
73
|
## 输出格式
|
|
67
74
|
|
|
68
75
|
产出 `specline/changes/<change-name>/specs/<capability>/spec-review.json`:
|
|
@@ -81,6 +88,7 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
|
|
|
81
88
|
"total": 6,
|
|
82
89
|
"independent": 4,
|
|
83
90
|
"parallel_ratio": 0.67,
|
|
91
|
+
"testable_count": 3,
|
|
84
92
|
"types": { "frontend": 2, "backend": 3, "config": 1 }
|
|
85
93
|
},
|
|
86
94
|
"design_review": {
|
|
@@ -98,11 +106,13 @@ description: 审核 spec.md、design.md、tasks.md 的完整性和一致性。
|
|
|
98
106
|
"[spec.md] 缺少异常路径场景:未定义 'worker 数量为 0' 时的行为",
|
|
99
107
|
"[tasks.md] 任务 3 缺少 Covers 标注",
|
|
100
108
|
"[tasks.md] 任务 1 和 任务 2 的 Files 有交集:都包含了 src/utils/api.ts",
|
|
109
|
+
"[tasks.md] 任务 3 Testable=true 但存在上游依赖 (Depends: 1),应为 false",
|
|
110
|
+
"[tasks.md] 任务 5 (Depends: none, Type: backend) 建议标记为 Testable: true",
|
|
101
111
|
"[design.md] 提到使用 Redis 缓存,但 tasks.md 中没有对应的 infra 任务",
|
|
102
112
|
"[coverage] Scenario '用户登出' 未被任何任务覆盖"
|
|
103
113
|
],
|
|
104
114
|
"coverage": { "requirements_covered": 4, "requirements_total": 5, "scenarios_covered": 10, "scenarios_total": 14 },
|
|
105
|
-
"task_stats": { "total": 6, "independent": 4, "parallel_ratio": 0.67, "types": { "frontend": 2, "backend": 3, "config": 1 } },
|
|
115
|
+
"task_stats": { "total": 6, "independent": 4, "parallel_ratio": 0.67, "testable_count": 3, "types": { "frontend": 2, "backend": 3, "config": 1 } },
|
|
106
116
|
"design_review": { "issues": ["Redis 缓存方案缺少对应的 infra 任务"] }
|
|
107
117
|
}
|
|
108
118
|
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: /specline-quickfix
|
|
3
|
+
id: specline-quickfix
|
|
4
|
+
category: Workflow
|
|
5
|
+
description: 轻量修改 Skill —— 小改动用 quickfix,大功能用 pipeline
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
轻量修改流程,在不创建规划文档的情况下快速执行小改动。
|
|
9
|
+
|
|
10
|
+
**用法:**
|
|
11
|
+
- `/specline-quickfix <描述>` — 执行轻量修改流程(修 bug、改配置、文档微调等小改动)
|
|
12
|
+
|
|
13
|
+
**阶段:**
|
|
14
|
+
1. UNDERSTAND — 读取相关代码,理解变更上下文
|
|
15
|
+
2. IMPLEMENT — 直接编辑文件(单 Agent,无子 Agent)
|
|
16
|
+
3. REVIEW — ReadLints 自动校验 + 修复(最多 2 次)+ Agent 自审
|
|
17
|
+
4. TEST — 运行项目已有单元测试(失败修复最多 2 次),无测试则跳过
|
|
18
|
+
5. ARCHIVE — 生成 summary.md + files-changed.json,询问 git commit
|
|
19
|
+
|
|
20
|
+
**适用场景**:1-3 个文件改动、单一关注点、不涉及架构变更、不需要新测试。
|
|
21
|
+
|
|
22
|
+
**不适合的场景**(应使用 `/specline-pipeline`):4+ 个文件改动、新增功能/重构、需要新测试、跨模块/多关注点。
|
|
23
|
+
|
|
24
|
+
0 个人工确认点,全程自动质量保证。
|
|
@@ -15,19 +15,27 @@
|
|
|
15
15
|
|
|
16
16
|
set -euo pipefail
|
|
17
17
|
|
|
18
|
-
# ===== 参数解析 =====
|
|
19
18
|
PHASE="${1:-}"
|
|
20
19
|
CHANGE=""
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
EXECUTE_ARCHIVE=""
|
|
21
|
+
|
|
22
|
+
# 遍历所有参数,不依赖位置
|
|
23
|
+
shift # 跳过 PHASE
|
|
24
|
+
while [ $# -gt 0 ]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--change)
|
|
27
|
+
CHANGE="$2"
|
|
28
|
+
shift 2
|
|
29
|
+
;;
|
|
30
|
+
--execute)
|
|
31
|
+
EXECUTE_ARCHIVE="--execute"
|
|
32
|
+
shift
|
|
33
|
+
;;
|
|
34
|
+
*)
|
|
35
|
+
shift
|
|
36
|
+
;;
|
|
37
|
+
esac
|
|
38
|
+
done
|
|
31
39
|
# ===== 项目根目录 =====
|
|
32
40
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
33
41
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
@@ -300,7 +308,31 @@ gate_spec() {
|
|
|
300
308
|
fi
|
|
301
309
|
pass "tasks.md 标注完整性检查通过 ($task_count 个任务)"
|
|
302
310
|
|
|
303
|
-
# 10.
|
|
311
|
+
# 10. Testable 字段校验
|
|
312
|
+
local testable_count
|
|
313
|
+
testable_count=$(grep -c '\*\*Testable\*\*:' "$tasks_file" || echo "0")
|
|
314
|
+
|
|
315
|
+
if [ "$testable_count" -eq 0 ]; then
|
|
316
|
+
echo "⚠️ Testable 标注缺失(向后兼容模式:缺失字段的任务将被视为 Testable: false)"
|
|
317
|
+
elif [ "$testable_count" -gt 0 ] && [ "$testable_count" -lt "$task_count" ]; then
|
|
318
|
+
local missing_testable_tasks
|
|
319
|
+
missing_testable_tasks=$(awk '
|
|
320
|
+
/^## / {
|
|
321
|
+
if (in_task && !has_testable) missing = missing (missing ? ", " : "") prev_task
|
|
322
|
+
prev_task = $2; gsub(/\..*/, "", prev_task)
|
|
323
|
+
in_task = 1; has_testable = 0
|
|
324
|
+
}
|
|
325
|
+
/\*\*Testable\*\*:/ { has_testable = 1 }
|
|
326
|
+
END {
|
|
327
|
+
if (in_task && !has_testable) missing = missing (missing ? ", " : "") prev_task
|
|
328
|
+
print missing
|
|
329
|
+
}' "$tasks_file")
|
|
330
|
+
echo "⚠️ Testable 标注不完整:任务=$task_count, Testable=$testable_count(缺失任务: $missing_testable_tasks;缺失字段的任务将被视为 Testable: false)"
|
|
331
|
+
else
|
|
332
|
+
pass "Testable 标注完整性检查通过 ($testable_count/$task_count)"
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
# 11. 至少 1 个任务无依赖
|
|
304
336
|
local independent_count
|
|
305
337
|
independent_count=$(grep -c '\*\*Depends\*\*: (none)' "$tasks_file" || echo "0")
|
|
306
338
|
if [ "$independent_count" -lt 1 ]; then
|
|
@@ -337,6 +369,86 @@ gate_build() {
|
|
|
337
369
|
pass "Python 语法检查通过"
|
|
338
370
|
fi
|
|
339
371
|
|
|
372
|
+
# 单元测试文件存在性检查(Testable=true 任务)
|
|
373
|
+
local tasks_file="$PROJECT_ROOT/specline/changes/$CHANGE/tasks.md"
|
|
374
|
+
if [ -f "$tasks_file" ]; then
|
|
375
|
+
local testable_true_count
|
|
376
|
+
testable_true_count=$(grep -c '\*\*Testable\*\*:.*true' "$tasks_file" || echo "0")
|
|
377
|
+
|
|
378
|
+
if [ "$testable_true_count" -gt 0 ]; then
|
|
379
|
+
echo "正在检查 $testable_true_count 个 Testable=true 任务的单元测试文件..."
|
|
380
|
+
|
|
381
|
+
local missing_files=""
|
|
382
|
+
local syntax_errors=""
|
|
383
|
+
|
|
384
|
+
while IFS='|' read -r task_id file_path; do
|
|
385
|
+
if [ -z "$file_path" ]; then
|
|
386
|
+
missing_files="${missing_files}
|
|
387
|
+
任务 $task_id: 未在 Files 列表中声明 tests/unit/ 或 tests/models/ 下的测试文件"
|
|
388
|
+
continue
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
if [ ! -f "$PROJECT_ROOT/$file_path" ]; then
|
|
392
|
+
missing_files="${missing_files}
|
|
393
|
+
任务 $task_id: $file_path"
|
|
394
|
+
continue
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# 语法检查
|
|
398
|
+
case "$file_path" in
|
|
399
|
+
*.py)
|
|
400
|
+
if ! python -m py_compile "$PROJECT_ROOT/$file_path" 2>&1; then
|
|
401
|
+
syntax_errors="${syntax_errors}
|
|
402
|
+
任务 $task_id: $file_path (Python 语法错误)"
|
|
403
|
+
fi
|
|
404
|
+
;;
|
|
405
|
+
*.ts|*.tsx)
|
|
406
|
+
if ! npx tsc --noEmit "$PROJECT_ROOT/$file_path" 2>&1; then
|
|
407
|
+
syntax_errors="${syntax_errors}
|
|
408
|
+
任务 $task_id: $file_path (TypeScript 语法错误)"
|
|
409
|
+
fi
|
|
410
|
+
;;
|
|
411
|
+
esac
|
|
412
|
+
done < <(awk '
|
|
413
|
+
/^## / {
|
|
414
|
+
task_id = $2; gsub(/\..*/, "", task_id)
|
|
415
|
+
testable = ""; files_line = ""
|
|
416
|
+
}
|
|
417
|
+
/\*\*Testable\*\*:.*true/ { testable = "true" }
|
|
418
|
+
/\*\*Files\*\*:/ {
|
|
419
|
+
if (testable == "true") {
|
|
420
|
+
files_line = $0
|
|
421
|
+
gsub(/.*\*\*Files\*\*:[ \t]*/, "", files_line)
|
|
422
|
+
split(files_line, paths, /,[ \t]*/)
|
|
423
|
+
has_unit_test = 0
|
|
424
|
+
for (i in paths) {
|
|
425
|
+
gsub(/^[ \t]+|[ \t]+$/, "", paths[i])
|
|
426
|
+
if (paths[i] ~ /^tests\/(unit|models)\//) {
|
|
427
|
+
print task_id "|" paths[i]
|
|
428
|
+
has_unit_test = 1
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (has_unit_test == 0) {
|
|
432
|
+
print task_id "|"
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
' "$tasks_file")
|
|
437
|
+
|
|
438
|
+
if [ -n "$missing_files" ]; then
|
|
439
|
+
fail "单元测试文件缺失:${missing_files}"
|
|
440
|
+
fi
|
|
441
|
+
|
|
442
|
+
if [ -n "$syntax_errors" ]; then
|
|
443
|
+
fail "单元测试文件语法错误:${syntax_errors}"
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
pass "单元测试文件存在性检查通过"
|
|
447
|
+
else
|
|
448
|
+
echo "ℹ️ 无 Testable=true 任务,跳过单元测试文件检查"
|
|
449
|
+
fi
|
|
450
|
+
fi
|
|
451
|
+
|
|
340
452
|
write_gate_passed "phases.coding.gates.build_gate"
|
|
341
453
|
pass "Build Gate 全部通过"
|
|
342
454
|
}
|
|
@@ -551,7 +663,7 @@ gate_archive() {
|
|
|
551
663
|
fi
|
|
552
664
|
|
|
553
665
|
# 如果传了 --execute,执行实际归档动作
|
|
554
|
-
if [ "$
|
|
666
|
+
if [ -n "$EXECUTE_ARCHIVE" ]; then
|
|
555
667
|
local src_dir="$PROJECT_ROOT/specline/changes/$CHANGE"
|
|
556
668
|
local archive_dir="$PROJECT_ROOT/specline/changes/archive"
|
|
557
669
|
local date_prefix
|
|
@@ -6,10 +6,7 @@
|
|
|
6
6
|
# 2. 检查当前 session 是否已有绑定
|
|
7
7
|
# 3. 有绑定且 pipeline 仍活跃 → 使用已有绑定,注入上下文
|
|
8
8
|
# 4. 有绑定但 pipeline 已失效 → 清理脏数据,重新扫描
|
|
9
|
-
# 5. 无绑定 →
|
|
10
|
-
# - 0 个 → 透明放行(echo '{}')
|
|
11
|
-
# - 1 个 → 自动绑定并注入上下文
|
|
12
|
-
# - 2+ 个 → 注入"请选择"提示
|
|
9
|
+
# 5. 无绑定 → 透明放行(echo '{}')——不自动绑定,避免跨窗口污染
|
|
13
10
|
#
|
|
14
11
|
# Input (stdin JSON):
|
|
15
12
|
# { "session_id": "...", "is_background_agent": bool, ... }
|
|
@@ -254,63 +251,9 @@ if [ -n "$existing_change" ]; then
|
|
|
254
251
|
fi
|
|
255
252
|
fi
|
|
256
253
|
|
|
257
|
-
# 3. No (valid) binding — scan active pipelines
|
|
258
|
-
scan_result=$(scan_active_pipelines)
|
|
259
254
|
|
|
260
|
-
#
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
fi
|
|
265
|
-
|
|
266
|
-
# 0 active pipelines → transparent pass-through
|
|
267
|
-
if [ "$pipeline_count" -eq 0 ]; then
|
|
268
|
-
echo '{}'
|
|
269
|
-
exit 0
|
|
270
|
-
fi
|
|
271
|
-
|
|
272
|
-
# 1 active pipeline → auto-bind and inject context
|
|
273
|
-
if [ "$pipeline_count" -eq 1 ]; then
|
|
274
|
-
change_name=$(printf '%s' "$scan_result" | cut -d'|' -f1)
|
|
275
|
-
phase=$(printf '%s' "$scan_result" | cut -d'|' -f2)
|
|
276
|
-
state_file=$(printf '%s' "$scan_result" | cut -d'|' -f3)
|
|
277
|
-
|
|
278
|
-
# Auto-bind
|
|
279
|
-
write_binding "$session_id" "$change_name"
|
|
280
|
-
|
|
281
|
-
# Inject context
|
|
282
|
-
ctx_json=$(build_context "$change_name" "$phase" "$state_file")
|
|
283
|
-
|
|
284
|
-
printf '{\n "additional_context": %s\n}\n' "$ctx_json"
|
|
285
|
-
exit 0
|
|
286
|
-
fi
|
|
287
|
-
|
|
288
|
-
# 4. 2+ active pipelines → inject selection prompt (do NOT auto-bind)
|
|
289
|
-
ctx_json=$({
|
|
290
|
-
printf '🚨 **Specline Pipeline 运行中 — 请选择 Pipeline**\n\n'
|
|
291
|
-
printf '**活跃 Pipeline** (%s 个):\n' "$pipeline_count"
|
|
292
|
-
|
|
293
|
-
while IFS='|' read -r change_name phase state_file; do
|
|
294
|
-
[ -n "$change_name" ] || continue
|
|
295
|
-
printf -- '- **%s**: %s 阶段' "$change_name" "$phase"
|
|
296
|
-
|
|
297
|
-
# Add task progress for coding phase
|
|
298
|
-
if [ "$phase" = "coding" ] && [ -f "$state_file" ]; then
|
|
299
|
-
completed=$(jq -r '[.phases.coding.tasks[]? | select(.status == "completed")] | length' "$state_file" 2>/dev/null || printf '0')
|
|
300
|
-
total=$(jq -r '[.phases.coding.tasks[]?] | length' "$state_file" 2>/dev/null || printf '0')
|
|
301
|
-
if [ "$total" != "0" ]; then
|
|
302
|
-
printf ' (%s/%s 任务完成)' "$completed" "$total"
|
|
303
|
-
fi
|
|
304
|
-
fi
|
|
305
|
-
printf '\n'
|
|
306
|
-
done <<< "$scan_result"
|
|
307
|
-
|
|
308
|
-
printf '\n**说明**: 当前发现了 %s 个活跃 Pipeline,但尚未绑定到本会话。\n' "$pipeline_count"
|
|
309
|
-
printf '**操作**: 请使用 `/specline-pipeline --change <名称>` 选择一个 Pipeline 以继续工作。\n\n'
|
|
310
|
-
printf '**阶段约束参考**:\n'
|
|
311
|
-
phase_constraint_table
|
|
312
|
-
printf '\n**提示**: 绑定后系统会自动注入正确的阶段上下文和约束。\n'
|
|
313
|
-
} | jq -Rs '.')
|
|
314
|
-
|
|
315
|
-
printf '{\n "additional_context": %s\n}\n' "$ctx_json"
|
|
255
|
+
# 3. No (valid) binding → transparent pass-through
|
|
256
|
+
# 不再自动绑定或注入任何 pipeline 上下文,避免跨窗口污染。
|
|
257
|
+
# 用户需通过 /specline-pipeline --change <name> 显式绑定。
|
|
258
|
+
echo '{}'
|
|
316
259
|
exit 0
|
|
@@ -13,42 +13,18 @@ description: >-
|
|
|
13
13
|
|
|
14
14
|
### 概述
|
|
15
15
|
|
|
16
|
-
每个 Cursor 会话通过 `session_id`
|
|
16
|
+
每个 Cursor 会话通过 `session_id` **显式绑定**到特定的 Pipeline(通过 `/specline-pipeline --change <name>` 命令)。Hook 脚本通过查 `specline/.pipeline-sessions.json` 表获得确定性映射。
|
|
17
17
|
|
|
18
|
-
###
|
|
18
|
+
### 启动时行为
|
|
19
19
|
|
|
20
20
|
`sessionStart` Hook (`specline-session-start.sh`) 自动处理:
|
|
21
21
|
|
|
22
22
|
1. **已绑定且有效** → 直接使用,注入正确的阶段约束
|
|
23
|
-
2.
|
|
24
|
-
3.
|
|
25
|
-
4.
|
|
26
|
-
5. **脏数据清理** → 绑定指向已归档/不存在的 Pipeline 时自动删除
|
|
23
|
+
2. **无绑定** → 透明放行(`echo '{}'`),不自动扫描活跃 pipeline,不注入任何上下文
|
|
24
|
+
3. **过期绑定清理** → `bound_at` 超过 7 天的绑定自动删除
|
|
25
|
+
4. **脏数据清理** → 绑定指向已归档/不存在的 Pipeline 时自动删除
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
当 sessionStart 注入上下文提示有多个 pipeline 未绑定时,编排者**必须**使用 AskUserQuestion 让用户选择:
|
|
31
|
-
|
|
32
|
-
```javascript
|
|
33
|
-
AskUserQuestion({
|
|
34
|
-
title: "选择 Pipeline",
|
|
35
|
-
questions: [{
|
|
36
|
-
id: "pipeline_select",
|
|
37
|
-
prompt: "当前有 " + count + " 个活跃 Pipeline,请选择要绑定到本会话的:",
|
|
38
|
-
options: [
|
|
39
|
-
{ id: "change-a", label: "change-a (SPEC 阶段)" },
|
|
40
|
-
{ id: "change-b", label: "change-b (CODING 阶段, 3/7 任务)" },
|
|
41
|
-
],
|
|
42
|
-
allow_multiple: false
|
|
43
|
-
}]
|
|
44
|
-
})
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
用户选择后,编排者执行绑定命令:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
.cursor/hooks/specline-pipeline-gate.sh bind <session_id> <selected_change>
|
|
51
|
-
```
|
|
27
|
+
> **重要**:sessionStart 不再自动扫描和绑定活跃 pipeline。要绑定到一个 Pipeline,需要显式执行 `/specline-pipeline --change <name>`。
|
|
52
28
|
|
|
53
29
|
### 用户要求切换 Pipeline 时
|
|
54
30
|
|
|
@@ -105,6 +81,21 @@ AskUserQuestion({
|
|
|
105
81
|
|
|
106
82
|
归档到 `specline/changes/archive/YYYY-MM-DD-<name>/`
|
|
107
83
|
|
|
84
|
+
### Quickfix vs Pipeline 边界判断
|
|
85
|
+
|
|
86
|
+
| 维度 | Quickfix (`/specline-quickfix`) | Pipeline (`/specline-pipeline`) |
|
|
87
|
+
|------|-------------------------------|-------------------------------|
|
|
88
|
+
| 文件改动数 | 1-3 个 | 4+ 个 |
|
|
89
|
+
| 关注点 | 单一关注点 | 多关注点/跨模块 |
|
|
90
|
+
| 架构变更 | 无新架构/新组件 | 需要新组件/新 API |
|
|
91
|
+
| 测试 | 不需要新测试 | 需要写新测试 |
|
|
92
|
+
| 典型场景 | 修 bug、改配置、文档微调 | 新增功能、重构 |
|
|
93
|
+
| 产出 | summary.md + files-changed.json | proposal/design/tasks/specs + 全部测试 |
|
|
94
|
+
| 人工确认 | 0 个 | 3 个 |
|
|
95
|
+
| 耗时 | 1-3 分钟 | 10-30 分钟 |
|
|
96
|
+
|
|
97
|
+
**使用建议**:如果不确定,优先用 quickfix。如果需要更严格的流程保证,用 pipeline。
|
|
98
|
+
|
|
108
99
|
---
|
|
109
100
|
|
|
110
101
|
## Layer 2: Happy Path — 新建流水线
|
|
@@ -266,6 +257,7 @@ Task({
|
|
|
266
257
|
2. 先检测项目的测试框架(读配置文件),按项目实际语言和框架编写测试
|
|
267
258
|
3. 每个 Scenario 至少生成 1 个对应的测试函数(命名遵循框架约定)
|
|
268
259
|
4. 基于 Covers 追溯链,确保每个 Scenario 都有测试
|
|
260
|
+
5. 只编写 tests/integration/** 和 tests/e2e/** 目录下的测试,不得编写 tests/unit/** 和 tests/models/** 下的测试(单元测试由 coding agent 的 TDD 流程负责)
|
|
269
261
|
|
|
270
262
|
## 产出报告
|
|
271
263
|
完成后在 specline/changes/${changeName}/.tmp/test-code-result.json 写入状态(含 test_framework / language / test_dir / scenarios_covered 等字段)
|
|
@@ -285,16 +277,17 @@ Task({
|
|
|
285
277
|
TASKS_FILE="specline/changes/<name>/tasks.md"
|
|
286
278
|
|
|
287
279
|
# 提取每个任务的核心元数据
|
|
288
|
-
# 期望输出格式:TASK_NUM|TYPE|DEPS|COVERS|FILES
|
|
289
|
-
rg -N --no-line-number '^## \d+\.|^- \*\*Type\*\*:|^- \*\*Depends\*\*:|^- \*\*Covers\*\*:|^- \*\*Files\*\*:' "$TASKS_FILE" | while read -r line; do
|
|
280
|
+
# 期望输出格式:TASK_NUM|TYPE|DEPS|COVERS|FILES|TESTABLE
|
|
281
|
+
rg -N --no-line-number '^## \d+\.|^- \*\*Type\*\*:|^- \*\*Depends\*\*:|^- \*\*Covers\*\*:|^- \*\*Files\*\*:|^- \*\*Testable\*\*:' "$TASKS_FILE" | while read -r line; do
|
|
290
282
|
# 解析并按批次分组
|
|
291
283
|
...
|
|
292
284
|
done
|
|
293
285
|
|
|
294
|
-
# 构建任务列表写入状态文件(含 Files 用于冲突检测,Covers
|
|
286
|
+
# 构建任务列表写入状态文件(含 Files 用于冲突检测,Covers 用于追溯,Testable 用于 TDD 判定)
|
|
287
|
+
# Testable 缺失时默认为 false(向后兼容)
|
|
295
288
|
jq --argjson tasks '[
|
|
296
|
-
{"id":"1","type":"backend","deps":[],"batch":1,"status":"pending","covers":"Requirement: 数据模型","files":["server/models.py"]},
|
|
297
|
-
{"id":"2","type":"frontend","deps":[],"batch":1,"status":"pending","covers":"Requirement: 登录页面","files":["src/components/Login.tsx"]}
|
|
289
|
+
{"id":"1","type":"backend","deps":[],"batch":1,"status":"pending","testable":true,"covers":"Requirement: 数据模型","files":["server/models.py"]},
|
|
290
|
+
{"id":"2","type":"frontend","deps":[],"batch":1,"status":"pending","testable":false,"covers":"Requirement: 登录页面","files":["src/components/Login.tsx"]}
|
|
298
291
|
]' '.phases.coding.tasks = $tasks' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
|
|
299
292
|
```
|
|
300
293
|
|
|
@@ -304,12 +297,31 @@ jq --argjson tasks '[
|
|
|
304
297
|
|
|
305
298
|
**6c. 文件冲突检测(每批次派发前)**:
|
|
306
299
|
|
|
307
|
-
|
|
300
|
+
在派发每批任务之前,将当前批次所有任务的 `Files` 按路径前缀分为三类进行冲突检测:
|
|
301
|
+
|
|
302
|
+
**文件类型分类规则**:
|
|
303
|
+
|
|
304
|
+
| 文件类型 | 路径前缀 | 编写者 | 说明 |
|
|
305
|
+
|---------|---------|--------|------|
|
|
306
|
+
| `implementation` | 不以 `tests/` 开头 | coding agent | 实现代码、配置文件、文档等 |
|
|
307
|
+
| `unit_test` | `tests/unit/` 或 `tests/models/` | coding agent(TDD) | 白盒单元测试 |
|
|
308
|
+
| `other_test` | `tests/integration/` 或 `tests/e2e/` | test-writer | 黑盒集成/E2E 测试 |
|
|
309
|
+
|
|
310
|
+
**冲突判定逻辑**:
|
|
311
|
+
|
|
312
|
+
| 场景 | 判定 |
|
|
313
|
+
|------|------|
|
|
314
|
+
| 同类型文件重叠(均为 `implementation`、`unit_test` 或 `other_test`) | **冲突** |
|
|
315
|
+
| `unit_test` 与 `other_test` 重叠 | **不冲突**(目录隔离保证互不干扰) |
|
|
316
|
+
| `implementation` 与任何测试文件重叠 | **不冲突**(实现和测试天然分离) |
|
|
308
317
|
|
|
309
318
|
```bash
|
|
310
|
-
#
|
|
311
|
-
# 读取当前批次所有任务的 files 数组
|
|
312
|
-
#
|
|
319
|
+
# 伪代码:基于文件类型的三类冲突检测算法
|
|
320
|
+
# 1. 读取当前批次所有任务的 files 数组
|
|
321
|
+
# 2. 对每个文件按路径前缀分类:implementation / unit_test / other_test
|
|
322
|
+
# 3. 在同类型文件集合内检查是否有交集
|
|
323
|
+
# 4. 如果同类型 files 有交集 → 标记冲突任务对,暂停并报告用户
|
|
324
|
+
# 5. 跨类型重叠不标记为冲突
|
|
313
325
|
```
|
|
314
326
|
|
|
315
327
|
#### Step 7: 按批次并发派发 Coding Agent
|
|
@@ -340,10 +352,114 @@ for (const task of currentBatchTasks) {
|
|
|
340
352
|
default: agentType = "specline-backend-dev";
|
|
341
353
|
}
|
|
342
354
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
prompt:
|
|
355
|
+
// 根据 Testable 和 Type 构造 Prompt
|
|
356
|
+
let prompt;
|
|
357
|
+
if (task.testable === true) {
|
|
358
|
+
// === TDD prompt(Testable: true) ===
|
|
359
|
+
prompt = `
|
|
360
|
+
你收到一个编码任务(Type: ${task.type}, Testable: true),请按 TDD(测试驱动开发)方式实现本任务范围内的代码。
|
|
361
|
+
|
|
362
|
+
## 上下文文件(只读参考)
|
|
363
|
+
- Spec: specline/changes/${changeName}/specs/${capability}/spec.md
|
|
364
|
+
- Design: specline/changes/${changeName}/design.md
|
|
365
|
+
- Tasks: specline/changes/${changeName}/tasks.md
|
|
366
|
+
|
|
367
|
+
## 当前任务(只实现这个)
|
|
368
|
+
任务 ID: ${task.id}
|
|
369
|
+
覆盖需求: ${task.covers}
|
|
370
|
+
预期文件: ${task.files}
|
|
371
|
+
|
|
372
|
+
从 tasks.md 中提取的任务 ${task.id} 的完整描述:
|
|
373
|
+
---
|
|
374
|
+
${task.content}
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## TDD 约束(RED-GREEN-REFACTOR)
|
|
378
|
+
|
|
379
|
+
你必须按以下 TDD 循环编写代码:
|
|
380
|
+
|
|
381
|
+
### RED 阶段
|
|
382
|
+
1. 分析 Spec 中本任务覆盖的 Scenario,提取需要测试的逻辑单元
|
|
383
|
+
2. 在 tests/unit/<module>/test_<feature>.{ext} 下编写测试文件
|
|
384
|
+
3. 每个 Scenario 至少 1 个测试函数/方法
|
|
385
|
+
4. 覆盖:Happy Path + 边界条件(空值、极值、边界值)+ 异常路径(错误输入、异常状态)
|
|
386
|
+
5. 运行测试,确认全部 FAIL(RED)
|
|
387
|
+
|
|
388
|
+
### GREEN 阶段
|
|
389
|
+
6. 编写最小实现代码,只使当前测试通过
|
|
390
|
+
7. 不编写测试未覆盖的逻辑
|
|
391
|
+
8. 运行测试,确认全部 PASS(GREEN)
|
|
392
|
+
|
|
393
|
+
### REFACTOR 阶段
|
|
394
|
+
9. 重构实现代码改善结构(提取方法、消除重复、优化命名)
|
|
395
|
+
10. 运行测试,确保持续 PASS
|
|
396
|
+
11. 如果需要,补充缺失的边界条件测试
|
|
397
|
+
|
|
398
|
+
## 关键约束
|
|
399
|
+
1. 只修改本任务 Files 范围内的文件
|
|
400
|
+
2. 不修改其他任务负责的文件
|
|
401
|
+
3. 与已完成任务的接口约定必须遵守(参考已生成的接口/类型定义文件)
|
|
402
|
+
4. 确认过 design.md 中的技术决策后再动手
|
|
403
|
+
5. 测试文件只能写入 tests/unit/ 或 tests/models/ 目录
|
|
404
|
+
6. 不得修改 tests/integration/ 或 tests/e2e/ 目录下的文件(属于 test-writer)
|
|
405
|
+
7. **完成后必须将 tasks.md 中本任务的 \`[ ]\` 改为 \`[x]\`**(方便断点续跑识别进度)
|
|
406
|
+
|
|
407
|
+
## 产出报告
|
|
408
|
+
完成后在 specline/changes/${changeName}/.tmp/task-${task.id}-result.json 写入:
|
|
409
|
+
{
|
|
410
|
+
"task_id": "${task.id}",
|
|
411
|
+
"type": "${task.type}",
|
|
412
|
+
"testable": true,
|
|
413
|
+
"covers": "${task.covers}",
|
|
414
|
+
"status": "completed",
|
|
415
|
+
"files_changed": [...],
|
|
416
|
+
"test_files": ["tests/unit/...", ...],
|
|
417
|
+
"tests_passed": true,
|
|
418
|
+
"summary": "..."
|
|
419
|
+
}
|
|
420
|
+
`;
|
|
421
|
+
} else if (["frontend", "backend", "infra", "db"].includes(task.type)) {
|
|
422
|
+
// === 标准编码 prompt(Testable: false,有代码逻辑) ===
|
|
423
|
+
prompt = `
|
|
424
|
+
你收到一个编码任务(Type: ${task.type}, Testable: false),请只实现本任务范围内的代码。
|
|
425
|
+
|
|
426
|
+
## 上下文文件(只读参考)
|
|
427
|
+
- Spec: specline/changes/${changeName}/specs/${capability}/spec.md
|
|
428
|
+
- Design: specline/changes/${changeName}/design.md
|
|
429
|
+
- Tasks: specline/changes/${changeName}/tasks.md
|
|
430
|
+
|
|
431
|
+
## 当前任务(只实现这个)
|
|
432
|
+
任务 ID: ${task.id}
|
|
433
|
+
覆盖需求: ${task.covers}
|
|
434
|
+
预期文件: ${task.files}
|
|
435
|
+
|
|
436
|
+
从 tasks.md 中提取的任务 ${task.id} 的完整描述:
|
|
437
|
+
---
|
|
438
|
+
${task.content}
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 约束
|
|
442
|
+
1. 只修改本任务 Files 范围内的文件
|
|
443
|
+
2. 不修改其他任务负责的文件
|
|
444
|
+
3. 与已完成任务的接口约定必须遵守(参考已生成的接口/类型定义文件)
|
|
445
|
+
4. 确认过 design.md 中的技术决策后再动手
|
|
446
|
+
5. **完成后必须将 tasks.md 中本任务的 \`[ ]\` 改为 \`[x]\`**(方便断点续跑识别进度)
|
|
447
|
+
|
|
448
|
+
## 产出报告
|
|
449
|
+
完成后在 specline/changes/${changeName}/.tmp/task-${task.id}-result.json 写入:
|
|
450
|
+
{
|
|
451
|
+
"task_id": "${task.id}",
|
|
452
|
+
"type": "${task.type}",
|
|
453
|
+
"testable": false,
|
|
454
|
+
"covers": "${task.covers}",
|
|
455
|
+
"status": "completed",
|
|
456
|
+
"files_changed": [...],
|
|
457
|
+
"summary": "..."
|
|
458
|
+
}
|
|
459
|
+
`;
|
|
460
|
+
} else {
|
|
461
|
+
// === 配置/文档 prompt(Type: config/docs) ===
|
|
462
|
+
prompt = `
|
|
347
463
|
你收到一个编码任务(Type: ${task.type}),请只实现本任务范围内的代码。
|
|
348
464
|
|
|
349
465
|
## 上下文文件(只读参考)
|
|
@@ -378,15 +494,25 @@ ${task.content}
|
|
|
378
494
|
"files_changed": [...],
|
|
379
495
|
"summary": "..."
|
|
380
496
|
}
|
|
381
|
-
|
|
497
|
+
`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
Task({
|
|
501
|
+
subagent_type: agentType,
|
|
502
|
+
description: `实现任务 ${task.id}: ${task.title} [${task.type}]${task.testable ? ' (TDD)' : ''}`,
|
|
503
|
+
prompt: prompt
|
|
382
504
|
})
|
|
383
505
|
}
|
|
384
506
|
```
|
|
385
507
|
|
|
386
508
|
**7b. 等待当前批次所有 Agent 完成后**:
|
|
387
509
|
1. 验证每个 Agent 的产出报告(`specline/changes/<name>/.tmp/task-<id>-result.json`)
|
|
388
|
-
2.
|
|
389
|
-
|
|
510
|
+
2. **对 Testable=true 的任务**,验证 `task-<id>-result.json` 中是否包含 `test_files` 字段且其值非空。如果 Testable=true 但 agent 未产出测试文件,标记为 warning 并记录到事件日志:
|
|
511
|
+
```
|
|
512
|
+
{"ts":"...","event":"tdd_warning","task":"<id>","reason":"Testable=true but no test_files produced"}
|
|
513
|
+
```
|
|
514
|
+
3. 更新状态文件中对应 task 的 `status` 和 `completed_at`
|
|
515
|
+
4. **验证 tasks.md 中对应任务的 checkbox 已从 `[ ]` 变为 `[x]`**(如果未标记,自动补标)
|
|
390
516
|
|
|
391
517
|
```bash
|
|
392
518
|
# 更新状态文件
|
|
@@ -411,6 +537,10 @@ sed -i '' "s/^## ${task_id}\. \[ \]/## ${task_id}. [x]/" specline/changes/<name>
|
|
|
411
537
|
.cursor/hooks/specline-pipeline-gate.sh build --change "<name>"
|
|
412
538
|
```
|
|
413
539
|
|
|
540
|
+
Build Gate 校验内容:
|
|
541
|
+
- 编译/语法检查(原有逻辑)
|
|
542
|
+
- **单元测试文件存在性检查**(新增):对 Testable=true 的任务,检查其 `tests/unit/` 和 `tests/models/` 下的单元测试文件是否存在且语法正确。如果 Testable=true 的任务未产出对应测试文件,Build Gate 失败
|
|
543
|
+
|
|
414
544
|
exit code 0 = 通过,进入 Phase 3。失败处理见 [Layer 3: Build Gate 失败处理](#build-gate-失败处理)。
|
|
415
545
|
|
|
416
546
|
### Phase 3: CODE REVIEW
|
|
@@ -423,11 +553,21 @@ exit code 0 = 通过,进入 Phase 3。失败处理见 [Layer 3: Build Gate 失
|
|
|
423
553
|
|
|
424
554
|
审查前端/后端代码变更。审查时利用 tasks.md 的 `Covers` 追溯链:每个 finding 应标注涉及的文件和对应的 Requirement/Scenario。
|
|
425
555
|
|
|
556
|
+
对 Testable=true 的任务,额外审查其 `tests/unit/` 和 `tests/models/` 下的单元测试文件质量,包括:
|
|
557
|
+
- 边界条件覆盖(空值、极值、边界值)
|
|
558
|
+
- 异常路径覆盖(错误输入、异常状态)
|
|
559
|
+
- 测试断言的有效性
|
|
560
|
+
|
|
561
|
+
code-review.json 中 unit test 相关的 finding 标注 `type` 为 `"unit_test_quality"`,示例:
|
|
562
|
+
```json
|
|
563
|
+
{ "severity": "warning", "type": "unit_test_quality", "file": "tests/unit/auth/test_login.py", "covers": "Requirement: Coding Agent Prompt 条件化 TDD 注入", "message": "缺少空密码输入边界条件测试" }
|
|
564
|
+
```
|
|
565
|
+
|
|
426
566
|
**9b. specline-config-reviewer**(有 config/docs 类型任务时):
|
|
427
567
|
|
|
428
568
|
审查 config/docs 变更——shell 脚本安全性、配置文件语法和一致性、Markdown 文档结构完整性。
|
|
429
569
|
|
|
430
|
-
> 两种审查 Agent 可并发启动。产出均为 `specline/changes/<name>/.tmp/code-review.json`(`{ "findings": [{ "severity": "error"|"warning", "file": "...", "covers": "Requirement: xxx", "message": "..." }] }`)。
|
|
570
|
+
> 两种审查 Agent 可并发启动。产出均为 `specline/changes/<name>/.tmp/code-review.json`(`{ "findings": [{ "severity": "error"|"warning", "type": "unit_test_quality"|"style"|"security"|"logic", "file": "...", "covers": "Requirement: xxx", "message": "..." }] }`)。
|
|
431
571
|
|
|
432
572
|
#### Step 10: Lint Gate
|
|
433
573
|
|
|
@@ -568,13 +708,46 @@ jq '.phases.coding.gates.build_gate.passed = null' "$STATE_FILE" > tmp && mv tmp
|
|
|
568
708
|
|
|
569
709
|
### 测试失败处理
|
|
570
710
|
|
|
571
|
-
⚠️
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
711
|
+
⚠️ 测试失败根据失败文件所在的目录区分处理路径:
|
|
712
|
+
|
|
713
|
+
#### 单元测试失败处理
|
|
714
|
+
|
|
715
|
+
失败文件在 `tests/unit/` 或 `tests/models/` 目录下:
|
|
716
|
+
|
|
717
|
+
- 利用 `Covers` 追溯链定位到具体 coding 任务
|
|
718
|
+
- 回对应 coding agent 修复实现代码或测试代码(最多 2 次循环)
|
|
719
|
+
- `spec_ambiguity`(Spec 模糊)→ **不自动循环修复**,暂停流水线并展示模糊点给用户
|
|
720
|
+
|
|
721
|
+
Gate 重置(仅重置 test_unit_gate):
|
|
722
|
+
|
|
723
|
+
```bash
|
|
724
|
+
jq '.phases.test.sub_phases.unit.gates.test_unit_gate.passed = null' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
#### 集成/E2E 测试失败处理
|
|
728
|
+
|
|
729
|
+
失败文件在 `tests/integration/` 或 `tests/e2e/` 目录下:
|
|
730
|
+
|
|
731
|
+
- specline-test-runner 分析原因:
|
|
732
|
+
- **测试代码问题** → specline-test-writer 自修(最多 2 次)
|
|
733
|
+
- **实现代码问题** → 利用 `Covers` 追溯链定位到具体任务,回对应 coding agent 修复 → **使用影响范围算法精确重置受影响任务的 Gate**
|
|
734
|
+
- **`spec_ambiguity`**(Spec 模糊)→ **不自动循环修复**,暂停流水线并展示模糊点给用户
|
|
575
735
|
- 循环最多 2 次
|
|
576
736
|
|
|
577
|
-
|
|
737
|
+
Gate 重置:
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
jq '
|
|
741
|
+
.phases.test.sub_phases.integration.gates.test_integration_gate.passed = null |
|
|
742
|
+
.phases.test.sub_phases.e2e.gates.test_e2e_gate.passed = null
|
|
743
|
+
' "$STATE_FILE" > tmp && mv tmp "$STATE_FILE"
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
#### 优先级规则
|
|
747
|
+
|
|
748
|
+
当单元测试和集成/E2E 测试同时失败时:**优先修复单元测试**(先执行 coding agent 修复循环),单元测试通过后再处理集成/E2E 测试失败。
|
|
749
|
+
|
|
750
|
+
代码修复后 Gate 全部重置(所有测试类型):
|
|
578
751
|
|
|
579
752
|
```bash
|
|
580
753
|
jq '
|
|
@@ -65,6 +65,7 @@ specline-pipeline-gate.sh new --change "<name>"
|
|
|
65
65
|
- **Type**: frontend | backend | infra | db | config | docs
|
|
66
66
|
- **Depends**: (none) | 依赖的任务编号
|
|
67
67
|
- **Covers**: Requirement: xxx, Scenario: xxx
|
|
68
|
+
- **Testable**: true | false
|
|
68
69
|
- **Files**: 任务涉及的文件路径列表
|
|
69
70
|
|
|
70
71
|
任务拆分原则:
|
|
@@ -100,8 +101,9 @@ specline-pipeline-gate.sh artifacts --change "<name>" --json
|
|
|
100
101
|
## 1. 数据模型 [x]
|
|
101
102
|
- Type: backend
|
|
102
103
|
- Depends: (none)
|
|
103
|
-
- Files: server/models/user.py
|
|
104
104
|
- Covers: Requirement: 用户数据模型
|
|
105
|
+
- Testable: true
|
|
106
|
+
- Files: server/models/user.py
|
|
105
107
|
```
|
|
106
108
|
|
|
107
109
|
> 任务粒度适中,Files 范围明确,Covers 可追溯到具体 Requirement。
|
|
@@ -126,6 +128,7 @@ specline-pipeline-gate.sh artifacts --change "<name>" --json
|
|
|
126
128
|
| 独立可测 | 每个任务可独立验证完成状态 |
|
|
127
129
|
| 文件不交叠 | 第 1 批次(Depends: none)任务的文件集合无交集 |
|
|
128
130
|
| 可追溯 | 每个任务必须通过 Covers 追溯到具体 Requirement/Scenario |
|
|
131
|
+
| Testable 标注 | 无依赖 + 有可测代码 + 非 config/docs → Testable: true |
|
|
129
132
|
|
|
130
133
|
---
|
|
131
134
|
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: specline-quickfix
|
|
3
|
+
description: 轻量修改 Skill —— 小改动用 quickfix,大功能用 pipeline
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /specline-quickfix 轻量修改 Skill
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Layer 1: 速览与定位
|
|
11
|
+
|
|
12
|
+
**一句话定位**:小改动用 quickfix,大功能用 pipeline。
|
|
13
|
+
|
|
14
|
+
**入口**:`/specline-quickfix <描述>`
|
|
15
|
+
|
|
16
|
+
**你做:**
|
|
17
|
+
- 读取相关代码理解上下文
|
|
18
|
+
- 直接 Write/StrReplace 编辑文件(不使用子 Agent)
|
|
19
|
+
- ReadLints 自动校验 + 修复(最多 2 次循环)
|
|
20
|
+
- 运行项目已有单元测试(失败修复最多 2 次循环)
|
|
21
|
+
- 生成轻量归档(summary.md + files-changed.json)
|
|
22
|
+
|
|
23
|
+
**你不做:**
|
|
24
|
+
- 创建 proposal.md、design.md、tasks.md、specs/ 等规划文档
|
|
25
|
+
- 启动任何 specline-* 子 Agent
|
|
26
|
+
- 创建 `.pipeline-state.json` 或 `.pipeline-sessions.json`
|
|
27
|
+
- 写新测试、跑集成/E2E 测试
|
|
28
|
+
|
|
29
|
+
### 流程概览
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
UNDERSTAND ──→ IMPLEMENT ──→ REVIEW ──→ TEST ──→ ARCHIVE
|
|
33
|
+
(读代码) (直编辑) (Lint+自审) (现有单测) (轻量归档)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Hook 透明
|
|
37
|
+
|
|
38
|
+
Quickfix 不绑定 Pipeline session,所有 Hook(sessionStart、preToolUse、postToolUse、subagentStart、beforeShellExecution)自动透明放行,不产生任何拦截或提醒。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Layer 2: Happy Path
|
|
43
|
+
|
|
44
|
+
### Phase 1: UNDERSTAND
|
|
45
|
+
|
|
46
|
+
**目标**:理解变更上下文,明确修改范围。
|
|
47
|
+
|
|
48
|
+
**Steps**:
|
|
49
|
+
|
|
50
|
+
1. 解析用户描述,提取关键词(文件名、函数名、错误信息等)
|
|
51
|
+
2. 使用 Read 工具读取相关源文件,理解当前逻辑
|
|
52
|
+
3. 确认变更范围:
|
|
53
|
+
- 1-3 个文件 ✓
|
|
54
|
+
- 单一关注点 ✓
|
|
55
|
+
- 不涉及架构变更 ✓
|
|
56
|
+
- 不需要新测试 ✓
|
|
57
|
+
4. **意图模糊时**:使用 AskUserQuestion 向用户确认变更范围和目标,不要猜测
|
|
58
|
+
|
|
59
|
+
**准入条件**:变更范围已验证在 quickfix 适用范围内(参见 Layer 3 边界判断)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### Phase 2: IMPLEMENT
|
|
64
|
+
|
|
65
|
+
**目标**:直接编辑源文件,完成修改。
|
|
66
|
+
|
|
67
|
+
**Steps**:
|
|
68
|
+
|
|
69
|
+
1. 使用 Write / StrReplace 工具直接编辑文件
|
|
70
|
+
2. **不使用子 Agent**,不调用 Task 工具
|
|
71
|
+
3. 编辑完成后,运行 ReadLints 检查新增的 lint 错误
|
|
72
|
+
|
|
73
|
+
**约束**:
|
|
74
|
+
- 只修改 UNDERSTAND 阶段确认的文件
|
|
75
|
+
- 如果发现需要修改第 4 个文件 → 暂停并建议转 `/specline-pipeline`
|
|
76
|
+
- 保持现有代码风格和命名约定
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### Phase 3: REVIEW
|
|
81
|
+
|
|
82
|
+
**目标**:通过 Lint 检查和 Agent 自审确保代码质量。
|
|
83
|
+
|
|
84
|
+
**Steps**:
|
|
85
|
+
|
|
86
|
+
1. 运行 ReadLints 收集所有 lint 问题
|
|
87
|
+
2. **如有 lint 错误**:自动修复 → 再次 ReadLints → 最多循环 2 次
|
|
88
|
+
- 第 1 次修复后仍有错误 → 分析原因,再次修复
|
|
89
|
+
- 第 2 次修复后仍有错误 → 报告用户,附错误列表和修复尝试记录,暂停
|
|
90
|
+
3. **Agent 自审**:
|
|
91
|
+
- 变更逻辑是否正确?
|
|
92
|
+
- 是否处理了边界条件?
|
|
93
|
+
- 是否引入了新问题(如未使用的导入、副作用)?
|
|
94
|
+
- 是否破坏现有功能?
|
|
95
|
+
4. 自审通过 → 进入 TEST 阶段
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### Phase 4: TEST
|
|
100
|
+
|
|
101
|
+
**目标**:运行项目已有单元测试,确保不引入回归。
|
|
102
|
+
|
|
103
|
+
**Steps**:
|
|
104
|
+
|
|
105
|
+
1. **自动检测测试框架**:
|
|
106
|
+
- 检查 `package.json` scripts → Jest / Mocha / Vitest
|
|
107
|
+
- 检查 `pytest` / `go test` / `cargo test` 配置
|
|
108
|
+
2. **有测试配置**:运行现有单元测试
|
|
109
|
+
- 通过 → 进入 ARCHIVE 阶段
|
|
110
|
+
- 失败 → 分析失败原因,修复代码 → 重新运行 → 最多循环 2 次
|
|
111
|
+
- 第 2 次修复后仍失败 → 报告用户(附失败详情和修复尝试记录),暂停
|
|
112
|
+
3. **无测试配置**:跳过 TEST 阶段,进入 ARCHIVE 阶段(在 summary.md 中标注)
|
|
113
|
+
|
|
114
|
+
**不运行**:集成测试、E2E 测试、新编写的测试。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Phase 5: ARCHIVE
|
|
119
|
+
|
|
120
|
+
**目标**:生成轻量归档,提供变更可追溯性。
|
|
121
|
+
|
|
122
|
+
**Steps**:
|
|
123
|
+
|
|
124
|
+
1. 在 `specline/changes/archive/` 下创建归档目录:
|
|
125
|
+
```
|
|
126
|
+
specline/changes/archive/YYYY-MM-DD-<description>/
|
|
127
|
+
├── summary.md
|
|
128
|
+
└── files-changed.json
|
|
129
|
+
```
|
|
130
|
+
2. **summary.md** 内容:
|
|
131
|
+
```markdown
|
|
132
|
+
# <变更标题>
|
|
133
|
+
|
|
134
|
+
## What
|
|
135
|
+
<一句话描述做了什么>
|
|
136
|
+
|
|
137
|
+
## Why
|
|
138
|
+
<为什么要做这个修改>
|
|
139
|
+
|
|
140
|
+
## Files Changed
|
|
141
|
+
- path/to/file1 — <修改简述>
|
|
142
|
+
- path/to/file2 — <修改简述>
|
|
143
|
+
|
|
144
|
+
## Test Result
|
|
145
|
+
- 通过 / 跳过(无现有单元测试)/ 失败(附详情)
|
|
146
|
+
```
|
|
147
|
+
3. **files-changed.json** 内容:
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"files": ["path/to/file1", "path/to/file2"],
|
|
151
|
+
"change_count": 2,
|
|
152
|
+
"description": "<变更描述>"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
4. 展示变更摘要,**询问用户**:是否需要 git commit?
|
|
156
|
+
|
|
157
|
+
**无人确认点**:整个 quickfix 流程不暂停等待人工确认(lint + test 是自动质量底线)。
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Layer 3: 异常与边界
|
|
162
|
+
|
|
163
|
+
### Quickfix vs Pipeline 边界判断
|
|
164
|
+
|
|
165
|
+
使用以下规则判断变更是否适合 quickfix:
|
|
166
|
+
|
|
167
|
+
| 维度 | Quickfix (`/specline-quickfix`) | Pipeline (`/specline-pipeline`) |
|
|
168
|
+
|------|-------------------------------|-------------------------------|
|
|
169
|
+
| 文件改动数 | 1-3 个 | 4+ 个 |
|
|
170
|
+
| 关注点 | 单一关注点 | 多关注点/跨模块 |
|
|
171
|
+
| 架构变更 | 无新架构/新组件 | 需要新组件/新 API |
|
|
172
|
+
| 测试 | 不需要新测试 | 需要写新测试 |
|
|
173
|
+
| 典型场景 | 修 bug、改配置、文档微调 | 新增功能、重构 |
|
|
174
|
+
| 产出 | summary.md + files-changed.json | proposal/design/tasks/specs + 全部测试 |
|
|
175
|
+
| 人工确认 | 0 个 | 3 个 |
|
|
176
|
+
| 耗时 | 1-3 分钟 | 10-30 分钟 |
|
|
177
|
+
|
|
178
|
+
**边界处理规则**:
|
|
179
|
+
|
|
180
|
+
| 异常情况 | 处理方式 |
|
|
181
|
+
|----------|----------|
|
|
182
|
+
| 变更范围 > 3 个文件 | 暂停,建议转 `/specline-pipeline` |
|
|
183
|
+
| 需要写新测试 | 暂停,建议转 `/specline-pipeline` |
|
|
184
|
+
| 涉及架构变更/新 API | 暂停,建议转 `/specline-pipeline` |
|
|
185
|
+
| Lint 修复 2 次后仍有错误 | 报告用户(附错误列表和修复记录),暂停 |
|
|
186
|
+
| 测试失败 2 次后仍失败 | 报告用户(附失败详情和修复记录),暂停 |
|
|
187
|
+
| 实现过程中发现需要额外文件 | 如果总数仍 ≤ 3 → 继续;如果 > 3 → 暂停并建议转 pipeline |
|
|
188
|
+
| 项目无测试配置 | 跳过 TEST 阶段,在 summary.md 中标注 |
|
|
189
|
+
|
|
190
|
+
### 不适合 Quickfix 的典型场景
|
|
191
|
+
|
|
192
|
+
- 新增功能模块(需要 spec/design 规划)
|
|
193
|
+
- 跨 3+ 模块的接口变更
|
|
194
|
+
- 数据库 schema 变更
|
|
195
|
+
- 需要新增测试覆盖的复杂修复
|
|
196
|
+
- 需要多人/多步骤协调的改动
|
|
197
|
+
|
|
198
|
+
**使用建议**:如果不确定,优先用 quickfix。如果需要更严格的流程保证,用 pipeline。
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Layer 4: 附录
|
|
203
|
+
|
|
204
|
+
### 与 Pipeline 的关系
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
specline-pipeline (完整流程) specline-quickfix (轻量流程)
|
|
208
|
+
SPEC UNDERSTAND
|
|
209
|
+
↓ ↓
|
|
210
|
+
CODING (子 Agent 并发) IMPLEMENT (单 Agent 直编)
|
|
211
|
+
↓ ↓
|
|
212
|
+
CODE REVIEW (review Agent) REVIEW (ReadLints + 自审)
|
|
213
|
+
↓ ↓
|
|
214
|
+
TEST (unit → integration → e2e) TEST (现有单测 only)
|
|
215
|
+
↓ ↓
|
|
216
|
+
ARCHIVE (Delta sync) ARCHIVE (summary + files-changed)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
两者完全独立,通过边界判断规则选择。不共享状态文件,不互相依赖。
|
|
220
|
+
|
|
221
|
+
### 归档目录结构兼容性
|
|
222
|
+
|
|
223
|
+
Quickfix 归档目录结构与 Pipeline 归档保持一致:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
specline/changes/archive/
|
|
227
|
+
├── YYYY-MM-DD-<pipeline-change>/ ← Pipeline 归档
|
|
228
|
+
│ ├── proposal.md
|
|
229
|
+
│ ├── design.md
|
|
230
|
+
│ ├── tasks.md
|
|
231
|
+
│ ├── specs/
|
|
232
|
+
│ └── ...
|
|
233
|
+
│
|
|
234
|
+
└── YYYY-MM-DD-<quickfix-description>/ ← Quickfix 归档
|
|
235
|
+
├── summary.md
|
|
236
|
+
└── files-changed.json
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
两种归档方式共存于同一目录,通过内容区分(Pipeline 归档有 proposal.md 等完整文档,Quickfix 归档只有 summary.md + files-changed.json)。
|