Commit 2664a0f8ba8eebf212c792759e064b2054486452

Authored by yanghl
1 parent 13000c55

fix(add-req): new 单元不复位里程碑导致新需求静默不跑;proto 变更收敛到受影响 FE

步骤 5 原本只处理 changed 单元("new 单元无 tag,跳过"),但新单元挂靠的
模块可能已 done:

  routerPrompt 判 done = 里程碑字段匹配 且 tag 存在 → 模块已 done 时 reqs 为
  空数组 → coding.mjs 的 todo = routed.modules.filter(m => !m.done) 把该模块
  整个剔出待跑列表

于是新 REQ 挂进已有模块时不报错、不 halt,就是永远不跑。新 FE 行挂进已打
milestone/frontend-phase 的前端阶段同理。

步骤 5 改为六个显式分支:changed 处理功能级 tag + 里程碑,new 只处理里程碑;
kind=proto(new) 显式声明为基线建立、不作废任何 tag(原先只在步骤 3 旁注里)。

kind=proto(changed) 另收窄重跑范围:__prototype__ 是聚合单元,只能说明"原型变了",
原先据此删全部 req-done/FE-*,改一个原型文件就重跑整个前端阶段。改为先用
git log -1 -- .req-ledger.json 取上次台账 commit 作基线、git diff --name-only
还原到文件级,再经 AskUserQuestion 确认映射到受影响 FE,只删这些 tag;取不到
基线/diff 为空/用户选"说不准"一律回退整体重跑,语义同改动前。

coding.mjs 无需改动——runPrototypePreview / featureLoop / runBehaviorGate
本就按 feItems 伸缩,粗粒度完全来自 add-req 删 tag 的范围。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Showing 1 changed file with 25 additions and 8 deletions
skills/plan/add-req/SKILL.md
1 --- 1 ---
2 name: add-req 2 name: add-req
3 -description: 增量需求入口——初始 Plan 完结后追加 / 修改需求时运行。基于 lib/req-ledger.mjs 内容哈希台账识别新增 / 变更的 REQ 卡片、FE 行与原型快照(prototype/**/*.html),只对增量做 A3-delta(写 V_n migration + 同步 docs/03,绝不改 V1)+ A5-delta(补 docs/05 端点 / docs/02 顺序 / docs/08 模块行),并作废变更单元已有的 req-done/milestone tag(原型变更则整体重跑前端阶段),使 coding.mjs Router 只重跑增量、不必整仓重跑。 3 +description: 增量需求入口——初始 Plan 完结后追加 / 修改需求时运行。基于 lib/req-ledger.mjs 内容哈希台账识别新增 / 变更的 REQ 卡片、FE 行与原型快照(prototype/**/*.html),只对增量做 A3-delta(写 V_n migration + 同步 docs/03,绝不改 V1)+ A5-delta(补 docs/05 端点 / docs/02 顺序 / docs/08 模块行),并作废变更单元已有的 req-done/milestone tag——新增单元同样复位其所属模块/前端阶段的里程碑(否则 Router 判 done 整个跳过),原型变更先按 git diff 收敛到受影响 FE、收敛不了才整体重跑前端阶段,使 coding.mjs Router 只重跑增量、不必整仓重跑。
4 user-invocable: true 4 user-invocable: true
5 allowed-tools: Read Write Edit Grep Glob AskUserQuestion Bash(node *) Bash(git *) Bash(ls *) Bash(mkdir *) 5 allowed-tools: Read Write Edit Grep Glob AskUserQuestion Bash(node *) Bash(git *) Bash(ls *) Bash(mkdir *)
6 --- 6 ---
@@ -102,19 +102,36 @@ node ${CLAUDE_PLUGIN_ROOT}/lib/req-ledger.mjs scan &lt;root&gt; @@ -102,19 +102,36 @@ node ${CLAUDE_PLUGIN_ROOT}/lib/req-ledger.mjs scan &lt;root&gt;
102 102
103 ## 步骤 5:作废变更单元的完成标记(关键——否则 Router 会跳过) 103 ## 步骤 5:作废变更单元的完成标记(关键——否则 Router 会跳过)
104 104
105 -对每个 `changed` 单元(`new` 单元无 tag,跳过): 105 +对每个 `changed` 单元处理**功能级 tag + 所属模块里程碑**;对每个 `new` 单元只处理**所属模块里程碑**(新单元自身无 tag 可删,但它挂靠的模块可能已 done——不复位就会被 Router 整模块跳过):
106 106
107 -- **kind=req**: 107 +- **kind=req(`changed`)**:
108 - `git -C <root> tag -l "req-done/<id>"` 存在 → `git -C <root> tag -d req-done/<id>`。 108 - `git -C <root> tag -l "req-done/<id>"` 存在 → `git -C <root> tag -d req-done/<id>`。
109 - 该 REQ 所属后端模块若已打里程碑(`git -C <root> tag -l "milestone/<module_id>"` 存在)→ `git -C <root> tag -d milestone/<module_id>`,并 `Edit` `docs/08 §二` 该模块 `里程碑:` 字段从 `milestone/<module_id>` 复位为 `—`。(否则 Router 判定模块 done 而整模块跳过,改动不会重跑。) 109 - 该 REQ 所属后端模块若已打里程碑(`git -C <root> tag -l "milestone/<module_id>"` 存在)→ `git -C <root> tag -d milestone/<module_id>`,并 `Edit` `docs/08 §二` 该模块 `里程碑:` 字段从 `milestone/<module_id>` 复位为 `—`。(否则 Router 判定模块 done 而整模块跳过,改动不会重跑。)
110 -- **kind=fe**: 110 +- **kind=req(`new`)**:新 REQ 无 `req-done` tag 可删,但**若步骤 4 把它挂进了已有模块**,必须同样复位该模块里程碑:
  111 + - 从步骤 4 的 docs/08 §二 落点确定所属 `<module_id>`(新建模块的里程碑字段本就是 `—`,跳过本条)。
  112 + - `git -C <root> tag -l "milestone/<module_id>"` 存在 → `git -C <root> tag -d milestone/<module_id>`,并 `Edit` `docs/08 §二` 该模块 `里程碑:` 字段复位为 `—`。
  113 + - **为何必须**:`routerPrompt` 判 `done = 里程碑字段匹配 且 tag 存在`,且「模块已 done → `reqs` 空数组」;`coding.mjs` 的 `todo = routed.modules.filter(m => !m.done)` 又把 done 模块整个剔出待跑列表。漏掉这步 → 新 REQ **不报错、不 halt,就是永远不跑**。
  114 +- **kind=fe(`changed`)**:
111 - `git -C <root> tag -l "req-done/<FE-NN>"` 存在 → 删。 115 - `git -C <root> tag -l "req-done/<FE-NN>"` 存在 → 删。
112 - 前端阶段若已 `milestone/frontend-phase` → 删该 tag,并 `Edit` `docs/08 §三` `整体里程碑:` 复位 `—`。 116 - 前端阶段若已 `milestone/frontend-phase` → 删该 tag,并 `Edit` `docs/08 §三` `整体里程碑:` 复位 `—`。
  117 +- **kind=fe(`new`)**:新 FE 行无 `req-done` tag 可删,但前端阶段若已 `milestone/frontend-phase`,同样要删该 tag 并把 `docs/08 §三` `整体里程碑:` 复位 `—`(否则 Router 判前端阶段 done,新 FE 不跑)。**只删里程碑,不动任何既有 `req-done/FE-*`**——那些 FE 没变,让它们保持 done,Router 的 `feItems` 便只含新 FE。
113 118
114 -- **kind=proto**(`__prototype__`,原型快照变更):原型是整个前端的权威,FE 行与具体原型文件无显式映射,且前端在 Coding 阶段本就是单一 `frontend-phase` 里程碑——故任一原型文件改动按「前端阶段需整体重跑」处理:  
115 - - 删**全部**前端功能完成 tag:`git -C <root> tag -l "req-done/FE-*"` 列出后逐个 `git -C <root> tag -d <tag>`(Router 据 `req-done/<FE-NN>` 缺失把 FE 收回 `frontend-phase`,单删 milestone 不够)。  
116 - - `milestone/frontend-phase` 存在 → `git -C <root> tag -d milestone/frontend-phase`,并 `Edit` `docs/08 §三` `整体里程碑:` 复位 `—`。  
117 - - (若曾开 `frontendOverlap`:同时 `git -C <root> tag -l "fe-code-done/FE-*"` 逐个删,复位 phase1 重叠状态。缺省关时无此 tag,跳过。) 119 +- **kind=proto(`changed`)**(`__prototype__`,原型快照变更):`__prototype__` 是把全部原型 html 聚成的**单一**单元,只能告诉你「原型变了」,不能告诉你变的是哪个文件、影响哪些 FE。故先把范围**还原到文件级再收敛到 FE 级**,收敛不了才整体重跑。
  120 +
  121 + `milestone/frontend-phase` 无论走哪条路径都要删:`git -C <root> tag -l "milestone/frontend-phase"` 存在 → `git -C <root> tag -d milestone/frontend-phase`,并 `Edit` `docs/08 §三` `整体里程碑:` 复位 `—`。(否则 Router 判前端阶段 done,什么都不会跑。)差别只在**删哪些 `req-done/FE-*`**:
  122 +
  123 + 1. **取基线 commit**:`git -C <root> log -1 --format=%H -- .req-ledger.json`(台账由步骤 6 随增量产物一并提交,故它最后一次被改动的 commit 就是上次 add-req 的基线)。无输出(台账从未提交 / 首次建立基线)→ 跳到第 4 条整体重跑。
  124 + 2. **取变更原型文件**:`git -C <root> diff --name-only <基线commit> -- prototype/`。命令失败或结果为空 → 第 4 条整体重跑。
  125 + 3. **映射到 FE 行**:`Read` 这些原型文件 + `docs/08 §三` FE 清单,判定每个变更文件影响哪些 `FE-NN`,然后用 `AskUserQuestion` 把「变更文件 → 受影响 FE」的判定结果交用户确认,并始终提供「说不准,整体重跑」选项。
  126 + - 用户确认了受影响 FE 集合 → **只**删这些 `req-done/FE-<NN>`,其余 FE 保持 done。Router 的 `feItems` 于是只含受影响 FE,`coding.mjs` 的 `runPrototypePreview` / `featureLoop` / `runBehaviorGate` 全部按 `feItems` 伸缩,前端阶段成本随之收敛。
  127 + - 用户选「说不准」→ 第 4 条。
  128 + - **纯新增原型文件**(新界面,对应步骤 4 刚追加的新 FE 行)是最干净的情形:受影响集合 = 新 FE,既有 `req-done/FE-*` **一个都不用删**。
  129 + 4. **回退:整体重跑**(保守路径,语义同本次改动之前)——删**全部**前端功能完成 tag:`git -C <root> tag -l "req-done/FE-*"` 列出后逐个 `git -C <root> tag -d <tag>`(Router 据 `req-done/<FE-NN>` 缺失把 FE 收回 `frontend-phase`,单删 milestone 不够)。
  130 + - (若曾开 `frontendOverlap`:对**上面实际删掉的那些 FE**,同时删对应的 `fe-code-done/FE-<NN>`(整体重跑则 `git -C <root> tag -l "fe-code-done/FE-*"` 全删),复位 phase1 重叠状态。缺省关时无此 tag,跳过。)
  131 +
  132 + > 收窄的是**重跑范围**,不是验收强度:受影响 FE 仍走完整流水线(Preview → featureLoop → Behavior 行为门 → testGate 全量回归)。未受影响的 FE 保持既有 `req-done`,其行为验收证据来自上一轮,且步骤末尾的 `testGate` 本就是**全量**回归,会兜住任何跨 FE 的连带回归。
  133 +
  134 +- **kind=proto(`new`)**:唯一例外,**不作废任何 tag**——`__prototype__` 作为 `new` 出现只发生在老项目首次升级到带原型跟踪的台账时(无历史哈希可比),此为基线建立而非真实变更,见步骤 3 的说明。
118 135
119 逐条记录已删的 tag,供步骤 6 横幅汇总。 136 逐条记录已删的 tag,供步骤 6 横幅汇总。
120 137