@xulthekl/team-flow 0.34.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +30 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/agents/build-executor.md +23 -0
- package/docs/README_en.md +1 -1
- package/docs/solutions/INDEX.md +1 -0
- package/docs/solutions/cross-phase/2026-08-05-no-summary.md +17 -0
- 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/scripts/lib/cmd-deisolate.mjs +292 -0
- package/scripts/lib/cmd-execution.mjs +16 -3
- package/scripts/lib/execution-plan.mjs +37 -5
- package/scripts/lib/test-merge.mjs +7 -4
- package/scripts/team-flow.mjs +3 -0
- package/skills/release-archivist/SKILL.md +14 -0
- package/skills/workflow-start/SKILL.md +1 -0
- package/skills/workflow-start/references/routing-rules.md +1 -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.
|
|
12
|
+
"version": "0.35.0",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "LT",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
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.
|
|
5
|
+
"version": "0.35.0",
|
|
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.
|
|
9
|
+
"version": "0.35.0"
|
|
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.
|
|
15
|
+
"version": "0.35.0",
|
|
16
16
|
"source": ".",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "LT",
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,36 @@ The format loosely follows Keep a Changelog.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.35.0] - 2026-08-05
|
|
10
|
+
|
|
11
|
+
### Added(C1 workflow-feedback 根因修复——6 条 feedback 4 个根因聚类)
|
|
12
|
+
|
|
13
|
+
#### 新命令
|
|
14
|
+
- **tf deisolate**:worktree 生命周期管理(与 `tf isolate` 对称)
|
|
15
|
+
- 支持 Case A(多仓库工作区)和 Case B(单仓库)布局检测
|
|
16
|
+
- `--dry-run`(默认):输出 worktree 状态报告(分支、ahead/behind、未提交改动)
|
|
17
|
+
- `--merge`:合并 worktree 分支回 master
|
|
18
|
+
- `--clean`:删除 worktree + 分支
|
|
19
|
+
- **tf execution refresh-hash**:独立刷新 plan 的 artifacts_hash/contract_hash,不升 revision、不清 receipts
|
|
20
|
+
|
|
21
|
+
#### Bug Fixes
|
|
22
|
+
- **test-merge.mjs**:修复顶层 `main()` 无参自调用崩溃(→ `run()` + `import.meta.url` 守卫)
|
|
23
|
+
- **execution-plan.mjs**:`writePlan` revise 时保留 reviews/ 目录(旧 receipt 通过 plan_hash/plan_revision 校验自动失效,不丢证据)
|
|
24
|
+
- **execution-plan.mjs**:stale plan 报错信息附 hash 前缀,便于定位是哪个产物变更导致
|
|
25
|
+
|
|
26
|
+
#### Agent 质量增强
|
|
27
|
+
- **build-executor**:BLOCKED 报告强制结构化证据链(blocker.category/root_cause/evidence/attempted_fixes)
|
|
28
|
+
- **build-executor**:集成测试环境问题优先走 test-strategy skill,禁止 guess-and-check
|
|
29
|
+
- **原子代理协议**:增加"上下文水位例外"(transcript >2MB 或 token >200 万时允许启动新代理替代恢复)
|
|
30
|
+
|
|
31
|
+
#### Closing 流程增强
|
|
32
|
+
- **release-archivist**:Post-Verification 新增 Worktree Deisolation advisory 步骤
|
|
33
|
+
- **workflow-start**:§37 原子代理协议更新
|
|
34
|
+
|
|
35
|
+
#### 来源
|
|
36
|
+
- workflow-feedback 2026-08-05(6 条:P1-1~P1-4 + P2-1~P2-2)
|
|
37
|
+
- 交叉验证:4 个独立专家验证,16/16 断言通过
|
|
38
|
+
|
|
9
39
|
## [0.34.1] - 2026-08-04
|
|
10
40
|
|
|
11
41
|
### Added(工作空间支持——conventions-generator 支持多子项目扫描)
|
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.
|
|
11
|
+
# team-flow v0.35.0 | 阶段: {{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.
|
|
3
|
+
> 当前版本:`v0.35.0`
|
|
4
4
|
|
|
5
5
|
> 统一插件:**team-flow**(spec 驱动开发)+ **compound-engineering 核心子集**(全局复利)+ **architecture-design**(4A+DDD 增量设计)+ **prototype**(本地 HTML 原型)+ **e2e**(AC 驱动 E2E)+ **workflow-orchestrator**(产品级编排)+ **workflow-bootstrap**(既有项目接入)。一次安装,七套能力协同。
|
|
6
6
|
|
package/agents/build-executor.md
CHANGED
|
@@ -45,6 +45,28 @@ dp_5: "<resolution>" # only if debug escalation occurred
|
|
|
45
45
|
summary: "..." # what was built + open risks for release-archivist
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
**BLOCKED Report Format (v0.35.0)**: When `status: blocked`, the summary MUST include a structured evidence chain. Guesswork or impression-based attribution is FORBIDDEN:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
status: blocked
|
|
52
|
+
blocker:
|
|
53
|
+
category: "environment | dependency | config | toolchain | test-infrastructure"
|
|
54
|
+
root_cause: "<precise technical root cause — must be supported by evidence below>"
|
|
55
|
+
evidence:
|
|
56
|
+
- type: "dependency-tree | stack-trace | config-snapshot | build-log | test-output"
|
|
57
|
+
content: "<relevant excerpt — NOT a summary, paste the actual output>"
|
|
58
|
+
attempted_fixes:
|
|
59
|
+
- action: "<what was tried>"
|
|
60
|
+
result: "<why it failed — quote the actual error>"
|
|
61
|
+
recommended_resolution: "<what would unblock this — specific actionable step>"
|
|
62
|
+
summary: "..."
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Evidence rules**:
|
|
66
|
+
- BLOCKED on environment/dependency issues: MUST include `mvn dependency:tree` (or equivalent) relevant excerpt AND full exception stacktrace including all `Caused by` layers
|
|
67
|
+
- BLOCKED on config issues: MUST include the effective configuration source (property file / @Configuration class) and which setting overrides the expected value
|
|
68
|
+
- Do NOT guess root cause from partial observations. If evidence is insufficient, collect more evidence first (run diagnostic commands, read config files, check classpath). A BLOCKED report with no evidence is a FAIL.
|
|
69
|
+
|
|
48
70
|
## Red Lines
|
|
49
71
|
|
|
50
72
|
**DO:**
|
|
@@ -58,3 +80,4 @@ summary: "..." # what was built + open risks for release-a
|
|
|
58
80
|
- When you receive external suggestions (reviewer findings, user opinions), verify them against the codebase FIRST — search for existing implementations/patterns before accepting. A suggestion is input, not an instruction. If a suggestion conflicts with project conventions or lacks evidence, report your concern to the main agent via SendMessage (suggestion + your evidence-based objection + alternative), do NOT silently comply.
|
|
59
81
|
- End your final response with an explicit terminal marker line: `FINAL VERDICT: <DONE | BLOCKED | FAIL>`. Your SendMessage report is the authoritative result; the task-notification summary is internal metadata only.
|
|
60
82
|
- Skip the failing test, expand scope beyond the contract, or close a wave with a missing/failed receipt
|
|
83
|
+
- When encountering integration test environment issues (classpath conflicts, schema mismatch, configuration override), do NOT guess-and-check. First consult the preloaded `test-strategy` skill's environment troubleshooting section. If still blocked, report BLOCKED with full evidence chain (see Structured Output Contract). Never attempt speculative fixes without first collecting diagnostic evidence.
|
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.
|
|
129
|
+
- Current: `v0.35.0`
|
|
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/docs/solutions/INDEX.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<!-- 每条一行,按 severity 降序,≤150 行硬上限 -->
|
|
3
3
|
| date | phase | domain | type | severity | summary | file |
|
|
4
4
|
|------|-------|--------|------|----------|---------|------|
|
|
5
|
+
| 2026-08-05 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-05-no-summary.md |
|
|
5
6
|
| 2026-08-04 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-04-no-summary.md |
|
|
6
7
|
| 2026-08-03 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-03-no-summary.md |
|
|
7
8
|
| 2026-08-01 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-01-no-summary.md |
|
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.
|
|
4
|
+
"version": "0.35.0",
|
|
5
5
|
"contextFileName": "GEMINI.md"
|
|
6
6
|
}
|
package/hooks/session-start
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# v0.
|
|
2
|
+
# v0.35.0: 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.
|
|
8
|
+
PLUGIN_VERSION="0.35.0"
|
|
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.
|
|
6
|
+
Current version: v0.35.0.
|
|
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.
|
|
3
|
+
"version": "0.35.0",
|
|
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.
|
|
3
|
+
"version": "0.35.0",
|
|
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"
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/lib/cmd-deisolate.mjs — worktree deisolation (v0.35.0)
|
|
3
|
+
//
|
|
4
|
+
// 检测 change 的 worktree 状态,输出合并建议或执行合并/清理。
|
|
5
|
+
// 与 ensure-branch.mjs(创建)对称,补全 worktree 生命周期。
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// tf deisolate <change-dir> # dry-run: 输出状态报告
|
|
9
|
+
// tf deisolate <change-dir> --merge # 合并 worktree 分支回 master
|
|
10
|
+
// tf deisolate <change-dir> --merge --clean # 合并后删除 worktree + 分支
|
|
11
|
+
// tf deisolate <change-dir> --clean # 仅删除 worktree(不合并)
|
|
12
|
+
//
|
|
13
|
+
// 布局识别(对称 ensure-branch.mjs):
|
|
14
|
+
// Case A 多仓库工作区:<root>/.worktrees/<change>/<repo>/ 存在
|
|
15
|
+
// Case B 单仓库:<change-dir> 同级 ../<repo>-<change-name> 存在
|
|
16
|
+
|
|
17
|
+
import { execFileSync } from 'node:child_process';
|
|
18
|
+
import { existsSync, readdirSync, rmSync } from 'node:fs';
|
|
19
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
20
|
+
import { parseArgs } from 'node:util';
|
|
21
|
+
|
|
22
|
+
export async function run(args) {
|
|
23
|
+
const { positionals, values } = parseArgs({
|
|
24
|
+
args,
|
|
25
|
+
options: {
|
|
26
|
+
merge: { type: 'boolean', default: false },
|
|
27
|
+
clean: { type: 'boolean', default: false },
|
|
28
|
+
'project-root': { type: 'string' },
|
|
29
|
+
json: { type: 'boolean', default: false },
|
|
30
|
+
help: { type: 'boolean', default: false },
|
|
31
|
+
},
|
|
32
|
+
allowPositionals: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (values.help || positionals.length === 0) {
|
|
36
|
+
printHelp();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const changeDir = resolve(positionals[0]);
|
|
41
|
+
const changeName = basename(changeDir);
|
|
42
|
+
|
|
43
|
+
// 检测布局
|
|
44
|
+
const workspaceRoot = detectWorkspaceRoot(changeDir);
|
|
45
|
+
const caseA = workspaceRoot ? detectCaseA(workspaceRoot, changeName) : null;
|
|
46
|
+
const caseB = detectCaseB(changeDir, changeName);
|
|
47
|
+
|
|
48
|
+
if (!caseA && !caseB) {
|
|
49
|
+
const msg = `No worktrees found for change '${changeName}'.`;
|
|
50
|
+
if (values.json) {
|
|
51
|
+
console.log(JSON.stringify({ found: false, message: msg }));
|
|
52
|
+
} else {
|
|
53
|
+
console.log(`✅ ${msg}`);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const repos = caseA || caseB;
|
|
59
|
+
const results = [];
|
|
60
|
+
|
|
61
|
+
for (const repo of repos) {
|
|
62
|
+
const result = processRepo(repo, values);
|
|
63
|
+
results.push(result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (values.json) {
|
|
67
|
+
console.log(JSON.stringify({ found: true, repos: results }, null, 2));
|
|
68
|
+
} else {
|
|
69
|
+
printReport(results, values);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function detectWorkspaceRoot(absChangeDir) {
|
|
74
|
+
const parent = dirname(absChangeDir);
|
|
75
|
+
if (basename(parent) !== 'changes') return null;
|
|
76
|
+
return dirname(parent);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Case A: <root>/.worktrees/<change>/<repo>/
|
|
80
|
+
function detectCaseA(root, changeName) {
|
|
81
|
+
const changeWorktreeDir = join(root, '.worktrees', changeName);
|
|
82
|
+
if (!existsSync(changeWorktreeDir)) return null;
|
|
83
|
+
|
|
84
|
+
let entries;
|
|
85
|
+
try {
|
|
86
|
+
entries = readdirSync(changeWorktreeDir, { withFileTypes: true });
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const repos = entries
|
|
92
|
+
.filter(e => e.isDirectory() && existsSync(join(changeWorktreeDir, e.name, '.git')))
|
|
93
|
+
.map(e => ({
|
|
94
|
+
name: e.name,
|
|
95
|
+
worktreePath: join(changeWorktreeDir, e.name),
|
|
96
|
+
repoPath: join(root, e.name),
|
|
97
|
+
layout: 'A',
|
|
98
|
+
}))
|
|
99
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
100
|
+
|
|
101
|
+
return repos.length > 0 ? repos : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Case B: ../<repo>-<change-name>
|
|
105
|
+
function detectCaseB(changeDir, changeName) {
|
|
106
|
+
const parentDir = dirname(changeDir);
|
|
107
|
+
const repoName = basename(changeDir) || 'repo';
|
|
108
|
+
// 旧 ensure-branch 行为:worktree 在 ../<repo>-<name>
|
|
109
|
+
// 分支名经过 sanitize,这里用 changeName 作为近似匹配
|
|
110
|
+
const candidates = [
|
|
111
|
+
join(parentDir, `${repoName}-${changeName}`),
|
|
112
|
+
join(parentDir, `${repoName}-${basename(changeDir)}`),
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const wtPath of candidates) {
|
|
116
|
+
if (existsSync(join(wtPath, '.git'))) {
|
|
117
|
+
return [{
|
|
118
|
+
name: repoName,
|
|
119
|
+
worktreePath: wtPath,
|
|
120
|
+
repoPath: changeDir,
|
|
121
|
+
layout: 'B',
|
|
122
|
+
}];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function processRepo(repo, values) {
|
|
129
|
+
const result = { name: repo.name, worktreePath: repo.worktreePath, layout: repo.layout };
|
|
130
|
+
|
|
131
|
+
// 获取 worktree 分支名
|
|
132
|
+
let branch = '';
|
|
133
|
+
try {
|
|
134
|
+
branch = (execFileSync('git', ['branch', '--show-current'], {
|
|
135
|
+
encoding: 'utf-8', cwd: repo.worktreePath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
136
|
+
}) || '').trim();
|
|
137
|
+
} catch {
|
|
138
|
+
result.status = 'error';
|
|
139
|
+
result.message = 'Could not determine worktree branch';
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
result.branch = branch;
|
|
143
|
+
|
|
144
|
+
// 获取相对 master 的 diff
|
|
145
|
+
const masterBranch = detectMasterBranch(repo.repoPath);
|
|
146
|
+
try {
|
|
147
|
+
const ahead = (execFileSync('git', ['rev-list', '--count', `${masterBranch}..${branch}`], {
|
|
148
|
+
encoding: 'utf-8', cwd: repo.worktreePath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
149
|
+
}) || '0').trim();
|
|
150
|
+
const behind = (execFileSync('git', ['rev-list', '--count', `${branch}..${masterBranch}`], {
|
|
151
|
+
encoding: 'utf-8', cwd: repo.worktreePath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
152
|
+
}) || '0').trim();
|
|
153
|
+
result.ahead = parseInt(ahead, 10);
|
|
154
|
+
result.behind = parseInt(behind, 10);
|
|
155
|
+
} catch {
|
|
156
|
+
result.ahead = '?';
|
|
157
|
+
result.behind = '?';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 检查未提交改动
|
|
161
|
+
try {
|
|
162
|
+
const status = execFileSync('git', ['status', '--porcelain'], {
|
|
163
|
+
encoding: 'utf-8', cwd: repo.worktreePath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
164
|
+
});
|
|
165
|
+
result.dirty = status.trim().length > 0;
|
|
166
|
+
} catch {
|
|
167
|
+
result.dirty = '?';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 操作
|
|
171
|
+
if (values.merge) {
|
|
172
|
+
result.merge = mergeBranch(repo, branch, masterBranch);
|
|
173
|
+
}
|
|
174
|
+
if (values.clean) {
|
|
175
|
+
result.clean = cleanWorktree(repo, branch);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
result.status = 'ok';
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function detectMasterBranch(repoPath) {
|
|
183
|
+
try {
|
|
184
|
+
const branch = (execFileSync('git', ['branch', '--show-current'], {
|
|
185
|
+
encoding: 'utf-8', cwd: repoPath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
186
|
+
}) || '').trim();
|
|
187
|
+
if (branch) return branch;
|
|
188
|
+
} catch { /* ignore */ }
|
|
189
|
+
// fallback: try master, then main
|
|
190
|
+
for (const candidate of ['master', 'main']) {
|
|
191
|
+
try {
|
|
192
|
+
execFileSync('git', ['rev-parse', `refs/heads/${candidate}`], {
|
|
193
|
+
encoding: 'utf-8', cwd: repoPath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
194
|
+
});
|
|
195
|
+
return candidate;
|
|
196
|
+
} catch { /* continue */ }
|
|
197
|
+
}
|
|
198
|
+
return 'master';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function mergeBranch(repo, branch, masterBranch) {
|
|
202
|
+
try {
|
|
203
|
+
const output = execFileSync('git', ['merge', branch, '--no-edit'], {
|
|
204
|
+
encoding: 'utf-8', cwd: repo.repoPath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
205
|
+
});
|
|
206
|
+
return { success: true, message: output.trim() };
|
|
207
|
+
} catch (e) {
|
|
208
|
+
return { success: false, message: (e.stderr || e.stdout || e.message || '').toString().trim() };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function cleanWorktree(repo, branch) {
|
|
213
|
+
const result = { worktreeRemoved: false, branchDeleted: false, errors: [] };
|
|
214
|
+
|
|
215
|
+
// Remove worktree
|
|
216
|
+
try {
|
|
217
|
+
execFileSync('git', ['worktree', 'remove', repo.worktreePath, '--force'], {
|
|
218
|
+
encoding: 'utf-8', cwd: repo.repoPath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
219
|
+
});
|
|
220
|
+
result.worktreeRemoved = true;
|
|
221
|
+
} catch (e) {
|
|
222
|
+
result.errors.push(`worktree remove: ${(e.stderr || e.message || '').toString().trim()}`);
|
|
223
|
+
// Fallback: rm -rf
|
|
224
|
+
try {
|
|
225
|
+
rmSync(repo.worktreePath, { recursive: true, force: true });
|
|
226
|
+
result.worktreeRemoved = true;
|
|
227
|
+
} catch (e2) {
|
|
228
|
+
result.errors.push(`rm fallback: ${e2.message}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Delete branch (skip if merged)
|
|
233
|
+
try {
|
|
234
|
+
execFileSync('git', ['branch', '-d', branch], {
|
|
235
|
+
encoding: 'utf-8', cwd: repo.repoPath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
236
|
+
});
|
|
237
|
+
result.branchDeleted = true;
|
|
238
|
+
} catch {
|
|
239
|
+
// Branch not merged — use -D only if --force equivalent
|
|
240
|
+
try {
|
|
241
|
+
execFileSync('git', ['branch', '-D', branch], {
|
|
242
|
+
encoding: 'utf-8', cwd: repo.repoPath, stdio: ['ignore', 'pipe', 'pipe'],
|
|
243
|
+
});
|
|
244
|
+
result.branchDeleted = true;
|
|
245
|
+
} catch (e) {
|
|
246
|
+
result.errors.push(`branch delete: ${(e.stderr || e.message || '').toString().trim()}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function printReport(results, values) {
|
|
254
|
+
console.log(`\n📦 Worktree Deisolation Report\n`);
|
|
255
|
+
|
|
256
|
+
for (const r of results) {
|
|
257
|
+
console.log(` Repo: ${r.name} (${r.layout === 'A' ? 'multi-repo' : 'single-repo'})`);
|
|
258
|
+
console.log(` Branch: ${r.branch || 'unknown'}`);
|
|
259
|
+
console.log(` Worktree: ${r.worktreePath}`);
|
|
260
|
+
if (r.ahead !== undefined) {
|
|
261
|
+
console.log(` Ahead of master: ${r.ahead} commit(s)`);
|
|
262
|
+
console.log(` Behind master: ${r.behind} commit(s)`);
|
|
263
|
+
}
|
|
264
|
+
console.log(` Uncommitted changes: ${r.dirty === true ? '⚠️ YES' : r.dirty === false ? 'no' : 'unknown'}`);
|
|
265
|
+
|
|
266
|
+
if (r.merge) {
|
|
267
|
+
console.log(` Merge: ${r.merge.success ? '✅ ' + r.merge.message : '❌ ' + r.merge.message}`);
|
|
268
|
+
}
|
|
269
|
+
if (r.clean) {
|
|
270
|
+
console.log(` Worktree removed: ${r.clean.worktreeRemoved ? '✅' : '❌'}`);
|
|
271
|
+
console.log(` Branch deleted: ${r.clean.branchDeleted ? '✅' : '❌'}`);
|
|
272
|
+
if (r.clean.errors.length > 0) {
|
|
273
|
+
console.log(` Clean errors: ${r.clean.errors.join('; ')}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
console.log();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (!values.merge && !values.clean) {
|
|
280
|
+
console.log(` To merge: tf deisolate <change-dir> --merge`);
|
|
281
|
+
console.log(` To clean: tf deisolate <change-dir> --merge --clean`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function printHelp() {
|
|
286
|
+
console.log(`Usage:
|
|
287
|
+
tf deisolate <change-dir> # dry-run: show worktree status
|
|
288
|
+
tf deisolate <change-dir> --merge # merge worktree branch back to master
|
|
289
|
+
tf deisolate <change-dir> --merge --clean # merge + remove worktree + delete branch
|
|
290
|
+
tf deisolate <change-dir> --clean # remove worktree only (no merge)
|
|
291
|
+
tf deisolate <change-dir> --json # JSON output`);
|
|
292
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
|
-
import { createPlan, describeWaves, EXECUTION_MODES, readPlan, recordReview, validatePlan, writePlan } from './execution-plan.mjs';
|
|
2
|
+
import { createPlan, describeWaves, EXECUTION_MODES, readPlan, recordReview, refreshPlanHash, validatePlan, writePlan } from './execution-plan.mjs';
|
|
3
3
|
import {
|
|
4
4
|
createRecommendationReceipt,
|
|
5
5
|
readCurrentRecommendationReceipt,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from './execution-recommendation.mjs';
|
|
8
8
|
import { readState, writeState } from './state-loader.mjs';
|
|
9
9
|
|
|
10
|
-
const SUBCOMMANDS = ['recommend', 'plan', 'show', 'revise', 'review'];
|
|
10
|
+
const SUBCOMMANDS = ['recommend', 'plan', 'show', 'revise', 'review', 'refresh-hash'];
|
|
11
11
|
|
|
12
12
|
export async function run(args) {
|
|
13
13
|
const { positionals, values } = parseArgs({
|
|
@@ -52,6 +52,8 @@ export async function run(args) {
|
|
|
52
52
|
return createAndPrintPlan(changeDir, values, true);
|
|
53
53
|
case 'review':
|
|
54
54
|
return recordAndPrintReview(changeDir, values);
|
|
55
|
+
case 'refresh-hash':
|
|
56
|
+
return refreshHash(changeDir, values.json);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -203,6 +205,16 @@ function parseTestsStats(values) {
|
|
|
203
205
|
return parsed;
|
|
204
206
|
}
|
|
205
207
|
|
|
208
|
+
function refreshHash(changeDir, json) {
|
|
209
|
+
const result = refreshPlanHash(changeDir);
|
|
210
|
+
if (!result) {
|
|
211
|
+
print(json, { refreshed: false }, 'Artifacts hash is already up to date.');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
print(json, { refreshed: true, artifacts_hash: result.artifacts_hash, contract_hash: result.contract_hash },
|
|
215
|
+
`Refreshed artifacts hash: ${result.artifacts_hash?.slice(0, 12)}…`);
|
|
216
|
+
}
|
|
217
|
+
|
|
206
218
|
function requireSafeReason(reason) {
|
|
207
219
|
if (/[\p{Cc}\p{Zl}\p{Zp}]/u.test(reason)) {
|
|
208
220
|
throw new Error('--reason must not contain control characters or line separators');
|
|
@@ -225,5 +237,6 @@ function printHelp() {
|
|
|
225
237
|
tf execution plan <dir> --mode <mode> --confirm --reason <text> --wave <id>:<strategy>:<task,...>[:<depends-on,...>] [--acknowledge-recommendation]
|
|
226
238
|
tf execution show <dir> [--json]
|
|
227
239
|
tf execution revise <dir> --mode sdd --confirm --reason <text> --wave <id>:<strategy>:<task,...>[:<depends-on,...>] [--acknowledge-recommendation]
|
|
228
|
-
tf execution review <dir> --wave <id> --base <sha> --head <sha> --report <path> --verdict pass|fail [--tests-total N --tests-passed N --tests-failed N]
|
|
240
|
+
tf execution review <dir> --wave <id> --base <sha> --head <sha> --report <path> --verdict pass|fail [--tests-total N --tests-passed N --tests-failed N]
|
|
241
|
+
tf execution refresh-hash <dir> [--json] # refresh artifacts/contract hash without bumping revision`);
|
|
229
242
|
}
|
|
@@ -53,9 +53,17 @@ export function writePlan(changeDir, plan) {
|
|
|
53
53
|
mkdirSync(paths.root, { recursive: true });
|
|
54
54
|
const previousPlan = readPlan(changeDir);
|
|
55
55
|
if (previousPlan && (previousPlan.revision !== plan.revision || previousPlan.hash !== plan.hash)) {
|
|
56
|
-
// Review
|
|
57
|
-
//
|
|
58
|
-
|
|
56
|
+
// v0.35.0: Review receipts are RETAINED across revisions — readCurrentReview
|
|
57
|
+
// validates plan_hash/plan_revision matching, so stale receipts are
|
|
58
|
+
// automatically invalidated without losing evidence (base/head/report SHA).
|
|
59
|
+
// This prevents legitimate artifact updates (e.g., design.md 横展结论) from
|
|
60
|
+
// causing cascading receipt loss. See workflow-feedback 20260805.
|
|
61
|
+
//
|
|
62
|
+
// Recommendation receipt is revision-scoped and must be cleaned:
|
|
63
|
+
const recommendationFile = paths.executionRecommendation;
|
|
64
|
+
if (recommendationFile && existsSync(recommendationFile)) {
|
|
65
|
+
rmSync(recommendationFile, { force: true });
|
|
66
|
+
}
|
|
59
67
|
}
|
|
60
68
|
atomicWrite(paths.executionPlan, `${JSON.stringify(plan, null, 2)}\n`);
|
|
61
69
|
writeExecutionPlanSummary(changeDir, plan);
|
|
@@ -76,10 +84,16 @@ export function validatePlan(changeDir, plan) {
|
|
|
76
84
|
failures.push('execution plan mode does not match state');
|
|
77
85
|
}
|
|
78
86
|
if (plan?.artifacts_hash !== computeArtifactsHash(changeDir)) {
|
|
79
|
-
|
|
87
|
+
const actual = computeArtifactsHash(changeDir);
|
|
88
|
+
const planPrefix = plan?.artifacts_hash?.slice(0, 12) ?? 'null';
|
|
89
|
+
const actualPrefix = actual?.slice(0, 12) ?? 'null';
|
|
90
|
+
failures.push(`execution plan is stale: artifacts hash mismatch (plan: ${planPrefix}…, current: ${actualPrefix}…)`);
|
|
80
91
|
}
|
|
81
92
|
if (plan?.contract_hash !== computeContractHash(changeDir)) {
|
|
82
|
-
|
|
93
|
+
const actual = computeContractHash(changeDir);
|
|
94
|
+
const planPrefix = plan?.contract_hash?.slice(0, 12) ?? 'null';
|
|
95
|
+
const actualPrefix = actual?.slice(0, 12) ?? 'null';
|
|
96
|
+
failures.push(`execution plan is stale: contract hash mismatch (plan: ${planPrefix}…, current: ${actualPrefix}…)`);
|
|
83
97
|
}
|
|
84
98
|
if (plan?.workflow !== state.workflow) {
|
|
85
99
|
failures.push('execution plan workflow does not match state');
|
|
@@ -442,6 +456,24 @@ function hashPlan(plan) {
|
|
|
442
456
|
return `sha256:${createHash('sha256').update(stableJson(content)).digest('hex')}`;
|
|
443
457
|
}
|
|
444
458
|
|
|
459
|
+
/**
|
|
460
|
+
* v0.35.0: Refresh plan's artifacts_hash and contract_hash without bumping revision.
|
|
461
|
+
* Returns the updated plan (already persisted via writePlan), or null if no change needed.
|
|
462
|
+
*/
|
|
463
|
+
export function refreshPlanHash(changeDir) {
|
|
464
|
+
const plan = readPlan(changeDir);
|
|
465
|
+
if (!plan) return null;
|
|
466
|
+
const newArtifactsHash = computeArtifactsHash(changeDir);
|
|
467
|
+
const newContractHash = computeContractHash(changeDir);
|
|
468
|
+
if (plan.artifacts_hash === newArtifactsHash && plan.contract_hash === newContractHash) {
|
|
469
|
+
return null; // no change needed
|
|
470
|
+
}
|
|
471
|
+
plan.artifacts_hash = newArtifactsHash;
|
|
472
|
+
plan.contract_hash = newContractHash;
|
|
473
|
+
plan.hash = hashPlan(plan);
|
|
474
|
+
return writePlan(changeDir, plan);
|
|
475
|
+
}
|
|
476
|
+
|
|
445
477
|
function tryHashPlan(plan) {
|
|
446
478
|
if (!isObject(plan)) return null;
|
|
447
479
|
try {
|
|
@@ -534,7 +534,10 @@ async function main(argv, projectRoot) {
|
|
|
534
534
|
console.log(`\n🎉 test-merge complete for ${changeName}`);
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
-
main()
|
|
538
|
-
|
|
539
|
-
process.
|
|
540
|
-
|
|
537
|
+
// 直接运行(v0.35.0 修复:main() 无参自调用崩溃 → 调用 run() 走正确参数解析)
|
|
538
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
539
|
+
run(process.argv.slice(2)).catch(err => {
|
|
540
|
+
console.error('test-merge error:', err.message);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
});
|
|
543
|
+
}
|
package/scripts/team-flow.mjs
CHANGED
|
@@ -17,6 +17,7 @@ const COMMANDS = {
|
|
|
17
17
|
checkpoint: () => import('./lib/cmd-checkpoint.mjs'),
|
|
18
18
|
handoff: () => import('./lib/cmd-handoff.mjs'),
|
|
19
19
|
isolate: () => import('./lib/cmd-isolate.mjs'),
|
|
20
|
+
deisolate: () => import('./lib/cmd-deisolate.mjs'),
|
|
20
21
|
execution: () => import('./lib/cmd-execution.mjs'),
|
|
21
22
|
runtime: () => import('./lib/cmd-runtime.mjs'),
|
|
22
23
|
solutions: () => import('./lib/cmd-solutions.mjs'),
|
|
@@ -88,6 +89,8 @@ Commands:
|
|
|
88
89
|
Upgrade inline/batch to SDD, or replan existing SDD waves, as a new revision
|
|
89
90
|
execution review <change-dir> --wave <id> --base <sha> --head <sha> --report <path> --verdict pass|fail
|
|
90
91
|
Record one review receipt for a planned wave
|
|
92
|
+
deisolate <change-dir> [--merge] [--clean] [--json]
|
|
93
|
+
Show worktree status / merge branch back / clean worktree
|
|
91
94
|
runtime check-update Run a portable update check for canonical skills
|
|
92
95
|
runtime infer <dir> Infer workflow mode without a plugin-root path
|
|
93
96
|
runtime guard ... Run a portable phase-transition guard
|
|
@@ -201,6 +201,20 @@ tf solutions capture \
|
|
|
201
201
|
|
|
202
202
|
This check is advisory — skip silently if no learnable moments are detected.
|
|
203
203
|
|
|
204
|
+
### Worktree Deisolation (v0.35.0) — advisory
|
|
205
|
+
|
|
206
|
+
After compound promotion, check if worktree isolation exists for this change:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
tf deisolate <change-dir>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
If worktrees exist (output shows `found: true` or ahead/behind stats), include the deisolation report in the closing summary. **Do NOT auto-merge** — the orchestrator or user decides the merge strategy. Mark `Worktree deisolation` in the Step 5 report as:
|
|
213
|
+
- `PENDING` — worktrees detected, user action required
|
|
214
|
+
- `N/A` — no worktrees found (single-repo or already cleaned)
|
|
215
|
+
|
|
216
|
+
This step is advisory — skip silently when `tf deisolate` reports no worktrees.
|
|
217
|
+
|
|
204
218
|
### Workflow Feedback Check (v0.16.0)
|
|
205
219
|
|
|
206
220
|
Before finalizing the closing, ask the user if they encountered any workflow issues during this change:
|
|
@@ -61,6 +61,7 @@ Validate mode against artifact content. If hotfix/tweak criteria not met → upg
|
|
|
61
61
|
产出型路由(spec-writer / contract-builder / build-executor / release-archivist)通过 `Agent(subagent_type: "team-flow:<name>")` **dispatch 子代理**执行——agent 定义经 `skills:` 预加载对应 SKILL.md,主代理**不得**凭记忆手写产物格式 prompt。need-explorer 为交互式澄清,**主进程执行**(需与用户对话;其 agent 定义 `team-flow:need-explorer` 仅作可 dispatch 后备)。
|
|
62
62
|
|
|
63
63
|
- **记录 agentId**:dispatch 后立即记录子代理 ID;修复一律 SendMessage 恢复原子代理(禁启动新子代理,见 v0.29.0 §37)
|
|
64
|
+
- **上下文水位例外(v0.35.0)**:当原子代理 transcript 超过 2MB 或累计 token 消耗超过 200 万时,主代理可以启动新代理替代恢复。新代理必须接收完整交接文档(当前进度、已完成 wave 列表、未解决问题、artifact 位置、已知 blocker),以保证连续性。此为 SendMessage 恢复原则的唯一补充例外
|
|
64
65
|
- **返回即验证(validation gate)**:子代理返回产物后**立即** `tf validate <change-dir>`;FAIL → SendMessage 回**原**子代理修复,通过后才能继续/转换。把格式失败拦截在返回时,而非状态转换时(避免浪费整次执行后再失败)。**build-executor 附加验证(v0.13 §52 B3,SDD/full 模式)**:返回的 diff 中必须包含测试文件(src/test/ 或项目测试目录);零测试文件的实现返回一律 BLOCK,SendMessage 回原子代理按 test-matrix.md / TDD Iron Law 补齐——不得以"手动验证"替代(C1-domain-policy 事件教训)。例外:已显式记录 `test_matrix_skipped=true` + 理由的 change(纯文档/配置类)不要求测试文件
|
|
65
66
|
- **结果协议**:子代理终态须标注 `FINAL VERDICT: <DONE|BLOCKED|FAIL>`(审查类用 PASS/PASS_WITH_WARNINGS/FAIL);**SendMessage 报告为权威结果**,task-notification.result 仅内部元数据
|
|
66
67
|
- **子代理状态边界**:子代理 MUST NOT 修改 `.team-flow.yaml` 的 `state`/`workflow` 核心字段(状态转换是主代理专有职责,经 `tf state transition` 执行),只写自己的 `dp_N_*` 决策字段
|
|
@@ -69,6 +69,7 @@ reviewer 报告 → PASS / PASS_WITH_WARNINGS → 进入 Step 3
|
|
|
69
69
|
- 重新审查:`SendMessage(to: arch_reviewer_agent_id, message: "第 {N} 轮审查,架构产物已修正。请重新执行 6 维度审查。")`
|
|
70
70
|
- **禁止**在循环中启动新的 architecture-design 或 architecture-reviewer 子代理——原子代理拥有完整的设计上下文和审查历史,新子代理需要重新加载全部上下文(浪费 10-20K tokens 且可能丢失修正连贯性)
|
|
71
71
|
- **唯一例外**:SendMessage 恢复失败(子代理 transcript 不可用)时,启动新子代理作为 fallback,但必须在 dispatch prompt 中传入:原产物路径 + auto-review 报告 + 历次修正记录
|
|
72
|
+
- **上下文水位例外(v0.35.0)**:当原子代理 transcript 超过 2MB 或累计 token 消耗超过 200 万时,主代理可以启动新代理替代恢复。新代理必须接收完整交接文档(当前进度、已完成 wave 列表、未解决问题、artifact 位置、已知 blocker),以保证连续性
|
|
72
73
|
|
|
73
74
|
报告落盘到 `changes/<name>/architecture/auto-review.md`。
|
|
74
75
|
|