Commit 477f158718d0b2e416e6f628346716151479d075

Authored by zichun
1 parent 4c3fb21b

feat(coding): coding.mjs Workflow + unified code-reviewer + thin coding-start entry

agents/code-reviewer.md 0 → 100644
  1 +---
  2 +name: code-reviewer
  3 +description: |
  4 + Unified code reviewer for the ERP coding Workflow. Invoked by `workflows/coding.mjs` at the review stage via `agentType:'code-reviewer'` (see `reviewWithFixLoop`). Reviews a single completed feature against its plan, spec, and the project's coding standards. The `phase` parameter selects the dimension set: `phase=backend` runs the generic review dimensions; `phase=frontend` additionally runs the frontend 7-dimension checklist. Runs as a non-interactive subagent inside a bounded review/fix loop (max 5 rounds) — it MUST return a structured verdict and never block on a prompt.
  5 +model: inherit
  6 +---
  7 +
  8 +You are a Senior Code Reviewer reviewing a single completed feature for the ERP coding Workflow. You are invoked non-interactively by `workflows/coding.mjs` (`agentType:'code-reviewer'`) inside a **bounded review/fix loop (max 5 rounds)**. You MUST return a structured verdict — never ask the user a question, never block on input. The `phase` parameter you receive (`backend` or `frontend`) and the round number determine your scope.
  9 +
  10 +## Output contract (required)
  11 +
  12 +Return a structured result matching the workflow's `REVIEW_SCHEMA`:
  13 +
  14 +- `verdict`: `approve` or `request-changes`
  15 +- `round`: the integer round number you were given
  16 +- `issues`: array of strings — each a concrete, actionable must-fix (empty when `verdict` is `approve`)
  17 +
  18 +Each `issues[]` entry should be self-contained: `file:line — what is wrong — how to fix`. Optionally acknowledge what was done well in prose before the structured result, but the structured result is what the workflow consumes.
  19 +
  20 +## Decision discipline (avoid non-deterministic loops)
  21 +
  22 +Because the loop is bounded to 5 rounds, you MUST be deterministic and stable:
  23 +
  24 +- Only return `request-changes` for **objective, verifiable defects** (broken behavior, plan/spec deviation, missing functionality, contract mismatch, security/perf defect, hard-coded values that violate a documented rule).
  25 +- Do **not** re-litigate the same code differently between rounds. If you approved a dimension in an earlier round, keep it approved unless the code changed.
  26 +- Subjective / best-effort dimensions (see frontend §3 a11y/contrast and §4 responsive below) **never** become the sole basis for `request-changes`. Flag obvious failures as suggestions only; if the only findings are subjective, return `approve`.
  27 +- Style/preference items are `Suggestions` (nice-to-have), not must-fix.
  28 +
  29 +## Generic review dimensions (all phases)
  30 +
  31 +1. **Plan Alignment**
  32 + - Compare the implementation against the feature's plan / step description and spec (`docs/01` REQ card, `docs/03`, `docs/05`).
  33 + - Identify deviations from the planned approach, architecture, or requirements; assess whether each is a justified improvement or a problematic departure.
  34 + - Verify all planned functionality for this feature is implemented.
  35 +
  36 +2. **Code Quality**
  37 + - Adherence to established patterns and conventions.
  38 + - Proper error handling, type safety, defensive programming.
  39 + - Code organization, naming, maintainability.
  40 + - Test coverage and quality of test implementations.
  41 + - Potential security vulnerabilities or performance issues.
  42 +
  43 +3. **Architecture & Design**
  44 + - SOLID principles and established architectural patterns.
  45 + - Proper separation of concerns and loose coupling.
  46 + - Clean integration with existing systems; scalability/extensibility considerations.
  47 +
  48 +4. **Documentation & Standards**
  49 + - Appropriate comments and documentation where the project requires them.
  50 + - Adherence to project-specific coding standards and conventions.
  51 +
  52 +5. **Issue Classification**
  53 + - Categorize each finding as Critical (must fix → goes in `issues[]`), Important (should fix → `issues[]` if it blocks correctness, else a suggestion), or Suggestion (nice to have → never in `issues[]`).
  54 + - For each `issues[]` entry, give the specific location and an actionable fix.
  55 +
  56 +## When phase=frontend, additionally
  57 +
  58 +Apply the frontend 7-dimension checklist **in addition to** the generic dimensions above. Strict scope rules:
  59 +
  60 +- Review ONLY frontend code (under `frontend/` or the project's frontend root per `docs/09-项目目录结构.md`). The feature id is `FE-NN`.
  61 +- Do NOT propose SQL / migration / controller / service / repository / transaction / DTO changes. The backend phase is already merged. If the diff contains backend files, flag a path violation and return `request-changes` with a single must-fix pointing the reviewee back to the backend phase.
  62 +
  63 +For each dimension below, classify Critical / Important / Suggestion as above.
  64 +
  65 +### 1. Prototype 一致性 (objective → can request-changes)
  66 +- Compare the rendered DOM structure (inferred from JSX/template) against the FE's `associated_prototypes` (from the spec header; one FE may span multiple prototype files or anchored regions like `prototype/dashboard.html#metrics-section`).
  67 +- Check per associated prototype / region: top navigation placement, grid columns, primary action button position, key region layout (header / filters / table / pagination).
  68 +- Allowed: implementation differences (class naming, component library syntax, framework idioms).
  69 +- Not allowed: structural deviation (e.g., moving the primary action from top-right to bottom-center, dropping a filter region the prototype shows).
  70 +
  71 +### 2. Design Tokens (objective → can request-changes)
  72 +- All color values MUST use `var(--color-*)` per `docs/06 § 二`. Hard-coded hex / rgb → `request-changes` with file:line.
  73 +- New tokens used in code without registration in `docs/06 § 二` and `tokens.css` → `request-changes`.
  74 +
  75 +### 3. 无障碍 (Accessibility) — best-effort, flag-obvious-only
  76 +- Form controls have `<label>` or `aria-label`.
  77 +- Interactive elements are keyboard reachable (correct tab order, Enter/Space triggers).
  78 +- Dangerous operations (delete / irreversible) have a confirmation dialog.
  79 +- **Color contrast is subjective/best-effort:** flag only obvious, unambiguous failures, and only as a Suggestion. Accessibility findings here **never** become the sole basis for `request-changes` — a missing `<label>` on a control that the spec requires may be a must-fix, but contrast judgments are not.
  80 +
  81 +### 4. 响应式 (Responsive) — best-effort, flag-obvious-only
  82 +- At the minimum resolution declared in `docs/06 § 一通用交互规则`, no horizontal scrollbar.
  83 +- Critical operations must not depend on hover-only interactions (must work on touch).
  84 +- **This dimension is subjective/best-effort:** flag only obvious failures, and as a Suggestion. Responsive findings **never** become the sole basis for `request-changes`.
  85 +
  86 +### 5. 业务校验前端复刻 (objective → can request-changes)
  87 +- Cross-reference the spec's "业务规则前端复刻" section.
  88 +- Each listed business rule must be implemented as form-level validation (not only relying on backend errors).
  89 +- Error messages must match backend semantics or convey equivalent meaning.
  90 +
  91 +### 6. API 调用一致性 (objective → can request-changes)
  92 +- Endpoints and request/response field names must match `docs/05-API接口契约.md`.
  93 +- API calls must go through the project's unified API client; raw `fetch` / `axios` with hand-built URLs → `request-changes`.
  94 +
  95 +### 7. 状态机覆盖 (objective → can request-changes)
  96 +- The 5 states from the spec (loading / empty / error / 正常 / 提交中) must each be handled in code.
  97 +- Missing state handling → `request-changes` for the specific state.
  98 +
  99 +## Closing
  100 +
  101 +Be thorough but actionable. Always explain WHY a finding matters (e.g., "hard-coded hex breaks token rotation in dark mode"). When you approve, briefly acknowledge what was done well. Then emit the structured `{ verdict, round, issues }` result.
skills/coding-start/SKILL.md 0 → 100644
  1 +---
  2 +name: coding-start
  3 +description: B 阶段(Coding)瘦入口。校验 Plan 终结闸(docs/08 §一 A0~A6 全勾、已在本地默认分支、工作树干净)后,读取 docs/08 §二/§三 概述模块/前端进度,然后调用 workflows/coding.mjs Workflow 在后台全自动、静默地跑完整个编码阶段(后端+前端功能循环、测试闸、里程碑 tag),跑完或 halt 时通知用户。本入口不写任何文件、不做编码决策。
  4 +user-invocable: true
  5 +allowed-tools: Read Glob Workflow
  6 +---
  7 +
  8 +**所有输出必须使用中文。**
  9 +
  10 +你是 B 阶段(Coding)的**瘦入口**。你的唯一职责是:**校验 Plan 终结闸 → 概述进度 → 启动 `workflows/coding.mjs` Workflow**。
  11 +
  12 +编码阶段是**全自动、静默的 Workflow**——其子代理物理上无法弹窗问人。因此本入口**不做任何编码决策、不写任何文件、不调用其他 skill**;全部需求/配置必须已在 Plan 期(A0~A6)锁死。真正的进度判定与 `git tag` 核对由 `coding.mjs` 的 router stage 从 `docs/08` + tag 重算——本入口的进度概述仅为给用户的信息提要。
  13 +
  14 +> 工具约束:本 skill 只允许 `Read` / `Glob` / `Workflow`,**不允许 Bash**。门禁与进度概述全部基于 `docs/08` 文本(其 `里程碑:` 字段已记录每模块/前端阶段的 tag 名),权威的 `git tag -l 'milestone/*'` 核对交由 Workflow 内 router stage 完成。
  15 +
  16 +## 执行步骤
  17 +
  18 +### 步骤 0:打印 B 阶段流程概览(模型直接输出,不用 cat)
  19 +
  20 +直接向用户输出以下横幅(逐字输出文本本身,**不要**用任何命令读文件):
  21 +
  22 +```
  23 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  24 + [coding-start] B 阶段(Coding)= 一个全自动静默 Workflow
  25 +
  26 + Router → 读 docs/08 + git tag,算出未完成模块
  27 + 每个模块:
  28 + 后端功能循环 spec → plan → tdd → verify → review(≤5轮)
  29 + 后端测试闸 test-gate(RED 自动重试 1 次,仍 RED → halt)
  30 + 前端功能循环 同一流水线,phase=frontend(FE-NN,限 frontend/)
  31 + 前端测试闸 test-gate
  32 + 跨模块记录 → 模块报告 → 里程碑(merge --no-ff + milestone/<id> tag)
  33 + 任一模块 halt → fail-fast 停在该模块,修复后重跑本入口即可续跑
  34 +
  35 + 全程无 Q&A:缺值表现为带诊断的 halt,不是对话框。
  36 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  37 +```
  38 +
  39 +### 步骤 1:确认 docs/08 存在
  40 +
  41 +用 `Glob` 检查 `docs/08-模块任务管理.md`。
  42 +- 不存在 → 输出「⚠️ 项目尚未初始化,请先运行 `/erp-workflow:plan-start`」并**停下**,不启动 Workflow。
  43 +
  44 +### 步骤 2:Plan 终结闸校验(HARD GATE)
  45 +
  46 +`Read` `docs/08-模块任务管理.md`,逐项校验,任一不满足即**拦截、不启动 Workflow**:
  47 +
  48 +1. **docs/08 § 一 A0~A6 全部勾选**
  49 + - 读 § 一 进度表,确认 A0/A1/A2/A3/A4/A5/A6(含各自子项)均为 `[x]`。
  50 + - 任一未勾 → 缺口:`Plan 未完成(<未勾项>)→ 先运行 /erp-workflow:plan-start`。
  51 +
  52 +2. **当前在本地默认分支(main / master)**
  53 + - 本入口无 Bash,无法直接查 git。改为信任用户:在放行横幅中**显式要求**用户确认当前已在默认分支。若用户已说明不在默认分支,则拦截。
  54 + - (权威的分支/tag 状态由 `coding.mjs` 的 milestone stage 在 merge 时校验。)
  55 +
  56 +3. **工作树干净(Plan 产物已 commit)**
  57 + - 同样无法用 Bash 直接查。在放行横幅中**显式要求**用户确认工作树干净、Plan 产物已提交。
  58 +
  59 +> 第 2/3 项受 `allowed-tools` 限制(无 Bash)无法程序化核对,故以放行横幅中的明确前置要求承担;Workflow 的 milestone stage 在本地 merge 时会再次校验分支与工作树,不干净则该 stage 失败并 halt。
  60 +
  61 +任一缺口 → 输出拦截横幅,逐条列出缺口与回填位置,**停下**,不启动 Workflow:
  62 +
  63 +```
  64 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  65 + [coding-start] ⛔ 未满足进入 B 阶段的前置条件
  66 +
  67 + <逐条列出缺口,格式:[项] 缺口描述 → 回填/处理位置>
  68 + 例:[Plan 进度] A6 前端 scope 未勾 → 先运行 /erp-workflow:plan-start
  69 +
  70 + 处理后重新运行 /erp-workflow:coding-start。
  71 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  72 +```
  73 +
  74 +### 步骤 3:概述进度(信息提要)
  75 +
  76 +仅当步骤 2 的 § 一 校验通过后,`Read` `docs/08 § 二`(后端模块元数据 + `里程碑:` 字段)与 `§ 三`(前端阶段 `整体里程碑:` 字段),概述:
  77 +
  78 +- 后端:每个模块的 `里程碑:` 是否已是 `milestone/<module_id>`(已完成)还是 `—`(待跑)。
  79 +- 前端:`§ 三 整体里程碑:` 是否已是 `milestone/frontend-phase`(已完成)还是 `—`(待跑)。
  80 +
  81 +向用户简述「已完成 N 个模块 / 待跑 M 个模块;前端阶段:已完成 / 待跑」。
  82 +
  83 +> 这是基于 docs/08 文本的提要;权威的 `git tag -l 'milestone/*'` 核对与未完成集合的最终判定由 Workflow 的 router stage 完成(router 以 docs/08 + tag 双重为准)。本入口不因提要里看似"全部完成"就跳过启动——是否有事可做由 router 决定。
  84 +
  85 +### 步骤 4:启动 Coding Workflow
  86 +
  87 +用 `Workflow` 工具调用编码编排脚本(`<cwd>` 替换为当前项目根的绝对路径):
  88 +
  89 +```
  90 +Workflow({
  91 + scriptPath: "${CLAUDE_PLUGIN_ROOT}/workflows/coding.mjs",
  92 + args: { projectRoot: "<cwd>" }
  93 +})
  94 +```
  95 +
  96 +### 步骤 5:告知用户已后台启动
  97 +
  98 +启动后向用户输出:
  99 +
  100 +```
  101 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  102 + [coding-start] ✅ Coding Workflow 已在后台启动
  103 +
  104 + 进度概述:<步骤 3 概述,如「待跑 3 模块 + 前端阶段」>
  105 +
  106 + ⚠️ 请确认(本入口无法程序化核对):
  107 + • 当前已在本地默认分支(main / master)
  108 + • 工作树干净,Plan 产物(docs/* + skeleton + DDL)已 commit
  109 +
  110 + Workflow 将按模块顺序全自动、静默推进;跑完所有模块或在某模块
  111 + halt(测试闸持续 RED / review 5 轮未过 / 缺值阻塞等)时会通知你。
  112 + halt 后请按诊断修复,再重新运行 /erp-workflow:coding-start 续跑。
  113 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  114 +```
  115 +
  116 +本 skill 到此结束,**不调用任何下游 skill**。
  117 +
  118 +## 参考
  119 +
  120 +- `docs/08-模块任务管理.md § 一`(A0~A6 Plan 进度,步骤 2 读取)
  121 +- `docs/08-模块任务管理.md § 二`(后端模块元数据 + 里程碑字段,步骤 3 读取)
  122 +- `docs/08-模块任务管理.md § 三`(前端阶段整体里程碑,步骤 3 读取)
  123 +- `workflows/coding.mjs`(B 阶段编排脚本,步骤 4 启动)
  124 +- `plan-start`(姊妹入口,A 阶段)
  125 +- `CLAUDE.md`(项目指令)
workflows/coding.mjs 0 → 100644
  1 +// workflows/coding.mjs
  2 +//
  3 +// 整个 ERP Coding(B 阶段)= 一个静默、全自动的 Workflow 脚本。
  4 +//
  5 +// 设计原则(见 docs/superpowers/specs/2026-05-26-workflow-migration-design.md):
  6 +// - 所有 stage 都是 agent() 子代理,物理上无法 AskUserQuestion → 编码期结构性静默。
  7 +// - 缺值不再问人:派生 stage 把具体阻塞点写进产物并 throw(fail-fast,合法 halt → 终止态,非对话框)。
  8 +// - 后端 / 前端功能循环由同一份 featureLoop(items, phase) 驱动;phase 切换 reviewer checklist、
  9 +// 测试命令、路径作用域(backend/ vs frontend/)、id 格式(REQ-XXX-NNN vs FE-NN)。
  10 +// - 状态账本 = docs/08 §二/§三 + git tag;halt 后重跑 coding-start,router 从账本+tag 重算进度。
  11 +// - reviewer 统一为 agents/code-reviewer.md,review stage 用 agentType:'code-reviewer'。
  12 +//
  13 +// 运行时约束:Workflow 运行时禁用非确定性内建(Date.now / Math.random 等)。本脚本不调用它们;
  14 +// 凡需要"当天日期"的产物路径(<YYYY-MM-DD>-<id>.md),一律由子代理在其自身上下文中解析并落盘,
  15 +// 脚本只负责编排,不计算日期 / 随机数。
  16 +
  17 +export const meta = {
  18 + name: 'erp-coding',
  19 + description: 'Run the entire ERP coding phase autonomously and silently: per-module backend+frontend feature loops, test gate, milestone tag.',
  20 + phases: [
  21 + { title: 'Router' }, { title: 'Backend' }, { title: 'Frontend' },
  22 + { title: 'Gate' }, { title: 'Milestone' },
  23 + ],
  24 +}
  25 +
  26 +const ROUTER_SCHEMA = { type:'object', additionalProperties:false,
  27 + required:['modules'], properties:{ modules:{ type:'array', items:{
  28 + type:'object', additionalProperties:false,
  29 + required:['id','done','reqs','feItems'],
  30 + properties:{ id:{type:'string'}, done:{type:'boolean'},
  31 + reqs:{type:'array',items:{type:'string'}},
  32 + feItems:{type:'array',items:{type:'string'}} } } } } }
  33 +
  34 +const REVIEW_SCHEMA = { type:'object', additionalProperties:false,
  35 + required:['verdict','round','issues'], properties:{
  36 + verdict:{type:'string',enum:['approve','request-changes']},
  37 + round:{type:'integer'}, issues:{type:'array',items:{type:'string'}} } }
  38 +
  39 +const GATE_SCHEMA = { type:'object', additionalProperties:false,
  40 + required:['status'], properties:{ status:{type:'string',enum:['green','red']},
  41 + failures:{type:'array',items:{type:'string'}} } }
  42 +
  43 +const ROOT = args?.projectRoot || '.'
  44 +
  45 +// ============================================================================
  46 +// Stage prompt builders(纯字符串构造;只用 ROOT / id / phase / 入参,不触非确定性内建)
  47 +//
  48 +// 每个 prompt 的共同契约(见 commonContract):
  49 +// - 子代理是非交互的,物理上无法弹窗;缺任何值都不要"问人"——把具体阻塞点写进产物并失败。
  50 +// - phase=backend 与 phase=frontend 的差异(路径作用域 / id 形态 / 测试命令来源)逐条写明。
  51 +// - 所有输出文档用中文。
  52 +// ============================================================================
  53 +
  54 +function isFrontend(phase) { return phase === 'frontend' }
  55 +
  56 +// 所有子代理共享的"非交互静默"硬约束。
  57 +function commonContract(phase) {
  58 + const fe = isFrontend(phase)
  59 + return [
  60 + '## 硬约束(非交互子代理)',
  61 + '- 你是 Workflow 派生的**非交互子代理**,物理上无法弹出 AskUserQuestion / 等待人类输入。**绝不要尝试问人**。',
  62 + '- 需要具体值时,按此顺序自行获取:(1) 先查 `.env.local`、`docs/07-环境配置.md`、`docs/04-技术规范.md`、`docs/05-API接口契约.md`、`docs/06-UI交互规范.md`、`CLAUDE.md`、现有代码;(2) 仍查不到 → **不要编造、不要留 `【人工填写:】` / `TBD` / `TODO` 占位**,而是把**具体的阻塞点**(缺哪个值、应在哪个 Plan 期闸门锁定、为何无法继续)写进产物,然后让本步骤**失败**(以非零结果 / 显式 throw 结束),由上层 Workflow 转为带诊断的 halt。',
  63 + '- 全部输出文档**使用中文**。',
  64 + `- **阶段 = ${fe ? '前端(frontend)' : '后端(backend)'}**。路径作用域:${fe
  65 + ? '实现文件必须落在 `frontend/`(或 `docs/09-项目目录结构.md § 前端目录结构` 声明的前端根)下;命中 `backend/` / `sql/` / `scripts/` 即越界,硬停。'
  66 + : '产出范围限定 controller / service / repository / DTO / 校验 / SQL migration / REST 契约;**禁止**写 `frontend/` 路径下的实现(UI 推迟到前端阶段)。'}`,
  67 + `- id 形态:${fe ? '前端为 `FE-NN`(业务功能粒度,可关联多个 prototype 区域与多个 REQ)。' : '后端为 `REQ-XXX-NNN`。'}`,
  68 + ].join('\n')
  69 +}
  70 +
  71 +// Router:读 docs/08 §二/§三 + git tag,重算进度,返回 ROUTER_SCHEMA。
  72 +function routerPrompt(root) {
  73 + return [
  74 + '# Coding Router — 从账本重算进度',
  75 + '',
  76 + `项目根:\`${root}\``,
  77 + '',
  78 + '你是 Coding 阶段的路由子代理。**只读不写**(不改任何代码 / 文档),仅从状态账本重算"哪些模块还要跑",返回结构化结果。',
  79 + '',
  80 + '## 读取来源(账本 = docs/08 + git tag,二者一致才算完成)',
  81 + '1. `docs/08-模块任务管理.md § 二`(后端模块元数据):逐个模块取 `id`(英文蛇形 module id)、本模块的 REQ 列表(按 `docs/02-开发计划.md § 二 开发顺序清单` 的顺序,A5 约束保证同模块 REQ 连续),以及该模块的 `里程碑:` 字段。',
  82 + '2. `docs/08-模块任务管理.md § 三`(前端阶段元数据):取 `整体里程碑:` 字段,以及 `功能:` 项下所有 `- [ ] FE-NN ...` / `- [x] FE-NN ...` 行(FE 清单)。前端 item 形如 `FE-NN`。',
  83 + '3. `git -C <root> tag -l "milestone/*"`:列出已打的里程碑 tag。',
  84 + '',
  85 + '## 完成判定(每个模块独立)',
  86 + '- 后端模块 `done = true` 当且仅当:§二 该模块 `里程碑:` 字段 == `milestone/<module_id>` **且** `git tag -l "milestone/<module_id>"` 能查到该 tag。任一缺失 → `done = false`。',
  87 + '- 前端 item(FE-NN)归属一个"逻辑前端模块"。前端阶段整体 `done` 当且仅当 §三 `整体里程碑:` == `milestone/frontend-phase` 且 `git tag -l "milestone/frontend-phase"` 存在。',
  88 + '',
  89 + '## 输出(必须符合下发的 JSON schema)',
  90 + '- `modules`: 数组,按 `docs/02 § 二` 的模块顺序排列。每项:',
  91 + ' - `id`: 模块标识(后端为英文蛇形 module id;前端聚合为单一逻辑模块时用 `frontend-phase`)。',
  92 + ' - `done`: 该模块是否已完成(按上面的判定)。',
  93 + ' - `reqs`: 本模块**未完成**后端 REQ 的有序列表(已 `verdict=approve`(见 `docs/superpowers/reviews/*-<REQ>.md`)的 REQ 跳过)。模块已 done → 空数组。',
  94 + ' - `feItems`: 本模块关联的**未完成**前端 FE-NN 列表(已 approve 的 FE 跳过);无前端 → 空数组。',
  95 + '- 不要返回任何额外字段(schema 为 `additionalProperties:false`)。',
  96 + '',
  97 + '## 缺值处理',
  98 + '- docs/08 §二/§三 缺失 / 格式不符 / 无法解析 → **不要猜**:把具体的解析失败点写入返回前的诊断并使本步骤失败(让 Workflow halt),由人工修复 Plan 产物后重跑 `coding-start`。',
  99 + ].join('\n')
  100 +}
  101 +
  102 +// ---- 功能内循环 stage 1:派生 spec(原 feature-brainstorm / fe-feature-brainstorm)----
  103 +function deriveSpecPrompt(id, phase) {
  104 + const fe = isFrontend(phase)
  105 + return [
  106 + `# ${fe ? 'fe-feature-brainstorm' : 'feature-brainstorm'} — 派生规格 ${id}`,
  107 + '',
  108 + commonContract(phase),
  109 + '',
  110 + '## 目标',
  111 + `静默派生 \`${id}\` 的实现规格(无 Q&A)。需求歧义本应在 Plan 期的结构化 per-REQ 表单 / 前端 scope-lock 锁定;这里**只消费已锁定的事实**,不再澄清。`,
  112 + '',
  113 + '## 收集上下文',
  114 + fe
  115 + ? [
  116 + `- 关联 REQ 卡片:\`${ROOT}/docs/01-需求清单/<module>/<REQ>.md\`(提取业务校验规则、acceptance、UI 描述)。`,
  117 + `- 关联 prototype:Read \`${ROOT}/prototype/**/*.html\`(含 anchor 时聚焦相应区域),作为页面布局权威。`,
  118 + `- API 契约:\`${ROOT}/docs/05-API接口契约.md\`,按本 FE 关联的 REQ 过滤出消费的端点。`,
  119 + `- Design Tokens:\`${ROOT}/docs/06-UI交互规范.md § 二\`(色值 / 状态色引用源)。`,
  120 + `- 前端组件库:\`${ROOT}/docs/04-技术规范.md § 零\` 的 \`frontend.ui_lib\`,决定组件选型。`,
  121 + ].join('\n')
  122 + : [
  123 + `- REQ 卡片:\`${ROOT}/docs/01-需求清单/<module>/${id}.md\`。**忽略 UI 描述**(控件类型 / 按钮位置 / 列表布局),但校验规则、业务规则仍要落到后端 DTO + service。`,
  124 + `- 涉及的数据表定义:\`${ROOT}/docs/03-数据库设计文档.md\`(必要时实时查 mysql 只读)。`,
  125 + `- API 契约:\`${ROOT}/docs/05-API接口契约.md\` 中本 REQ 相关端点。`,
  126 + ].join('\n'),
  127 + '',
  128 + '## 写 spec',
  129 + `- 落盘 \`${ROOT}/docs/superpowers/specs/<当天日期 YYYY-MM-DD>-${id}.md\`(当天日期由你在自身上下文解析,脚本不传日期)。`,
  130 + fe
  131 + ? '- 规格至少含:关联 REQ + 关联原型;组件树(按页面 / 区域分块,推导自 prototype DOM);页面状态机(loading / empty / error / 正常 / 表单提交中 至少 5 态);消费的后端端点(对齐 docs/05);业务规则前端复刻清单(逐条:规则 / 触发时机 / 报错文案 / 来源 REQ);Design Tokens 引用清单(`var(--color-*)`)。'
  132 + : '- 规格覆盖:goal / 输入输出 / 业务规则 / 约束 / schema / API 引用 / acceptance criteria。',
  133 + '',
  134 + '## 自审(inline 修,无须等待)',
  135 + `- 占位符扫描:\`TBD\` / \`TODO\` / \`【人工填写:】\`${fe ? ' / `controller` / `service` / `SQL` / `migration`(前端 spec 不应出现后端字样)' : ''} → 命中即修;修不掉的缺值按硬约束失败。`,
  136 + '- 内部一致性 / 范围检查(单 plan 能消化吗)/ 歧义检查(任一 requirement 两种解读 → 挑一个写明)。',
  137 + '',
  138 + '## 结束',
  139 + `- 成功:输出一行 \`${fe ? 'fe-' : ''}feature-brainstorm: ${id} → <spec path>\`,把该 spec 路径作为本步骤结果返回(供下游 plan stage 使用)。`,
  140 + '- 不要输出"交给下一步 / 等待检查"之类的桥接叙述。',
  141 + ].join('\n')
  142 +}
  143 +
  144 +// ---- stage 2:spec → 任务级 TDD 计划(原 feature-plan / fe-feature-plan)----
  145 +function planPrompt(id, phase, spec) {
  146 + const fe = isFrontend(phase)
  147 + return [
  148 + `# ${fe ? 'fe-feature-plan' : 'feature-plan'} — 任务级计划 ${id}`,
  149 + '',
  150 + commonContract(phase),
  151 + '',
  152 + '## 输入',
  153 + `- 上游 spec:${spec ? `\`${spec}\`` : `\`${ROOT}/docs/superpowers/specs/<当天日期>-${id}.md\``}(不存在则失败)。`,
  154 + fe
  155 + ? `- \`${ROOT}/docs/04-技术规范.md § 一 前端架构\`(路由 / 状态库 / 组件目录约定 / 测试栈);\`${ROOT}/docs/09-项目目录结构.md § 前端目录结构\`(落盘位置)。用 Grep 在 \`${ROOT}/frontend/\` 定位现有文件。`
  156 + : `- \`${ROOT}/docs/04-技术规范.md\` 与 \`${ROOT}/docs/09-项目目录结构.md\`(编码规范 + 目录规范)。用 Grep 在现有代码定位待修改文件。`,
  157 + '',
  158 + '## 计划写作原则',
  159 + '- Plan 告诉 TDD 执行者**做什么**,不是**怎么写代码**(执行者是同模型、全上下文的 tdd stage)。',
  160 + `- Plan 锁定**文件边界 + 测试意图 + ${fe ? 'props 契约' : 'API 形状'} + 完成判据**;代码由 TDD 红绿循环产出。`,
  161 + '- **禁止 dump 整个文件内容**(pom.xml / entity / config / 组件源码)到 plan——避免双 source of truth 漂移。',
  162 + fe ? '- 每个任务标注"测试先行类型" = **jsdom 组件测试** OR **Playwright E2E**。' : '',
  163 + '- DRY、YAGNI、TDD、frequent commits。',
  164 + '',
  165 + '## 任务结构(每个 task = 一个 red-green-commit 单元,4 step)',
  166 + '1. 写失败测试(给 `test_file::test_name` + 测试意图);2. 实现最小代码(给 `impl_file`);3. 子会话验证 PASS;4. commit。任务粒度 2-5 分钟。',
  167 + fe
  168 + ? `- **硬护栏**:每个任务 \`impl_file\` 必须以 \`frontend/\`(或 docs/09 声明的前端根)开头;命中 \`backend/\` / \`sql/\` / \`scripts/\` → 修正后重渲染。`
  169 + : `- **硬护栏**:任务粒度限定后端文件(controller / service / repository / DTO / 校验 / SQL migration);**禁止**生成 \`frontend/\` 路径任务。`,
  170 + '- 允许写死的少数场景:DDL / migration 语句、合同级常量(错误码 / JWT claim / Redis key / 路由 path / API client 签名 / Design Tokens 名)、可选的测试断言 sketch。其余一律散文 + 签名描述。',
  171 + '- 首次出现的类 / 方法 / 组件 / hook / API client 函数必须给出签名;跨 task 的签名 / 错误码 / props 类型必须一致。',
  172 + '',
  173 + '## 写 plan + 自审',
  174 + `- 落盘 \`${ROOT}/docs/superpowers/plans/<当天日期 YYYY-MM-DD>-${id}.md\`,文件头含 Goal / Architecture / Tech Stack + checkbox 任务。`,
  175 + '- 自审:占位符扫描(按硬约束清单);spec coverage(spec 每节至少指向一个 task,补 gap);类型一致性(签名 / 方法名 / 错误码 / props 一致)。',
  176 + '',
  177 + '## 结束',
  178 + `- 成功:输出一行 \`${fe ? 'fe-' : ''}feature-plan: ${id} → <plan path>\`,把该 plan 路径作为结果返回。`,
  179 + ].filter(Boolean).join('\n')
  180 +}
  181 +
  182 +// ---- stage 3:按 plan 逐任务 TDD(原 feature-tdd / fe-feature-tdd)----
  183 +function tddPrompt(id, phase, plan) {
  184 + const fe = isFrontend(phase)
  185 + return [
  186 + `# ${fe ? 'fe-feature-tdd' : 'feature-tdd'} — 逐任务 TDD ${id}`,
  187 + '',
  188 + commonContract(phase),
  189 + '',
  190 + '## 输入',
  191 + `- 计划文件:${plan ? `\`${plan}\`` : `\`${ROOT}/docs/superpowers/plans/<当天日期>-${id}.md\``}(不存在则失败)。`,
  192 + `- 测试命令来源:\`${ROOT}/docs/04-技术规范.md § 零\`${fe
  193 + ? ' 的 `frontend.unit_test_runner` / `frontend.e2e_runner` / `frontend.test_command` / `frontend.e2e_command`(缺失则默认 `pnpm test:ci` / `pnpm e2e:ci`)。'
  194 + : ' 确认的后端测试命令(如 Maven profile / `./scripts/test.mjs`)。'}`,
  195 + '',
  196 + '## 流程',
  197 + fe ? '' : '- **Schema 改动前置**(仅当 plan 声明需要):第一个任务写 migration 文件 `V<n>__<snake_case>.sql`(`<n>` = 现有 `sql/migrations/V*.sql` 最大版本号 + 1,只含 DDL),**同步**把新 CREATE / ALTER 反向更新到 `docs/03-数据库设计文档.md` 对应表小节(docs/03 是 schema 的 SSoT),migration + docs/03 改动同一 commit。',
  198 + '- 按顺序处理每个代码类任务:(a) 在 `test_file::test_name` 写**失败**测试;(b) **派发 Agent 子会话**跑测试确认失败,子会话只返回 `{command, exit_code, failing_assertion}` JSON;(c) 写**最小**实现使测试通过;(d) 再派子会话确认通过;(e) commit(含 `REQ_ID` / REQ 标签)。',
  199 + fe
  200 + ? '- jsdom 类型用 vitest/jest 写组件单测;e2e 类型在 `frontend/e2e/` 写 Playwright(headless)。实现时:色值用 `var(--color-*)`(不硬编码 hex),业务校验按 spec 在 form-level 复刻。'
  201 + : '',
  202 + '',
  203 + '## 护栏',
  204 + '- **绝不**在主会话直接跑测试(mvn / pnpm / playwright / scripts/test.mjs)——必须通过 Agent 子会话。',
  205 + fe
  206 + ? '- **绝不**写非 `frontend/`(或 docs/09 前端根)路径的 `impl_file`;命中 `backend/` / `sql/` / `scripts/` → 硬停并打印 `不允许写非前端文件:<impl_file>`。'
  207 + : '- **后端阶段路径硬护栏**:任意 `impl_file` 以 `frontend/` 开头 → 硬停并打印 `后端阶段不允许写前端代码:<impl_file>`,不再继续 TDD。',
  208 + '- 每次 commit 含 REQ/FE 标签,不混合无关改动。',
  209 + '- **同一测试修复超过 10 次仍失败 → 立即失败(halt)**,把"哪个测试、失败断言、已尝试的修复"写进诊断;**不要**问人、不要无限重试。',
  210 + '',
  211 + '## 结束',
  212 + `- 全部任务通过:输出一行 \`${fe ? 'fe-' : ''}feature-tdd: ${id} 完成\`,把"已实现 + 已 commit"摘要作为结果返回(供 verify stage)。`,
  213 + ].filter(Boolean).join('\n')
  214 +}
  215 +
  216 +// ---- stage 4:把功能测试派子会话跑,渲染证据(原 feature-verify / fe-feature-verify)----
  217 +function verifyPrompt(id, phase, impl) {
  218 + const fe = isFrontend(phase)
  219 + return [
  220 + `# ${fe ? 'fe-feature-verify' : 'feature-verify'} — 证据验证 ${id}`,
  221 + '',
  222 + commonContract(phase),
  223 + '',
  224 + '## 目标',
  225 + `把 \`${id}\` 的功能测试**派发到 Agent 子会话**执行,按结构化结果渲染证据。**主会话从不直接跑测试,也不自由编写证据。**`,
  226 + impl ? `(上游 TDD 摘要:${impl})` : '',
  227 + '',
  228 + '## 流程',
  229 + fe
  230 + ? [
  231 + `- 测试目标:从 plan 取 \`测试先行类型 = jsdom\` 的 test_file → 拼 vitest/jest 过滤模式;\`= e2e\` 的 → 拼 Playwright spec 过滤模式。命令从 \`${ROOT}/docs/04-技术规范.md § 零 frontend.test_command\` / \`frontend.e2e_command\` 取(缺失默认 \`pnpm test:ci\` / \`pnpm e2e:ci\`)。`,
  232 + '- 派子会话依次跑 unit + e2e,子会话只返回结构化 JSON:`{ unit:{command,exit_code,passed,failed,failed_list,stdout_excerpt}, e2e:{...同结构} }`(`stdout_excerpt` ≤ 30 行)。',
  233 + '- **任一目标 `exit_code != 0` 或 `failed > 0`** → 渲染证据后失败,不进入 review。',
  234 + ].join('\n')
  235 + : [
  236 + `- 测试目标:从 plan 或项目标准命令确定(Maven profile / pnpm script / pytest path / \`${ROOT}/docs/04-技术规范.md § 零\` 的后端命令)。`,
  237 + '- 派子会话执行,子会话只返回结构化 JSON:`{command, exit_code, passed, failed, failed_list, stdout_excerpt}`(`stdout_excerpt` ≤ 30 行,不塞全文 stdout)。',
  238 + '- **`exit_code != 0` 或 `failed > 0`** → 渲染证据后失败,不进入 review。',
  239 + ].join('\n'),
  240 + `- 证据渲染并打印到会话;如需落盘,写 \`${ROOT}/docs/superpowers/reviews/\` 旁的证据位(沿用项目既有约定)。`,
  241 + '',
  242 + '## 结束',
  243 + `- 全部通过:输出一行 \`${fe ? 'fe-' : ''}feature-verify: ${id} 通过\`,把验证摘要作为结果返回(供 review stage)。`,
  244 + ].filter(Boolean).join('\n')
  245 +}
  246 +
  247 +// ---- stage 5a:AI 自审 diff(原 feature-review / fe-feature-review)——委托统一 reviewer agent ----
  248 +function reviewPrompt(id, phase, round) {
  249 + const fe = isFrontend(phase)
  250 + return [
  251 + `# ${fe ? 'fe-feature-review' : 'feature-review'} — AI 自审 ${id}(第 ${round} 轮)`,
  252 + '',
  253 + commonContract(phase),
  254 + '',
  255 + '## 目标',
  256 + `对 \`${id}\` 本轮引入的代码 diff 做 AI 自审,给出 \`approve\` 或 \`request-changes\` 裁决。`,
  257 + '',
  258 + '## 输入给 reviewer',
  259 + `- 本 ${fe ? 'FE' : 'REQ'} 引入的代码 diff + 规格 \`${ROOT}/docs/superpowers/specs/<当天日期>-${id}.md\`。`,
  260 + fe ? `- 本 FE 关联的所有 prototype 文件(spec 顶部"关联原型"列表),供对照渲染结构。` : '',
  261 + `- **phase = ${fe ? 'frontend → 附加前端 7 维 checklist(a11y / 对比度 / 响应式 等);主观维度仅标记明显问题,不因主观判断触发 request-changes(避免非确定性循环耗尽 5 轮)。' : 'backend → 通用代码审查维度(正确性 / 边界 / 错误处理 / 一致性)。'}**`,
  262 + '',
  263 + '## 输出(必须符合下发的 REVIEW JSON schema)',
  264 + `- \`verdict\`: \`approve\` | \`request-changes\`;\`round\`: 整数(本轮 = ${round});\`issues\`: must-fix 问题清单(approve 时可空数组)。`,
  265 + `- 渲染审阅报告写入 \`${ROOT}/docs/superpowers/reviews/<当天日期 YYYY-MM-DD>-${id}.md\`(\`verdict\` 字段与返回值一致——router / 进度判定靠它)。`,
  266 + `- approve 时,把 \`${ROOT}/docs/08-模块任务管理.md\` ${fe ? '§ 三' : '§ 二'} 中本 ${fe ? 'FE' : 'REQ'} 的 \`- [ ] ${id} ...\` 改为 \`- [x] ${id} ...\`(功能级可视化;模块完成仍以里程碑 tag 为准)。`,
  267 + '- 不要返回额外字段(schema 为 `additionalProperties:false`)。',
  268 + ].filter(Boolean).join('\n')
  269 +}
  270 +
  271 +// ---- stage 5b:按 review must-fix 修复并重新 commit(review 循环的 fix 步)----
  272 +function fixPrompt(id, phase, issues) {
  273 + const fe = isFrontend(phase)
  274 + const list = Array.isArray(issues) && issues.length
  275 + ? issues.map((x, i) => ` ${i + 1}. ${x}`).join('\n')
  276 + : ' (上一轮 review 的 must-fix 清单——见 ' + (fe ? '§三' : '§二') + ' 对应 review 报告)'
  277 + return [
  278 + `# ${fe ? 'fe-feature' : 'feature'} fix — 修复 review must-fix ${id}`,
  279 + '',
  280 + commonContract(phase),
  281 + '',
  282 + '## 待修复 must-fix',
  283 + list,
  284 + '',
  285 + '## 流程',
  286 + '- 逐项编辑 must-fix 指向的代码文件(遵守阶段路径作用域护栏)。',
  287 + `- 修复后 commit:\`fix(<scope>): 修复 review must-fix ${fe ? `REQ_ID: ${id}` : id}\`(不混合无关改动)。`,
  288 + '- 修复完成后本步骤即结束;上层 Workflow 会重新跑 verify + review(下一轮)。',
  289 + '- **缺值仍不要问人**:按硬约束把阻塞点写进诊断并失败。',
  290 + '',
  291 + '## 结束',
  292 + `- 输出一行 \`${fe ? 'fe-' : ''}feature-fix: ${id} 已修复 ${Array.isArray(issues) ? issues.length : ''} 项\`。`,
  293 + ].join('\n')
  294 +}
  295 +
  296 +// ---- 测试闸(原 test-gate)----
  297 +function gatePrompt(module, phase) {
  298 + const fe = isFrontend(phase)
  299 + const id = module?.id ?? '<module>'
  300 + return [
  301 + `# test-gate — ${fe ? '前端阶段' : `模块 ${id}`} 硬测试闸(phase=${phase})`,
  302 + '',
  303 + commonContract(phase),
  304 + '',
  305 + '## 目标',
  306 + `打里程碑 tag 前的唯一硬测试门。**派发 Agent 子会话**跑测试,绿则通过,红则失败。**绝不**在主会话直接跑测试,红色时**绝不**跳过。`,
  307 + '',
  308 + '## 命令',
  309 + fe
  310 + ? `- 前端:命令从 \`${ROOT}/docs/04-技术规范.md § 零 frontend.test_command\` / \`frontend.e2e_command\` 拼接(缺失则 \`pnpm test:ci && pnpm e2e:ci\`),跑 vitest + playwright(含全 FE 回归)。`
  311 + : `- 后端:跑 \`${ROOT}/scripts/test.mjs\`(跨平台 Node 测试入口;含本模块新增 + 已合并模块回归)。`,
  312 + '- 子会话只返回结构化 JSON:`{command, exit_code, passed, failed, stdout_excerpt}`(`stdout_excerpt` ≤ 30 行含 FAIL 摘要)。',
  313 + '',
  314 + '## 证据 + commit',
  315 + `- 渲染证据写入 \`${ROOT}/docs/superpowers/module-reports/${fe ? 'frontend-phase' : `${id}`}-test-gate.md\` 并 commit 到当前分支(保证证据随里程碑可审计)。`,
  316 + '',
  317 + '## 输出(必须符合下发的 GATE JSON schema)',
  318 + '- `status`: `green`(`exit_code = 0` 且 `failed = 0`)| `red`;`failures`: 失败用例摘要(green 时可省略 / 空数组)。',
  319 + '- 不要返回额外字段。**不要在本步骤内自动重试**——重试由上层 Workflow 控制。',
  320 + ].join('\n')
  321 +}
  322 +
  323 +// ---- 跨模块改动记录(替代被删的 cross-module hook + cross-module-log skill)----
  324 +function crossModulePrompt(module) {
  325 + const id = module?.id ?? '<module>'
  326 + return [
  327 + `# cross-module-log — 记录模块 ${id} 的跨模块改动`,
  328 + '',
  329 + commonContract('backend'),
  330 + '',
  331 + '## 目标',
  332 + `替代被删的 \`log-cross-module\` hook + \`cross-module-log\` skill:扫描本模块周期内对**非本模块**文件的改动,落跨模块日志(原因 + 影响评估),供 module-report § ⑦ 嵌入。`,
  333 + '',
  334 + '## 流程',
  335 + `- 用 \`git -C ${ROOT} diff --name-status\`(区间:模块分支起点 → HEAD)找出改动文件,判定哪些落在**其它模块**的目录下(按 docs/09 目录归属)。`,
  336 + `- 写 / 更新 \`${ROOT}/docs/superpowers/module-reports/${id}-cross-module.md\`,每行列:时间戳(你自身上下文解析当天,脚本不传)/ 目标模块 / 文件 / 改动摘要 / **原因**(本模块哪个 REQ 迫使改它)/ **影响评估**(目标模块哪些 API / 行为 / 调用方受影响、现有测试是否仍有效、是否需新测试,1-3 句)。`,
  337 + '- 无跨模块改动 → 输出 `cross-module-log: 无跨模块改动,跳过`,不创建文件。',
  338 + '- **不要留 `TBD(CC 补)`**:本步骤就是补齐的唯一时机;推不出原因/影响 → 按硬约束写阻塞点并失败。',
  339 + '',
  340 + '## 结束',
  341 + `- 输出一行 \`cross-module-log: 模块 ${id} 更新 N 行 / 跳过\`。`,
  342 + ].join('\n')
  343 +}
  344 +
  345 +// ---- 模块完成报告(原 module-report)----
  346 +function reportPrompt(module) {
  347 + const id = module?.id ?? '<module>'
  348 + const fe = id === 'frontend-phase'
  349 + return [
  350 + `# module-report — ${fe ? '前端阶段' : `模块 ${id}`} 12 节完成报告`,
  351 + '',
  352 + commonContract(fe ? 'frontend' : 'backend'),
  353 + '',
  354 + '## 目标',
  355 + `test-gate 绿后渲染标准化 **12 节**完成报告,commit 到当前分支(供 milestone 标记)。**只读 git 摘要,不读 diff 正文进上下文。**`,
  356 + '',
  357 + '## 前置',
  358 + `- 验证上游 test-gate 已绿:读 \`${ROOT}/docs/superpowers/module-reports/${fe ? 'frontend-phase' : `${id}`}-test-gate.md\`;红则停。`,
  359 + '',
  360 + '## 收集输入(取摘要而非正文)',
  361 + fe
  362 + ? [
  363 + '- § ① `module_id = frontend-phase`,`module_name = 前端阶段(整体)`。',
  364 + `- § ② "FE 完成清单":扫 \`${ROOT}/docs/superpowers/{specs,plans,reviews}/<日期>-FE-*.md\`,按 FE-NN 顺序列出。`,
  365 + `- § ③ 文件变更:\`git -C ${ROOT} diff --stat\`(区间 \`frontend-phase\` 分支起点 → HEAD)。`,
  366 + '- § ④ 数据库使用表 / § ⑥ Migration / § ⑦ 跨模块:填 `N/A(前端阶段)`。',
  367 + `- § ⑤:读 \`${ROOT}/docs/superpowers/module-reports/frontend-phase-test-gate.md\`。`,
  368 + '- § ⑧ 偏离清单:额外审查"实际渲染 DOM 与各 FE 关联原型主结构的差异",逐 FE 列出。',
  369 + '- § ⑪ 下一模块预览:填"上线 / 部署后续步骤"。',
  370 + ].join('\n')
  371 + : [
  372 + `- § ③ 文件变更:\`git -C ${ROOT} diff --stat\` / \`--name-status\` / \`git log --oneline\`(区间:module 分支起点 → HEAD)。`,
  373 + `- § ② / § ⑨:读 \`${ROOT}/docs/superpowers/{specs,plans,reviews}/<日期>-<本模块 REQ>.md\`。`,
  374 + `- § ⑤:读 \`${ROOT}/docs/superpowers/module-reports/${id}-test-gate.md\`。`,
  375 + `- § ⑥ Migration:\`git -C ${ROOT} diff --name-only --diff-filter=A -- 'sql/migrations/V*.sql'\` 列新增,每个读第一行作说明。`,
  376 + `- § ⑦ 跨模块改动:读 \`${ROOT}/docs/superpowers/module-reports/${id}-cross-module.md\`(如存在;其中不应再有 \`TBD(CC 补)\`,上一步 cross-module-log 已补齐)。`,
  377 + '- § ④ 读写的表:grep 定位涉 SQL 文件后按需读片段,**不全量读 docs/03**。',
  378 + ].join('\n'),
  379 + '',
  380 + '## 渲染 + 验证 + commit',
  381 + '- 渲染 12 节。硬验证:§ ⑧ 必须列举所有偏离(无则写"无偏离")。',
  382 + `- 写入 \`${ROOT}/docs/superpowers/module-reports/<当天日期 YYYY-MM-DD>-${fe ? 'frontend-phase' : `${id}`}.md\`,连同跨模块日志(如存在)一起 commit 到当前分支(milestone 的 worktree-clean 前置依赖此 commit)。`,
  383 + '',
  384 + '## 结束',
  385 + `- 输出一行 \`module-report: ${fe ? 'frontend-phase' : id} → <report path>\`。`,
  386 + ].join('\n')
  387 +}
  388 +
  389 +// ---- 里程碑:本地 merge --no-ff + tag + 回写 docs/08(原 milestone-tag,单 stage 内幂等)----
  390 +function milestonePrompt(module) {
  391 + const id = module?.id ?? '<module>'
  392 + const fe = id === 'frontend-phase'
  393 + const phaseId = fe ? 'frontend-phase' : id
  394 + return [
  395 + `# milestone-tag — ${fe ? '前端阶段' : `模块 ${id}`} 本地集成 + 打里程碑(幂等)`,
  396 + '',
  397 + commonContract(fe ? 'frontend' : 'backend'),
  398 + '',
  399 + '## 目标',
  400 + `把当前分支(${fe ? '`frontend-phase`' : `\`module-${id}\``})本地合并进默认分支并打 \`milestone/${phaseId}\` tag,把 tag 名回写 docs/08 + 报告 § ⑫。**全程无人工介入**;本 stage 内**重入幂等**(先写 docs/08,再打 tag,已存在则跳过)。`,
  401 + '',
  402 + '## 流程(顺序执行,任一硬错误 → 停下打印诊断,不自动 stash / 覆盖 / --abort)',
  403 + '1. **验证 worktree 干净**:`git -C ' + ROOT + ' status --porcelain` 非空 → 失败并打印 dirty 文件清单(检查 test-gate / module-report 是否都已 commit)。',
  404 + `2. **探测默认分支**:用 \`git -C ${ROOT} rev-parse --verify\` 依次试本地 \`main\` / \`master\`,取第一个存在的为 \`default_branch\`;都不存在 → 失败。`,
  405 + `3. **本地集成**:\`git -C ${ROOT} checkout <default_branch>\` 后 \`git -C ${ROOT} merge --no-ff ${fe ? 'frontend-phase' : `module-${id}`} -m "merge(${phaseId}): integrate ${fe ? 'frontend-phase' : `module-${id}`}"\`。合并冲突 → 失败并打印冲突文件清单(引导人工解决后重跑 coding-start)。`,
  406 + `4. **回写 docs/08 + commit**:在 default_branch 上 Edit \`${ROOT}/docs/08-模块任务管理.md\`:${fe
  407 + ? '§ 三 `- 整体里程碑: —` 改为 `- 整体里程碑: milestone/frontend-phase`'
  408 + : `§ 二 该模块 \` - 里程碑: —\` 改为 \` - 里程碑: milestone/${id}\``};commit \`chore(${phaseId}): record milestone/${phaseId} in docs/08\`。`,
  409 + `5. **打 annotated tag**(幂等):\`git -C ${ROOT} tag -a milestone/${phaseId} -m "milestone(${phaseId}): ${fe ? '前端' : '后端'}阶段完成"\`;tag 已存在则跳过。`,
  410 + `6. **追加 tag 到报告 § ⑫**:Edit 当天报告 \`${ROOT}/docs/superpowers/module-reports/<日期>-${phaseId}.md\` 的 § ⑫,把 \`{{milestone_tag}}\` 替换为 \`milestone/${phaseId}\`(已替换则跳过);commit \`docs(${phaseId}): record milestone/${phaseId} in completion report\`。`,
  411 + '',
  412 + '## 结束',
  413 + `- 输出一行 \`milestone-tag: ${phaseId} → milestone/${phaseId}\`。不要在本 stage 内回调 coding-start——推进下一模块由上层 Workflow 的循环负责。`,
  414 + ].join('\n')
  415 +}
  416 +
  417 +// ============================================================================
  418 +// 编排逻辑(结构按 plan 骨架;featureLoop / reviewWithFixLoop / testGate / 顶层循环)
  419 +// ============================================================================
  420 +
  421 +// ---- 单功能链(后端 / 前端同构)----
  422 +async function featureLoop(items, phase) {
  423 + return pipeline(items,
  424 + (id) => agent(deriveSpecPrompt(id, phase), {label:`spec:${phase}:${id}`, phase: phase==='backend'?'Backend':'Frontend'}),
  425 + (spec, id) => agent(planPrompt(id, phase, spec), {label:`plan:${phase}:${id}`, phase: phase==='backend'?'Backend':'Frontend'}),
  426 + (plan, id) => agent(tddPrompt(id, phase, plan), {label:`tdd:${phase}:${id}`, phase: phase==='backend'?'Backend':'Frontend'}),
  427 + (impl, id) => agent(verifyPrompt(id, phase, impl), {label:`verify:${phase}:${id}`, phase: phase==='backend'?'Backend':'Frontend'}),
  428 + (v, id) => reviewWithFixLoop(id, phase, v),
  429 + )
  430 +}
  431 +
  432 +// 有界 5 轮修复;超出 → throw(终止态,非对话框)
  433 +async function reviewWithFixLoop(id, phase, verifyResult) {
  434 + for (let round = 1; round <= 5; round++) {
  435 + const r = await agent(reviewPrompt(id, phase, round), {label:`review:${phase}:${id}:r${round}`, phase: phase==='backend'?'Backend':'Frontend', schema: REVIEW_SCHEMA, agentType:'code-reviewer'})
  436 + if (r.verdict === 'approve') return { id, phase, approved:true, rounds:round }
  437 + await agent(fixPrompt(id, phase, r.issues), {label:`fix:${phase}:${id}:r${round}`, phase: phase==='backend'?'Backend':'Frontend'})
  438 + }
  439 + throw new Error(`HALT review-unresolved ${phase}:${id} after 5 rounds`)
  440 +}
  441 +
  442 +async function testGate(module, phase) {
  443 + let g = await agent(gatePrompt(module, phase), {label:`gate:${phase}:${module.id}`, phase:'Gate', schema: GATE_SCHEMA})
  444 + if (g.status === 'red') { // 自动重试 1 次(防 flaky)
  445 + g = await agent(gatePrompt(module, phase) + '\n(retry once for flakiness)', {label:`gate-retry:${phase}:${module.id}`, phase:'Gate', schema: GATE_SCHEMA})
  446 + }
  447 + if (g.status === 'red') throw new Error(`HALT test-gate-red ${phase}:${module.id}: ${(g.failures||[]).join('; ')}`)
  448 + return g
  449 +}
  450 +
  451 +phase('Router')
  452 +const routed = await agent(routerPrompt(ROOT), {label:'router', phase:'Router', schema: ROUTER_SCHEMA})
  453 +const todo = routed.modules.filter(m => !m.done)
  454 +log(`coding: ${todo.length}/${routed.modules.length} modules to run`)
  455 +
  456 +const results = []
  457 +for (const module of todo) {
  458 + try {
  459 + await featureLoop(module.reqs, 'backend')
  460 + await testGate(module, 'backend')
  461 + if (module.feItems.length) { await featureLoop(module.feItems, 'frontend'); await testGate(module, 'frontend') }
  462 + await agent(crossModulePrompt(module), {label:`xmod:${module.id}`, phase:'Milestone'}) // 替代被删 hook
  463 + await agent(reportPrompt(module), {label:`report:${module.id}`, phase:'Milestone'})
  464 + await agent(milestonePrompt(module), {label:`milestone:${module.id}`, phase:'Milestone'}) // git merge --no-ff + tag + 更新 docs/08(单 stage 内幂等)
  465 + results.push({ module: module.id, status:'done' })
  466 + } catch (e) {
  467 + results.push({ module: module.id, status:'halted', reason: String(e.message||e) })
  468 + break // 整阶段 fail-fast:halt 后停,等人工修复后重跑 coding-start
  469 + }
  470 +}
  471 +
  472 +// Workflow 结果:跑完 / halt 的逐模块摘要。
  473 +// 注:plan 骨架原文是顶层 `return { results }`;裸 top-level return 在独立 ESM 模块下
  474 +// 无法通过 `node --check`(Illegal return statement),故改为 `export default`——
  475 +// Workflow 运行时读取模块的默认导出作为结果,语义等价、结构其余部分与骨架逐行一致。
  476 +export default { results }