@xulthekl/team-flow 0.36.1 → 0.36.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/CHANGELOG.md +5 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/agents/architecture-reviewer.md +1 -0
- package/docs/README_en.md +1 -1
- package/gemini-extension.json +1 -1
- package/hooks/session-start +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/workflow-start/SKILL.md +1 -1
- package/skills/workflow-start/references/routing-rules.md +3 -1
- package/wave-batch-receipt-analysis-report.md +561 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "team-flow",
|
|
11
11
|
"description": "8-state spec workflow + compound global compounding + architecture-design (4A/DDD) + local HTML prototype + product-level orchestration + bootstrap + e2e + session handoff + workflow feedback. 24 skills + 15 agents with embedded TDD, SDD, code review, debugging, delta spec sync, and design-system-driven prototyping.",
|
|
12
|
-
"version": "0.36.
|
|
12
|
+
"version": "0.36.2",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "LT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-flow",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.2",
|
|
4
4
|
"description": "Unified workflow plugin: team-flow (spec-driven dev: TDD/SDD/code-review/debugging) + compound-engineering core subset (brainstorm/plan/compound/strategy/ideate/proof, global compounding) + architecture-design (4A+DDD incremental design & global compounding) + prototype (local HTML prototype, zero external deps) + e2e (Playwright E2E, AC-driven) + workflow-orchestrator (product-level workflow orchestration) + workflow-bootstrap (existing project onboarding) + session-handoff (context transfer) + workflow-feedback (issue tracking). 24 skills + 15 agents, one install.",
|
|
5
5
|
"source": "./",
|
|
6
6
|
"author": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "team-flow",
|
|
3
3
|
"displayName": "team-flow",
|
|
4
4
|
"description": "Unified workflow plugin: team-flow (spec-driven dev: TDD/SDD/code-review/debugging) + compound-engineering core subset (brainstorm/plan/compound/strategy/ideate/proof, global compounding) + architecture-design (4A+DDD incremental design & global compounding) + prototype (local HTML prototype, zero external deps) + e2e (Playwright E2E, AC-driven) + workflow-orchestrator (product-level workflow orchestration) + workflow-bootstrap (existing project onboarding) + session-handoff (context transfer) + workflow-feedback (issue tracking). 24 skills + 15 agents, one install.",
|
|
5
|
-
"version": "0.36.
|
|
5
|
+
"version": "0.36.2",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "LT",
|
|
8
8
|
"url": "https://github.com/LT"
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Unified workflow plugins and skills for AI coding agents (team-flow: team-flow + compound + architecture-design + prototype).",
|
|
9
|
-
"version": "0.36.
|
|
9
|
+
"version": "0.36.2"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "team-flow",
|
|
14
14
|
"description": "Unified workflow with planning artifacts, execution contracts, TDD, review gates, systematic debugging, delta spec sync, architecture-design, and local HTML prototyping.",
|
|
15
|
-
"version": "0.36.
|
|
15
|
+
"version": "0.36.2",
|
|
16
16
|
"source": ".",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "LT",
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,11 @@ The format loosely follows Keep a Changelog.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.36.2] - 2026-08-05
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **变更级架构设计输入链缺口修复**:0.36.0 在 architecture-design/SKILL.md 声明产品级快照(`iterations/vN/architecture.md`)为主输入,但 workflow-start 实际 dispatch 子代理的输入列表(`routing-rules.md`)未传该路径——变更级架构设计不会将产品级架构设计作为输入。已同步:routing-rules.md(architecture-design + architecture-reviewer dispatch 输入)、workflow-start/SKILL.md(Step 1)、architecture-reviewer(A5 基线一致性对照产品级快照)
|
|
13
|
+
|
|
9
14
|
## [0.36.1] - 2026-08-05
|
|
10
15
|
|
|
11
16
|
### Fixed
|
package/GEMINI.md
CHANGED
|
@@ -8,7 +8,7 @@ The workflow is self-contained and does not require OpenSpec or Superpowers at r
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
<!-- team-flow-phase-guard-start -->
|
|
11
|
-
# team-flow v0.36.
|
|
11
|
+
# team-flow v0.36.2 | 阶段: {{state}} | 工作流: {{workflow}}
|
|
12
12
|
当前阶段允许的操作由 workflow-start 路由规则定义。
|
|
13
13
|
禁止跨越 DP gate 进入下一阶段。变更范围以 execution-contract.md 的 Intent Lock 为准。
|
|
14
14
|
<!-- team-flow-phase-guard-end -->
|
package/INSTALL.md
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# team-flow
|
|
2
2
|
|
|
3
|
-
> 当前版本:`v0.36.
|
|
3
|
+
> 当前版本:`v0.36.2`
|
|
4
4
|
|
|
5
5
|
> 统一插件:**team-flow**(spec 驱动开发)+ **compound-engineering 核心子集**(全局复利)+ **architecture-design**(4A+DDD 增量设计)+ **prototype**(本地 HTML 原型)+ **e2e**(AC 驱动 E2E)+ **workflow-orchestrator**(产品级编排)+ **workflow-bootstrap**(既有项目接入)。一次安装,七套能力协同。
|
|
6
6
|
|
|
@@ -103,6 +103,7 @@ Dimensions requiring semantic understanding, marked as "advisory, false positive
|
|
|
103
103
|
- Read global `ARCHITECTURE.md` → check naming conventions, BC boundaries
|
|
104
104
|
- Read global `PHYSICAL-MODEL.md` → check table naming patterns, field conventions
|
|
105
105
|
- Read global `API-INDEX.md` → check API routing patterns
|
|
106
|
+
- Read product snapshot `docs/architecture/iterations/<vN>/architecture.md`(若存在,v0.36.0)→ 对照产品级 BC 边界/聚合注册表(变更级增量不得与产品级决策矛盾)
|
|
106
107
|
- Compare incremental design against baseline:
|
|
107
108
|
- Aggregate naming conflicts with existing aggregates → Critical
|
|
108
109
|
- Table naming conflicts with PHYSICAL-MODEL → Critical
|
package/docs/README_en.md
CHANGED
|
@@ -126,7 +126,7 @@ npm install -g team-flow
|
|
|
126
126
|
|
|
127
127
|
### Version
|
|
128
128
|
|
|
129
|
-
- Current: `v0.36.
|
|
129
|
+
- Current: `v0.36.2`
|
|
130
130
|
- v0.9.1 highlights: DP-4 execution-mode recommendations, a portable runtime across 17 platforms, and a raw-package smoke with no plugin-root variable.
|
|
131
131
|
- Self-contained — no OpenSpec or Superpowers runtime required
|
|
132
132
|
- Upstream: [Fission-AI/OpenSpec](https://github.com/Fission-AI/OpenSpec), [obra/superpowers](https://github.com/obra/superpowers)
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-flow",
|
|
3
3
|
"description": "Unified workflow plugin: team-flow (spec-driven dev) + compound-engineering core subset + architecture-design (4A/DDD) + prototype (local HTML). 24 skills, one install.",
|
|
4
|
-
"version": "0.36.
|
|
4
|
+
"version": "0.36.2",
|
|
5
5
|
"contextFileName": "GEMINI.md"
|
|
6
6
|
}
|
package/hooks/session-start
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# v0.36.
|
|
2
|
+
# v0.36.2: auto-sync CLI version with plugin version
|
|
3
3
|
set -e
|
|
4
4
|
|
|
5
5
|
# ═══════════════════════════════════════════════════════════════
|
|
6
6
|
# Plugin version (update this when releasing new versions)
|
|
7
7
|
# ═══════════════════════════════════════════════════════════════
|
|
8
|
-
PLUGIN_VERSION="0.36.
|
|
8
|
+
PLUGIN_VERSION="0.36.2"
|
|
9
9
|
|
|
10
10
|
# ═══════════════════════════════════════════════════════════════
|
|
11
11
|
# Step 1: Auto-sync CLI version with plugin version
|
package/llms.txt
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Overview
|
|
4
4
|
spec-superflow is a self-contained workflow integration plugin for Claude Code, Cursor, OpenAI Codex CLI/App, GitHub Copilot CLI, Gemini CLI, OpenCode, WorkBuddy, and Trae. It merges spec-driven planning artifacts (proposal, specs, design, tasks) with disciplined execution guardrails (TDD, review gates, controlled handoff) into one unified workflow.
|
|
5
5
|
|
|
6
|
-
Current version: v0.36.
|
|
6
|
+
Current version: v0.36.2.
|
|
7
7
|
|
|
8
8
|
## Key Documents
|
|
9
9
|
- README.md: Chinese homepage with full usage guide and FAQ
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xulthekl/team-flow",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.2",
|
|
4
4
|
"description": "Unified plugin (24 skills + 15 agents) integrating team-flow, compound-engineering, architecture-design, prototype, design-system, workflow-orchestrator, workflow-bootstrap, e2e, session-handoff, workflow-feedback for multi-agent coding tools.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-flow",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.2",
|
|
4
4
|
"description": "Unified workflow plugin: team-flow (spec-driven dev: TDD/SDD/code-review/debugging) + compound-engineering core subset (brainstorm/plan/compound/strategy/ideate/proof, global compounding) + architecture-design (4A+DDD incremental design & global compounding) + prototype (local HTML prototype, zero external deps) + e2e (Playwright E2E, AC-driven) + workflow-orchestrator (product-level workflow orchestration) + workflow-bootstrap (existing project onboarding) + session-handoff (context transfer) + workflow-feedback (issue tracking). 24 skills + 15 agents, one install.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LT"
|
|
@@ -76,7 +76,7 @@ Guard: `arch_design_decision` in `.team-flow.yaml` is `null` → must run before
|
|
|
76
76
|
|
|
77
77
|
> **⛔ 串行约束(v0.30.0)**:四步严格串行。**修复子代理(architecture-design)完成前 MUST NOT dispatch 审查子代理(architecture-reviewer)**——并行会使审查跑在修复之前、误报"全部未修复"FAIL(来源:workflow-feedback 2026-08-01)。并行白名单:仅多个独立 change 的工作可并行;修复→审查、设计→审查必须串行。
|
|
78
78
|
|
|
79
|
-
1. **Dispatch**: `architecture-design` as sub-agent
|
|
79
|
+
1. **Dispatch**: `architecture-design` as sub-agent(**输入含 `docs/architecture/iterations/<vN>/architecture.md` 产品级架构快照**,v0.36.0——变更级只引用产品级聚合注册表,不重定义)→ returns `decision` + `reason` + `artifacts`。**⛔ 记录子代理 ID**(后续循环修正和 DP-A 调整必须通过此 ID 恢复,禁止启动新子代理)
|
|
80
80
|
2. **Auto-review** (decision=required 时触发): 校验产物文件存在且非空 → dispatch `architecture-reviewer` sub-agent(**记录子代理 ID**)→ FAIL 则通过 **SendMessage 恢复原 architecture-design 子代理**修正(≤3 轮 + 收敛检测,不收敛转人工)→ 报告落盘 `changes/<name>/architecture/auto-review.md`
|
|
81
81
|
3. **Reasonableness check + state write**: PASS/PASS_WITH_WARNINGS → write `arch_design_decision` + `arch_review_*` to yaml; skipped + brief 含架构关键词 → BLOCK; required + artifacts 缺失 → BLOCK; required + auto-review FAIL → BLOCK
|
|
82
82
|
4. **DP-A 用户确认门(v0.29.0 §37)**: 输出架构决策摘要 → AskUserQuestion 确认 → 需要调整时**必须通过 SendMessage 恢复原子代理**修改(禁止主代理直接修改,禁止启动新子代理)→ 修改后 SendMessage 恢复原 reviewer 重新 auto-review → 回到本步骤重新确认。含项目规范变更提示(advisory)。详见 `references/routing-rules.md`「Step 4: DP-A」
|
|
@@ -14,8 +14,9 @@ Guard: `arch_design_decision` in `.team-flow.yaml` is `null` → must run before
|
|
|
14
14
|
Dispatch `architecture-design` as sub-agent with inputs:
|
|
15
15
|
- `change-brief.md`(scope / AC / 技术方向)
|
|
16
16
|
- `prd/vN/plan.md` 高阶技术设计段
|
|
17
|
+
- `docs/architecture/iterations/<vN>/architecture.md`(**产品级架构快照,主输入**,v0.36.0)——BC 边界/聚合所有权/全局契约唯一事实源;vN 从 change-brief 的 `upstream_plan_ref`(prd/vN/plan.md)推导;变更级只引用不重定义(触及产品级决策 → 架构修订决策门;仅 change 内细节 → 增量设计)
|
|
17
18
|
- existing `specs/`
|
|
18
|
-
- global `docs/architecture/`(As-Is
|
|
19
|
+
- global `docs/architecture/`(As-Is 基线 + 已落地部分)
|
|
19
20
|
|
|
20
21
|
**⛔ 子代理 ID 记录(v0.29.0 §37,必须执行)**:dispatch 后**立即记录**子代理 ID(Agent 工具返回的 `agentId` 或 task-notification 中的 `task-id`),后续 Step 2 审查循环和 Step 4 DP-A 调整**必须通过此 ID 恢复子代理**,不得启动新子代理。记录格式:
|
|
21
22
|
```
|
|
@@ -41,6 +42,7 @@ artifacts: # required 时必填
|
|
|
41
42
|
When `decision: required`, dispatch `architecture-reviewer` as sub-agent with inputs:
|
|
42
43
|
- `prd_path`, `plan_path`, `change_brief_path`
|
|
43
44
|
- `architecture_dir`(Step 1 产出目录)
|
|
45
|
+
- `product_snapshot_path`(`docs/architecture/iterations/<vN>/architecture.md`,v0.36.0——A5 基线一致性对照产品级决策)
|
|
44
46
|
- `global_arch_dir`(`docs/architecture/`)
|
|
45
47
|
- `conventions_config`(从 `team-flow.config.json` 读取)
|
|
46
48
|
|
|
@@ -0,0 +1,561 @@
|
|
|
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**:记录已完成的批次数量
|