sillyspec 3.20.2 → 3.20.4

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 (133) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +21 -21
  2. package/.claude/skills/sillyspec-auto/SKILL.md +83 -83
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -44
  4. package/.claude/skills/sillyspec-commit/SKILL.md +106 -106
  5. package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +31 -31
  7. package/.claude/skills/sillyspec-execute/SKILL.md +30 -30
  8. package/.claude/skills/sillyspec-explore/SKILL.md +109 -109
  9. package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
  10. package/.claude/skills/sillyspec-plan/SKILL.md +21 -21
  11. package/.claude/skills/sillyspec-propose/SKILL.md +21 -21
  12. package/.claude/skills/sillyspec-quick/SKILL.md +21 -21
  13. package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
  14. package/.claude/skills/sillyspec-scan/SKILL.md +21 -21
  15. package/.claude/skills/sillyspec-state/SKILL.md +54 -54
  16. package/.claude/skills/sillyspec-status/SKILL.md +21 -21
  17. package/.claude/skills/sillyspec-verify/SKILL.md +21 -21
  18. package/.claude/skills/sillyspec-workspace/SKILL.md +157 -157
  19. package/.husky/pre-push +13 -13
  20. package/CLAUDE.md +18 -18
  21. package/README.md +198 -188
  22. package/SKILL.md +90 -91
  23. package/bin/sillyspec.js +2 -2
  24. package/docs/brainstorm-plan-contract.md +64 -64
  25. package/docs/plan-execute-contract.md +123 -123
  26. package/docs/platform-scan-protocol.md +298 -298
  27. package/docs/revision-mode.md +115 -115
  28. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -99
  29. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -218
  30. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -167
  31. package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -148
  32. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -193
  33. package/docs/sillyspec/file-lifecycle.md +125 -125
  34. package/docs/workflow-contract-regression.md +106 -106
  35. package/docs/worktree-isolation.md +252 -252
  36. package/package.json +40 -40
  37. package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
  38. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
  39. package/packages/dashboard/dist/index.html +16 -16
  40. package/packages/dashboard/index.html +15 -15
  41. package/packages/dashboard/package-lock.json +2384 -2384
  42. package/packages/dashboard/package.json +25 -25
  43. package/packages/dashboard/server/executor.js +86 -86
  44. package/packages/dashboard/server/index.js +588 -588
  45. package/packages/dashboard/server/parser.js +526 -526
  46. package/packages/dashboard/server/watcher.js +344 -344
  47. package/packages/dashboard/src/App.vue +558 -558
  48. package/packages/dashboard/src/components/ActionBar.vue +93 -93
  49. package/packages/dashboard/src/components/CommandPalette.vue +96 -96
  50. package/packages/dashboard/src/components/DetailPanel.vue +137 -137
  51. package/packages/dashboard/src/components/LogStream.vue +65 -65
  52. package/packages/dashboard/src/components/PipelineStage.vue +95 -95
  53. package/packages/dashboard/src/components/PipelineView.vue +156 -156
  54. package/packages/dashboard/src/components/ProjectList.vue +210 -210
  55. package/packages/dashboard/src/components/StageBadge.vue +67 -67
  56. package/packages/dashboard/src/components/StepCard.vue +94 -94
  57. package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
  58. package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
  59. package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
  60. package/packages/dashboard/src/composables/useDashboard.js +170 -170
  61. package/packages/dashboard/src/composables/useKeyboard.js +119 -119
  62. package/packages/dashboard/src/composables/useWebSocket.js +129 -129
  63. package/packages/dashboard/src/main.js +8 -8
  64. package/packages/dashboard/src/style.css +132 -132
  65. package/packages/dashboard/vite.config.js +18 -18
  66. package/src/brainstorm-postcheck.js +158 -158
  67. package/src/change-list.js +52 -52
  68. package/src/change-risk-profile.js +352 -352
  69. package/src/classify-change.js +73 -73
  70. package/src/constants.js +70 -70
  71. package/src/contract-matrix.js +278 -278
  72. package/src/db.js +201 -201
  73. package/src/endpoint-extractor.js +315 -315
  74. package/src/hooks/claude-pre-tool-use.cjs +125 -125
  75. package/src/hooks/worktree-guard.js +653 -653
  76. package/src/index.js +922 -900
  77. package/src/init.js +431 -431
  78. package/src/knowledge-match.js +130 -130
  79. package/src/migrate.js +117 -117
  80. package/src/modules.js +482 -482
  81. package/src/progress.js +1734 -1734
  82. package/src/run.js +3465 -3358
  83. package/src/scan-postcheck.js +387 -383
  84. package/src/setup.js +398 -398
  85. package/src/stage-contract.js +700 -700
  86. package/src/stages/archive.js +160 -160
  87. package/src/stages/brainstorm-auto.js +229 -229
  88. package/src/stages/brainstorm.js +645 -645
  89. package/src/stages/doctor.js +365 -365
  90. package/src/stages/execute.js +625 -625
  91. package/src/stages/explore.js +34 -34
  92. package/src/stages/index.js +29 -29
  93. package/src/stages/knowledge.js +498 -498
  94. package/src/stages/plan-postcheck.js +511 -513
  95. package/src/stages/plan.js +582 -582
  96. package/src/stages/propose.js +174 -174
  97. package/src/stages/quick.js +82 -82
  98. package/src/stages/scan.js +558 -558
  99. package/src/stages/status.js +65 -65
  100. package/src/stages/verify.js +322 -322
  101. package/src/sync.js +497 -497
  102. package/src/task-review.js +346 -346
  103. package/src/workflow.js +785 -785
  104. package/src/worktree-apply.js +549 -549
  105. package/src/worktree-deps.js +185 -0
  106. package/src/worktree.js +982 -932
  107. package/templates/workflows/archive-impact.yaml +79 -79
  108. package/templates/workflows/scan-docs.yaml +132 -132
  109. package/test/brainstorm-plan-contract.test.mjs +273 -273
  110. package/test/check-syntax.mjs +26 -26
  111. package/test/contract-artifacts.test.mjs +323 -323
  112. package/test/decision-supersede.test.mjs +277 -277
  113. package/test/knowledge-match.test.mjs +231 -231
  114. package/test/plan-execute-contract.test.mjs +330 -330
  115. package/test/plan-optimization.test.mjs +572 -572
  116. package/test/platform-artifacts.test.mjs +166 -166
  117. package/test/platform-failure-samples.test.mjs +199 -199
  118. package/test/platform-recovery-chain.test.mjs +167 -167
  119. package/test/platform-recovery.test.mjs +136 -136
  120. package/test/platform-scan-p0.test.mjs +168 -168
  121. package/test/revision-v1.test.mjs +1145 -1145
  122. package/test/run-scan-project-parse.test.mjs +200 -200
  123. package/test/run-tests.mjs +48 -48
  124. package/test/scan-knowledge.test.mjs +175 -175
  125. package/test/scan-paths.test.mjs +68 -68
  126. package/test/scan-postcheck.test.mjs +197 -197
  127. package/test/spec-dir.test.mjs +206 -206
  128. package/test/stage-contract.test.mjs +299 -299
  129. package/test/stage-definitions.test.mjs +39 -39
  130. package/test/wait-gates.test.mjs +496 -496
  131. package/test/worktree-deps-provision.test.mjs +148 -0
  132. package/test/worktree-guard.test.mjs +71 -71
  133. package/test/worktree-native-overlay.test.mjs +188 -188
@@ -1,330 +1,330 @@
1
- /**
2
- * Plan → Execute Contract v1 测试
3
- *
4
- * 验证 plan.md 到 execute 的契约:
5
- * 1. 各复杂度场景的 plan 校验通过
6
- * 2. 非法 plan 被正确拒绝
7
- * 3. execute 不复用旧 task
8
- */
9
- import { validatePlanForExecute } from '../src/stages/execute.js'
10
-
11
- let failed = 0
12
- const failures = []
13
-
14
- function assert(condition, msg) {
15
- if (!condition) {
16
- failed++
17
- failures.push(msg)
18
- console.log(` ❌ FAIL: ${msg}`)
19
- } else {
20
- console.log(` ✅ PASS: ${msg}`)
21
- }
22
- }
23
-
24
- console.log('=== Plan → Execute Contract v1 测试 ===\n')
25
-
26
- // ─────────────────────────────────────────
27
- // Case 1: none plan(最小变更)通过
28
- // ─────────────────────────────────────────
29
- console.log('--- Case 1: none plan contract 通过 ---')
30
- {
31
- const plan = `# Plan
32
-
33
- ## Wave 1
34
- - [ ] task-01: 修复 typo
35
- `
36
- const result = validatePlanForExecute(plan)
37
- assert(result.ok, 'none plan 应校验通过')
38
- assert(result.tasks.length === 1, `应有 1 个 task,实际 ${result.tasks.length}`)
39
- assert(result.waves.length === 1, `应有 1 个 wave,实际 ${result.waves.length}`)
40
- }
41
-
42
- // ─────────────────────────────────────────
43
- // Case 2: light plan 通过
44
- // ─────────────────────────────────────────
45
- console.log('\n--- Case 2: light plan contract 通过 ---')
46
- {
47
- const plan = `# Plan
48
-
49
- ## Wave 1
50
- - [ ] task-01: 添加 API 端点
51
- - [ ] task-02: 添加前端调用
52
- `
53
- const result = validatePlanForExecute(plan)
54
- assert(result.ok, 'light plan 应校验通过')
55
- assert(result.tasks.length === 2, `应有 2 个 task,实际 ${result.tasks.length}`)
56
- }
57
-
58
- // ─────────────────────────────────────────
59
- // Case 3: full plan with waves 通过
60
- // ─────────────────────────────────────────
61
- console.log('\n--- Case 3: full plan wave contract 通过 ---')
62
- {
63
- const plan = `# Plan
64
-
65
- ## Wave 1: 基础设施
66
- - [ ] task-01: 数据库 schema
67
- - 修改: db/migrate/001.sql
68
- - [ ] task-02: 模型定义
69
-
70
- ## Wave 2: 业务逻辑
71
- - [ ] task-03: API 实现
72
- - [ ] task-04: 业务规则
73
-
74
- ## Wave 3: 测试
75
- - [ ] task-05: 集成测试
76
- - 参考: tests/integration/
77
- `
78
- const result = validatePlanForExecute(plan)
79
- assert(result.ok, 'full plan 应校验通过')
80
- assert(result.tasks.length === 5, `应有 5 个 task,实际 ${result.tasks.length}`)
81
- assert(result.waves.length === 3, `应有 3 个 wave,实际 ${result.waves.length}`)
82
- assert(result.tasks[0].index === 1, 'task-01 index 应为 1')
83
- assert(result.tasks[4].index === 5, 'task-05 index 应为 5')
84
- }
85
-
86
- // ─────────────────────────────────────────
87
- // Case 4: 无 checkbox task 失败
88
- // ─────────────────────────────────────────
89
- console.log('\n--- Case 4: 无 checkbox task 失败 ---')
90
- {
91
- const plan = `# Plan
92
-
93
- 这个 plan 只有描述,没有任何 task。
94
-
95
- ## 注意事项
96
- - 设计文档已就绪
97
- `
98
- const result = validatePlanForExecute(plan)
99
- assert(!result.ok, '无 checkbox task 应失败')
100
- assert(result.errors.some(e => e.includes('checkbox task')), '错误应提到 checkbox task')
101
- }
102
-
103
- // ─────────────────────────────────────────
104
- // Case 5: task id 重复失败
105
- // ─────────────────────────────────────────
106
- console.log('\n--- Case 5: task id 重复失败 ---')
107
- {
108
- const plan = `# Plan
109
-
110
- ## Wave 1
111
- - [ ] task-01: 第一个任务
112
- - [ ] task-01: 重复的任务
113
- `
114
- const result = validatePlanForExecute(plan)
115
- assert(!result.ok, 'task id 重复应失败')
116
- assert(result.errors.some(e => e.includes('重复')), '错误应提到重复')
117
- }
118
-
119
- // ─────────────────────────────────────────
120
- // Case 6: task id 不连续失败
121
- // ─────────────────────────────────────────
122
- console.log('\n--- Case 6: task id 不连续失败 ---')
123
- {
124
- const plan = `# Plan
125
-
126
- ## Wave 1
127
- - [ ] task-01: 第一个
128
- - [ ] task-03: 跳过了第二个
129
- `
130
- const result = validatePlanForExecute(plan)
131
- assert(!result.ok, 'task id 不连续应失败')
132
- assert(result.errors.some(e => e.includes('不连续')), '错误应提到不连续')
133
- }
134
-
135
- // ─────────────────────────────────────────
136
- // Case 7: 空 plan 失败
137
- // ─────────────────────────────────────────
138
- console.log('\n--- Case 7: 空 plan 失败 ---')
139
- {
140
- const result1 = validatePlanForExecute('')
141
- assert(!result1.ok, '空字符串应失败')
142
-
143
- const result2 = validatePlanForExecute(null)
144
- assert(!result2.ok, 'null 应失败')
145
-
146
- const result3 = validatePlanForExecute(' ')
147
- assert(!result3.ok, '纯空格应失败')
148
- }
149
-
150
- // ─────────────────────────────────────────
151
- // Case 8: task name 非空
152
- // ─────────────────────────────────────────
153
- console.log('\n--- Case 8: task name 为空失败 ---')
154
- {
155
- // 注意:parseWavesFromPlan 在 task name 为空字符串时可能不触发
156
- // 这个 case 验证 validator 能检测到空 name
157
- const plan = `# Plan
158
-
159
- ## Wave 1
160
- - [ ] task-01:
161
- `
162
- const result = validatePlanForExecute(plan)
163
- // task name 为空时 trim 后为空
164
- if (result.tasks.length > 0 && !result.tasks[0].name.trim()) {
165
- assert(!result.ok, 'task name 为空应失败')
166
- } else {
167
- // 如果 parser 把空 name 过滤了,那至少 plan 能解析
168
- console.log(' ℹ️ parser 过滤了空 name,跳过此 case')
169
- }
170
- }
171
-
172
- // ─────────────────────────────────────────
173
- // Case 9: task 无 id 只有 warning
174
- // ─────────────────────────────────────────
175
- console.log('\n--- Case 9: task 无 id 只有 warning ---')
176
- {
177
- const plan = `# Plan
178
-
179
- ## Wave 1
180
- - [ ] 实现登录功能
181
- `
182
- const result = validatePlanForExecute(plan)
183
- // 无 id 的 task 只产生 warning,不阻止执行
184
- assert(result.ok, '无 id task 不应阻止执行')
185
- assert(result.warnings.length > 0, '应有 warning 关于缺少 task id')
186
- }
187
-
188
- // ─────────────────────────────────────────
189
- // Case 10: 连续 id 从 1 开始
190
- // ─────────────────────────────────────────
191
- console.log('\n--- Case 10: task-02 起始不报不连续(兼容) ---')
192
- {
193
- const plan = `# Plan
194
-
195
- ## Wave 1
196
- - [ ] task-02: 第二个
197
- - [ ] task-03: 第三个
198
- `
199
- const result = validatePlanForExecute(plan)
200
- // 从 task-02 开始,ids[0]=2 ≠ 1,不触发连续性检查
201
- assert(result.ok, 'task-02 起始不应报不连续')
202
- }
203
-
204
- // ─────────────────────────────────────────
205
- // Case 11: 子行信息解析正确
206
- // ─────────────────────────────────────────
207
- console.log('\n--- Case 11: 子行信息(修改/参考)解析正确 ---')
208
- {
209
- const plan = `# Plan
210
-
211
- ## Wave 1
212
- - [ ] task-01: 实现功能
213
- - 修改: src/auth.js
214
- - 参考: docs/auth.md
215
- - 步骤: 1. 创建模型 2. 写中间件
216
- `
217
- const result = validatePlanForExecute(plan)
218
- assert(result.ok, '有子行的 plan 应校验通过')
219
- assert(result.tasks[0].file === 'src/auth.js', 'task file 应为 src/auth.js')
220
- assert(result.tasks[0].reference === 'docs/auth.md', 'task reference 应为 docs/auth.md')
221
- }
222
-
223
- // ─────────────────────────────────────────
224
- // Case 12: 多 Wave 各自有 task
225
- // ─────────────────────────────────────────
226
- console.log('\n--- Case 12: 多 Wave 各自有 task ---')
227
- {
228
- const plan = `# Plan
229
-
230
- ## Wave 1
231
- - [ ] task-01: A
232
-
233
- ## Wave 2
234
- - [ ] task-02: B
235
-
236
- ## Wave 3
237
- - [ ] task-03: C
238
- `
239
- const result = validatePlanForExecute(plan)
240
- assert(result.ok, '多 Wave plan 应校验通过')
241
- assert(result.waves.length === 3, '应有 3 个 wave')
242
- assert(result.waves[0].tasks.length === 1, 'wave 1 应有 1 task')
243
- assert(result.waves[2].tasks[0].index === 3, 'wave 3 task 应为 task-03')
244
- }
245
-
246
- // ─────────────────────────────────────────
247
- // Plan Postcheck Contract: valid none plan 通过
248
- // ─────────────────────────────────────────
249
- console.log('\n--- Plan Postcheck: valid none plan 通过 ---')
250
- {
251
- const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: 修复 bug\n`
252
- const result = validatePlanForExecute(plan)
253
- assert(result.ok, 'none plan 应通过 postcheck contract')
254
- assert(result.errors.length === 0, '不应有 errors')
255
- }
256
-
257
- // ─────────────────────────────────────────
258
- // Plan Postcheck Contract: valid light plan 通过
259
- // ─────────────────────────────────────────
260
- console.log('\n--- Plan Postcheck: valid light plan 通过 ---')
261
- {
262
- const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: API\n- [ ] task-02: 前端\n`
263
- const result = validatePlanForExecute(plan)
264
- assert(result.ok, 'light plan 应通过 postcheck contract')
265
- }
266
-
267
- // ─────────────────────────────────────────
268
- // Plan Postcheck Contract: valid full plan 通过
269
- // ─────────────────────────────────────────
270
- console.log('\n--- Plan Postcheck: valid full plan 通过 ---')
271
- {
272
- const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: A\n## Wave 2\n- [ ] task-02: B\n## Wave 3\n- [ ] task-03: C\n`
273
- const result = validatePlanForExecute(plan)
274
- assert(result.ok, 'full plan 应通过 postcheck contract')
275
- assert(result.waves.length === 3, '应有 3 个 wave')
276
- }
277
-
278
- // ─────────────────────────────────────────
279
- // Plan Postcheck Contract: missing checkbox 失败
280
- // ─────────────────────────────────────────
281
- console.log('\n--- Plan Postcheck: missing checkbox 失败 ---')
282
- {
283
- const plan = `# Plan\n\n只有描述没有 task。\n`
284
- const result = validatePlanForExecute(plan)
285
- assert(!result.ok, '无 checkbox 应不通过 postcheck')
286
- assert(result.errors.length > 0, '应有 errors')
287
- assert(result.errors.some(e => e.includes('checkbox task')), '应有 checkbox task 错误')
288
- }
289
-
290
- // ─────────────────────────────────────────
291
- // Plan Postcheck Contract: warning 不阻断
292
- // ─────────────────────────────────────────
293
- console.log('\n--- Plan Postcheck: warning 不阻断 completed ---')
294
- {
295
- const plan = `# Plan\n\n## Wave 1\n- [ ] 实现功能(无 task id)\n`
296
- const result = validatePlanForExecute(plan)
297
- assert(result.ok, '有 warning 但应通过 postcheck(不阻断 completed)')
298
- assert(result.warnings.length > 0, '应有 warning')
299
- }
300
-
301
- // ─────────────────────────────────────────
302
- // Plan Postcheck Contract: id 重复失败
303
- // ─────────────────────────────────────────
304
- console.log('\n--- Plan Postcheck: task id 重复失败 ---')
305
- {
306
- const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: A\n- [ ] task-01: B\n`
307
- const result = validatePlanForExecute(plan)
308
- assert(!result.ok, 'id 重复应不通过 postcheck')
309
- }
310
-
311
- // ─────────────────────────────────────────
312
- // Plan Postcheck Contract: id 不连续失败
313
- // ─────────────────────────────────────────
314
- console.log('\n--- Plan Postcheck: task id 不连续失败 ---')
315
- {
316
- const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: A\n- [ ] task-03: C\n`
317
- const result = validatePlanForExecute(plan)
318
- assert(!result.ok, 'id 不连续应不通过 postcheck')
319
- }
320
-
321
- // ── 结果 ──
322
- console.log(`\n${'='.repeat(50)}`)
323
- console.log(`✅ 通过: ${12 - failed} ❌ 失败: ${failed}`)
324
- if (failures.length > 0) {
325
- console.log(`失败项:`)
326
- failures.forEach(f => console.log(` - ${f}`))
327
- }
328
- console.log(`${'='.repeat(50)}`)
329
-
330
- if (failed > 0) process.exit(1)
1
+ /**
2
+ * Plan → Execute Contract v1 测试
3
+ *
4
+ * 验证 plan.md 到 execute 的契约:
5
+ * 1. 各复杂度场景的 plan 校验通过
6
+ * 2. 非法 plan 被正确拒绝
7
+ * 3. execute 不复用旧 task
8
+ */
9
+ import { validatePlanForExecute } from '../src/stages/execute.js'
10
+
11
+ let failed = 0
12
+ const failures = []
13
+
14
+ function assert(condition, msg) {
15
+ if (!condition) {
16
+ failed++
17
+ failures.push(msg)
18
+ console.log(` ❌ FAIL: ${msg}`)
19
+ } else {
20
+ console.log(` ✅ PASS: ${msg}`)
21
+ }
22
+ }
23
+
24
+ console.log('=== Plan → Execute Contract v1 测试 ===\n')
25
+
26
+ // ─────────────────────────────────────────
27
+ // Case 1: none plan(最小变更)通过
28
+ // ─────────────────────────────────────────
29
+ console.log('--- Case 1: none plan contract 通过 ---')
30
+ {
31
+ const plan = `# Plan
32
+
33
+ ## Wave 1
34
+ - [ ] task-01: 修复 typo
35
+ `
36
+ const result = validatePlanForExecute(plan)
37
+ assert(result.ok, 'none plan 应校验通过')
38
+ assert(result.tasks.length === 1, `应有 1 个 task,实际 ${result.tasks.length}`)
39
+ assert(result.waves.length === 1, `应有 1 个 wave,实际 ${result.waves.length}`)
40
+ }
41
+
42
+ // ─────────────────────────────────────────
43
+ // Case 2: light plan 通过
44
+ // ─────────────────────────────────────────
45
+ console.log('\n--- Case 2: light plan contract 通过 ---')
46
+ {
47
+ const plan = `# Plan
48
+
49
+ ## Wave 1
50
+ - [ ] task-01: 添加 API 端点
51
+ - [ ] task-02: 添加前端调用
52
+ `
53
+ const result = validatePlanForExecute(plan)
54
+ assert(result.ok, 'light plan 应校验通过')
55
+ assert(result.tasks.length === 2, `应有 2 个 task,实际 ${result.tasks.length}`)
56
+ }
57
+
58
+ // ─────────────────────────────────────────
59
+ // Case 3: full plan with waves 通过
60
+ // ─────────────────────────────────────────
61
+ console.log('\n--- Case 3: full plan wave contract 通过 ---')
62
+ {
63
+ const plan = `# Plan
64
+
65
+ ## Wave 1: 基础设施
66
+ - [ ] task-01: 数据库 schema
67
+ - 修改: db/migrate/001.sql
68
+ - [ ] task-02: 模型定义
69
+
70
+ ## Wave 2: 业务逻辑
71
+ - [ ] task-03: API 实现
72
+ - [ ] task-04: 业务规则
73
+
74
+ ## Wave 3: 测试
75
+ - [ ] task-05: 集成测试
76
+ - 参考: tests/integration/
77
+ `
78
+ const result = validatePlanForExecute(plan)
79
+ assert(result.ok, 'full plan 应校验通过')
80
+ assert(result.tasks.length === 5, `应有 5 个 task,实际 ${result.tasks.length}`)
81
+ assert(result.waves.length === 3, `应有 3 个 wave,实际 ${result.waves.length}`)
82
+ assert(result.tasks[0].index === 1, 'task-01 index 应为 1')
83
+ assert(result.tasks[4].index === 5, 'task-05 index 应为 5')
84
+ }
85
+
86
+ // ─────────────────────────────────────────
87
+ // Case 4: 无 checkbox task 失败
88
+ // ─────────────────────────────────────────
89
+ console.log('\n--- Case 4: 无 checkbox task 失败 ---')
90
+ {
91
+ const plan = `# Plan
92
+
93
+ 这个 plan 只有描述,没有任何 task。
94
+
95
+ ## 注意事项
96
+ - 设计文档已就绪
97
+ `
98
+ const result = validatePlanForExecute(plan)
99
+ assert(!result.ok, '无 checkbox task 应失败')
100
+ assert(result.errors.some(e => e.includes('checkbox task')), '错误应提到 checkbox task')
101
+ }
102
+
103
+ // ─────────────────────────────────────────
104
+ // Case 5: task id 重复失败
105
+ // ─────────────────────────────────────────
106
+ console.log('\n--- Case 5: task id 重复失败 ---')
107
+ {
108
+ const plan = `# Plan
109
+
110
+ ## Wave 1
111
+ - [ ] task-01: 第一个任务
112
+ - [ ] task-01: 重复的任务
113
+ `
114
+ const result = validatePlanForExecute(plan)
115
+ assert(!result.ok, 'task id 重复应失败')
116
+ assert(result.errors.some(e => e.includes('重复')), '错误应提到重复')
117
+ }
118
+
119
+ // ─────────────────────────────────────────
120
+ // Case 6: task id 不连续失败
121
+ // ─────────────────────────────────────────
122
+ console.log('\n--- Case 6: task id 不连续失败 ---')
123
+ {
124
+ const plan = `# Plan
125
+
126
+ ## Wave 1
127
+ - [ ] task-01: 第一个
128
+ - [ ] task-03: 跳过了第二个
129
+ `
130
+ const result = validatePlanForExecute(plan)
131
+ assert(!result.ok, 'task id 不连续应失败')
132
+ assert(result.errors.some(e => e.includes('不连续')), '错误应提到不连续')
133
+ }
134
+
135
+ // ─────────────────────────────────────────
136
+ // Case 7: 空 plan 失败
137
+ // ─────────────────────────────────────────
138
+ console.log('\n--- Case 7: 空 plan 失败 ---')
139
+ {
140
+ const result1 = validatePlanForExecute('')
141
+ assert(!result1.ok, '空字符串应失败')
142
+
143
+ const result2 = validatePlanForExecute(null)
144
+ assert(!result2.ok, 'null 应失败')
145
+
146
+ const result3 = validatePlanForExecute(' ')
147
+ assert(!result3.ok, '纯空格应失败')
148
+ }
149
+
150
+ // ─────────────────────────────────────────
151
+ // Case 8: task name 非空
152
+ // ─────────────────────────────────────────
153
+ console.log('\n--- Case 8: task name 为空失败 ---')
154
+ {
155
+ // 注意:parseWavesFromPlan 在 task name 为空字符串时可能不触发
156
+ // 这个 case 验证 validator 能检测到空 name
157
+ const plan = `# Plan
158
+
159
+ ## Wave 1
160
+ - [ ] task-01:
161
+ `
162
+ const result = validatePlanForExecute(plan)
163
+ // task name 为空时 trim 后为空
164
+ if (result.tasks.length > 0 && !result.tasks[0].name.trim()) {
165
+ assert(!result.ok, 'task name 为空应失败')
166
+ } else {
167
+ // 如果 parser 把空 name 过滤了,那至少 plan 能解析
168
+ console.log(' ℹ️ parser 过滤了空 name,跳过此 case')
169
+ }
170
+ }
171
+
172
+ // ─────────────────────────────────────────
173
+ // Case 9: task 无 id 只有 warning
174
+ // ─────────────────────────────────────────
175
+ console.log('\n--- Case 9: task 无 id 只有 warning ---')
176
+ {
177
+ const plan = `# Plan
178
+
179
+ ## Wave 1
180
+ - [ ] 实现登录功能
181
+ `
182
+ const result = validatePlanForExecute(plan)
183
+ // 无 id 的 task 只产生 warning,不阻止执行
184
+ assert(result.ok, '无 id task 不应阻止执行')
185
+ assert(result.warnings.length > 0, '应有 warning 关于缺少 task id')
186
+ }
187
+
188
+ // ─────────────────────────────────────────
189
+ // Case 10: 连续 id 从 1 开始
190
+ // ─────────────────────────────────────────
191
+ console.log('\n--- Case 10: task-02 起始不报不连续(兼容) ---')
192
+ {
193
+ const plan = `# Plan
194
+
195
+ ## Wave 1
196
+ - [ ] task-02: 第二个
197
+ - [ ] task-03: 第三个
198
+ `
199
+ const result = validatePlanForExecute(plan)
200
+ // 从 task-02 开始,ids[0]=2 ≠ 1,不触发连续性检查
201
+ assert(result.ok, 'task-02 起始不应报不连续')
202
+ }
203
+
204
+ // ─────────────────────────────────────────
205
+ // Case 11: 子行信息解析正确
206
+ // ─────────────────────────────────────────
207
+ console.log('\n--- Case 11: 子行信息(修改/参考)解析正确 ---')
208
+ {
209
+ const plan = `# Plan
210
+
211
+ ## Wave 1
212
+ - [ ] task-01: 实现功能
213
+ - 修改: src/auth.js
214
+ - 参考: docs/auth.md
215
+ - 步骤: 1. 创建模型 2. 写中间件
216
+ `
217
+ const result = validatePlanForExecute(plan)
218
+ assert(result.ok, '有子行的 plan 应校验通过')
219
+ assert(result.tasks[0].file === 'src/auth.js', 'task file 应为 src/auth.js')
220
+ assert(result.tasks[0].reference === 'docs/auth.md', 'task reference 应为 docs/auth.md')
221
+ }
222
+
223
+ // ─────────────────────────────────────────
224
+ // Case 12: 多 Wave 各自有 task
225
+ // ─────────────────────────────────────────
226
+ console.log('\n--- Case 12: 多 Wave 各自有 task ---')
227
+ {
228
+ const plan = `# Plan
229
+
230
+ ## Wave 1
231
+ - [ ] task-01: A
232
+
233
+ ## Wave 2
234
+ - [ ] task-02: B
235
+
236
+ ## Wave 3
237
+ - [ ] task-03: C
238
+ `
239
+ const result = validatePlanForExecute(plan)
240
+ assert(result.ok, '多 Wave plan 应校验通过')
241
+ assert(result.waves.length === 3, '应有 3 个 wave')
242
+ assert(result.waves[0].tasks.length === 1, 'wave 1 应有 1 task')
243
+ assert(result.waves[2].tasks[0].index === 3, 'wave 3 task 应为 task-03')
244
+ }
245
+
246
+ // ─────────────────────────────────────────
247
+ // Plan Postcheck Contract: valid none plan 通过
248
+ // ─────────────────────────────────────────
249
+ console.log('\n--- Plan Postcheck: valid none plan 通过 ---')
250
+ {
251
+ const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: 修复 bug\n`
252
+ const result = validatePlanForExecute(plan)
253
+ assert(result.ok, 'none plan 应通过 postcheck contract')
254
+ assert(result.errors.length === 0, '不应有 errors')
255
+ }
256
+
257
+ // ─────────────────────────────────────────
258
+ // Plan Postcheck Contract: valid light plan 通过
259
+ // ─────────────────────────────────────────
260
+ console.log('\n--- Plan Postcheck: valid light plan 通过 ---')
261
+ {
262
+ const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: API\n- [ ] task-02: 前端\n`
263
+ const result = validatePlanForExecute(plan)
264
+ assert(result.ok, 'light plan 应通过 postcheck contract')
265
+ }
266
+
267
+ // ─────────────────────────────────────────
268
+ // Plan Postcheck Contract: valid full plan 通过
269
+ // ─────────────────────────────────────────
270
+ console.log('\n--- Plan Postcheck: valid full plan 通过 ---')
271
+ {
272
+ const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: A\n## Wave 2\n- [ ] task-02: B\n## Wave 3\n- [ ] task-03: C\n`
273
+ const result = validatePlanForExecute(plan)
274
+ assert(result.ok, 'full plan 应通过 postcheck contract')
275
+ assert(result.waves.length === 3, '应有 3 个 wave')
276
+ }
277
+
278
+ // ─────────────────────────────────────────
279
+ // Plan Postcheck Contract: missing checkbox 失败
280
+ // ─────────────────────────────────────────
281
+ console.log('\n--- Plan Postcheck: missing checkbox 失败 ---')
282
+ {
283
+ const plan = `# Plan\n\n只有描述没有 task。\n`
284
+ const result = validatePlanForExecute(plan)
285
+ assert(!result.ok, '无 checkbox 应不通过 postcheck')
286
+ assert(result.errors.length > 0, '应有 errors')
287
+ assert(result.errors.some(e => e.includes('checkbox task')), '应有 checkbox task 错误')
288
+ }
289
+
290
+ // ─────────────────────────────────────────
291
+ // Plan Postcheck Contract: warning 不阻断
292
+ // ─────────────────────────────────────────
293
+ console.log('\n--- Plan Postcheck: warning 不阻断 completed ---')
294
+ {
295
+ const plan = `# Plan\n\n## Wave 1\n- [ ] 实现功能(无 task id)\n`
296
+ const result = validatePlanForExecute(plan)
297
+ assert(result.ok, '有 warning 但应通过 postcheck(不阻断 completed)')
298
+ assert(result.warnings.length > 0, '应有 warning')
299
+ }
300
+
301
+ // ─────────────────────────────────────────
302
+ // Plan Postcheck Contract: id 重复失败
303
+ // ─────────────────────────────────────────
304
+ console.log('\n--- Plan Postcheck: task id 重复失败 ---')
305
+ {
306
+ const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: A\n- [ ] task-01: B\n`
307
+ const result = validatePlanForExecute(plan)
308
+ assert(!result.ok, 'id 重复应不通过 postcheck')
309
+ }
310
+
311
+ // ─────────────────────────────────────────
312
+ // Plan Postcheck Contract: id 不连续失败
313
+ // ─────────────────────────────────────────
314
+ console.log('\n--- Plan Postcheck: task id 不连续失败 ---')
315
+ {
316
+ const plan = `# Plan\n\n## Wave 1\n- [ ] task-01: A\n- [ ] task-03: C\n`
317
+ const result = validatePlanForExecute(plan)
318
+ assert(!result.ok, 'id 不连续应不通过 postcheck')
319
+ }
320
+
321
+ // ── 结果 ──
322
+ console.log(`\n${'='.repeat(50)}`)
323
+ console.log(`✅ 通过: ${12 - failed} ❌ 失败: ${failed}`)
324
+ if (failures.length > 0) {
325
+ console.log(`失败项:`)
326
+ failures.forEach(f => console.log(` - ${f}`))
327
+ }
328
+ console.log(`${'='.repeat(50)}`)
329
+
330
+ if (failed > 0) process.exit(1)