@xulthekl/team-flow 0.36.2 → 0.36.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.
- package/.claude/always/phase-guard.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/marketplace.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +2 -2
- package/AGENTS.md +4 -4
- package/CHANGELOG.md +23 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +1 -1
- package/README.md +2 -2
- package/agents/architecture-design.md +24 -4
- package/agents/architecture-reviewer.md +10 -6
- package/agents/cross-change-consistency-checker.md +2 -1
- package/docs/README_en.md +1 -1
- package/gemini-extension.json +1 -1
- package/hooks/pre-tool-use-guard +15 -0
- package/hooks/session-start +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/guard/checks/arch-gate-exemptions.mjs +32 -17
- package/scripts/guard/checks/arch-snapshot.mjs +7 -0
- package/scripts/lib/arch-merge.mjs +22 -6
- package/scripts/lib/cmd-doctor.mjs +27 -0
- package/scripts/lib/solutions-capture.mjs +1 -1
- package/skills/architecture-design/SKILL.md +14 -0
- package/skills/architecture-design/chapters/ch06-integration.md +2 -2
- package/skills/architecture-design/references/s3.5-architecture-template.md +1 -1
- package/skills/architecture-design/references/s3.5-loading-protocol.md +1 -1
- package/skills/architecture-design/references/s3.5-product-architecture.md +13 -1
- package/skills/ce-compound/references/v0.5-upgrade.md +3 -0
- package/skills/release-archivist/SKILL.md +2 -0
- package/skills/workflow-bootstrap/SKILL.md +8 -5
- package/skills/workflow-bootstrap/references/b1-reconnaissance.md +2 -2
- package/skills/workflow-orchestrator/SKILL.md +1 -1
- package/skills/workflow-orchestrator/references/s4-split-validate.md +2 -1
- package/skills/workflow-orchestrator/references/s5-monitoring.md +1 -0
- package/skills/workflow-orchestrator/references/state-model.md +1 -1
- package/skills/workflow-start/SKILL.md +3 -0
- package/skills/workflow-start/references/routing-rules.md +2 -0
- package/wave-batch-receipt-analysis-report.md +0 -561
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
# Wave/Batch/Receipt 实现分析报告
|
|
2
|
-
|
|
3
|
-
## 1. Execution Plan 相关文件
|
|
4
|
-
|
|
5
|
-
### 核心文件路径
|
|
6
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/lib/execution-plan.mjs`
|
|
7
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/lib/cmd-execution.mjs`
|
|
8
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/lib/execution-recommendation.mjs`
|
|
9
|
-
|
|
10
|
-
### Wave 数据结构定义
|
|
11
|
-
|
|
12
|
-
```javascript
|
|
13
|
-
// 从 execution-plan.mjs 和测试文件中提取
|
|
14
|
-
{
|
|
15
|
-
id: string, // wave 唯一标识,如 'wave-1', 'foundation'
|
|
16
|
-
strategy: 'serial' | 'parallel', // 执行策略
|
|
17
|
-
tasks: string[], // 任务 ID 列表,如 ['1.1', '1.2']
|
|
18
|
-
depends_on: string[] // 依赖的其他 wave ID 列表,如 ['wave-1', 'foundation']
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### Execution Plan 完整数据结构
|
|
23
|
-
|
|
24
|
-
```javascript
|
|
25
|
-
{
|
|
26
|
-
mode: 'inline' | 'batch-inline' | 'sdd', // 执行模式(EXECUTION_MODES)
|
|
27
|
-
source: string, // 计划来源,如 'user-confirmed', 'user-confirmed-revision'
|
|
28
|
-
rationale: string, // 选择该模式的理由
|
|
29
|
-
waves: Wave[], // wave 数组
|
|
30
|
-
artifacts_hash: string, // 制品哈希(sha256:...)
|
|
31
|
-
contract_hash: string, // 契约哈希(sha256:...)
|
|
32
|
-
workflow: string, // 工作流类型:'full' | 'hotfix' | 'tweak'
|
|
33
|
-
revision: number, // 修订版本号
|
|
34
|
-
hash: string, // 计划内容哈希(sha256:...)
|
|
35
|
-
recommendation?: { // 推荐信息(full/hotfix 必需)
|
|
36
|
-
available_modes: string[],
|
|
37
|
-
recommendation: {
|
|
38
|
-
mode: string,
|
|
39
|
-
reasons: string[]
|
|
40
|
-
},
|
|
41
|
-
facts: object
|
|
42
|
-
},
|
|
43
|
-
recommendation_receipt?: { // 推荐凭证(full/hotfix 必需)
|
|
44
|
-
recommendation: object,
|
|
45
|
-
waves: Wave[],
|
|
46
|
-
artifacts_hash: string,
|
|
47
|
-
contract_hash: string,
|
|
48
|
-
workflow: string,
|
|
49
|
-
execution_plan_revision_at_recommendation: number | null,
|
|
50
|
-
created_at: string,
|
|
51
|
-
hash: string
|
|
52
|
-
},
|
|
53
|
-
selection?: { // 用户选择信息(full/hotfix 必需)
|
|
54
|
-
confirmed: boolean,
|
|
55
|
-
followed_recommendation: boolean,
|
|
56
|
-
acknowledged_non_recommendation: boolean
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 关键代码片段
|
|
62
|
-
|
|
63
|
-
**创建计划 (execution-plan.mjs:14-33)**
|
|
64
|
-
```javascript
|
|
65
|
-
export function createPlan(changeDir, input) {
|
|
66
|
-
const state = readState(changeDir);
|
|
67
|
-
const plan = {
|
|
68
|
-
mode: input?.mode,
|
|
69
|
-
source: input?.source,
|
|
70
|
-
rationale: input?.rationale,
|
|
71
|
-
waves: input?.waves,
|
|
72
|
-
artifacts_hash: computeArtifactsHash(changeDir),
|
|
73
|
-
contract_hash: computeContractHash(changeDir),
|
|
74
|
-
workflow: state.workflow,
|
|
75
|
-
revision: input?.revision ?? state.revision ?? 1,
|
|
76
|
-
};
|
|
77
|
-
// ... validation and hash computation
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**Wave 解析 (cmd-execution.mjs:163-178)**
|
|
82
|
-
```javascript
|
|
83
|
-
function parseWaves(values) {
|
|
84
|
-
// 格式: <id>:<strategy>:<task,...>[:<depends-on,...>]
|
|
85
|
-
// 示例: 'wave-1:parallel:1.1,1.2' 或 'wave-2:serial:2.1:wave-1'
|
|
86
|
-
return values.map(value => {
|
|
87
|
-
const [id, strategy, taskList, dependencyList, ...extra] = value.split(':');
|
|
88
|
-
const tasks = taskList.split(',').map(task => task.trim()).filter(Boolean);
|
|
89
|
-
const depends_on = dependencyList === undefined ? [] : dependencyList.split(',').map(id => id.trim()).filter(Boolean);
|
|
90
|
-
return { id, strategy, tasks, depends_on };
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## 2. Receipt 相关实现
|
|
98
|
-
|
|
99
|
-
### 核心文件路径
|
|
100
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/lib/execution-plan.mjs` (recordReview, readCurrentReview)
|
|
101
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/guard/checks/execution-reviews-passed.mjs`
|
|
102
|
-
|
|
103
|
-
### Review Receipt 数据结构
|
|
104
|
-
|
|
105
|
-
```javascript
|
|
106
|
-
{
|
|
107
|
-
status: 'pass' | 'fail', // 审查状态
|
|
108
|
-
base: string, // git base commit hash(完整 SHA)
|
|
109
|
-
head: string, // git head commit hash(完整 SHA)
|
|
110
|
-
report: string, // 审查报告文件路径(相对于 change 目录)
|
|
111
|
-
tests?: { // 可选的测试统计(v0.13 §51.2)
|
|
112
|
-
total: number, // 测试总数
|
|
113
|
-
passed: number, // 通过数
|
|
114
|
-
failed: number // 失败数
|
|
115
|
-
},
|
|
116
|
-
plan_hash: string, // 关联的执行计划哈希
|
|
117
|
-
plan_revision: number, // 关联的执行计划修订版本
|
|
118
|
-
recorded_at: string // 记录时间(ISO 格式)
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Receipt 存储位置
|
|
123
|
-
```
|
|
124
|
-
<change-dir>/.superpowers/sdd/reviews/<base64url-encoded-wave-id>.json
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Receipt 校验逻辑 (execution-plan.mjs:133-177)
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
export function recordReview(changeDir, waveId, receipt) {
|
|
131
|
-
// 1. 验证计划有效性
|
|
132
|
-
const plan = readPlan(changeDir);
|
|
133
|
-
const validation = validatePlan(changeDir, plan);
|
|
134
|
-
if (!validation.valid) throw new Error(...);
|
|
135
|
-
|
|
136
|
-
// 2. 验证 wave 是否存在于计划中
|
|
137
|
-
const wave = plan.waves.find(candidate => candidate?.id === waveId);
|
|
138
|
-
if (!wave) throw new Error(`Review receipt references unknown wave '${waveId}'`);
|
|
139
|
-
|
|
140
|
-
// 3. 检查依赖是否已通过(阻塞依赖检查)
|
|
141
|
-
const blockedBy = blockedDependencies(changeDir, plan, wave);
|
|
142
|
-
if (blockedBy.length > 0) {
|
|
143
|
-
throw new Error(`Wave '${waveId}' cannot be reviewed before dependencies have passing receipts: ${blockedBy.join(', ')}`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 4. 验证 status
|
|
147
|
-
if (!REVIEW_STATUSES.has(receipt?.status)) {
|
|
148
|
-
throw new Error("Review receipt status must be 'pass' or 'fail'");
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 5. 验证 base 和 head
|
|
152
|
-
for (const field of ['base', 'head']) requireText(receipt?.[field], `receipt.${field}`);
|
|
153
|
-
|
|
154
|
-
// 6. 验证 report 文件
|
|
155
|
-
const report = validateReviewReportEvidence(changeDir, receipt?.report);
|
|
156
|
-
|
|
157
|
-
// 7. 验证 git 范围(禁止空 diff)
|
|
158
|
-
const { base, head } = validateReviewRange(changeDir, receipt.base, receipt.head);
|
|
159
|
-
// 关键校验:base !== head(禁止 base==head 空 diff review)
|
|
160
|
-
if (resolvedBase === resolvedHead) {
|
|
161
|
-
throw new Error('Review receipt base must differ from head...');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// 8. 验证 base 是 head 的祖先
|
|
165
|
-
execFileSync('git', ['-C', gitRoot, 'merge-base', '--is-ancestor', resolvedBase, resolvedHead]);
|
|
166
|
-
|
|
167
|
-
// 9. 保存 receipt
|
|
168
|
-
const savedReceipt = {
|
|
169
|
-
status: receipt.status,
|
|
170
|
-
base, head, report,
|
|
171
|
-
...(tests ? { tests } : {}),
|
|
172
|
-
plan_hash: plan.hash,
|
|
173
|
-
plan_revision: plan.revision,
|
|
174
|
-
recorded_at: new Date().toISOString(),
|
|
175
|
-
};
|
|
176
|
-
atomicWrite(join(paths.reviews, `${safeFileName(waveId)}.json`), JSON.stringify(savedReceipt));
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### 读取当前 Receipt (execution-plan.mjs:183-200)
|
|
181
|
-
|
|
182
|
-
```javascript
|
|
183
|
-
export function readCurrentReview(changeDir, waveId, plan = readPlan(changeDir)) {
|
|
184
|
-
// 1. 检查 receipt 文件是否存在
|
|
185
|
-
// 2. 验证 plan_hash 和 plan_revision 匹配当前计划
|
|
186
|
-
// 3. 验证 base/head 范围仍然有效
|
|
187
|
-
// 4. 对于 passing receipt,验证 report 文件仍然安全可读
|
|
188
|
-
if (receipt?.status === 'pass') validateReviewReportEvidence(changeDir, receipt.report);
|
|
189
|
-
return receipt;
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## 3. state-loader.mjs 中的字段
|
|
196
|
-
|
|
197
|
-
### 核心文件路径
|
|
198
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/lib/state-loader.mjs`
|
|
199
|
-
|
|
200
|
-
### BUILTIN_DEFAULTS 中的相关字段
|
|
201
|
-
|
|
202
|
-
```javascript
|
|
203
|
-
const BUILTIN_DEFAULTS = {
|
|
204
|
-
// 核心状态
|
|
205
|
-
state: 'exploring',
|
|
206
|
-
workflow: 'auto',
|
|
207
|
-
revision: null,
|
|
208
|
-
|
|
209
|
-
// 哈希校验
|
|
210
|
-
artifacts_hash: null,
|
|
211
|
-
contract_hash: null,
|
|
212
|
-
|
|
213
|
-
// 执行进度(与 wave/batch/execution 相关)
|
|
214
|
-
execution_mode: null, // 执行模式:'inline' | 'batch-inline' | 'sdd'
|
|
215
|
-
execution_plan_hash: null, // 执行计划哈希
|
|
216
|
-
execution_plan_revision: null, // 执行计划修订版本
|
|
217
|
-
batches_completed: 0, // 已完成的批次数量
|
|
218
|
-
test_result: null, // 测试结果
|
|
219
|
-
spec_merged: false, // 规格是否已合并
|
|
220
|
-
|
|
221
|
-
// 其他字段...
|
|
222
|
-
};
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 关键代码片段
|
|
226
|
-
|
|
227
|
-
**写入状态 (state-loader.mjs:92-188)**
|
|
228
|
-
```javascript
|
|
229
|
-
export function writeState(changeDir, state) {
|
|
230
|
-
// 序列化到 .team-flow.yaml
|
|
231
|
-
lines.push('# === Execution progress ===');
|
|
232
|
-
lines.push(`execution_mode: ${state.execution_mode ?? 'null'}`);
|
|
233
|
-
lines.push(`execution_plan_hash: ${state.execution_plan_hash ?? 'null'}`);
|
|
234
|
-
lines.push(`execution_plan_revision: ${state.execution_plan_revision ?? 'null'}`);
|
|
235
|
-
lines.push(`batches_completed: ${state.batches_completed ?? 0}`);
|
|
236
|
-
lines.push(`test_result: ${state.test_result ?? 'null'}`);
|
|
237
|
-
lines.push(`spec_merged: ${state.spec_merged ?? false}`);
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
## 4. guard.mjs 中的 Wave 门控逻辑
|
|
244
|
-
|
|
245
|
-
### 核心文件路径
|
|
246
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/guard/guard.mjs`
|
|
247
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/guard/checks/execution-plan-ready.mjs`
|
|
248
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/guard/checks/execution-reviews-passed.mjs`
|
|
249
|
-
|
|
250
|
-
### 门控转换矩阵
|
|
251
|
-
|
|
252
|
-
```javascript
|
|
253
|
-
// guard.mjs 中定义的转换检查维度
|
|
254
|
-
const TRANSITION_CHECKS = {
|
|
255
|
-
'approved-for-build:executing': [
|
|
256
|
-
'artifacts-exist',
|
|
257
|
-
'contract-fresh',
|
|
258
|
-
'dp-gate-passed',
|
|
259
|
-
'execution-plan-ready', // ★ 执行计划就绪检查
|
|
260
|
-
'test-matrix-ready'
|
|
261
|
-
],
|
|
262
|
-
'executing:closing': [
|
|
263
|
-
'tasks-complete',
|
|
264
|
-
'tests-passing',
|
|
265
|
-
'specs-merged',
|
|
266
|
-
'execution-plan-ready', // ★ 执行计划就绪检查
|
|
267
|
-
'execution-reviews-passed', // ★ 所有 wave receipt 通过检查
|
|
268
|
-
'compound-captured',
|
|
269
|
-
'test-matrix-complete',
|
|
270
|
-
'arch-snapshot'
|
|
271
|
-
],
|
|
272
|
-
'debugging:executing': [
|
|
273
|
-
'contract-fresh',
|
|
274
|
-
'execution-plan-ready' // ★ 从 debugging 返回时也检查
|
|
275
|
-
],
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
// Workflow 特定检查
|
|
279
|
-
const WORKFLOW_TRANSITION_CHECKS = {
|
|
280
|
-
hotfix: {
|
|
281
|
-
'approved-for-build:executing': ['contract-current', 'dp3-approved', 'execution-plan-ready'],
|
|
282
|
-
'executing:closing': ['tasks-complete', 'tests-passing', 'specs-merged', 'execution-plan-ready', 'execution-reviews-passed', 'compound-captured'],
|
|
283
|
-
},
|
|
284
|
-
tweak: {
|
|
285
|
-
// Tweak 豁免 execution-plan 和 review receipt 检查
|
|
286
|
-
'approved-for-build:executing': ['artifacts-exist', 'contract-fresh', 'dp-gate-passed'],
|
|
287
|
-
'executing:closing': ['tasks-complete', 'tests-passing', 'specs-merged'],
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### execution-plan-ready 检查逻辑 (execution-plan-ready.mjs)
|
|
293
|
-
|
|
294
|
-
```javascript
|
|
295
|
-
export function checkExecutionPlanReady(changeDir) {
|
|
296
|
-
// 1. 检查执行计划是否存在
|
|
297
|
-
const plan = readPlan(changeDir);
|
|
298
|
-
if (!plan) {
|
|
299
|
-
return { pass: false, failures: ['execution plan is missing...'] };
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// 2. 验证计划有效性
|
|
303
|
-
const validation = validatePlan(changeDir, plan);
|
|
304
|
-
if (!validation.valid) {
|
|
305
|
-
return { pass: false, failures: validation.failures };
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// 3. 验证 DP-4 引用了当前计划修订版本
|
|
309
|
-
const state = readState(changeDir);
|
|
310
|
-
const revisionReference = new RegExp(`\\bplan revision\\s+${plan.revision}\\b`, 'i');
|
|
311
|
-
if (!revisionReference.test(decision)) {
|
|
312
|
-
return { pass: false, failures: [`DP-4 must precisely reference current plan revision...`] };
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return { pass: true, failures: [] };
|
|
316
|
-
}
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### execution-reviews-passed 检查逻辑 (execution-reviews-passed.mjs)
|
|
320
|
-
|
|
321
|
-
```javascript
|
|
322
|
-
export function checkExecutionReviewsPassed(changeDir) {
|
|
323
|
-
// 1. 读取执行计划
|
|
324
|
-
const plan = readPlan(changeDir);
|
|
325
|
-
if (!plan) return { pass: true, failures: [] }; // tweak 豁免
|
|
326
|
-
|
|
327
|
-
// 2. 验证计划有效性
|
|
328
|
-
const validation = validatePlan(changeDir, plan);
|
|
329
|
-
if (!validation.valid) return { pass: false, failures: validation.failures };
|
|
330
|
-
|
|
331
|
-
// 3. 遍历所有 wave,检查 receipt
|
|
332
|
-
const failures = [];
|
|
333
|
-
for (const wave of plan.waves) {
|
|
334
|
-
const receipt = readCurrentReview(changeDir, wave.id, plan);
|
|
335
|
-
if (!receipt) {
|
|
336
|
-
failures.push(`review receipt missing for planned wave '${wave.id}'`);
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
if (receipt?.status !== 'pass') {
|
|
340
|
-
failures.push(`review receipt for planned wave '${wave.id}' has status '${receipt?.status}'`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return { pass: failures.length === 0, failures };
|
|
345
|
-
}
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## 5. depends_on 和拓扑相关实现
|
|
351
|
-
|
|
352
|
-
### 核心文件路径
|
|
353
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/scripts/lib/execution-plan.mjs`
|
|
354
|
-
|
|
355
|
-
### 依赖验证逻辑 (execution-plan.mjs:381-433)
|
|
356
|
-
|
|
357
|
-
```javascript
|
|
358
|
-
function validateStructure(plan) {
|
|
359
|
-
// ...
|
|
360
|
-
|
|
361
|
-
// 验证每个 wave 的 depends_on
|
|
362
|
-
for (const [index, wave] of plan.waves.entries()) {
|
|
363
|
-
// 1. depends_on 必须是数组
|
|
364
|
-
if (!Array.isArray(wave.depends_on)) failures.push(`${label} depends_on must be an array`);
|
|
365
|
-
|
|
366
|
-
// 2. 依赖项必须是非空字符串
|
|
367
|
-
else if (wave.depends_on.some(id => !isNonEmptyText(id))) failures.push(`${label} dependencies must be non-empty strings`);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// 3. 验证依赖引用的有效性
|
|
371
|
-
for (const wave of plan.waves.filter(isObject)) {
|
|
372
|
-
for (const dependency of wave.depends_on) {
|
|
373
|
-
// 不能自依赖
|
|
374
|
-
if (dependency === wave.id) failures.push(`wave '${wave.id}' cannot depend on itself`);
|
|
375
|
-
// 不能依赖不存在的 wave
|
|
376
|
-
else if (!ids.has(dependency)) failures.push(`wave '${wave.id}' depends on unknown wave '${dependency}'`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// 4. 检测循环依赖
|
|
381
|
-
if (canCheckCycles && hasDependencyCycle(plan.waves)) {
|
|
382
|
-
failures.push('execution plan waves contain a dependency cycle');
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### 循环依赖检测算法 (execution-plan.mjs:436-452)
|
|
388
|
-
|
|
389
|
-
```javascript
|
|
390
|
-
function hasDependencyCycle(waves) {
|
|
391
|
-
// 使用 DFS 检测有向图中的环
|
|
392
|
-
const dependencies = new Map(waves.map(wave => [wave.id, wave.depends_on]));
|
|
393
|
-
const visiting = new Set(); // 当前正在访问的节点(用于检测后向边)
|
|
394
|
-
const visited = new Set(); // 已完成访问的节点
|
|
395
|
-
|
|
396
|
-
const visit = id => {
|
|
397
|
-
if (visiting.has(id)) return true; // 发现环!
|
|
398
|
-
if (visited.has(id)) return false; // 已经检查过,无环
|
|
399
|
-
visiting.add(id);
|
|
400
|
-
for (const dependency of dependencies.get(id) || []) {
|
|
401
|
-
if (visit(dependency)) return true;
|
|
402
|
-
}
|
|
403
|
-
visiting.delete(id);
|
|
404
|
-
visited.add(id);
|
|
405
|
-
return false;
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
return [...dependencies.keys()].some(visit);
|
|
409
|
-
}
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
### 阻塞依赖检查 (execution-plan.mjs:324-327)
|
|
413
|
-
|
|
414
|
-
```javascript
|
|
415
|
-
function blockedDependencies(changeDir, plan, wave) {
|
|
416
|
-
if (!Array.isArray(wave?.depends_on)) return [];
|
|
417
|
-
// 返回所有未通过的依赖 wave
|
|
418
|
-
return wave.depends_on.filter(dependency =>
|
|
419
|
-
readCurrentReview(changeDir, dependency, plan)?.status !== 'pass'
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
### Wave 可执行性判断 (execution-plan.mjs:207-224)
|
|
425
|
-
|
|
426
|
-
```javascript
|
|
427
|
-
export function describeWaves(changeDir, plan = readPlan(changeDir)) {
|
|
428
|
-
return plan.waves.map(wave => {
|
|
429
|
-
const receipt = readCurrentReview(changeDir, wave.id, plan);
|
|
430
|
-
const blockers = blockedDependencies(changeDir, plan, wave);
|
|
431
|
-
const retryable = receipt?.status === 'fail';
|
|
432
|
-
return {
|
|
433
|
-
id: wave.id,
|
|
434
|
-
strategy: wave.strategy,
|
|
435
|
-
tasks: wave.tasks,
|
|
436
|
-
depends_on: wave.depends_on,
|
|
437
|
-
eligible: (receipt === null || retryable) && blockers.length === 0, // 可执行条件
|
|
438
|
-
retryable,
|
|
439
|
-
receipt,
|
|
440
|
-
blockers,
|
|
441
|
-
};
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
---
|
|
447
|
-
|
|
448
|
-
## 6. 测试文件中的示例
|
|
449
|
-
|
|
450
|
-
### 核心测试文件路径
|
|
451
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/tests/lib/execution-plan.test.mjs`
|
|
452
|
-
- `/Users/litong/Documents/work/code/practice/team-flow-workspace/team-flow/tests/lib/guard.test.mjs`
|
|
453
|
-
|
|
454
|
-
### Wave 定义示例
|
|
455
|
-
|
|
456
|
-
```javascript
|
|
457
|
-
// 单 wave,无依赖
|
|
458
|
-
waves: [{ id: 'wave-1', strategy: 'serial', tasks: ['1.1'], depends_on: [] }]
|
|
459
|
-
|
|
460
|
-
// 多 wave,有依赖
|
|
461
|
-
waves: [
|
|
462
|
-
{ id: 'wave-1', strategy: 'parallel', tasks: ['1.1', '1.2'], depends_on: [] },
|
|
463
|
-
{ id: 'wave-2', strategy: 'serial', tasks: ['2.1'], depends_on: ['wave-1'] }
|
|
464
|
-
]
|
|
465
|
-
|
|
466
|
-
// 循环依赖(会被拒绝)
|
|
467
|
-
waves: [
|
|
468
|
-
{ id: 'wave-1', strategy: 'serial', tasks: ['1.1'], depends_on: ['wave-2'] },
|
|
469
|
-
{ id: 'wave-2', strategy: 'serial', tasks: ['1.2'], depends_on: ['wave-1'] }
|
|
470
|
-
]
|
|
471
|
-
|
|
472
|
-
// 自依赖(会被拒绝)
|
|
473
|
-
waves: [{ id: 'wave-1', strategy: 'parallel', tasks: ['1.1'], depends_on: ['wave-1'] }]
|
|
474
|
-
|
|
475
|
-
// 依赖不存在的 wave(会被拒绝)
|
|
476
|
-
waves: [{ id: 'wave-1', strategy: 'parallel', tasks: ['1.1'], depends_on: ['missing'] }]
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
### Receipt 记录示例
|
|
480
|
-
|
|
481
|
-
```javascript
|
|
482
|
-
// 记录通过的 receipt
|
|
483
|
-
const receipt = recordReview(changeDir, 'wave-1', {
|
|
484
|
-
status: 'pass',
|
|
485
|
-
base: gitRefs.base, // git base commit
|
|
486
|
-
head: gitRefs.head, // git head commit
|
|
487
|
-
report: reportPath, // 审查报告路径
|
|
488
|
-
tests: { // 可选测试统计
|
|
489
|
-
total: 12,
|
|
490
|
-
passed: 11,
|
|
491
|
-
failed: 1
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// 记录失败的 receipt
|
|
496
|
-
const receipt = recordReview(changeDir, 'wave-2', {
|
|
497
|
-
status: 'fail',
|
|
498
|
-
base: gitRefs.base,
|
|
499
|
-
head: gitRefs.head,
|
|
500
|
-
report: reportPath
|
|
501
|
-
});
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
### Guard 测试示例
|
|
505
|
-
|
|
506
|
-
```javascript
|
|
507
|
-
// 测试:所有 wave 必须有 passing receipt 才能 closing
|
|
508
|
-
it('blocks closing until every planned wave has a passing review receipt', () => {
|
|
509
|
-
// 1. 创建计划(wave-1 和 wave-2)
|
|
510
|
-
createCurrentPlan();
|
|
511
|
-
|
|
512
|
-
// 2. 尝试 closing,应该失败(没有 receipt)
|
|
513
|
-
let result = run('executing', 'closing');
|
|
514
|
-
assert.equal(result.exitCode, 1);
|
|
515
|
-
assert.match(reviewCheck.failures.join('\n'), /wave-1|receipt/i);
|
|
516
|
-
|
|
517
|
-
// 3. 记录 wave-1 passing,wave-2 failing
|
|
518
|
-
runNodeScript(CLI_PATH, ['execution', 'review', dir, '--wave', 'wave-1', ...]);
|
|
519
|
-
runNodeScript(CLI_PATH, ['execution', 'review', dir, '--wave', 'wave-2', ..., '--verdict', 'fail']);
|
|
520
|
-
|
|
521
|
-
// 4. 尝试 closing,应该失败(wave-2 failing)
|
|
522
|
-
result = run('executing', 'closing');
|
|
523
|
-
assert.match(reviewCheck.failures.join('\n'), /wave-2.*fail/i);
|
|
524
|
-
|
|
525
|
-
// 5. 修复 wave-2 为 passing
|
|
526
|
-
runNodeScript(CLI_PATH, ['execution', 'review', dir, '--wave', 'wave-2', ..., '--verdict', 'pass']);
|
|
527
|
-
|
|
528
|
-
// 6. 尝试 closing,应该成功
|
|
529
|
-
result = run('executing', 'closing');
|
|
530
|
-
assert.equal(result.exitCode, 0);
|
|
531
|
-
});
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
---
|
|
535
|
-
|
|
536
|
-
## 7. 关键设计要点总结
|
|
537
|
-
|
|
538
|
-
### Wave 设计
|
|
539
|
-
1. **Wave 是执行计划的基本单位**,每个 wave 包含一组任务和执行策略
|
|
540
|
-
2. **策略类型**:`serial`(顺序执行)和 `parallel`(并行执行)
|
|
541
|
-
3. **依赖关系**:通过 `depends_on` 字段定义 wave 之间的依赖
|
|
542
|
-
4. **拓扑约束**:禁止自依赖、禁止依赖不存在的 wave、禁止循环依赖
|
|
543
|
-
|
|
544
|
-
### Receipt 设计
|
|
545
|
-
1. **Receipt 是 wave 级别的审查证据**,记录在 `.superpowers/sdd/reviews/` 目录
|
|
546
|
-
2. **必须包含**:status、base、head、report
|
|
547
|
-
3. **可选包含**:tests(测试统计)
|
|
548
|
-
4. **关联性**:receipt 通过 plan_hash 和 plan_revision 关联到特定的执行计划版本
|
|
549
|
-
5. **有效性**:receipt 在计划变更后自动失效(hash 不匹配)
|
|
550
|
-
|
|
551
|
-
### 门控设计
|
|
552
|
-
1. **execution-plan-ready**:在进入 executing 和 closing 时检查
|
|
553
|
-
2. **execution-reviews-passed**:在 closing 时检查,要求所有 wave 都有 passing receipt
|
|
554
|
-
3. **依赖阻塞**:wave 的 receipt 记录依赖其所有 depends_on wave 的 receipt 先通过
|
|
555
|
-
4. **Workflow 豁免**:tweak workflow 豁免 execution-plan 和 review receipt 检查
|
|
556
|
-
|
|
557
|
-
### 状态持久化
|
|
558
|
-
1. **execution_mode**:记录选择的执行模式
|
|
559
|
-
2. **execution_plan_hash**:记录当前执行计划的哈希
|
|
560
|
-
3. **execution_plan_revision**:记录当前执行计划的修订版本
|
|
561
|
-
4. **batches_completed**:记录已完成的批次数量
|