@xulthekl/team-flow 0.56.1 → 0.57.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 +17 -11
- package/CHANGELOG.md +85 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/docs/README_en.md +1 -1
- package/docs/solutions/INDEX.md +3 -3
- package/docs/usage-guide.md +1 -1
- package/gemini-extension.json +1 -1
- package/hooks/session-start +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/lib/cmd-doctor.mjs +140 -1
- package/scripts/lib/cmd-solutions.mjs +14 -4
- package/scripts/lib/md-normalize.mjs +39 -0
- package/scripts/lib/solutions-backfill.mjs +116 -0
- package/scripts/lib/solutions-capture.mjs +67 -20
- package/scripts/lib/solutions-entry.mjs +185 -0
- package/scripts/lib/solutions-index-gen.mjs +117 -57
- package/scripts/lib/solutions-inject.mjs +102 -24
- package/scripts/lib/solutions-promote.mjs +147 -95
- package/scripts/lib/test-merge.mjs +73 -12
- package/scripts/team-flow.mjs +7 -3
- package/skills/architecture-design/SKILL.md +2 -2
- package/skills/architecture-design/references/s3.5-product-architecture.md +1 -1
- package/skills/build-executor/SKILL.md +5 -1
- package/skills/ce-brainstorm/references/grounding.md +2 -2
- package/skills/ce-compound/references/promotion-rules.md +26 -9
- package/skills/ce-compound/references/schema.yaml +4 -2
- package/skills/ce-compound/references/three-tier-index.md +10 -7
- package/skills/ce-compound/references/write-flow.md +22 -10
- package/skills/ce-ideate/references/agents/learnings-researcher.md +9 -2
- package/skills/ce-ideate/references/grounding.md +1 -1
- package/skills/ce-plan/references/agents/learnings-researcher.md +9 -2
- package/skills/ce-plan/references/research-workflow.md +2 -2
- package/skills/code-reviewer/SKILL.md +7 -0
- package/skills/code-reviewer/code-reviewer-prompt.md +6 -0
- package/skills/contract-builder/SKILL.md +9 -0
- package/skills/release-archivist/SKILL.md +2 -2
- package/skills/release-archivist/references/closing-procedures.md +3 -1
- package/skills/spec-writer/SKILL.md +1 -1
- package/skills/workflow-orchestrator/SKILL.md +2 -2
- package/skills/workflow-orchestrator/references/s1-path-router.md +4 -2
- package/skills/workflow-orchestrator/references/s3-plan-pipeline.md +1 -1
- package/templates/learnings.md +17 -5
|
@@ -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.57.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.57.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) + design-system (design tokens + component contract + AI primer + showcase) + test-strategy (test strategy design) + project-initialize (new service onboarding). 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.57.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.57.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.57.0",
|
|
16
16
|
"source": ".",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "LT",
|
package/AGENTS.md
CHANGED
|
@@ -109,9 +109,9 @@ prototype/ 全局原型系统(UI 契约真相源):index.html /
|
|
|
109
109
|
docs/
|
|
110
110
|
├── architecture/ 全局架构三层(v0.36.0):L1 当前态(ARCHITECTURE.md marker 区 / PHYSICAL-MODEL.md / DATABASE.md / API-INDEX.md / INDEX.md / domains/<bc>.md / diagrams/ / schema-baseline.sql / baseline.md)+ L2 changelog/ + L3 iterations/vN/architecture.md(产品级快照,archived 退役)
|
|
111
111
|
└── solutions/ 复利经验库(v0.5 新增,三层索引)
|
|
112
|
-
├── INDEX.md # L1 轻量索引(≤150
|
|
112
|
+
├── INDEX.md # L1 轻量索引(≤150 条,每条一行摘要+标签)
|
|
113
113
|
├── prd/ plan/ architecture/ prototype/ spec/ build/ review/ cross-phase/ # L2 分阶段目录(枚举权威:scripts/lib/solutions-phases.mjs)
|
|
114
|
-
└── <file>.md # L3 经验文件(YAML frontmatter: phase/domain/type/severity)
|
|
114
|
+
└── <file>.md # L3 经验文件(YAML frontmatter: phase/domain/type/severity/date/source/title/confirmations)
|
|
115
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
116
|
specs/<cap>/ 每变更规格:spec.md(v0.49.0 §83.3.5:learnings.md 归 change 根,不在此目录)
|
|
117
117
|
STRATEGY.md CONCEPTS.md 产品策略(BA) / 领域词汇
|
|
@@ -134,7 +134,7 @@ STRATEGY.md CONCEPTS.md 产品策略(BA) / 领域词汇
|
|
|
134
134
|
产品级编排层(workflow-orchestrator,v0.7 重设计 + v0.15.0 多需求)
|
|
135
135
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
136
136
|
S1 路径路由器 → 需求选择(.team-flow/registry.yaml)+ 判断入口路径
|
|
137
|
-
检查 baseline.md / CONCEPTS.md → 注入上下文 +
|
|
137
|
+
检查 baseline.md / CONCEPTS.md → 注入上下文 + 复利注入(默认 top-5,调用点显式 `--limit`)
|
|
138
138
|
S2 PRD + 原型阶段 → ce-brainstorm 产出 PRD
|
|
139
139
|
冻结前 PRD 完整性评审(prd-completeness-reviewer,v0.15.0)
|
|
140
140
|
原型循环由 orchestrator 直接编排(prototype skill 内部编排器,主代理只编排):
|
|
@@ -252,26 +252,32 @@ STRATEGY.md CONCEPTS.md 产品策略(BA) / 领域词汇
|
|
|
252
252
|
|
|
253
253
|
```
|
|
254
254
|
docs/solutions/
|
|
255
|
-
├── INDEX.md # L1:轻量索引(≤150
|
|
255
|
+
├── INDEX.md # L1:轻量索引(≤150 条,每条一行摘要+标签;排序 severity → date → file)
|
|
256
256
|
├── prd/ plan/ architecture/ prototype/ spec/ build/ review/ cross-phase/ # L2:分阶段目录(枚举权威:scripts/lib/solutions-phases.mjs)
|
|
257
|
-
└── <date>-<
|
|
257
|
+
└── <date>-<slug>.md # L3:经验文件(YAML frontmatter: phase/domain/type/severity/date/source/title/confirmations)
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
-
###
|
|
260
|
+
### 核心动作(五)
|
|
261
261
|
|
|
262
262
|
| 动作 | 触发时机 | CLI |
|
|
263
263
|
|------|---------|-----|
|
|
264
|
-
| **注入** |
|
|
264
|
+
| **注入** | 进入每阶段时,自动加载相关经验(默认 top-5,**调用方须显式 `--limit`**) | `tf solutions inject --phase <p> --domain <d> --limit <n>` |
|
|
265
265
|
| **捕获** | 阶段转换点检测到可复利时刻 | `tf solutions capture --phase <p> --domain <d> --type <t> --severity <s> --summary "<text>"` |
|
|
266
266
|
| **索引重建** | INDEX.md 损坏或手动触发 | `tf solutions index-gen` |
|
|
267
267
|
| **晋升** | change closing 时,经验验证后晋升为产品级 | `tf solutions promote <change-dir>` |
|
|
268
|
+
| **补字段** | 存量条目缺 `title:` 时(learnings-researcher 按它检索) | `tf solutions backfill [--dry-run]` |
|
|
269
|
+
|
|
270
|
+
> **注入语义(v0.57.0)**:`--phase` 的 `cross-phase` 是**通配符**——任何 phase 查询都会一并返回它,
|
|
271
|
+
> 故 `--phase prd` 在无 prd 条目的项目上注入的就是 cross-phase 那批。默认 top-5 在 cross-phase
|
|
272
|
+
> 条目已达 5 条时**满载**,本阶段新增条目(低 severity)永不可达,故存在调用点时必须显式传 `--limit`。
|
|
268
273
|
|
|
269
274
|
### 关键约束
|
|
270
275
|
|
|
271
|
-
- 所有复利操作为 **advisory
|
|
272
|
-
- INDEX.md 硬上限 150
|
|
273
|
-
- 晋升条件:severity ≥ medium 且 type = pitfall/pattern
|
|
274
|
-
-
|
|
276
|
+
- 所有复利操作为 **advisory 级**:INDEX 缺失/读取失败时 CLI 输出一行 WARN 并返回空结果,**不阻断流程**(v0.57.0 前为完全静默,无人可见"注入其实没发生")
|
|
277
|
+
- INDEX.md 硬上限 **150 条**(`MAX_INDEX_ENTRIES`,非行数),超限时按 **severity → date → file** 三级序保留(末键保证同日同 severity 的去留在跨机器上可复现);被淘汰的文件保留在阶段目录中且**逐条 WARN 列出**(v0.57.0 前为静默)
|
|
278
|
+
- 晋升条件:severity ≥ medium 且 type = pitfall/pattern。**命中既有条目(文件名 + 正文签名都相同)**则登记来源 change 到 `confirmations` 并按其升级 severity(`low → medium → high → critical`,新来源才升一档、重跑不升)——**不合并正文**(v0.57.0 废止 `domain+type` 合并:旧实现只把计数 +1 而丢弃正文,实测丢 13 条)。同名但正文不同的经验是**两条独立条目**,碰撞时文件名加 `-2`/`-3` 后缀
|
|
279
|
+
- INDEX.md 是**派生物**,唯一写入者是 `refreshIndex()`(capture/promote 内部调用)——**禁止手工编辑 INDEX**
|
|
280
|
+
- 上下文占用控制:最坏 ~6.5k token(L1 索引全量加载);注入时**同源条目**(同 `source`)折叠为 1 条 + 标注条数,避免一次事故的多个面占满窗口
|
|
275
281
|
|
|
276
282
|
## 配置注入(插件层扩展字段)
|
|
277
283
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,91 @@ 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.57.0] - 2026-09-12
|
|
8
|
+
|
|
9
|
+
### Changed(复利机制 P1 重构;**含 4 项行为变更,升级必读**)
|
|
10
|
+
|
|
11
|
+
设计来源:`docs/plan/compound-lifecycle-governance-design.md` **v2.3**(P1.5 完整档**三轮**对抗验证:37 + 32 + 11 = **80 项问题全部采纳**,三轮 Critical 全闭合;P4 双门禁评审(plugin-validator + skill-reviewer)**均判 FAIL**,据此另修 **12 项**(5 Critical),见该文档附录 C.4)。
|
|
12
|
+
|
|
13
|
+
**① 判重语义:废止 `domain+type` 合并(D1-B,最高影响)**
|
|
14
|
+
|
|
15
|
+
旧实现命中同 `domain+type` 即"合并",只把 `confirmed` 计数 +1 而**正文丢弃**——emp-auth 实测 **13 条内容丢失**(仅存于 change 的 `learnings.md`,其中含 2 条 critical 因旧 bug 从未晋升)。
|
|
16
|
+
|
|
17
|
+
- **改为**:每条经验**独立成条**,内容零丢失;跨 change 的"复发"信号改由 `confirmations: [change-id…]` 来源集合承载
|
|
18
|
+
- **幂等由「文件名 + 正文签名」保证**:同 change 重复 promote 记为 `unchanged`(旧实现每次重跑都 +1 confirmed 并升一档 severity,实测连跑 3 次即把 `medium` 推到 `critical`)
|
|
19
|
+
- ⚠️ **P4 修正**:初版仅用文件名作身份键,`cleanTitle` 剥离序号后 `## 3. 契约漂移` 与 `## 7. 契约漂移` 产出同一 slug ⇒ 第二条被判"已登记"直接跳过 ⇒ **正文与 severity 整条消失**(隔离复现证实,本版新引入的回归)。现身份 = 标题分量(**清洗后**,因序号是 change 局部编号)+ 内容分量(**不清洗**,区分同标题经验),碰撞时探测 `-2`/`-3` 后缀
|
|
20
|
+
- **行为变更**:**同标题但正文不同**的经验现在各自成条(旧版会静默丢弃后一条);同 change 重跑仍为 `unchanged`,但跨 change 的确认需"标题与正文都相同"——触发窗口比初版更窄(零丢失优先于复发信号)
|
|
21
|
+
|
|
22
|
+
**② INDEX 写入权收归单一入口**
|
|
23
|
+
|
|
24
|
+
旧实现有 **3 个 writer** 互相回滚:`promote` 只升 INDEX 行的 severity、`capture` 自建表头 + append、`index-gen` 从零重建(且**无任何流程调用者**)。实测:先 promote 再 index-gen 会把 `critical` **打回 `high`**(3/20 条)、`summary` 被改写(14/20 条)。
|
|
25
|
+
|
|
26
|
+
- **改为**:`refreshIndex()` 为唯一写入者;`capture`/`promote` 写完条目文件后调用它(**入口内建**,不靠调用点枚举——两者各有多个落盘分支,逐处追加必然漏改)
|
|
27
|
+
- `severity` 与 `confirmations` 的**真相源落条目文件 frontmatter**(唯一不被重建抹掉的持久层)
|
|
28
|
+
- **否决**"重建时取 `max(旧 INDEX, 文件)`"——那会让 INDEX 成为并列真值源,把漂移固化为**不可降档**的不变量
|
|
29
|
+
- 目录缺失时 `refreshIndex` **创建**而非 `exit 1`(旧 `index-gen` 会直接失败,中断首次晋升链)
|
|
30
|
+
- **行为变更**:手工编辑 `INDEX.md` 会在下次捕获/晋升时被覆盖(`skills/ce-compound/references/write-flow.md` 已同步)
|
|
31
|
+
|
|
32
|
+
**③ `tf test-merge` 的模块边界改为多段白名单**
|
|
33
|
+
|
|
34
|
+
`extractModuleSections` 的 docstring 写「`## Cases` 下的 `###` 子段」,**实现却是全文扫描所有 `### `**。emp-auth 实测 v1-C1 的 21 个 `###` 中 **12 个是附录/台账段**(`### 执行结果(三仓实测…)`、`### Task 5.x`、`### W6-1 / W6-2 收口`),生成 440–520B 的无复用价值 baseline。
|
|
35
|
+
|
|
36
|
+
- **改为**:白名单 = `## Cases` + **`## E2E / AC Verification`**(后者是 v0.38.0 引入 E2E 层级时的遗漏——只列 `## Cases` 会让 `vrm4teamflow` 的 `### AC1..AC8`(**32 个 AC/E2E 用例**)**静默冻结**:落选文件不会从 INDEX 消失,因 `rewriteIndex` 扫目录 ⇒ 既无报错也无计数变化)
|
|
37
|
+
- 精确段名比较(`## Adversarial Cases` 含子串 `Cases`,用 `includes` 会误判)
|
|
38
|
+
- 无白名单段时**回退全文扫描 + WARN**(旧实现静默打印 `0 modules processed`)
|
|
39
|
+
- 导出 `extractModuleSections` / `MODULE_SECTIONS` 并补**真实 test-matrix 形态**测试(原 fixture 全是内联 `### Foo` 块,无法捕获本类缺陷)
|
|
40
|
+
- **行为变更**:升级后新 `###` 若位于白名单段之外,**不再生成 baseline**;既有文件不受影响(不删除)
|
|
41
|
+
|
|
42
|
+
**④ `tf solutions inject` 的排序与容量**
|
|
43
|
+
|
|
44
|
+
- 排序补两级次级键:**阶段匹配度**(本阶段优先于通配的 `cross-phase`)→ **date 降序**(新条目优先)。旧实现仅 `severityRank` 单键,同级顺序取决于 INDEX 行序 ⇒ 旧条目永久压制新条目
|
|
45
|
+
- 新增 `--limit <n>`(默认仍 5)与 `--all`;`--phase build` 与 `--phase cross-phase` 在旧实现下输出**只差表头**(cross-phase 是通配符),故 `build-executor` 改传 `--phase build` 并**删除**重复的 cross-phase 调用;`code-reviewer` 新增 `--phase review`(审查阶段此前无任何消费方)
|
|
46
|
+
- **同源聚类**(P4 补实现):同 `source` 的条目(同一次事故的多个面)折叠为 1 条 + 标注条数,把 top-N 槽位还给其他经验。仅作用于**展示**,不合并内容(与 ① 的 D1-B 一致)。INDEX 加 `source` 列(7→8,消费方对 7/8 列都容错)
|
|
47
|
+
- ⚠️ **P4 修正**:初版只加了参数**没改任何调用点**(4 处全未传 `--limit`)——正是设计 §4.1 预警的"可配但没人配"。现 4 处全补 `--limit 15`,并加机械门禁断言"每个 inject 调用点必带 `--limit`/`--all`"
|
|
48
|
+
- **行为变更**:同一批条目的 top-5 **内容与顺序可能变化**
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- `tf solutions backfill [--dry-run]`:为存量条目补写 `title:` 字段(`learnings-researcher` 按 `title:`/`tags:`/`module:`/`problem_type:` grep,而 CLI 条目此前只有 `phase/domain/type/...` ⇒ 实测四模式在 emp-auth 21 条中**各只命中 1 条**)
|
|
53
|
+
- `tf doctor` 新增 `Solutions` 维度(四类检查):INDEX↔文件漂移、条目数/截断、非白名单目录与平铺文件、未 promote 的 change
|
|
54
|
+
- 共享层新增 `md-normalize.parseTableRow`(**保留空列**的表格行解析)与 `solutions-index-gen.refreshIndex` / `parseFrontmatter` / `MAX_INDEX_ENTRIES`
|
|
55
|
+
- `tests/lib/doc-consistency.test.mjs`(**机械门禁**):把"同口径副本"清单固化为断言——**10 个**已废止口径短语零命中(含历史溯源行豁免)+ "每个 inject 调用点必带 `--limit`/`--all`"+ **正例/负例对照组**(防止断言退化为真空)。动因:本仓"修复未传播"已复发 4 次(v0.38.0 / v0.53.0 / v0.55.0 / v0.57.0 单次改动发现 8 处残留)
|
|
56
|
+
- `scripts/lib/solutions-entry.mjs`(**共享层**):条目身份(正文签名 + 碰撞定位)与 **frontmatter 读取**(含行尾注释剥离)的共享实现,capture / index-gen / cmd-doctor 及 promote 的 `parseLearnings` 共用;`promote.confirmByFile`(需整块替换)与 `backfill`(仅键存在性检测)为**已知例外**,理由见各自注释
|
|
57
|
+
- `solutions-backfill.mjs` 与 3 个测试文件(`solutions-backfill` / `cmd-doctor-solutions` + inject/index-gen/promote/test-merge 的扩充用例)
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- **列解析空列错位**:`filter(Boolean)` 丢空列且守卫只有下界 ⇒ 单元格含 `|` 时列数错位不被拦截。`inject`、`test-merge` 的 `extractCandidateLedger`/`extractDeferredItems` 改用 `parseTableRow` + 上下界校验;`promote` 的那处随判重通道整体删除(它已不再解析 INDEX)。另横展 `test-merge` 内 **4 处**仍在用的 inline 表格切分(`test-merge` 在 v0.38.0 修过同型缺陷,当时未横展到其余各处)
|
|
62
|
+
- **INDEX 单元格转义**(P4):内容含 `|` 会让该行列数膨胀 ⇒ 消费方列数校验丢弃整行 ⇒ 条目**永久隐身**,且 doctor 的修复建议(重跑 index-gen)产出同一坏行 ⇒ **死循环**。生成侧转义 `\|`、解析侧还原,并加 `file` 列语义校验兜住"列数在界内但已错位"
|
|
63
|
+
- **`promote` 幂等判据塌缩**(P4,本版 ① 引入的回归):见 ① 的 P4 修正条
|
|
64
|
+
- **`refreshIndex` 排序不确定**(P4):同日同 severity 条目在 `maxEntries` 边界的去留取决于 `readdirSync` 顺序 ⇒ 补文件名作决定性末键
|
|
65
|
+
- **条目 frontmatter 值不剥行尾注释**(P4):`severity: high # 说明` 会被当作未知值原样返回 ⇒ 该条**永不升级且无提示**
|
|
66
|
+
- **`parseTableRow` 同名冲突**(P4):本仓有 3 份同名不同语义实现(`md-normalize` 永不返回 null / `arch-parse` 与 `ds-parse` 返回 null 且做清洗)⇒ 补差异矩阵注释防止按名误挑
|
|
67
|
+
- **`promote` 缺 phase 白名单校验**(`capture` 一直有):实测接受 `phase: tools` 会建出 `docs/solutions/tools/` 并追加 INDEX 行,随后重建时该目录不被扫描 ⇒ 条目**静默消失且不计入 dropped**
|
|
68
|
+
- **截断由静默改为可见**:`refreshIndex` 超 150 条时 WARN 并逐条列出被丢弃的文件(旧实现只打印一行计数)
|
|
69
|
+
- **索引缺失由静默改为 WARN**:`solutions-inject` 原输出一行 HTML 注释并以 0 退出,无人可见
|
|
70
|
+
- **`CONCEPTS.md` 路径**:`learnings-researcher` 只查仓库根,而 `workflow-bootstrap` B3 产出在 `docs/architecture/` ⇒ grounding 静默落空;改为两处探测
|
|
71
|
+
- `MAX_INDEX_LINES` → `MAX_INDEX_ENTRIES`:该常量约束的是**条目数**(`entries.slice(0, N)`),但全仓 **15 处**文案写作"150 行"(文件另含 4 行头部,封顶时实为 154 行)
|
|
72
|
+
|
|
73
|
+
### 验证
|
|
74
|
+
|
|
75
|
+
- 全量测试 **1199/1199 通过**(基线 1112 项 —— 即 v0.56.1 的测试数,故 +87)
|
|
76
|
+
- **变异验证**(本轮 3 项,均确认新增测试具备判别力):
|
|
77
|
+
- 排序反向 → 「全部 critical 条目须被保留」等 3 条失败
|
|
78
|
+
- 白名单退化为「任意 `##` 段」(等价原全文扫描)→ 4 条边界用例失败,报「附录段「执行结果(三仓实测,命令 + 实际输出)」不得被当作测试模块」
|
|
79
|
+
- 去掉 `resolveBaselinePath` 的 legacy 回退 → v0.56.1 的回归用例失败
|
|
80
|
+
- **[P4]** `escapeCell` 不转义 `|` → index-gen 的管道符用例失败
|
|
81
|
+
- **[P4]** 去掉同源聚类 → inject 的聚类用例失败
|
|
82
|
+
- **[P4]** 身份键退化为单文件名(复现原缺陷)→ 「两条同标题经验都应落盘」报 `1 !== 2`
|
|
83
|
+
- **[P4]** 往 skill 注入肯定式旧口径 / 无 `--limit` 的 inject 命令 → doc-consistency 门禁精确报出行号与豁免提示
|
|
84
|
+
- **P4 双门禁结论(七轮)**:每轮均由独立 agent 隔离复现;**前三轮均判 FAIL**,第四轮 reviewer PASS + validator 另报阻断,**第五~七轮 validator 均 PASS(0 阻断)**——七轮共处置 **52 项**:
|
|
85
|
+
- 第一轮(16 项,含 1 Critical):**promote 身份键塌缩丢条**(本版新引入的回归,已隔离复现)→ 身份改为「标题分量 + 正文签名」;`--limit` 调用点落实数为 0;口径传播未闭合
|
|
86
|
+
- 第二轮(13 项,含 3 Critical):`solutions-capture` **无碰撞保护会静默覆盖**(使 ①「内容零丢失」在 capture 通道为假)→ 抽 `solutions-entry.mjs` 共享层;frontmatter 注释剥离只落在 promote 一条读取路径;两处假闭合;门禁真空断言
|
|
87
|
+
- 第三轮(11 项,含 2 阻断):`frontmatterBlock` 的 `(?:^|\n)` 误吞正文水平线 ⇒ **INDEX 出现幽灵条目**(本轮新引入的回归,已补反例断言);`code-reviewer` 的注入只覆盖 agent 路径、`code-reviewer-prompt.md` 路径零处 inject(假闭合)→ 两条路径都注入 + 新增**正向存在性门禁**
|
|
88
|
+
- 第四/五轮:`frontmatterBlock` 的宽松匹配**三轮修复未触根因**(一轮放宽正则 → 二轮加防线 → 三轮加领域键校验),第四轮**换判据**(严格锚定段首)才闭合——前三轮都在"加防线",真问题是判据查的是"块内有没有领域键"而非"该块是文件自身的 frontmatter";第五轮 validator 判 **PASS(0 阻断)**
|
|
89
|
+
- 第五轮:validator **PASS(0 阻断)**;第六轮另报 1 项本轮引入的新失效面(`tf doctor` 的 Solutions 维度遇非普通文件抛异常 ⇒ **中止整轮巡检**,12 个维度全丢)——已把「条目枚举」抽为共享层 `listSolutionEntries`(判据 + 两层防御),三个消费方(`index-gen` / `cmd-doctor` / `backfill`)共用
|
|
90
|
+
- 逐轮闭合表见设计文档附录 **C.4 – C.10**;由此新增的机械门禁 `tests/lib/doc-consistency.test.mjs` 已含正例/负例对照组、`INJECT_CONSUMERS` 正向存在性与窗口参数断言
|
|
91
|
+
|
|
7
92
|
## [0.56.1] - 2026-09-12
|
|
8
93
|
|
|
9
94
|
### Fixed(v0.56.0 自身引入的回归 + P1.5 第一轮暴露的遗漏;**升级优先于 0.56.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.57.0 | 阶段: {{state}} | 工作流: {{workflow}}
|
|
12
12
|
当前阶段允许的操作由 workflow-start 路由规则定义。
|
|
13
13
|
禁止跨越 DP gate 进入下一阶段。变更范围以 execution-contract.md 的 Intent Lock 为准。
|
|
14
14
|
<!-- team-flow-phase-guard-end -->
|
package/INSTALL.md
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# team-flow
|
|
2
2
|
|
|
3
|
-
> 当前版本:`v0.
|
|
3
|
+
> 当前版本:`v0.57.0`
|
|
4
4
|
|
|
5
5
|
> 统一插件:**team-flow**(spec 驱动开发)+ **compound-engineering 核心子集**(全局复利)+ **architecture-design**(4A+DDD 增量设计)+ **prototype**(本地 HTML 原型)+ **e2e**(AC 驱动 E2E)+ **workflow-orchestrator**(产品级编排)+ **workflow-bootstrap**(既有项目接入)。一次安装,七套能力协同。
|
|
6
6
|
|
package/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.57.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
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# Solutions Index
|
|
2
|
-
<!--
|
|
3
|
-
| date | phase | domain | type | severity | summary | file |
|
|
4
|
-
|
|
2
|
+
<!-- 每条一行,排序 severity 降序 → date 降序 → file 兜底,≤150 条上限;summary/source 内的 | 转义为 \| -->
|
|
3
|
+
| date | phase | domain | type | severity | summary | file | source |
|
|
4
|
+
|------|-------|--------|------|----------|---------|------|--------|
|
package/docs/usage-guide.md
CHANGED
|
@@ -239,7 +239,7 @@ tf arch-merge(架构增量 → docs/architecture/ 全局当前态)
|
|
|
239
239
|
→ tf deisolate --merge(worktree 代码合并回主分支,Step 5c Code Landing)
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
这就是"改动不烂尾、知识不流失"的机制保障。经验沉淀后,下一个 change 的对应阶段会通过 `tf solutions inject`
|
|
242
|
+
这就是"改动不烂尾、知识不流失"的机制保障。经验沉淀后,下一个 change 的对应阶段会通过 `tf solutions inject` 自动注入相关经验(默认 top-5;调用点须显式传 `--limit`,否则 cross-phase 条目满载时本阶段新增条目永不可达)。
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
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.57.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.57.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.57.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.57.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.57.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.57.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) + design-system (design tokens + component contract + AI primer + showcase) + test-strategy (test strategy design) + project-initialize (new service onboarding). 26 skills + 17 agents, one install.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LT"
|
|
@@ -7,6 +7,13 @@ import { PLATFORM_RUNTIME_INVENTORY } from './platform-runtime-inventory.mjs';
|
|
|
7
7
|
import { readState, VALID_STATES } from './state-loader.mjs';
|
|
8
8
|
// v0.22 §85:tasks_skipped 豁免判定复用 guard 侧唯一真相源,避免同一规则两处实现漂移。
|
|
9
9
|
import { hasExplicitTasksSkip } from '../guard/checks/tasks-gate-exemptions.mjs';
|
|
10
|
+
// v0.57.0 §4.4:复利库健康检查(漂移/截断/白名单外布局/未晋升)复用既有共享层
|
|
11
|
+
import { parseTableRow } from './md-normalize.mjs';
|
|
12
|
+
import { parseFrontmatter, MAX_INDEX_ENTRIES } from './solutions-index-gen.mjs';
|
|
13
|
+
// 条目枚举与 index-gen **同源**——含判据(不一致会报出"重跑 index-gen 也消不掉"的告警)
|
|
14
|
+
// 与防御(非普通文件不抛异常;本维度无逐维度隔离,抛出会中止整个 doctor)
|
|
15
|
+
import { listSolutionEntries } from './solutions-entry.mjs';
|
|
16
|
+
import { SOLUTION_PHASES } from './solutions-phases.mjs';
|
|
10
17
|
|
|
11
18
|
const RUNTIME_SKILLS = new Set([
|
|
12
19
|
'workflow-start', 'need-explorer', 'spec-writer', 'contract-builder',
|
|
@@ -332,6 +339,137 @@ function checkArchState(root) {
|
|
|
332
339
|
return { pass: warnings.length === 0, message: warnings.length ? warnings.join('; ') : 'arch-state/iterations 一致' };
|
|
333
340
|
}
|
|
334
341
|
|
|
342
|
+
/**
|
|
343
|
+
* v0.57.0 §4.4:复利库健康检查(设计文档 compound-lifecycle-governance-design v2.2 §5 P1#6)。
|
|
344
|
+
*
|
|
345
|
+
* 四个维度合并为一条 doctor 结论(doctor 每维度输出一行):
|
|
346
|
+
* A. **INDEX ↔ 文件漂移** —— 注入只读 INDEX(`solutions-inject.mjs`),任何失配都会静默影响 top-5。
|
|
347
|
+
* 本项是 §4.2「文件是唯一真相源、INDEX 只做投影」的**防复发**手段。
|
|
348
|
+
* B. **条目数 / 截断** —— `refreshIndex` 收进 capture/promote 后,截断从"从未触发"变为
|
|
349
|
+
* "过 150 即每次静默淘汰",须有可见的临近预警。
|
|
350
|
+
* C. **白名单外目录与平铺文件** —— 不被 `SOLUTION_PHASES` 收录的目录(如 ce-compound 的
|
|
351
|
+
* category 目录、`workflow-patterns/`)与直接放在 `docs/solutions/` 下的条目文件,
|
|
352
|
+
* 对 CLI 通道**永久不可见**(不入 INDEX、不可注入)。
|
|
353
|
+
* D. **未 promote 的 change** —— 有 `learnings.md` 但 solutions 中无任何条目引用它的 change
|
|
354
|
+
* (emp-auth 实测:C1 有 2 条 critical 因旧 bug 从未晋升)。
|
|
355
|
+
*
|
|
356
|
+
* advisory:与 doctor 其余维度一致,不阻断。
|
|
357
|
+
*/
|
|
358
|
+
function checkSolutions(root) {
|
|
359
|
+
const solutionsDir = join(root, 'docs', 'solutions');
|
|
360
|
+
if (!existsSync(solutionsDir)) {
|
|
361
|
+
return { pass: true, message: '未使用复利库(docs/solutions/ 不存在)' };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const problems = [];
|
|
365
|
+
|
|
366
|
+
// 收集条目文件(限白名单 phase 目录)
|
|
367
|
+
const entryFiles = [];
|
|
368
|
+
for (const phase of SOLUTION_PHASES) {
|
|
369
|
+
// v0.57.0 P4 五/六轮:条目枚举走**共享层**——它同时保证 ①与 `index-gen` 同判据
|
|
370
|
+
//(判据不一致会报出"重跑 index-gen 也消不掉"的告警)②**非普通文件不抛异常**。
|
|
371
|
+
// 后者对本维度尤为关键:`checkSolutions` 内联在 `checks` 数组字面量中、**无逐维度隔离**,
|
|
372
|
+
// 一次抛出会中止**整个 doctor**(12 个维度全丢),而它本来只是 advisory 巡检。
|
|
373
|
+
for (const e of listSolutionEntries(join(solutionsDir, phase))) {
|
|
374
|
+
entryFiles.push({ phase, file: e.file, rel: e.rel, content: e.content });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// A. INDEX ↔ 文件漂移
|
|
379
|
+
const indexPath = join(solutionsDir, 'INDEX.md');
|
|
380
|
+
if (existsSync(indexPath)) {
|
|
381
|
+
const indexRows = new Map();
|
|
382
|
+
for (const line of readFileSync(indexPath, 'utf-8').split('\n')) {
|
|
383
|
+
if (!line.startsWith('|') || line.startsWith('| date') || line.startsWith('|--')) continue;
|
|
384
|
+
const cells = parseTableRow(line);
|
|
385
|
+
// v0.57.0 P4:`source` 列新增后列数为 7(存量 INDEX)或 8(本版重建);
|
|
386
|
+
// 另校验 `file` 列形状——列数在界内仍可能因未转义的 `|` 而错位。
|
|
387
|
+
if (cells.length < 7 || cells.length > 8) continue;
|
|
388
|
+
if (!/^[a-z][a-z-]*\/.+\.md$/.test(cells[6])) continue;
|
|
389
|
+
indexRows.set(cells[6], { date: cells[0], phase: cells[1], domain: cells[2], type: cells[3], severity: cells[4] });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const drifts = [];
|
|
393
|
+
const FIELDS = ['date', 'phase', 'domain', 'type', 'severity'];
|
|
394
|
+
for (const [rel, row] of indexRows) {
|
|
395
|
+
const fp = join(solutionsDir, rel);
|
|
396
|
+
if (!existsSync(fp)) { drifts.push(`${rel}(INDEX 有行、文件缺失)`); continue; }
|
|
397
|
+
// 防御同 `listSolutionEntries`(v0.57.0 P4 收尾):INDEX 行指向的路径可能是目录 /
|
|
398
|
+
// 断链 symlink / 无读权限 / **FIFO**。前三类让本维度抛异常,而 `checkSolutions`
|
|
399
|
+
// 内联在 `checks` 字面量中、**无逐维度隔离** ⇒ 会**中止整个 doctor**(12 维度全丢);
|
|
400
|
+
// FIFO 更会让 `readFileSync` **阻塞挂死**(不是异常,try/catch 也挡不住)——故先 `statSync` 判类型。
|
|
401
|
+
// INDEX 由 index-gen 生成、只对可枚举的普通文件出行,故现实实例为 0;但兜底成本是几行。
|
|
402
|
+
let raw;
|
|
403
|
+
try {
|
|
404
|
+
if (!statSync(fp).isFile()) { drifts.push(`${rel}(INDEX 有行、但该路径不是普通文件)`); continue; }
|
|
405
|
+
raw = readFileSync(fp, 'utf-8');
|
|
406
|
+
} catch {
|
|
407
|
+
drifts.push(`${rel}(INDEX 有行、但无法读取)`); continue;
|
|
408
|
+
}
|
|
409
|
+
const fm = parseFrontmatter(raw);
|
|
410
|
+
if (!fm) { drifts.push(`${rel}(无 frontmatter)`); continue; }
|
|
411
|
+
for (const k of FIELDS) {
|
|
412
|
+
if (fm[k] && fm[k] !== row[k]) { drifts.push(`${rel} ${k}: INDEX=${row[k]} 文件=${fm[k]}`); break; }
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const e of entryFiles) {
|
|
416
|
+
if (!indexRows.has(e.rel)) drifts.push(`${e.rel}(文件存在、INDEX 无行)`);
|
|
417
|
+
}
|
|
418
|
+
if (drifts.length > 0) {
|
|
419
|
+
problems.push(`${drifts.length} 处 INDEX/文件漂移(${drifts.slice(0, 3).join(';')}${drifts.length > 3 ? ' …' : ''})→ 运行 tf solutions index-gen 重建`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// B. 条目数 / 截断
|
|
424
|
+
const total = entryFiles.length;
|
|
425
|
+
if (total > MAX_INDEX_ENTRIES) {
|
|
426
|
+
problems.push(`条目数 ${total} 已超索引上限 ${MAX_INDEX_ENTRIES},超出部分不被索引(详见 index-gen 的 dropped 提示)`);
|
|
427
|
+
} else if (total > Math.floor(MAX_INDEX_ENTRIES * 0.8)) {
|
|
428
|
+
problems.push(`条目数 ${total} 接近索引上限 ${MAX_INDEX_ENTRIES}(>80%)`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// C. 白名单外目录与平铺文件
|
|
432
|
+
const strayDirs = readdirSync(solutionsDir, { withFileTypes: true })
|
|
433
|
+
.filter(e => e.isDirectory() && !SOLUTION_PHASES.includes(e.name))
|
|
434
|
+
.map(e => `${e.name}/`);
|
|
435
|
+
const strayFiles = readdirSync(solutionsDir, { withFileTypes: true })
|
|
436
|
+
.filter(e => e.isFile() && e.name.endsWith('.md') && e.name !== 'INDEX.md')
|
|
437
|
+
.map(e => e.name);
|
|
438
|
+
if (strayDirs.length > 0) {
|
|
439
|
+
problems.push(`非白名单目录 ${strayDirs.join(', ')}(不被索引:CLI 通道不可见)`);
|
|
440
|
+
}
|
|
441
|
+
if (strayFiles.length > 0) {
|
|
442
|
+
problems.push(`平铺条目文件 ${strayFiles.join(', ')}(须移入 phase 子目录才可被索引)`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// D. 未 promote 的 change
|
|
446
|
+
const changesDir = join(root, 'changes');
|
|
447
|
+
if (existsSync(changesDir)) {
|
|
448
|
+
const referenced = new Set();
|
|
449
|
+
for (const e of entryFiles) {
|
|
450
|
+
const fm = parseFrontmatter(readFileSync(join(solutionsDir, e.rel), 'utf-8'));
|
|
451
|
+
if (!fm) continue;
|
|
452
|
+
if (fm.source) referenced.add(fm.source.trim());
|
|
453
|
+
const confirmations = fm.confirmations || '';
|
|
454
|
+
for (const c of confirmations.replace(/^\[|\]$/g, '').split(',')) {
|
|
455
|
+
if (c.trim()) referenced.add(c.trim());
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const unpromoted = readdirSync(changesDir, { withFileTypes: true })
|
|
459
|
+
.filter(e => e.isDirectory() && existsSync(join(changesDir, e.name, 'learnings.md')))
|
|
460
|
+
.map(e => e.name)
|
|
461
|
+
.filter(name => !referenced.has(name));
|
|
462
|
+
if (unpromoted.length > 0) {
|
|
463
|
+
problems.push(`${unpromoted.length} 个 change 有 learnings.md 但未被晋升(${unpromoted.slice(0, 3).join(', ')}${unpromoted.length > 3 ? ' …' : ''})`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
pass: problems.length === 0,
|
|
469
|
+
message: problems.length ? problems.join(' | ') : `${total} 条经验,索引与文件一致`,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
335
473
|
export async function run(args) {
|
|
336
474
|
const root = process.cwd();
|
|
337
475
|
const config = loadConfig(root);
|
|
@@ -351,6 +489,7 @@ export async function run(args) {
|
|
|
351
489
|
['Change states', checkChangeStates(root)],
|
|
352
490
|
['Change test gates', checkChangeTestGates(root)],
|
|
353
491
|
['Arch state', checkArchState(root)],
|
|
492
|
+
['Solutions', checkSolutions(root)],
|
|
354
493
|
];
|
|
355
494
|
|
|
356
495
|
// Config check
|
|
@@ -379,4 +518,4 @@ export async function run(args) {
|
|
|
379
518
|
}
|
|
380
519
|
}
|
|
381
520
|
|
|
382
|
-
export { checkVersionConsistency, checkHooks, checkCodexManifest, checkSkills, checkRuntimeDistribution, checkDist, checkRootPluginAuthor, checkNodeVersion, checkDocs, checkChangeStates, checkChangeTestGates };
|
|
521
|
+
export { checkVersionConsistency, checkHooks, checkCodexManifest, checkSkills, checkRuntimeDistribution, checkDist, checkRootPluginAuthor, checkNodeVersion, checkDocs, checkChangeStates, checkChangeTestGates, checkSolutions };
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 设计说明(v0.5 / 2026-07-21):
|
|
4
4
|
// - 与 checkpoint / handoff / execution 一致,采用「单 COMMANDS key + 内部子命令分发」模式。
|
|
5
|
-
// -
|
|
5
|
+
// - 底层脚本 solutions-{index-gen,inject,capture,promote,backfill}.mjs 的 run(args) 接收「对象」参数,
|
|
6
6
|
// 而 CLI 入口传入的是「字符串数组」。本分发器用 parseArgs 把数组解析成各脚本需要的对象后再调用,
|
|
7
7
|
// 完成 数组 → 对象 的桥接。
|
|
8
8
|
import { parseArgs } from 'node:util';
|
|
9
9
|
|
|
10
|
-
const USAGE = 'Usage: tf solutions <index-gen|inject|capture|promote> [options]';
|
|
10
|
+
const USAGE = 'Usage: tf solutions <index-gen|inject|capture|promote|backfill> [options]';
|
|
11
11
|
|
|
12
12
|
export async function run(args) {
|
|
13
13
|
const { positionals, values } = parseArgs({
|
|
@@ -21,6 +21,10 @@ export async function run(args) {
|
|
|
21
21
|
severity: { type: 'string' },
|
|
22
22
|
summary: { type: 'string' },
|
|
23
23
|
source: { type: 'string' },
|
|
24
|
+
// v0.57.0 §4.1:inject 的展示条数控制与 backfill 的预演
|
|
25
|
+
limit: { type: 'string' },
|
|
26
|
+
all: { type: 'boolean' },
|
|
27
|
+
'dry-run': { type: 'boolean' },
|
|
24
28
|
},
|
|
25
29
|
});
|
|
26
30
|
|
|
@@ -35,7 +39,7 @@ export async function run(args) {
|
|
|
35
39
|
|
|
36
40
|
if (subcommand === 'inject') {
|
|
37
41
|
const mod = await import('./solutions-inject.mjs');
|
|
38
|
-
mod.run({ phase: values.phase, domain: values.domain, dir: values.dir });
|
|
42
|
+
mod.run({ phase: values.phase, domain: values.domain, dir: values.dir, limit: values.limit, all: values.all });
|
|
39
43
|
return;
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -61,7 +65,13 @@ export async function run(args) {
|
|
|
61
65
|
return;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
if (subcommand === 'backfill') {
|
|
69
|
+
const mod = await import('./solutions-backfill.mjs');
|
|
70
|
+
mod.run({ dir: values.dir, 'dry-run': values['dry-run'] });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.error(`Unknown solutions subcommand: ${subcommand}. Valid: index-gen, inject, capture, promote, backfill`);
|
|
65
75
|
process.exit(2);
|
|
66
76
|
}
|
|
67
77
|
|
|
@@ -106,3 +106,42 @@ export function parseTaskLine(line) {
|
|
|
106
106
|
if (!m) return null;
|
|
107
107
|
return { indent: m[1].length, checked: m[3].toLowerCase() === 'x', text: m[4] };
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 解析 Markdown 表格行 → 单元格字符串数组(**保留空列**)。
|
|
112
|
+
*
|
|
113
|
+
* v0.57.0 §4.4 抽为共享层。背景:`solutions-inject.mjs` 此前用
|
|
114
|
+
* `split('|').map(c => c.trim()).filter(Boolean)` —— `filter(Boolean)` **会丢空列**,
|
|
115
|
+
* 而守卫只有下界(`cols.length < 7`)⇒ 某单元格内容含 `|` 时列数变多、**不被拦截**,
|
|
116
|
+
* `file` 列取到错误值(与 `test-merge.mjs` 在 v0.38.0 修过的同型缺陷一致,当时未横展)。
|
|
117
|
+
*
|
|
118
|
+
* 约定:
|
|
119
|
+
* - `split('|')` 对 `| a | b |` 产出 `['', ' a ', ' b ', '']`,故取 `slice(1, -1)` 去首尾空串
|
|
120
|
+
* - 支持 `\|` 转义(v0.57.0 P4):生成侧 `escapeCell` 把内容里的 `|` 写成 `\|`,
|
|
121
|
+
* 此处先换占位符再切分,否则转义符形同虚设(`\|` 仍会被 `split` 切开)。
|
|
122
|
+
* 无 `\|` 时与裸 `split('|').slice(1,-1)` **逐格一致**(含行尾缺竖线的非良构行——
|
|
123
|
+
* 两者对 `| a | b | c` 都返回 2 格)。与**本模块替换掉的旧版 `filter(Boolean)` 实现**
|
|
124
|
+
* 的差异才是实质的:旧版无 `slice`、且 `filter(Boolean)` 丢空列 ⇒ 同样的
|
|
125
|
+
* `| a | b | c` 返回 3 格、而 `| a | | c |` 返回 2 格(空列消失导致后续列全部左移)
|
|
126
|
+
* - **不做**列数校验——期望列数由调用方决定(INDEX 为 7/8 列,test-matrix 为 12 列)
|
|
127
|
+
* - 调用方 MUST 同时校验**上界与下界**(只查下界无法发现"多出的 `|` 导致的错位")
|
|
128
|
+
*
|
|
129
|
+
* ⚠ 同名函数差异矩阵(v0.57.0 P4 评审补充,避免按名字误挑):
|
|
130
|
+
* | 位置 | 非法输入 | 清洗 |
|
|
131
|
+
* |---|---|---|
|
|
132
|
+
* | 本函数(md-normalize) | **永不返回 null**,非表格行也产出数组 | 仅 trim + 还原 `\|` |
|
|
133
|
+
* | `arch-parse.mjs` | 返回 `null` | strip 反引号与强调标记 |
|
|
134
|
+
* | `ds-parse.mjs` | 返回 `null` | 走 `normalizeInline` |
|
|
135
|
+
* 本函数是最"裸"的一层 —— 调用方须自行判断行是否合法(如 `startsWith('|')`)。
|
|
136
|
+
*
|
|
137
|
+
* @param {string} line
|
|
138
|
+
* @returns {string[]}
|
|
139
|
+
*/
|
|
140
|
+
export function parseTableRow(line) {
|
|
141
|
+
const PIPE = '\u0001'; // 控制字符占位:正常 markdown 内容不含
|
|
142
|
+
return String(line)
|
|
143
|
+
.replace(/\\\|/g, PIPE)
|
|
144
|
+
.split('|')
|
|
145
|
+
.slice(1, -1)
|
|
146
|
+
.map(cell => cell.trim().replaceAll(PIPE, '|'));
|
|
147
|
+
}
|