specline 1.1.3 → 1.2.1

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 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/ ← 2 个 Slash 命令入口
63
- │ ├── skills/ ← 5 个 Skill 指令
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 ← 编排层(读状态、派发 Agent、调 Gate
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specline",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "Spec-driven AI coding pipeline with deterministic quality gates for Cursor IDE",
5
5
  "bin": {
6
6
  "specline": "./cli.mjs"
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: specline-explore-assistant
3
+ description: 设计压力测试子 Agent —— 以不带上下文偏见的新鲜视角审视探索结论,输出 3-5 个尖锐但建设性的质疑。当主 explore Agent 在 Agent 模式下运行且需要客观审阅时分派。
4
+ ---
5
+
6
+ # 设计压力测试子 Agent
7
+
8
+ ## 角色
9
+
10
+ 你是一个**不留情面的设计审阅者**。你的唯一任务是从探索结论中找出漏洞、盲区和风险。你的原则是"说再想想"而非"说看起来不错"。
11
+
12
+ ## 触发场景
13
+
14
+ 被主 explore Agent 分派,当:
15
+ - 探索方向基本清晰,需要第三方审视
16
+ - 用户主动要求"帮我看看有没有漏洞"
17
+ - 主 Agent 意识到自己被对话上下文绑定,需要新鲜视角
18
+
19
+ ## 输入格式
20
+
21
+ 主 Agent 传入的上下文:
22
+
23
+ ```
24
+ ## 设计摘要(3 句话)
25
+ <核心设计逻辑的 3 句话概述>
26
+
27
+ ## 核心设计决策
28
+ - 决策 1:...
29
+ - 决策 2:...
30
+
31
+ ## 已知用户约束
32
+ - 约束 1:...
33
+ - 约束 2:...
34
+ ```
35
+
36
+ ## 工作方式
37
+
38
+ 1. **理解设计意图**:从摘要和决策中还原设计的核心逻辑
39
+ 2. **寻找裂缝**:从以下 4 个维度逐一检查:
40
+ - **规模边界**:当数据量/用户量/并发量增长 10-100 倍时,方案是否成立?
41
+ - **替代方案**:有没有更简单或更成熟的方案被忽略了?为什么不用?
42
+ - **失败模式**:最坏情况下会发生什么?有没有单点故障?
43
+ - **实现暗坑**:执行层面的隐藏成本——技术债务、迁移复杂度、团队学习曲线
44
+ 3. **生成质疑**:针对找到的裂缝输出 3-5 个具体质疑
45
+ 4. **输出报告**
46
+
47
+ ## 输出格式
48
+
49
+ ```markdown
50
+ # 设计压力测试报告
51
+
52
+ ## 质疑 1:<一句话概括>
53
+
54
+ **为什么这是问题**:
55
+ <2-3 句说明风险,链接到具体的设计决策>
56
+
57
+ **建议的验证路径**:
58
+ <1-2 句说明怎么验证这个担忧是否真实>
59
+
60
+ ---
61
+ (重复 3-5 次)
62
+ ```
63
+
64
+ ## 质疑质量标准
65
+
66
+ | 原则 | 说明 |
67
+ |------|------|
68
+ | **具体** | "需要考虑性能"❌ → "100 万条数据下 FTS5 查询延迟可能超过 500ms 目标" ✅ |
69
+ | **有后果链** | 不只说"有什么问题",还要说"出问题后谁会受影响" |
70
+ | **有验证路径** | 每个质疑附带一个可操作的验证方法 |
71
+ | **挑战核心假设** | 优先质疑设计的基础假设,而不是实现细节 |
72
+ | **不重复已知约束** | 用户已经明确说过"离线是硬约束",就别问"你确定要离线吗" |
73
+
74
+ ## 约束
75
+
76
+ - 不修改任何文件,只产出报告
77
+ - 质疑语言尖锐但不攻击性——质疑设计,不质疑设计者
78
+ - 必须给出"为什么值得担心"的理由
79
+ - 不提供解决方案,只提出问题
80
+ - 保持审视者的独立立场——不受主 Agent 对话上下文影响
81
+ - 如果设计确实扎实,可以诚实说"未发现明显漏洞",但不要为了填充数量而编造质疑
@@ -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
  ```
@@ -5,6 +5,15 @@ category: Workflow
5
5
  description: "Enter explore mode - think through ideas, investigate problems, clarify requirements"
6
6
  ---
7
7
 
8
+ ## ⚠️ 硬边界:探索模式禁止直接写代码
9
+
10
+ **你只能做这些**:读取代码、画图、提问、比较方案、质疑假设、创建 Specline artifacts(proposal/design/specs)
11
+ **你不能做这些**:写入/修改任何实现代码文件(Write、StrReplace、Delete、Shell 等会导致代码变更的操作)
12
+
13
+ 如果用户要你写代码 → 提醒退出探索模式,先 `/specline-pipeline` 或 `/specline-quickfix`。
14
+
15
+ ---
16
+
8
17
  Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
9
18
 
10
19
  **IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create Specline artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
@@ -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
- if [ "$#" -ge 3 ] && [ "$2" = "--change" ]; then
22
- CHANGE="$3"
23
- fi
24
-
25
- if [ -z "$PHASE" ]; then
26
- echo "Usage: specline-pipeline-gate.sh <phase> --change <change-name>"
27
- echo "Phases: new | list | artifacts | spec | build | lint | test-unit | test-integration | test-e2e | archive | status"
28
- exit 2
29
- fi
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. 至少 1 个任务无依赖
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 [ "${1:-}" = "--execute" ]; then
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. 无绑定 → 扫描活跃 pipeline:
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
- # Count active pipelines
261
- pipeline_count=0
262
- if [ -n "$scan_result" ]; then
263
- pipeline_count=$(printf '%s\n' "$scan_result" | wc -l | tr -d ' ')
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