@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.
- package/.claude/always/phase-guard.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/marketplace.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +2 -2
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +40 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +2 -2
- package/README.md +3 -3
- package/docs/README_en.md +1 -1
- package/docs/solutions/INDEX.md +0 -14
- 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/guard/checks/compound-captured.mjs +6 -3
- package/scripts/lib/cmd-runtime.mjs +1 -0
- package/scripts/lib/execution-plan.mjs +72 -25
- package/scripts/lib/execution-recommendation.mjs +4 -18
- package/scripts/lib/hash.mjs +41 -2
- package/scripts/lib/prototype-sync.mjs +2 -1
- package/scripts/lib/sdd-overlay.mjs +3 -2
- package/scripts/lib/solutions-capture.mjs +2 -1
- package/scripts/lib/solutions-index-gen.mjs +2 -1
- package/scripts/lib/solutions-inject.mjs +2 -1
- package/scripts/lib/solutions-promote.mjs +90 -12
- package/scripts/lib/test-record.mjs +67 -21
- package/skills/build-executor/SKILL.md +8 -2
- package/skills/ce-compound/references/promotion-rules.md +1 -1
- package/skills/ce-compound/references/schema.yaml +10 -1
- package/skills/contract-builder/references/glaf4-delegation.md +5 -3
- package/skills/release-archivist/SKILL.md +7 -8
- package/skills/release-archivist/references/closing-procedures.md +7 -2
- package/skills/workflow-start/SKILL.md +2 -1
- package/templates/learnings.md +51 -0
- package/docs/solutions/cross-phase/2026-07-28-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-07-29-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-07-30-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-07-31-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-01-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-03-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-04-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-05-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-06-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-07-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-17-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-18-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-19-no-summary.md +0 -17
- package/docs/solutions/cross-phase/2026-08-21-no-summary.md +0 -17
|
@@ -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.
|
|
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.
|
|
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": {
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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>/
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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>/
|
|
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
|
-
|
|
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.
|
|
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)
|
package/docs/solutions/INDEX.md
CHANGED
|
@@ -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 |
|
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) + business-analysis (independent requirement/scenario artifact). 26 skills, one install.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.49.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.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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
'
|
|
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
|
}
|
|
@@ -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
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
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.
|
|
186
|
-
*
|
|
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?.
|
|
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
|
-
|
|
520
|
-
|
|
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 {
|
|
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
|
-
|
|
209
|
-
return
|
|
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) {
|
package/scripts/lib/hash.mjs
CHANGED
|
@@ -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]
|
|
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
|
-
|
|
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]
|
|
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('--')) {
|