@xulthekl/team-flow 0.34.0 → 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.
@@ -1,3 +1,3 @@
1
- # team-flow v0.34.0 | 阶段: {{state}} | 工作流: {{workflow}}
1
+ # team-flow v0.35.0 | 阶段: {{state}} | 工作流: {{workflow}}
2
2
  当前阶段允许的操作由 workflow-start 路由规则定义。
3
3
  禁止跨越 DP gate 进入下一阶段。变更范围以 execution-contract.md 的 Intent Lock 为准。
@@ -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.34.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.34.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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-flow",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "Spec-first workflow that bridges OpenSpec-style planning and Superpowers-style execution discipline.",
5
5
  "author": {
6
6
  "name": "MageByte",
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Unified workflow plugin marketplace for Cursor (team-flow: team-flow + compound + architecture-design + prototype).",
8
- "version": "0.34.0"
8
+ "version": "0.35.0"
9
9
  },
10
10
  "plugins": [
11
11
  {
@@ -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.34.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.34.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.34.0",
15
+ "version": "0.35.0",
16
16
  "source": ".",
17
17
  "author": {
18
18
  "name": "LT",
package/CHANGELOG.md CHANGED
@@ -6,6 +6,58 @@ 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
+
39
+ ## [0.34.1] - 2026-08-04
40
+
41
+ ### Added(工作空间支持——conventions-generator 支持多子项目扫描)
42
+
43
+ #### 工作空间扫描(v0.34.1 核心特性)
44
+ - **scripts/lib/conventions-generator.mjs**:新增工作空间模式
45
+ - `scanSubProjects(root)`:扫描工作空间中的所有子项目
46
+ - `aggregateTechStacks(subProjects)`:汇总所有子项目的技术栈
47
+ - `selectTemplatesForWorkspace(aggregatedTechStack)`:为工作空间选择 conventions 模板
48
+ - `run()` 函数支持自动检测工作空间模式(子项目 > 1 时进入)
49
+ - 输出模式标识:`mode: 'workspace'` 或 `mode: 'single'`
50
+
51
+ #### 使用场景
52
+ - **前后端分离项目**:Java 后端 + Vue 前端 → 生成 Java + JavaScript conventions
53
+ - **多语言项目**:Java 后端 + Python 脚本 + JavaScript 前端 → 生成三种语言 conventions
54
+ - **单项目兼容**:原有单项目逻辑完全保留
55
+
56
+ ### Tests
57
+ - **tests/lib/conventions-generator.test.mjs**:新增 12 个测试(scanSubProjects: 4 / aggregateTechStacks: 3 / selectTemplatesForWorkspace: 5)
58
+ - 全量回归:583/583 通过(含 27 个 conventions-generator 测试)
59
+ - check-versions ✅
60
+
9
61
  ## [0.34.0] - 2026-08-04
10
62
 
11
63
  ### Added(测试能力增强——吸收 glaf4-test 通用能力 + conventions 机制,设计见工作区级文档 team-flow-workspace/docs/plan/test-capability-enhancement-design.md)
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.34.0 | 阶段: {{state}} | 工作流: {{workflow}}
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
@@ -7,7 +7,7 @@
7
7
  - [Fission-AI/OpenSpec](https://github.com/Fission-AI/OpenSpec) — 规划引擎(Schema 验证、Delta Spec、工件解析)
8
8
  - [obra/superpowers](https://github.com/obra/superpowers) — 执行纪律(TDD 铁律、SDD、系统化调试、代码审查)
9
9
 
10
- 当前发布版本:**v0.34.0**。
10
+ 当前发布版本:**v0.35.0**。
11
11
 
12
12
  ---
13
13
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # team-flow
2
2
 
3
- > 当前版本:`v0.34.0`
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
 
@@ -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.34.0`
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)
@@ -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 |
@@ -0,0 +1,17 @@
1
+ ---
2
+ phase: cross-phase
3
+ domain: general
4
+ type: insight
5
+ severity: medium
6
+ date: 2026-08-05
7
+ source:
8
+ ---
9
+
10
+ ## 问题描述
11
+ (no summary)
12
+
13
+ ## 根因/模式
14
+ (待补充)
15
+
16
+ ## 预防措施/应用方式
17
+ (待补充)
@@ -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.34.0",
4
+ "version": "0.35.0",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bash
2
- # v0.34.0: auto-sync CLI version with plugin version
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.34.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.34.0.
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.34.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.34.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
  }
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- // scripts/lib/conventions-generator.mjs — conventions 生成器(v0.34.0 新增)
2
+ // scripts/lib/conventions-generator.mjs — conventions 生成器(v0.34.0 新增,v0.34.1 工作空间支持)
3
3
  //
4
4
  // 根据项目技术栈自动生成测试规范 conventions,符合 glaf4-test 要求。
5
- // 支持存量项目(自动识别)和全新项目(交互式引导)。
5
+ // 支持存量项目(自动识别)、全新项目(交互式引导)和工作空间(多子项目)。
6
6
  //
7
7
  // 使用方式:
8
8
  // node scripts/lib/conventions-generator.mjs --root <项目根目录> [--interactive]
9
9
  //
10
- // 设计来源:test-capability-enhancement-design v1.0 §四
10
+ // 设计来源:test-capability-enhancement-design v1.0 §四 + §55.4
11
11
 
12
12
  import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
13
13
  import path, { join } from 'node:path';
@@ -98,6 +98,102 @@ export function detectTechStack(root) {
98
98
  return result;
99
99
  }
100
100
 
101
+ // ── 工作空间扫描 ─────────────────────────────────────────────────────────
102
+
103
+ /**
104
+ * 扫描工作空间中的所有子项目
105
+ * @param {string} root 工作空间根目录
106
+ * @returns {Array<{ path: string, name: string, techStack: object }>}
107
+ */
108
+ export function scanSubProjects(root) {
109
+ const subProjects = [];
110
+
111
+ // 扫描一级子目录
112
+ const entries = readdirSync(root, { withFileTypes: true });
113
+
114
+ for (const entry of entries) {
115
+ if (!entry.isDirectory()) continue;
116
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
117
+
118
+ const subProjectPath = join(root, entry.name);
119
+ const techStack = detectTechStack(subProjectPath);
120
+
121
+ // 如果检测到技术栈,记录为子项目
122
+ if (techStack.language) {
123
+ subProjects.push({
124
+ path: subProjectPath,
125
+ name: entry.name,
126
+ techStack,
127
+ });
128
+ }
129
+ }
130
+
131
+ return subProjects;
132
+ }
133
+
134
+ /**
135
+ * 汇总所有子项目的技术栈
136
+ * @param {Array<{ path: string, name: string, techStack: object }>} subProjects
137
+ * @returns {{ language: string|null, buildTool: string|null, testFramework: string|null, appFramework: string|null, languages: string[], appFrameworks: string[] }}
138
+ */
139
+ export function aggregateTechStacks(subProjects) {
140
+ const languages = new Set();
141
+ const buildTools = new Set();
142
+ const testFrameworks = new Set();
143
+ const appFrameworks = new Set();
144
+
145
+ for (const { techStack } of subProjects) {
146
+ if (techStack.language) languages.add(techStack.language);
147
+ if (techStack.buildTool) buildTools.add(techStack.buildTool);
148
+ if (techStack.testFramework) testFrameworks.add(techStack.testFramework);
149
+ if (techStack.appFramework) appFrameworks.add(techStack.appFramework);
150
+ }
151
+
152
+ return {
153
+ // 主技术栈(第一个子项目)
154
+ language: subProjects[0]?.techStack.language || null,
155
+ buildTool: subProjects[0]?.techStack.buildTool || null,
156
+ testFramework: subProjects[0]?.techStack.testFramework || null,
157
+ appFramework: subProjects[0]?.techStack.appFramework || null,
158
+ // 汇总列表
159
+ languages: [...languages],
160
+ appFrameworks: [...appFrameworks],
161
+ };
162
+ }
163
+
164
+ /**
165
+ * 为工作空间选择 conventions 模板
166
+ * @param {{ languages: string[], appFrameworks: string[] }} aggregatedTechStack
167
+ * @returns {string[]} 模板文件名列表
168
+ */
169
+ export function selectTemplatesForWorkspace(aggregatedTechStack) {
170
+ const templates = [];
171
+
172
+ // Java 相关
173
+ if (aggregatedTechStack.languages.includes('java')) {
174
+ if (existsSync(join(GLAF4_TEMPLATE_DIR, 'java-testing.md'))) {
175
+ templates.push('glaf4-compliant/java-testing.md');
176
+ }
177
+ if (aggregatedTechStack.appFrameworks.includes('spring-boot')) {
178
+ if (existsSync(join(GLAF4_TEMPLATE_DIR, 'spring-patterns.md'))) {
179
+ templates.push('glaf4-compliant/spring-patterns.md');
180
+ }
181
+ }
182
+ }
183
+
184
+ // JavaScript 相关
185
+ if (aggregatedTechStack.languages.includes('javascript')) {
186
+ templates.push('js-testing.md');
187
+ }
188
+
189
+ // Python 相关
190
+ if (aggregatedTechStack.languages.includes('python')) {
191
+ templates.push('python-testing.md');
192
+ }
193
+
194
+ return templates;
195
+ }
196
+
101
197
  // ── 模板选择 ─────────────────────────────────────────────────────────────
102
198
 
103
199
  /**
@@ -290,54 +386,120 @@ export async function run(args) {
290
386
  process.exit(0);
291
387
  }
292
388
 
293
- // 检测技术栈
294
- let techStack = detectTechStack(root);
389
+ // 检测是否为工作空间(包含多个子项目)
390
+ const subProjects = scanSubProjects(root);
295
391
 
296
- // 如果未检测到技术栈且启用交互式模式
297
- if (!techStack.language && values.interactive) {
298
- techStack = await interactiveSetup();
299
- }
392
+ if (subProjects.length > 1) {
393
+ // 工作空间模式:扫描所有子项目
394
+ console.log(`\n=== 检测到工作空间(${subProjects.length} 个子项目)===\n`);
300
395
 
301
- // 如果仍未检测到技术栈
302
- if (!techStack.language) {
303
- console.error('❌ 未检测到技术栈特征');
304
- console.error(' 请指定项目根目录,或使用 --interactive 模式手动配置');
305
- process.exit(1);
306
- }
396
+ for (const { name, techStack } of subProjects) {
397
+ console.log(` - ${name}: ${techStack.language}`);
398
+ }
307
399
 
308
- // 选择模板
309
- const templates = selectTemplates(techStack);
400
+ // 汇总技术栈
401
+ const aggregatedTechStack = aggregateTechStacks(subProjects);
310
402
 
311
- if (templates.length === 0) {
312
- console.error('❌ 未找到匹配的 conventions 模板');
313
- console.error(` 技术栈: ${JSON.stringify(techStack)}`);
314
- process.exit(1);
315
- }
403
+ // 选择模板
404
+ const templates = selectTemplatesForWorkspace(aggregatedTechStack);
405
+
406
+ if (templates.length === 0) {
407
+ console.error('❌ 未找到匹配的 conventions 模板');
408
+ console.error(` 检测到的语言: ${aggregatedTechStack.languages.join(', ')}`);
409
+ process.exit(1);
410
+ }
411
+
412
+ // 生成 conventions
413
+ const result = generateConventions(root, aggregatedTechStack, templates);
414
+
415
+ // 更新配置
416
+ updateConfig(root, result.created);
417
+
418
+ // 输出结果
419
+ if (values.json) {
420
+ console.log(JSON.stringify({
421
+ ok: true,
422
+ mode: 'workspace',
423
+ subProjects: subProjects.map(p => ({ name: p.name, language: p.techStack.language })),
424
+ aggregatedTechStack: {
425
+ languages: aggregatedTechStack.languages,
426
+ appFrameworks: aggregatedTechStack.appFrameworks,
427
+ },
428
+ templates,
429
+ created: result.created,
430
+ skipped: result.skipped,
431
+ conventionsDir,
432
+ }));
433
+ } else {
434
+ console.log('\n✅ 工作空间级 conventions 生成完成');
435
+ console.log(` 检测到的语言:${aggregatedTechStack.languages.join(', ')}`);
436
+ console.log(` 创建:${result.created.join(', ')}`);
437
+ if (result.skipped.length > 0) {
438
+ console.log(` 跳过:${result.skipped.join(', ')}`);
439
+ }
440
+ console.log(` 路径:${conventionsDir}`);
441
+ }
316
442
 
317
- // 生成 conventions
318
- const result = generateConventions(root, techStack, templates);
319
-
320
- // 更新配置
321
- updateConfig(root, result.created);
322
-
323
- // 输出结果
324
- if (values.json) {
325
- console.log(JSON.stringify({
326
- ok: true,
327
- techStack,
328
- templates,
329
- created: result.created,
330
- skipped: result.skipped,
331
- conventionsDir,
332
- }));
333
443
  } else {
334
- console.log('✅ conventions 生成完成');
335
- console.log(` 技术栈: ${techStack.language} + ${techStack.testFramework}`);
336
- console.log(` 创建: ${result.created.join(', ')}`);
337
- if (result.skipped.length > 0) {
338
- console.log(` 跳过: ${result.skipped.join(', ')}`);
444
+ // 单项目模式
445
+ let techStack;
446
+
447
+ if (subProjects.length === 1) {
448
+ // 从子项目检测
449
+ techStack = subProjects[0].techStack;
450
+ console.log(`\n=== 检测到单项目:${subProjects[0].name} ===\n`);
451
+ } else {
452
+ // 从根目录检测
453
+ techStack = detectTechStack(root);
454
+ }
455
+
456
+ // 如果未检测到技术栈且启用交互式模式
457
+ if (!techStack.language && values.interactive) {
458
+ techStack = await interactiveSetup();
459
+ }
460
+
461
+ // 如果仍未检测到技术栈
462
+ if (!techStack.language) {
463
+ console.error('❌ 未检测到技术栈特征');
464
+ console.error(' 请指定项目根目录,或使用 --interactive 模式手动配置');
465
+ process.exit(1);
466
+ }
467
+
468
+ // 选择模板
469
+ const templates = selectTemplates(techStack);
470
+
471
+ if (templates.length === 0) {
472
+ console.error('❌ 未找到匹配的 conventions 模板');
473
+ console.error(` 技术栈: ${JSON.stringify(techStack)}`);
474
+ process.exit(1);
475
+ }
476
+
477
+ // 生成 conventions
478
+ const result = generateConventions(root, techStack, templates);
479
+
480
+ // 更新配置
481
+ updateConfig(root, result.created);
482
+
483
+ // 输出结果
484
+ if (values.json) {
485
+ console.log(JSON.stringify({
486
+ ok: true,
487
+ mode: 'single',
488
+ techStack,
489
+ templates,
490
+ created: result.created,
491
+ skipped: result.skipped,
492
+ conventionsDir,
493
+ }));
494
+ } else {
495
+ console.log('✅ conventions 生成完成');
496
+ console.log(` 技术栈: ${techStack.language} + ${techStack.testFramework}`);
497
+ console.log(` 创建: ${result.created.join(', ')}`);
498
+ if (result.skipped.length > 0) {
499
+ console.log(` 跳过: ${result.skipped.join(', ')}`);
500
+ }
501
+ console.log(` 路径: ${conventionsDir}`);
339
502
  }
340
- console.log(` 路径: ${conventionsDir}`);
341
503
  }
342
504
  }
343
505
 
@@ -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 evidence is scoped to exactly one plan revision. Removing it on
57
- // revision prevents an old wave ID from satisfying a changed plan.
58
- rmSync(paths.reviews, { recursive: true, force: true });
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
- failures.push('execution plan is stale: artifacts hash mismatch');
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
- failures.push('execution plan is stale: contract hash mismatch');
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().catch(err => {
538
- console.error('test-merge error:', err.message);
539
- process.exit(1);
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
+ }
@@ -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