@xulthekl/team-flow 0.48.0 → 0.49.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.
Files changed (53) hide show
  1. package/.claude/always/phase-guard.md +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/.cursor-plugin/marketplace.json +1 -1
  6. package/.cursor-plugin/plugin.json +1 -1
  7. package/.github/plugin/marketplace.json +2 -2
  8. package/AGENTS.md +2 -2
  9. package/CHANGELOG.md +40 -0
  10. package/GEMINI.md +1 -1
  11. package/INSTALL.md +2 -2
  12. package/README.md +3 -3
  13. package/docs/README_en.md +1 -1
  14. package/docs/solutions/INDEX.md +0 -14
  15. package/gemini-extension.json +1 -1
  16. package/hooks/session-start +2 -2
  17. package/llms.txt +1 -1
  18. package/package.json +1 -1
  19. package/plugin.json +1 -1
  20. package/scripts/guard/checks/compound-captured.mjs +6 -3
  21. package/scripts/lib/cmd-runtime.mjs +1 -0
  22. package/scripts/lib/execution-plan.mjs +72 -25
  23. package/scripts/lib/execution-recommendation.mjs +4 -18
  24. package/scripts/lib/hash.mjs +41 -2
  25. package/scripts/lib/prototype-sync.mjs +2 -1
  26. package/scripts/lib/sdd-overlay.mjs +3 -2
  27. package/scripts/lib/solutions-capture.mjs +2 -1
  28. package/scripts/lib/solutions-index-gen.mjs +2 -1
  29. package/scripts/lib/solutions-inject.mjs +2 -1
  30. package/scripts/lib/solutions-promote.mjs +90 -12
  31. package/scripts/lib/test-record.mjs +67 -21
  32. package/skills/build-executor/SKILL.md +8 -2
  33. package/skills/ce-compound/references/promotion-rules.md +1 -1
  34. package/skills/ce-compound/references/schema.yaml +10 -1
  35. package/skills/contract-builder/references/glaf4-delegation.md +5 -3
  36. package/skills/release-archivist/SKILL.md +7 -8
  37. package/skills/release-archivist/references/closing-procedures.md +7 -2
  38. package/skills/workflow-start/SKILL.md +2 -1
  39. package/templates/learnings.md +51 -0
  40. package/docs/solutions/cross-phase/2026-07-28-no-summary.md +0 -17
  41. package/docs/solutions/cross-phase/2026-07-29-no-summary.md +0 -17
  42. package/docs/solutions/cross-phase/2026-07-30-no-summary.md +0 -17
  43. package/docs/solutions/cross-phase/2026-07-31-no-summary.md +0 -17
  44. package/docs/solutions/cross-phase/2026-08-01-no-summary.md +0 -17
  45. package/docs/solutions/cross-phase/2026-08-03-no-summary.md +0 -17
  46. package/docs/solutions/cross-phase/2026-08-04-no-summary.md +0 -17
  47. package/docs/solutions/cross-phase/2026-08-05-no-summary.md +0 -17
  48. package/docs/solutions/cross-phase/2026-08-06-no-summary.md +0 -17
  49. package/docs/solutions/cross-phase/2026-08-07-no-summary.md +0 -17
  50. package/docs/solutions/cross-phase/2026-08-17-no-summary.md +0 -17
  51. package/docs/solutions/cross-phase/2026-08-18-no-summary.md +0 -17
  52. package/docs/solutions/cross-phase/2026-08-19-no-summary.md +0 -17
  53. package/docs/solutions/cross-phase/2026-08-21-no-summary.md +0 -17
@@ -1,3 +1,3 @@
1
- # team-flow v0.48.0 | 阶段: {{state}} | 工作流: {{workflow}}
1
+ # team-flow v0.49.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 + independent business analysis. 26 skills + 17 agents with embedded TDD, SDD, code review, debugging, delta spec sync, and design-system-driven prototyping.",
12
- "version": "0.48.0",
12
+ "version": "0.49.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.48.0",
3
+ "version": "0.49.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) + business-analysis (independent requirement/scenario artifact). 26 skills + 17 agents, one install.",
5
5
  "source": "./",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-flow",
3
- "version": "0.48.0",
3
+ "version": "0.49.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.48.0"
8
+ "version": "0.49.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) + business-analysis (independent requirement/scenario artifact). 26 skills + 17 agents, one install.",
5
- "version": "0.48.0",
5
+ "version": "0.49.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.48.0"
9
+ "version": "0.49.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, independent business analysis, and local HTML prototyping.",
15
- "version": "0.48.0",
15
+ "version": "0.49.0",
16
16
  "source": ".",
17
17
  "author": {
18
18
  "name": "LT",
package/AGENTS.md CHANGED
@@ -112,8 +112,8 @@ docs/
112
112
  ├── INDEX.md # L1 轻量索引(≤150行,每条一行摘要+标签)
113
113
  ├── requirement/ plan/ architecture/ prototype/ spec/ build/ review/ cross-phase/ # L2 分阶段目录(v0.36.0 加 architecture)
114
114
  └── <file>.md # L3 经验文件(YAML frontmatter: phase/domain/type/severity)
115
- changes/<name>/ 变更脚手架(S4 创建):.team-flow.yaml(变更级状态文件)/ change-brief.md(产品级交接物,含 upstream_arch_ref)/ architecture/(变更级三件套 + sql/)
116
- specs/<cap>/ 每变更设计/任务/契约:spec.md / design.md / tasks.md / execution-contract.md / learnings.md(v0.9 architecture/ 三件套已迁至 changes/<name>/architecture/)
115
+ changes/<name>/ 变更脚手架(S4 创建):.team-flow.yaml(变更级状态文件)/ change-brief.md(产品级交接物,含 upstream_arch_ref)/ proposal.md / design.md / tasks.md / execution-contract.md / learnings.md(变更级经验台账)/ architecture/(变更级三件套 + sql/)
116
+ specs/<cap>/ 每变更规格:spec.md(v0.49.0 §83.3.5:learnings.md change 根,不在此目录)
117
117
  STRATEGY.md CONCEPTS.md 产品策略(BA) / 领域词汇
118
118
  ```
119
119
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,46 @@ All notable changes to `team-flow` will be documented in this file.
4
4
 
5
5
  The format loosely follows Keep a Changelog.
6
6
 
7
+ ## [0.49.0] - 2026-09-09
8
+
9
+ ### Fixed(execution plan 冻结边界与复利链路修复)
10
+
11
+ 设计来源:`docs/architecture-api-db-design-enhancement-v0.21.md` §83(2026-09-09,fytx-gateway-parent 4 条 workflow-feedback:093305 / 093306 / 093307 / 093308)
12
+
13
+ **根因簇 A — execution plan 冻结边界错配**(3 个落点,构成闭合不可用回路)
14
+
15
+ - **A1 hash 范围修正**:`normalizeCheckboxes` 归一化 tasks.md 勾选状态后再入 `artifacts_hash` / `computeTaskHash` —— 勾选是执行进度而非规划内容。修复「勾选 → plan 过期 → recordReview 抛错 → build-executor 无法勾选 → closing 死锁」(`hash.mjs`、`sdd-overlay.mjs`)
16
+ - **A2 receipt 判据改 wave 定义指纹**:`recordReview` 写 `wave_fingerprint`(id/strategy/tasks/depends_on),`readCurrentReview` 以其为判据,`plan_hash`/`plan_revision` 降级为审计信息(存量 receipt 回退旧判据)。修复 revise / refresh-hash 后历史 receipt 全部失效且无补录路径;`describeWaves` 新增 `stale_receipt` 诊断(`execution-plan.mjs`)
17
+ - **A3 refresh-hash 同步 recommendation_receipt**:同步 receipt 的 artifacts_hash/contract_hash 并重算 receipt.hash —— 修复 refresh-hash 在其目标场景下自失败(`execution-plan.mjs`);`stableJson`/`hashObject` 抽到 `hash.mjs` 共享
18
+
19
+ **根因簇 B — 复利链路断裂**(6 个子项)
20
+
21
+ - **B1 消费通道**:build-executor Required Inputs 增加 `tf solutions inject --phase cross-phase`(标注为经验提示而非约束)
22
+ - **B2 路径统一**:5 处文档从 `specs/<cap>/learnings.md` 更正为 change 根 `learnings.md`
23
+ - **B3 契约分层**:`schema.yaml` 声明两条产出通道(ce-compound 手工 / promote 自动晋升)与共同消费面 INDEX.md
24
+ - **B4 静默丢弃可见化**:promote 输出「晋升 / 确认 / 跳过」汇总 + 逐条跳过原因
25
+ - **B5 promote 实现修复**:产出文件单 frontmatter 块(原双块,并补回 `## ` 标题前缀);重复命中真正落盘(INDEX severity 升级 + 条目 `confirmed` 计数)
26
+ - **B6 通道错配修正**:`compound-captured` guard 提示不再指向 `tf solutions capture`(它写全局目录、不创建 learnings.md);release-archivist 明确 learnings.md 写入方;新增 `templates/learnings.md`
27
+
28
+ **根因簇 C — 解析器不对称**
29
+
30
+ - **pytest junit XML 支持**:抽出通用 `parseJunitXml`(maven-surefire 与 pytest 共用);`parsePytest` 增加 XML 分支;`detectRunner` XML 优先嗅探;错误信息附输入形态指引(`test-record.mjs`)
31
+
32
+ **协议层**
33
+
34
+ - build-executor wave 完成协议增加「回写 tasks.md 勾选」;workflow-start 主动串行编排把勾选作为进下一波前置;Artifact Ownership 澄清 tasks.md 勾选状态归属
35
+ - `glaf4-delegation.md` §8 closing 时序协议同步更新
36
+
37
+ **横展修复**(P3 审查发现的既有缺陷)
38
+
39
+ - **CLI 入口守卫**:5 处 `process.argv[1]?.includes(...)` 改为 `import.meta.url === pathToFileURL(process.argv[1]).href` —— 原写法使测试文件(如 `solutions-capture.test.mjs`)在 import 时触发 CLI 执行,污染真实 `docs/solutions/`(`solutions-capture/inject/index-gen/promote.mjs`、`prototype-sync.mjs`)
40
+ - **测试隔离**:`ensure-branch.test.mjs` 的 `origin.git` 从固定 `tmpdir()` 路径改为独立随机目录 —— 原写法跨运行残留导致 push 被拒、before 钩子失败、`npm test` exit 1
41
+ - 清理 `docs/solutions/` 中 14 个历史测试污染条目
42
+
43
+ ### Tests
44
+
45
+ - 新增 18 个用例(A1 勾选归一化、A2 指纹判据与 stale 诊断、A3 refresh-hash 同步、pytest XML、promote 可见化与落盘);`npm test` 742/742,`npm run test:e2e` 68/68
46
+
7
47
  ## [0.48.0] - 2026-08-21
8
48
 
9
49
  ### Added(多仓库场景支持,v0.48.0)
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.48.0 | 阶段: {{state}} | 工作流: {{workflow}}
11
+ # team-flow v0.49.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.48.0**。
10
+ 当前发布版本:**v0.49.0**。
11
11
 
12
12
  ---
13
13
 
@@ -761,7 +761,7 @@ tf execution review changes/my-change --wave foundation --base <sha> --head <sha
761
761
  report 本身必须为普通、非空、非符号链接文件。
762
762
 
763
763
  每一个 wave 均须有当前 `pass` review receipt,才可启动依赖 wave 或进入 closing;
764
- 修订计划会废止旧 receipt。#47 所提出的恢复、切换与手动保存 slash command 尚未实现,
764
+ receipt 绑定该 wave 的定义(v0.49.0 起):wave 定义未变时,修订计划或刷新 hash 不会废止它。#47 所提出的恢复、切换与手动保存 slash command 尚未实现,
765
765
  不能假定有 `/tf:*` 命令。
766
766
 
767
767
  Delta spec 的规范路径是 `specs/<capability>/spec.md`。扁平的 `specs/<capability>.md` 和根级 `specs/spec.md` 都不会被当作合法规范静默通过。
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # team-flow
2
2
 
3
- > 当前版本:`v0.48.0`
3
+ > 当前版本:`v0.49.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
 
@@ -66,7 +66,7 @@ prototype/ 全局原型(UI 契约真相源)
66
66
  docs/
67
67
  ├── architecture/ 全局架构三层(v0.36.0):ARCHITECTURE.md / PHYSICAL-MODEL.md / DATABASE.md / API-INDEX.md / INDEX.md / domains/<bc>.md / iterations/vN/(产品级快照)/ baseline.md
68
68
  └── solutions/ 复利经验库(三层索引:INDEX.md → 分阶段目录 → 经验文件)
69
- specs/<cap>/ 每变更设计/任务/契约 + learnings.md
69
+ specs/<cap>/ 每变更规格:spec.md(learnings.md 归 change 根目录)
70
70
  STRATEGY.md CONCEPTS.md 策略 / 领域词汇
71
71
  ```
72
72
 
@@ -155,7 +155,7 @@ tf execution review changes/my-change --wave foundation --base <sha> --head <sha
155
155
  report 本身必须为普通、非空、非符号链接文件。
156
156
 
157
157
  每一个 wave 均须有当前 `pass` review receipt,才可启动依赖 wave 或进入 closing;
158
- 修订计划会废止旧 receipt。#47 所提出的恢复、切换与手动保存 slash command 尚未实现,
158
+ receipt 绑定该 wave 的定义(v0.49.0 起):wave 定义未变时,修订计划或刷新 hash 不会废止它。#47 所提出的恢复、切换与手动保存 slash command 尚未实现,
159
159
  不能假定有 `/tf:*` 命令。
160
160
 
161
161
  ## OpenAI Codex CLI 安装与升级
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.48.0`
129
+ - Current: `v0.49.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,17 +2,3 @@
2
2
  <!-- 每条一行,按 severity 降序,≤150 行硬上限 -->
3
3
  | date | phase | domain | type | severity | summary | file |
4
4
  |------|-------|--------|------|----------|---------|------|
5
- | 2026-08-21 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-21-no-summary.md |
6
- | 2026-08-19 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-19-no-summary.md |
7
- | 2026-08-18 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-18-no-summary.md |
8
- | 2026-08-17 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-17-no-summary.md |
9
- | 2026-08-07 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-07-no-summary.md |
10
- | 2026-08-06 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-06-no-summary.md |
11
- | 2026-08-05 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-05-no-summary.md |
12
- | 2026-08-04 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-04-no-summary.md |
13
- | 2026-08-03 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-03-no-summary.md |
14
- | 2026-08-01 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-08-01-no-summary.md |
15
- | 2026-07-31 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-07-31-no-summary.md |
16
- | 2026-07-30 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-07-30-no-summary.md |
17
- | 2026-07-29 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-07-29-no-summary.md |
18
- | 2026-07-28 | cross-phase | general | insight | medium | (no summary) | cross-phase/2026-07-28-no-summary.md |
@@ -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) + business-analysis (independent requirement/scenario artifact). 26 skills, one install.",
4
- "version": "0.48.0",
4
+ "version": "0.49.0",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bash
2
- # v0.48.0: auto-sync CLI version with plugin version
2
+ # v0.49.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.48.0"
8
+ PLUGIN_VERSION="0.49.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.48.0.
6
+ Current version: v0.49.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.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "Unified plugin (26 skills + 17 agents) integrating team-flow, compound-engineering, architecture-design, prototype, design-system, workflow-orchestrator, workflow-bootstrap, e2e, session-handoff, workflow-feedback, business-analysis 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.48.0",
3
+ "version": "0.49.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) + business-analysis (independent requirement/scenario artifact). 26 skills + 17 agents, one install.",
5
5
  "author": {
6
6
  "name": "LT"
@@ -61,10 +61,13 @@ export function checkCompoundCaptured(changeDir) {
61
61
  }
62
62
 
63
63
  // 3. 未捕获且未豁免 → fail
64
+ // v0.49.0 §83.3.5:修正通道错配——原提示指向 `tf solutions capture`,但该命令写的是
65
+ // 全局 docs/solutions/,不会创建 learnings.md(用户照做后 guard 仍 FAIL)。
64
66
  failures.push(
65
- 'Compound capture missing: no learnings.md found in change directory. ' +
66
- 'Run `tf solutions capture --phase review --domain <d> --type insight --severity medium --summary "..."` ' +
67
- 'to document session learnings, or `tf state set compound_skipped true` to explicitly skip.'
67
+ "Compound capture missing: no learnings.md found in change directory. "
68
+ + "Write this change's session learnings to <change-dir>/learnings.md (format: templates/learnings.md) during closing, "
69
+ + 'or `tf state set compound_skipped true` to explicitly skip. '
70
+ + 'Note: `tf solutions capture` writes to global docs/solutions/, not to learnings.md.'
68
71
  );
69
72
  return { pass: false, failures };
70
73
  }
@@ -15,6 +15,7 @@ const ASSETS = new Set([
15
15
  'skills/build-executor/task-reviewer-prompt.md',
16
16
  'templates/design.md',
17
17
  'templates/execution-contract.md',
18
+ 'templates/learnings.md',
18
19
  'templates/proposal.md',
19
20
  'templates/spec.md',
20
21
  'templates/tasks.md',
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from 'node:crypto';
2
2
  import { execFileSync } from 'node:child_process';
3
3
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
4
4
  import { isAbsolute, join, relative, resolve, sep } from 'node:path';
5
- import { computeArtifactsHash, computeContractHash } from './hash.mjs';
5
+ import { computeArtifactsHash, computeContractHash, hashObject, stableJson } from './hash.mjs';
6
6
  import { getOverlayPaths } from './sdd-overlay.mjs';
7
7
  import { readState } from './state-loader.mjs';
8
8
  import { detectWorkspaceRoot, findSubRepo } from './git-utils.mjs';
@@ -55,11 +55,11 @@ export function writePlan(changeDir, plan) {
55
55
  mkdirSync(paths.root, { recursive: true });
56
56
  const previousPlan = readPlan(changeDir);
57
57
  if (previousPlan && (previousPlan.revision !== plan.revision || previousPlan.hash !== plan.hash)) {
58
- // v0.35.0: Review receipts are RETAINED across revisions — readCurrentReview
59
- // validates plan_hash/plan_revision matching, so stale receipts are
60
- // automatically invalidated without losing evidence (base/head/report SHA).
61
- // This prevents legitimate artifact updates (e.g., design.md 横展结论) from
62
- // causing cascading receipt loss. See workflow-feedback 20260805.
58
+ // v0.35.0: Review receipts are RETAINED across revisions.
59
+ // v0.49.0 §83.3.2: 有效性判据改为 wave 定义指纹 —— receipt 只在该 wave 的定义
60
+ // 变化时失效,与 plan revision/artifacts_hash 无关(历史 receipt 因此可跨
61
+ // revise/refresh-hash 继续生效);无 wave_fingerprint 的存量 receipt 回退旧判据。
62
+ // See workflow-feedback 20260805 / 20260909.
63
63
  //
64
64
  // Recommendation receipt is revision-scoped and must be cleaned:
65
65
  const recommendationFile = paths.executionRecommendation;
@@ -171,6 +171,8 @@ export function recordReview(changeDir, waveId, receipt, repoPath) {
171
171
  // Persist repo path when SHA was resolved in a sub-repository, so
172
172
  // readCurrentReview can re-validate without re-scanning.
173
173
  ...(subRepoPath ? { repo: relative(changeDir, subRepoPath) } : {}),
174
+ // v0.49.0 §83.3.2:主判据 —— wave 定义指纹;plan_hash/plan_revision 降级为审计信息
175
+ wave_fingerprint: computeWaveFingerprint(wave),
174
176
  plan_hash: plan.hash,
175
177
  plan_revision: plan.revision,
176
178
  recorded_at: new Date().toISOString(),
@@ -182,16 +184,25 @@ export function recordReview(changeDir, waveId, receipt, repoPath) {
182
184
  }
183
185
 
184
186
  /**
185
- * Returns the current plan's receipt for one wave. Receipts from a previous
186
- * revision/hash are never evidence for the current plan.
187
+ * Returns the current plan's receipt for one wave.
188
+ *
189
+ * v0.49.0 §83.3.2:有效性判据为 **wave 定义指纹**(id/strategy/tasks/depends_on)——
190
+ * receipt 只在该 wave 的定义变化时失效,与 plan 的 revision/artifacts_hash 无关。
191
+ * 无 wave_fingerprint 的存量 receipt(v0.48.0 及以前)回退旧的 plan 全内容判据。
187
192
  */
188
193
  export function readCurrentReview(changeDir, waveId, plan = readPlan(changeDir)) {
189
194
  if (!plan) return null;
195
+ const wave = Array.isArray(plan.waves) ? plan.waves.find(candidate => candidate?.id === waveId) : null;
196
+ if (!wave) return null;
190
197
  const filePath = join(getOverlayPaths(changeDir).reviews, `${safeFileName(waveId)}.json`);
191
198
  if (!existsSync(filePath)) return null;
192
199
  try {
193
200
  const receipt = JSON.parse(readFileSync(filePath, 'utf8'));
194
- if (receipt?.plan_hash !== plan.hash || receipt?.plan_revision !== plan.revision) return null;
201
+ if (receipt?.wave_fingerprint !== undefined) {
202
+ if (receipt.wave_fingerprint !== computeWaveFingerprint(wave)) return null;
203
+ } else if (receipt?.plan_hash !== plan.hash || receipt?.plan_revision !== plan.revision) {
204
+ return null;
205
+ }
195
206
  // When the receipt records a repo path (sub-repo scenario), use it for
196
207
  // SHA re-validation. Otherwise fall back to the default main-repo path.
197
208
  const repoPath = receipt?.repo ? resolve(changeDir, receipt.repo) : undefined;
@@ -218,6 +229,8 @@ export function describeWaves(changeDir, plan = readPlan(changeDir)) {
218
229
  const receipt = readCurrentReview(changeDir, wave.id, plan);
219
230
  const blockers = blockedDependencies(changeDir, plan, wave);
220
231
  const retryable = receipt?.status === 'fail';
232
+ // v0.49.0 §83.3.2:receipt 文件存在但未被采纳时给出审计线索(来源:workflow-feedback 20260909)
233
+ const staleReceipt = receipt === null ? readStaleReview(changeDir, wave.id) : null;
221
234
  return {
222
235
  id: wave.id,
223
236
  strategy: wave.strategy,
@@ -226,11 +239,32 @@ export function describeWaves(changeDir, plan = readPlan(changeDir)) {
226
239
  eligible: (receipt === null || retryable) && blockers.length === 0,
227
240
  retryable,
228
241
  receipt,
242
+ ...(staleReceipt ? { stale_receipt: staleReceipt } : {}),
229
243
  blockers,
230
244
  };
231
245
  });
232
246
  }
233
247
 
248
+ /**
249
+ * v0.49.0 §83.3.2:诊断信息 —— receipt 文件存在但因判据失配而未被采纳时返回审计线索。
250
+ * 不参与任何门禁判定(门禁只看 readCurrentReview)。
251
+ */
252
+ function readStaleReview(changeDir, waveId) {
253
+ const filePath = join(getOverlayPaths(changeDir).reviews, `${safeFileName(waveId)}.json`);
254
+ if (!existsSync(filePath)) return null;
255
+ try {
256
+ const receipt = JSON.parse(readFileSync(filePath, 'utf8'));
257
+ return {
258
+ status: receipt?.status ?? null,
259
+ plan_revision: receipt?.plan_revision ?? null,
260
+ recorded_at: receipt?.recorded_at ?? null,
261
+ reason: 'the wave definition changed after this receipt was recorded',
262
+ };
263
+ } catch {
264
+ return null;
265
+ }
266
+ }
267
+
234
268
  function validateReviewReportEvidence(changeDir, report) {
235
269
  requireText(report, 'receipt.report');
236
270
  if (/[\p{Cc}\p{Zl}\p{Zp}]/u.test(report)) {
@@ -516,13 +550,35 @@ function hasDependencyCycle(waves) {
516
550
  }
517
551
 
518
552
  function hashPlan(plan) {
519
- const { hash, ...content } = plan;
520
- return `sha256:${createHash('sha256').update(stableJson(content)).digest('hex')}`;
553
+ // v0.49.0 §83.3.3:stableJson/hashObject 抽到 hash.mjs 共享(原为本文件与
554
+ // execution-recommendation.mjs 各一份副本)
555
+ return hashObject(plan);
556
+ }
557
+
558
+ /**
559
+ * v0.49.0 §83.3.2(来源:workflow-feedback 2026-09-09):wave 定义指纹 —— review receipt 的有效性判据。
560
+ *
561
+ * receipt 证明的是"该 wave 的实现在 base..head 间完成并已审查",其语义依赖仅为该 wave 的定义
562
+ * (id/strategy/tasks/depends_on)+ git 事实 + 审查报告,**不含** plan 的其余内容
563
+ * (rationale / artifacts_hash / contract_hash / revision / recommendation_receipt)。
564
+ * 绑定 plan 全内容会使任何与 wave 无关的变更(refresh-hash 刷新制品 hash、revise 升号)
565
+ * 误杀全部历史 receipt,且无补录路径。
566
+ */
567
+ function computeWaveFingerprint(wave) {
568
+ return `sha256:${createHash('sha256').update(stableJson({
569
+ id: wave?.id,
570
+ strategy: wave?.strategy,
571
+ tasks: wave?.tasks,
572
+ depends_on: wave?.depends_on,
573
+ })).digest('hex')}`;
521
574
  }
522
575
 
523
576
  /**
524
577
  * v0.35.0: Refresh plan's artifacts_hash and contract_hash without bumping revision.
525
578
  * Returns the updated plan (already persisted via writePlan), or null if no change needed.
579
+ *
580
+ * v0.49.0 §83.3.3:同步刷新内嵌 recommendation_receipt 的两个 hash(两者语义恒等),
581
+ * 否则 refresh-hash 之后 validatePlan 立即 FAIL(来源:workflow-feedback 20260909)。
526
582
  */
527
583
  export function refreshPlanHash(changeDir) {
528
584
  const plan = readPlan(changeDir);
@@ -534,6 +590,11 @@ export function refreshPlanHash(changeDir) {
534
590
  }
535
591
  plan.artifacts_hash = newArtifactsHash;
536
592
  plan.contract_hash = newContractHash;
593
+ if (plan.recommendation_receipt) {
594
+ plan.recommendation_receipt.artifacts_hash = newArtifactsHash;
595
+ plan.recommendation_receipt.contract_hash = newContractHash;
596
+ plan.recommendation_receipt.hash = hashObject(plan.recommendation_receipt);
597
+ }
537
598
  plan.hash = hashPlan(plan);
538
599
  return writePlan(changeDir, plan);
539
600
  }
@@ -547,20 +608,6 @@ function tryHashPlan(plan) {
547
608
  }
548
609
  }
549
610
 
550
- function stableJson(value, seen = new WeakSet()) {
551
- if (value === null || typeof value !== 'object') return JSON.stringify(value);
552
- if (seen.has(value)) throw new Error('circular plan data');
553
- seen.add(value);
554
- if (Array.isArray(value)) {
555
- const result = `[${value.map(item => stableJson(item, seen)).join(',')}]`;
556
- seen.delete(value);
557
- return result;
558
- }
559
- const result = `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${stableJson(value[key], seen)}`).join(',')}}`;
560
- seen.delete(value);
561
- return result;
562
- }
563
-
564
611
  function writeExecutionPlanSummary(changeDir, plan) {
565
612
  const statePath = join(changeDir, '.team-flow.yaml');
566
613
  const state = readState(changeDir);
@@ -1,10 +1,10 @@
1
1
  // Evidence-based execution-mode recommendation for DP-4.
2
2
 
3
- import { createHash, randomUUID } from 'node:crypto';
3
+ import { randomUUID } from 'node:crypto';
4
4
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
6
  import { loadConfig } from './config-loader.mjs';
7
- import { computeArtifactsHash, computeContractHash } from './hash.mjs';
7
+ import { computeArtifactsHash, computeContractHash, hashObject, stableJson } from './hash.mjs';
8
8
  import { getOverlayPaths } from './sdd-overlay.mjs';
9
9
  import { readState } from './state-loader.mjs';
10
10
  import { EXECUTION_MODES } from './execution-plan.mjs';
@@ -205,22 +205,8 @@ function normalizeWaves(waves) {
205
205
  }
206
206
 
207
207
  function hashReceipt(receipt) {
208
- const { hash, ...content } = receipt || {};
209
- return `sha256:${createHash('sha256').update(stableJson(content)).digest('hex')}`;
210
- }
211
-
212
- function stableJson(value, seen = new WeakSet()) {
213
- if (value === null || typeof value !== 'object') return JSON.stringify(value);
214
- if (seen.has(value)) throw new Error('circular recommendation data');
215
- seen.add(value);
216
- if (Array.isArray(value)) {
217
- const result = `[${value.map(item => stableJson(item, seen)).join(',')}]`;
218
- seen.delete(value);
219
- return result;
220
- }
221
- const result = `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${stableJson(value[key], seen)}`).join(',')}}`;
222
- seen.delete(value);
223
- return result;
208
+ // v0.49.0 §83.3.3:stableJson/hashObject 抽到 hash.mjs 共享
209
+ return hashObject(receipt);
224
210
  }
225
211
 
226
212
  function atomicWrite(targetPath, content) {
@@ -4,6 +4,45 @@ import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { findCanonicalSpecFiles } from './spec-paths.mjs';
6
6
 
7
+ /**
8
+ * Normalize tasks.md checkbox state: `- [x]` / `- [X]` → `- [ ]`.
9
+ *
10
+ * v0.49.0 §83.3.1(来源:workflow-feedback 2026-09-09):tasks.md 同时是规划制品
11
+ * (artifacts_hash 的冻结对象)与执行台账(tasks-complete guard 要求执行期回写勾选)。
12
+ * 勾选状态属执行进度、不属规划内容 —— 归一化后入 hash,使"执行期勾选"不再使 plan
13
+ * 过期(否则勾选后 recordReview 因 plan stale 抛错,形成 closing 死锁)。
14
+ * 任务文本、数量与结构的变更仍会改变 hash。
15
+ */
16
+ export function normalizeCheckboxes(content) {
17
+ return content.replace(/^([ \t]*- )\[[xX]\]/gm, '$1[ ]');
18
+ }
19
+
20
+ /**
21
+ * 稳定序列化:对象键排序后递归序列化,保证同一内容产生同一字符串。
22
+ *
23
+ * v0.49.0 §83.3.3:从 execution-plan.mjs / execution-recommendation.mjs 抽取共享
24
+ * (原为两份实现),供 plan hash 与 recommendation receipt hash 共用。
25
+ */
26
+ export function stableJson(value, seen = new WeakSet()) {
27
+ if (value === null || typeof value !== 'object') return JSON.stringify(value);
28
+ if (seen.has(value)) throw new Error('circular data');
29
+ seen.add(value);
30
+ if (Array.isArray(value)) {
31
+ const result = `[${value.map(item => stableJson(item, seen)).join(',')}]`;
32
+ seen.delete(value);
33
+ return result;
34
+ }
35
+ const result = `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${stableJson(value[key], seen)}`).join(',')}}`;
36
+ seen.delete(value);
37
+ return result;
38
+ }
39
+
40
+ /** 通用对象 hash:剔除顶层 `hash` 字段后取 sha256。 */
41
+ export function hashObject(value) {
42
+ const { hash, ...content } = value ?? {};
43
+ return `sha256:${crypto.createHash('sha256').update(stableJson(content)).digest('hex')}`;
44
+ }
45
+
7
46
  // Compute a joint SHA256 hash of all 4 planning artifacts.
8
47
  // Input: proposal.md + specs/*/spec.md (sorted) + design.md + tasks.md
9
48
  export function computeArtifactsHash(changeDir) {
@@ -30,10 +69,10 @@ export function computeArtifactsHash(changeDir) {
30
69
  hasContent = true;
31
70
  }
32
71
 
33
- // tasks.md
72
+ // tasks.md(v0.49.0 §83.3.1:勾选状态归一化后入 hash —— 执行进度不属规划内容)
34
73
  const tasks = path.join(changeDir, 'tasks.md');
35
74
  if (fs.existsSync(tasks)) {
36
- hash.update(fs.readFileSync(tasks, 'utf-8'));
75
+ hash.update(normalizeCheckboxes(fs.readFileSync(tasks, 'utf-8')));
37
76
  hasContent = true;
38
77
  }
39
78
 
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { readFileSync, writeFileSync, existsSync, cpSync, mkdirSync } from 'node:fs';
18
18
  import { join, basename, relative, sep, resolve } from 'node:path';
19
+ import { pathToFileURL } from 'node:url';
19
20
 
20
21
  /**
21
22
  * 解析 CLI 参数数组为结构化对象
@@ -216,7 +217,7 @@ export function run(args = {}) {
216
217
  }
217
218
 
218
219
  // 支持直接执行:node prototype-sync.mjs <change-dir> --source <path>
219
- if (process.argv[1]?.includes('prototype-sync')) {
220
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
220
221
  const args = { _: [] };
221
222
  for (let i = 2; i < process.argv.length; i++) {
222
223
  if (process.argv[i].startsWith('--')) {
@@ -3,7 +3,7 @@ import {
3
3
  existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync,
4
4
  } from 'node:fs';
5
5
  import { dirname, join } from 'node:path';
6
- import { computeArtifactsHash } from './hash.mjs';
6
+ import { computeArtifactsHash, normalizeCheckboxes } from './hash.mjs';
7
7
  import { readState } from './state-loader.mjs';
8
8
 
9
9
  export const HANDOFF_TYPES = new Set(['prototype', 'research', 'experiment']);
@@ -30,7 +30,8 @@ export function computeTaskHash(changeDir, taskId) {
30
30
  const escaped = taskId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
31
31
  const match = tasks.match(new RegExp(`^- \\[([ xX])\\] ${escaped}\\s+.+$`, 'm'));
32
32
  if (!match) throw new Error(`Task '${taskId}' was not found in tasks.md`);
33
- return `sha256:${createHash('sha256').update(match[0]).digest('hex')}`;
33
+ // v0.49.0 §83.3.1:勾选状态归一化 —— 勾选是执行进度,不应使 checkpoint 变 stale
34
+ return `sha256:${createHash('sha256').update(normalizeCheckboxes(match[0])).digest('hex')}`;
34
35
  }
35
36
 
36
37
  export function saveCheckpoint(changeDir, input) {
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
15
15
  import { join } from 'node:path';
16
+ import { pathToFileURL } from 'node:url';
16
17
 
17
18
  const PHASES = ['prd', 'plan', 'architecture', 'prototype', 'spec', 'build', 'review', 'cross-phase'];
18
19
  const MAX_INDEX_LINES = 150;
@@ -89,7 +90,7 @@ ${summary}
89
90
  return { file: `${phase}/${fileName}`, phase, domain, type, severity };
90
91
  }
91
92
 
92
- if (process.argv[1]?.includes('solutions-capture')) {
93
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
93
94
  const args = {};
94
95
  for (let i = 2; i < process.argv.length; i++) {
95
96
  if (process.argv[i].startsWith('--')) {