Commit 62e69e345314e5529c1735490b8b39874e56292b
1 parent
970bdee3
refactor(skills): coding/ + crosscut/ SKILL.md 纯意图化;mr-create bash 抽到 create-mr.sh
强化"skill 只讲目的与约束,具体方法交给 LLM 自决"原则: - 删冗余的 interrupt-check 预防性调用(除 feature-tdd 3.e 真触发位 + test-gate 失败横幅引导) - 删 verbatim 子会话 prompt prose(feature-verify / test-gate),保 JSON schema 契约 - 删模板槽位枚举、bash 字面量、fork 解释括号等噪音 架构调整: - coding-start 精简到守门 + 派发 4 步,模块定位职责完全交给 module-start - sync 默认分支挪到 module-start 创建新分支前(避免全完成场景的浪费 sync) - module-start 步骤 1 自包含 MR 状态契约(指向 CLAUDE.md 权威源避免 drift) - mr-create 4 段 bash 抽到 scripts/create-mr.sh,SKILL 减 41%; 靠 sed/awk pipe + curl --rawfile 保证模块报告全程不进 LLM 上下文 - 两入口(plan-start / coding-start)共用 flow-overview.txt 模式
Showing
19 changed files
with
423 additions
and
572 deletions
skills/coding/feature-brainstorm/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: feature-brainstorm |
| 3 | -description: 功能循环第 1 步。针对单个 REQ-XXX-NNN 进行交互式头脑风暴,产出功能规格文件到 docs/superpowers/specs/。 | |
| 3 | +description: 功能循环第 1 步。针对单个 REQ-XXX-NNN 进行交互式头脑风暴,产出功能规格到 docs/superpowers/specs/。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Skill Bash(mysql *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,31 +9,17 @@ allowed-tools: Read Write Skill Bash(mysql *) |
| 9 | 9 | |
| 10 | 10 | # feature-brainstorm |
| 11 | 11 | |
| 12 | -## 说明 | |
| 13 | - | |
| 14 | -针对一个 REQ-XXX-NNN,委托本插件的 `superpower-brainstorming`(superpowers:brainstorming 的本地 fork,已剥掉 approval gates)进行头脑风暴,再把输出填入标准规格模板,产出单页功能规格。 | |
| 12 | +针对单个 REQ,委托 `superpower-brainstorming` 做交互式头脑风暴,把输出按规格模板渲染,产出单页功能规格。 | |
| 15 | 13 | |
| 16 | 14 | ## 执行步骤 |
| 17 | 15 | |
| 18 | -1. **中断检查**:调用 `interrupt-check`。如果触发 → 停止。 | |
| 19 | -2. 确定输入: | |
| 20 | - - 当前 REQ-XXX-NNN(从对话中获取,或 `docs/08` 当前模块下一个未完成的 REQ)。 | |
| 21 | - - REQ 卡片:`docs/01-需求清单/<module>/<req_id>.md`(一 REQ 一文件)。 | |
| 22 | - - 相关数据表(从 `docs/03` 或实时 mysql 命令行查询)。 | |
| 23 | -3. 委托本插件 `superpower-brainstorming`,以 REQ 卡片 + schema 引用作为上下文;把步骤 4 推导出的落盘路径作为 caller-provided path 传入。 | |
| 24 | -4. 推导路径:`docs/superpowers/specs/$(date +%F)-<REQ-id>.md`。如已存在,征求用户确认后覆盖。 | |
| 25 | -5. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md`,从头脑风暴输出填充槽位: | |
| 26 | - - `goal`、`input`、`output`、`rules`、`constraints`、`schema_refs`、`api_refs`、`acceptance` | |
| 27 | -6. 将填充后的规格写入推导路径。 | |
| 28 | -7. **验证**:模板中每个顶级节必须非空;**spec 全文不得包含 `【人工填写:...】` 或 `TBD`**。如出现:先在 `.env.local` / `docs/07-环境配置.md` / `CLAUDE.md` / 现有代码中查找真值并写入(同时注明来源),查不到则用 `AskUserQuestion` 向用户询问;拒绝把"待人工填写"的标记写入 B 阶段 spec(该标记仅供 A 阶段用户审阅文档用)。 | |
| 29 | -8. 输出 `feature-brainstorm: <REQ> → <path>`。 | |
| 30 | - | |
| 31 | -## 衔接 | |
| 32 | - | |
| 33 | -立即调用 `Skill(feature-plan)` 进入下一步。 | |
| 16 | +1. 确定本次 REQ-XXX-NNN(由 module-start 派发时确定),收集上下文:REQ 卡片 `docs/01-需求清单/<module>/<req_id>.md` + 涉及的数据表定义(取自 docs/03 或实时 mysql 查询)。 | |
| 17 | +2. 委托 `superpower-brainstorming`,把上下文 + 落盘路径 `docs/superpowers/specs/<YYYY-MM-DD>-<REQ-id>.md` 作为 caller-provided path 传入。文件已存在 → 征求用户确认后覆盖。 | |
| 18 | +3. 按 `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md` 渲染头脑风暴输出,写入推导路径。 | |
| 19 | +4. **验证**:所有顶级节非空;**全文不得出现 `【人工填写:...】` 或 `TBD`**——这两类标记仅供 A 阶段文档审阅用,B 阶段 spec 必须写实际值(先在 `.env.local` / `docs/07-环境配置.md` / `CLAUDE.md` / 现有代码查找并注明来源,查不到则用 `AskUserQuestion` 问用户)。 | |
| 20 | +5. 输出 `feature-brainstorm: <REQ> → <path>`,立即调用 `Skill(feature-plan)`。 | |
| 34 | 21 | |
| 35 | 22 | ## 参考 |
| 36 | 23 | |
| 37 | 24 | - `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md` |
| 38 | -- 委托:`superpower-brainstorming`(本插件 `skills/internal/superpower-brainstorming/`,superpowers:brainstorming 的无门 fork) | |
| 39 | -- 守门:`interrupt-check` | |
| 25 | +- 委托:`superpower-brainstorming`(本插件 `skills/internal/superpower-brainstorming/`) | ... | ... |
skills/coding/feature-plan/SKILL.md
| ... | ... | @@ -9,26 +9,20 @@ allowed-tools: Read Write Grep Skill |
| 9 | 9 | |
| 10 | 10 | # feature-plan |
| 11 | 11 | |
| 12 | -## 执行步骤 | |
| 13 | - | |
| 14 | -1. **中断检查**:调用 `interrupt-check`。 | |
| 15 | -2. 确定输入: | |
| 16 | - - 当前 REQ-XXX-NNN 及其规格文件 `docs/superpowers/specs/YYYY-MM-DD-<REQ>.md`(规格不存在则报错)。 | |
| 17 | - - 相关代码指针(已有的待修改文件,通过 Grep 发现)。 | |
| 18 | - - `docs/04-技术规范.md` 和 `docs/09-项目目录结构.md`(编码规范 + 目录规范)。 | |
| 19 | -3. 委托本插件 `superpower-writing-plans`(superpowers:writing-plans 的本地 fork,已剥掉"Which approach?"执行交接门),以规格 + 代码指针 + 规范作为上下文;把步骤 4 推导出的落盘路径作为 caller-provided path 传入。 | |
| 20 | -4. 推导路径:`docs/superpowers/plans/$(date +%F)-<REQ-id>.md`。 | |
| 21 | -5. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md`,填充 `files[]`、`tasks[]`、`commits[]`。 | |
| 22 | -6. 强制要求:每个任务有失败测试标识、实现路径和完成标准;**plan 全文不得包含 `【人工填写:...】` 或 `TBD`**——该标记仅限 A 阶段用户审阅文档,B 阶段 plan 必须写具体值(先在 `.env.local` / `docs/07` / `CLAUDE.md` / 现有代码查找并注明来源;查不到就 `AskUserQuestion` 向用户问)。 | |
| 23 | -7. 写入计划文件。 | |
| 24 | -8. 输出 `feature-plan: <REQ> → <path>`。 | |
| 12 | +把当前 REQ 的功能规格转成任务级实现计划,委托 `superpower-writing-plans` 起草,按计划模板渲染落盘。 | |
| 25 | 13 | |
| 26 | -## 衔接 | |
| 14 | +## 执行步骤 | |
| 27 | 15 | |
| 28 | -立即调用 `Skill(feature-tdd)` 进入下一步。 | |
| 16 | +1. 收集输入: | |
| 17 | + - 当前 REQ 的规格文件 `docs/superpowers/specs/<YYYY-MM-DD>-<REQ-id>.md`(不存在则报错) | |
| 18 | + - 相关代码指针(待修改的现有文件,通过 grep 发现) | |
| 19 | + - `docs/04-技术规范.md` 与 `docs/09-项目目录结构.md`(编码规范 + 目录规范) | |
| 20 | +2. 委托 `superpower-writing-plans`,把上述上下文 + 落盘路径 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md` 作为 caller-provided path 传入。 | |
| 21 | +3. 按 `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md` 渲染输出,写入推导路径。 | |
| 22 | +4. **验证**:每个任务必须含失败测试标识、实现路径与完成标准;**plan 全文不得出现 `【人工填写:...】` 或 `TBD`**——这两类标记仅供 A 阶段文档审阅用,B 阶段 plan 必须写实际值(先在 `.env.local` / `docs/07-环境配置.md` / `CLAUDE.md` / 现有代码查找并注明来源,查不到则用 `AskUserQuestion` 问用户)。 | |
| 23 | +5. 输出 `feature-plan: <REQ> → <path>`,立即调用 `Skill(feature-tdd)`。 | |
| 29 | 24 | |
| 30 | 25 | ## 参考 |
| 31 | 26 | |
| 32 | 27 | - `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md` |
| 33 | -- 委托:`superpower-writing-plans`(本插件 `skills/internal/superpower-writing-plans/`,superpowers:writing-plans 的无门 fork) | |
| 34 | -- 守门:`interrupt-check` | |
| 28 | +- 委托:`superpower-writing-plans`(本插件 `skills/internal/superpower-writing-plans/`) | ... | ... |
skills/coding/feature-review/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: feature-review |
| 3 | -description: 功能循环第 5 步。AI 自审,输出审阅报告到 docs/superpowers/reviews/。approve 回调 module-start;request-changes 则编辑代码并 fix commit,重新执行 verify。自修复循环上限 5 轮。 | |
| 3 | +description: 功能循环第 5 步。AI 自审 REQ 的 diff,approve 则回调 module-start;request-changes 则自修复 + 重 verify,循环上限 5 轮。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,33 +9,29 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *) |
| 9 | 9 | |
| 10 | 10 | # feature-review |
| 11 | 11 | |
| 12 | -## 执行步骤 | |
| 12 | +委托 `superpower-code-reviewer` agent 对当前 REQ 引入的代码改动做 AI 自审,渲染审阅报告。`approve` 回模块主循环;`request-changes` 则自修复 must-fix 并重新 verify,最多 5 轮。 | |
| 13 | 13 | |
| 14 | -1. 通过 `Agent(subagent_type=superpower-code-reviewer)` 调用本插件 code-reviewer agent(superpowers:code-reviewer 的本地 fork),以该 REQ 的 diff(`git diff <feature-start>..HEAD`)和规格作为输入。 | |
| 15 | -2. 推导路径:`docs/superpowers/reviews/$(date +%F)-<REQ-id>.md`。 | |
| 16 | -3. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-review-template.md`,填充 `round`、`verdict`、`must_fix[]`、`nice_to_have[]`、`gaps`。verdict 必须是 `approve` 或 `request-changes`。 | |
| 17 | -4. 写入报告。 | |
| 14 | +## 执行步骤 | |
| 18 | 15 | |
| 19 | -5. 分发: | |
| 20 | - - **`verdict = approve`** → 用 `Edit` 在 `docs/08-模块任务管理.md § 二` 本模块 bullet 下找到 ` - [ ] <REQ-id> ...` 行勾选为 ` - [x] <REQ-id> ...`(功能级进度可视化;模块完成仍由 `MR:` + GitLab API state 判定,不依赖本勾选)。然后输出 `feature-review: <REQ> round <N> 通过`,调用 `Skill(module-start)` 回模块主循环(module-start 会自动把本 REQ 识别为 done 并推进下一个 REQ)。 | |
| 21 | - - **`verdict = request-changes`** → 执行"自修复子流程": | |
| 22 | - - 逐项处理 `must_fix[]`:对每个条目用 `Edit` 修改其指向的代码文件。 | |
| 23 | - - 所有 Must-fix 修复后,拼 commit 消息(格式与 `feature-tdd` 一致,单行):`fix(<module_id>): 修复 review round <N> must-fix <REQ-id>`。 | |
| 24 | - - `Bash`: `git add <修改的代码文件>` + `git commit -m "<上一步拼出的消息>"`。 | |
| 25 | - - 调用 `Skill(feature-verify)` 重新执行验证;verify 通过后会再次链到本 skill,作为 round `<N+1>` 重审。 | |
| 16 | +1. 派发 `Agent(subagent_type=superpower-code-reviewer)`,把本 REQ 引入的代码 diff 与规格作为输入。 | |
| 17 | +2. 按 `${CLAUDE_SKILL_DIR}/templates/feature-review-template.md` 渲染审阅报告,写入 `docs/superpowers/reviews/<YYYY-MM-DD>-<REQ-id>.md`。`verdict` 取 `approve` 或 `request-changes`。 | |
| 18 | +3. 按 `verdict` 分派: | |
| 26 | 19 | |
| 27 | -6. 上限:**5 轮**。第 5 轮仍为 `request-changes` → 停止并打印摘要(升级给用户手工介入),不再自动修复,不回调 module-start。 | |
| 20 | + **approve** | |
| 21 | + - `Edit docs/08-模块任务管理.md § 二`,把本模块下 `- [ ] <REQ-id> ...` 改为 `- [x] <REQ-id> ...`(仅功能级可视化;模块完成仍以 `MR:` + GitLab API state 为准,不依赖此勾选) | |
| 22 | + - 输出 `feature-review: <REQ> round <N> 通过`,调用 `Skill(module-start)` | |
| 28 | 23 | |
| 29 | -## 衔接 | |
| 24 | + **request-changes(round < 5)** | |
| 25 | + - 逐项编辑 `must_fix[]` 指向的代码文件 | |
| 26 | + - 按 `feature-tdd/templates/commit-message-template.md` 格式 commit:`fix(<module_id>): 修复 review round <N> must-fix <REQ-id>` | |
| 27 | + - 调用 `Skill(feature-verify)` 重新验证;verify 通过后会再次链回本 skill,round `<N+1>` 重审 | |
| 30 | 28 | |
| 31 | -- `approve` → `Skill(module-start)` 回主循环。 | |
| 32 | -- `request-changes`(round < 5)→ `Skill(feature-verify)` 重新执行。 | |
| 33 | -- `request-changes`(round == 5)→ 停止。 | |
| 29 | + **request-changes(round == 5)** | |
| 30 | + - 停止并打印摘要,升级给用户手工介入;不再自动修复,不回调 module-start | |
| 34 | 31 | |
| 35 | 32 | ## 参考 |
| 36 | 33 | |
| 37 | 34 | - `${CLAUDE_SKILL_DIR}/templates/feature-review-template.md` |
| 38 | -- Fix commit 格式与 `feature-tdd` 的 `commit-message-template.md` 对齐(`fix(<scope>): <subject> <req_id>`) | |
| 39 | -- 委托:`superpower-code-reviewer`(本插件 `agents/superpower-code-reviewer.md`,superpowers:code-reviewer 的本地 fork) | |
| 40 | -- 上游:`feature-verify` | |
| 41 | -- 下游:`module-start`(approve)或 `feature-verify`(request-changes) | |
| 35 | +- 委托:`superpower-code-reviewer`(本插件 `agents/superpower-code-reviewer.md`) | |
| 36 | +- Fix commit 格式与 `feature-tdd/templates/commit-message-template.md` 一致 | |
| 37 | +- 上游:`feature-verify`;下游:`module-start`(approve)/ `feature-verify`(request-changes) | ... | ... |
skills/coding/feature-tdd/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: feature-tdd |
| 3 | -description: 功能循环第 3 步。逐任务执行计划:写失败测试 → 实现代码 → 测试通过 → 提交。所有测试运行均派发到子会话执行。 | |
| 3 | +description: 功能循环第 3 步。按 plan 逐任务做 TDD(失败测试 → 实现 → 通过 → commit),测试运行强制派发到子会话。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,39 +9,33 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) |
| 9 | 9 | |
| 10 | 10 | # feature-tdd |
| 11 | 11 | |
| 12 | +按 plan 文件逐任务做 TDD:写失败测试 → 写最小实现 → 确认通过 → commit。**所有测试运行强制派发到 Agent 子会话**,主会话只接收 JSON 结果。 | |
| 13 | + | |
| 12 | 14 | ## 执行步骤 |
| 13 | 15 | |
| 14 | -1. **中断检查**:调用 `interrupt-check`。 | |
| 15 | -2. 加载计划文件 `docs/superpowers/plans/YYYY-MM-DD-<REQ>.md`。 | |
| 16 | -3. **Schema 改动优先(如果计划声明了需要)**:若 plan 标注 "本 REQ 需要 schema 改动",**第一个任务必须是写 migration 文件 + 同步更新 docs/03**: | |
| 17 | - - `ls sql/migrations/V*.sql` 得最大版本号 n_max,新文件版本号 = n_max + 1 | |
| 18 | - - 文件名格式 `V<n>__<snake_case_desc>.sql`,例 `V5__add_user_email.sql` | |
| 19 | - - `Write` 该文件(只含 DDL:`ALTER TABLE ... ADD COLUMN ...` / `CREATE TABLE ...` 等) | |
| 20 | - - **同步把新 CREATE/ALTER 反向更新到 `docs/03-数据库设计文档.md` 对应表小节**(字段表格 / 索引 / 外键 / 业务注记),保持 docs/03 仍是 schema SSoT;新增表则按 `docs-03-table-template.md` 格式追加一节 | |
| 21 | - - 把 migration + docs/03 改动**一起 commit**(避免 SSoT 与 migration 分裂) | |
| 22 | - - 之后的代码任务(entity / DAO / service / 测试)在此之后做;测试运行时 Spring Boot 启动会由 Flyway 自动 apply 这个新 migration(`scripts/setup-test-db.sh` 只负责清空库) | |
| 23 | -4. 按顺序处理每个(代码类)任务: | |
| 24 | - a. 在 `test_file::test_name` 处编写失败测试。 | |
| 25 | - b. **派发子会话**(通过 `Agent`,general-purpose)运行测试并确认失败;子会话只返回 `{command, exit_code, failing_assertion}`。主会话**不直接**运行测试。 | |
| 26 | - c. 在 `impl_file` 处实现最小代码使测试通过。 | |
| 27 | - d. **再次派发子会话**运行测试并确认通过。 | |
| 28 | - e. 持续失败(同一测试 >10 次修复尝试)→ 调用 `interrupt-check`(中断 #1)。 | |
| 29 | - f. 暂存变更并使用 `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` 提交;`scope` 匹配任务的模块,`subject` ≤50 字符,`req_id` 必填。 | |
| 30 | -5. 所有任务完成后 → 交接给 `feature-verify`。 | |
| 16 | +1. 加载计划文件 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md`。 | |
| 17 | +2. **Schema 改动前置**(仅当 plan 声明需要):第一个任务必须是写 migration 文件 + 同步更新 docs/03,并一起 commit。 | |
| 18 | + - 文件命名 `V<n>__<snake_case>.sql`,`<n>` = 现有 `sql/migrations/V*.sql` 最大版本号 + 1;文件只含 DDL | |
| 19 | + - **同步**把新 CREATE / ALTER 反向更新到 `docs/03-数据库设计文档.md` 对应表小节,保持 docs/03 是 schema 的 SSoT;新增表按表小节模板追加 | |
| 20 | + - migration + docs/03 改动**同一 commit**,避免 SSoT 与 migration 分裂 | |
| 21 | + - 测试运行时 Spring Boot 启动 Flyway 会自动 apply 这个新 migration;`scripts/setup-test-db.sh` 只负责清库 | |
| 22 | +3. 按顺序处理每个代码类任务: | |
| 23 | + a. 在 `test_file::test_name` 处写**失败**测试 | |
| 24 | + b. 派发 Agent 子会话(general-purpose)运行测试确认失败,子会话只返回 `{command, exit_code, failing_assertion}` JSON | |
| 25 | + c. 在 `impl_file` 处写**最小**实现使测试通过 | |
| 26 | + d. 再次派发子会话确认通过 | |
| 27 | + e. 同一测试 >10 次修复仍失败 → 调用 `interrupt-check`(中断 #1:测试反复失败) | |
| 28 | + f. 按 `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` 格式 commit(`scope` = 任务模块;`subject` ≤ 50 字符;`req_id` 必填) | |
| 29 | +4. 全部任务完成 → 调用 `Skill(feature-verify)`。 | |
| 31 | 30 | |
| 32 | 31 | ## 护栏 |
| 33 | 32 | |
| 34 | -- **绝不**在主会话直接运行 `mvn test` / `pnpm test` / `scripts/test.sh`,必须通过子会话。 | |
| 35 | -- 每次提交必须包含 REQ-XXX-NNN 标签。 | |
| 36 | -- 不要将不相关的变更合并到一次提交中。 | |
| 37 | -- **禁止**在主会话直接 `mysql -e "ALTER ..."` 执行业务 DDL;所有业务 schema 变更必须走 `sql/migrations/V_n__<desc>.sql` 文件(只读查询 / 临时调试探索除外)。 | |
| 38 | - | |
| 39 | -## 衔接 | |
| 40 | - | |
| 41 | -立即调用 `Skill(feature-verify)` 进入下一步。 | |
| 33 | +- **绝不**在主会话直接跑 `mvn test` / `pnpm test` / `scripts/test.sh`,必须通过子会话 | |
| 34 | +- **绝不**在主会话直接 `mysql -e "ALTER ..."`;业务 schema 改动一律走 `sql/migrations/V*.sql` 文件(只读查询 / 临时调试除外) | |
| 35 | +- 每次 commit 必须含 `REQ-XXX-NNN` 标签;不混合无关改动到同一 commit | |
| 42 | 36 | |
| 43 | 37 | ## 参考 |
| 44 | 38 | |
| 45 | 39 | - `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` |
| 46 | -- 原则参考:`superpowers:test-driven-development`(本插件未镜像;仅作为 TDD 原则手册参考,不做运行时 invoke — 本 skill 已把"子会话跑测试 + REQ tag commit"流程直接写死) | |
| 47 | -- 守门:`interrupt-check` | |
| 40 | +- 守门:`interrupt-check`(仅在步骤 3.e 触发,条件 1) | |
| 41 | +- 原则参考:`superpowers:test-driven-development`(外部 TDD 原则手册,本 skill 已固化"子会话跑测试 + REQ-tagged commit"流程,不做运行时 invoke) | ... | ... |
skills/coding/feature-verify/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: feature-verify |
| 3 | -description: 功能循环第 4 步。将本功能的测试派发到子会话执行,用模板渲染验证证据。无证据不得声称完成。 | |
| 3 | +description: 功能循环第 4 步。把功能测试派发到子会话跑,按模板渲染证据。无证据不声称完成。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Skill Read Agent |
| 6 | 6 | --- |
| ... | ... | @@ -9,32 +9,33 @@ allowed-tools: Skill Read Agent |
| 9 | 9 | |
| 10 | 10 | # feature-verify |
| 11 | 11 | |
| 12 | -## 执行步骤 | |
| 12 | +把当前 REQ 的功能测试派发到 Agent 子会话执行,按模板把结构化结果渲染成证据。**主会话从不直接跑测试**,也不自由编写证据。 | |
| 13 | 13 | |
| 14 | -1. 从计划文件或项目标准命令中确定功能的测试目标(Maven profile / pnpm script / pytest path)。 | |
| 15 | -2. **派发子会话**(通过 `Agent`,general-purpose),prompt 类似: | |
| 14 | +## 执行步骤 | |
| 16 | 15 | |
| 16 | +1. 从 plan 文件或项目标准命令中确定功能的测试目标(如 Maven profile / pnpm script / pytest path)。 | |
| 17 | +2. 派发 Agent 子会话(general-purpose)运行该目标,子会话只返回结构化 JSON(不输出描述文字): | |
| 18 | + ```json | |
| 19 | + { | |
| 20 | + "command": "<cmd>", | |
| 21 | + "exit_code": <int>, | |
| 22 | + "passed": <int>, | |
| 23 | + "failed": <int>, | |
| 24 | + "failed_list": ["<test>", ...], | |
| 25 | + "stdout_excerpt": "<最后 30 行或最相关的失败片段>" | |
| 26 | + } | |
| 17 | 27 | ``` |
| 18 | - 任务:运行功能测试目标并报告结果。不要修改任何代码。步骤: | |
| 19 | - 1. 执行:<command>(例如 mvn -pl user-module test -Dtest=REQ*) | |
| 20 | - 2. 仅返回结构化 JSON:{"command":"<cmd>","exit_code":<int>,"passed":<int>,"failed":<int>,"failed_list":["<test>", ...],"stdout_excerpt":"<最后 30 行或最相关的失败摘录>"} | |
| 21 | - 不要输出任何描述性文字。 | |
| 22 | - ``` | |
| 23 | - | |
| 24 | -3. 解析 JSON;用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-verify-evidence-template.md`,填充槽位(包括 `subagent_id` 和 `conclusion`)。 | |
| 25 | -4. 如果 `exit_code != 0` 或 `failed > 0` → 打印填充后的证据到会话并**停止**,不进入审阅。 | |
| 26 | -5. 通过 → 打印证据,交接给 `feature-review`。 | |
| 28 | +3. 按 `${CLAUDE_SKILL_DIR}/templates/feature-verify-evidence-template.md` 渲染证据并打印到会话。 | |
| 29 | +4. **`exit_code != 0` 或 `failed > 0`** → 停止,不进入 review。 | |
| 30 | +5. 通过 → 调用 `Skill(feature-review)`。 | |
| 27 | 31 | |
| 28 | 32 | ## 护栏 |
| 29 | 33 | |
| 30 | -- 不要将原始测试 stdout 粘贴到主会话(超过 30 行的 `stdout_excerpt`)。 | |
| 31 | -- 证据必须从模板渲染,不能自由编写。 | |
| 32 | - | |
| 33 | -## 衔接 | |
| 34 | - | |
| 35 | -立即调用 `Skill(feature-review)` 进入下一步。 | |
| 34 | +- **绝不**在主会话直接跑测试,必须通过子会话 | |
| 35 | +- **绝不**自由编写证据正文,必须从模板渲染 | |
| 36 | +- 不要把原始 stdout 全文塞进主会话(`stdout_excerpt` ≤ 30 行) | |
| 36 | 37 | |
| 37 | 38 | ## 参考 |
| 38 | 39 | |
| 39 | 40 | - `${CLAUDE_SKILL_DIR}/templates/feature-verify-evidence-template.md` |
| 40 | -- 原则参考:`superpowers:verification-before-completion`(本插件未镜像;仅作为"证据先于断言"原则参考,不做运行时 invoke — 本 skill 已把子会话派发 + 模板渲染证据流程直接写死) | |
| 41 | +- 原则参考:`superpowers:verification-before-completion`(外部"证据先于断言"原则手册,本 skill 已固化子会话派发 + 模板渲染流程,不做运行时 invoke) | ... | ... |
skills/coding/module-report/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: module-report |
| 3 | -description: 本地测试闸门通过后,生成标准化 12 节模块完成报告,嵌入本模块新增的 migration 清单和跨模块改动日志。 | |
| 3 | +description: 本地测试闸门通过后,生成标准化 12 节模块完成报告并 commit 到 module 分支供 MR 嵌入。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Glob Grep Skill Bash(git diff *) Bash(git log *) Bash(git add *) Bash(git commit *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,46 +9,34 @@ allowed-tools: Read Write Glob Grep Skill Bash(git diff *) Bash(git log *) Bash( |
| 9 | 9 | |
| 10 | 10 | # module-report |
| 11 | 11 | |
| 12 | -## 执行步骤 | |
| 13 | - | |
| 14 | -0. **中断检查**:调用 `Skill(interrupt-check)` → 触发则停止(与 `interrupt-check` SKILL 描述"生成模块级制品前"对齐)。 | |
| 12 | +`test-gate` 绿色后渲染 12 节模块完成报告,commit 到 module 分支供 MR 嵌入。**只读摘要,不读 diff 正文进上下文**。 | |
| 15 | 13 | |
| 16 | -1. 验证上游:`test-gate` 返回了绿色。否则停止。 | |
| 17 | -2. 收集输入(优先 shell 摘要,避免把 diff 正文读进上下文): | |
| 18 | - - **文件变更 § ③** — 只用摘要,**不**读 diff 正文: | |
| 19 | - - `git diff --stat <module-start-commit>..HEAD` → 每文件增减行数 | |
| 20 | - - `git diff --name-status <module-start-commit>..HEAD` → A/M/D 状态 | |
| 21 | - - `git log --oneline <module-start-commit>..HEAD` → commit 列表 | |
| 22 | - - `docs/superpowers/specs|plans|reviews/<date>-<本模块的 REQ>.md` → § ②、§ ⑨(正常 Read,一般不大) | |
| 23 | - - **§ ⑥ 本模块新增 migration**:用 `git diff --name-only --diff-filter=A <module-start-commit>..HEAD -- 'sql/migrations/V*.sql'` 列出本模块提交的新 migration 文件;每个文件 Read 第一行(V_n 描述)作为说明 | |
| 24 | - - `docs/superpowers/module-reports/<module_id>-cross-module.md` → § ⑦ | |
| 25 | - - `docs/superpowers/module-reports/<module_id>-test-gate.md` → § ⑤ | |
| 26 | - - § ④(读写的表):**用 `grep -rlE "(SELECT|INSERT|UPDATE|DELETE).*FROM|INTO"` 定位涉及 SQL 的文件,再按需读取片段**。不要全量读取 docs/03。 | |
| 27 | -3. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/module-report-template.md`,填充全部 12 节。 | |
| 28 | -4. **硬性验证 + 批量补齐跨模块 TBD**: | |
| 29 | - - § ⑦:这是整模块周期里**唯一补齐 `TBD(CC 补)` 的时机**(编辑中途 CC 不应主动调 `cross-module-log`)。如果 cross-module 有任何行含 `TBD(CC 补)` → 调用 `Skill(cross-module-log)` 一次性批量补齐所有 TBD,补完再回本步骤重验。 | |
| 30 | - - § ⑦:如果非空但某行缺少影响评估(被填成空/敷衍)→ 同样调 `cross-module-log` 重补。 | |
| 31 | - - § ⑧ 必须列举所有偏离规格之处;如果没有,明确写"无偏离"。 | |
| 32 | -5. 写入 `docs/superpowers/module-reports/$(date +%F)-<module_id>.md`。 | |
| 33 | - | |
| 34 | -5b. **commit 模块报告 + cross-module 日志到 module 分支**(确保审计证据随 MR 合并进默认分支;mr-create 的 worktree clean 前置条件依赖此步): | |
| 14 | +## 执行步骤 | |
| 35 | 15 | |
| 16 | +1. 验证上游 `test-gate` 已绿;红色则停。 | |
| 17 | +2. 收集输入(核心约束:取 git 摘要而非 diff 正文): | |
| 18 | + - § ③ 文件变更:用 `git diff --stat` / `--name-status` / `git log --oneline` 摘要(区间:module 分支起点 → HEAD) | |
| 19 | + - § ② / § ⑨:直接 Read `docs/superpowers/{specs,plans,reviews}/<日期>-<本模块的 REQ>.md`(小文件可全读) | |
| 20 | + - § ⑤:Read `docs/superpowers/module-reports/<module_id>-test-gate.md` | |
| 21 | + - § ⑥ Migration:`git diff --name-only --diff-filter=A -- 'sql/migrations/V*.sql'` 列新增 migration,每个 Read 第一行作说明 | |
| 22 | + - § ⑦ 跨模块改动:Read `docs/superpowers/module-reports/<module_id>-cross-module.md`(如存在) | |
| 23 | + - § ④ 读写的表:用 grep 定位涉 SQL 的文件后按需读片段,**不全量读 docs/03** | |
| 24 | +3. 按 `${CLAUDE_SKILL_DIR}/templates/module-report-template.md` 渲染 12 节。 | |
| 25 | +4. **硬验证**: | |
| 26 | + - § ⑦:跨模块日志中任何 `TBD(CC 补)` 或敷衍填充 → 调用 `Skill(cross-module-log)` 一次性批量补齐,补完回本步骤重验(**整模块周期内唯一补 TBD 的时机**) | |
| 27 | + - § ⑧:必须列举所有偏离规格之处;若无,写"无偏离" | |
| 28 | +5. 写入 `docs/superpowers/module-reports/<YYYY-MM-DD>-<module_id>.md`,连同跨模块日志(如存在)一起 commit 到 module 分支: | |
| 36 | 29 | ```bash |
| 37 | - git add docs/superpowers/module-reports/$(date +%F)-<module_id>.md | |
| 38 | - # cross-module log 若存在且有改动(cross-module-log 补齐过 TBD)也一并提交 | |
| 39 | - [ -f "docs/superpowers/module-reports/<module_id>-cross-module.md" ] && \ | |
| 30 | + git add docs/superpowers/module-reports/<YYYY-MM-DD>-<module_id>.md | |
| 31 | + [ -f docs/superpowers/module-reports/<module_id>-cross-module.md ] && \ | |
| 40 | 32 | git add docs/superpowers/module-reports/<module_id>-cross-module.md |
| 41 | 33 | git commit -m "docs(<module_id>): add module completion report + cross-module log" |
| 42 | 34 | ``` |
| 43 | - | |
| 44 | -6. 交接给 `mr-create`。 | |
| 45 | - | |
| 46 | -## 衔接 | |
| 47 | - | |
| 48 | -立即调用 `Skill(mr-create)` 推送并创建 MR。 | |
| 35 | + commit 是必需的——`mr-create` 的 worktree-clean 前置条件依赖此步。 | |
| 36 | +6. 调用 `Skill(mr-create)` 推送并创建 MR。 | |
| 49 | 37 | |
| 50 | 38 | ## 参考 |
| 51 | 39 | |
| 52 | 40 | - `${CLAUDE_SKILL_DIR}/templates/module-report-template.md`(12 节) |
| 53 | -- 上游:`test-gate` | |
| 41 | +- 上游:`test-gate`(绿色时派发) | |
| 54 | 42 | - 下游:`mr-create` | ... | ... |
skills/coding/module-report/templates/module-report-template.md
skills/coding/module-start/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: module-start |
| 3 | -description: 启动/恢复模块循环。按 docs/02 § 二 REQ 清单定位当前模块及其 REQ 序列,确保处于模块分支,扫描 docs/superpowers/reviews/ 计算已完成 REQ,驱动第一个未完成 REQ 的功能循环;全部完成则调用 test-gate。幂等可重入。 | |
| 3 | +description: 模块循环入口。定位当前模块与未完成 REQ,派发到 feature-brainstorm(每个 REQ 一次)或 test-gate(本模块全部完成)。幂等可重入。 | |
| 4 | 4 | user-invocable: false |
| 5 | -allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout *) Bash(git rev-parse *) Bash(curl *) Bash(jq *) Bash(mkdir -p .tmp) Bash(rm -f .tmp/*) | |
| 5 | +allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout *) Bash(git rev-parse *) Bash(git pull *) Bash(git status *) Bash(git symbolic-ref *) Bash(curl *) Bash(jq *) | |
| 6 | 6 | --- |
| 7 | 7 | |
| 8 | 8 | **所有输出必须使用中文。** |
| ... | ... | @@ -11,67 +11,48 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout * |
| 11 | 11 | |
| 12 | 12 | ## 执行步骤 |
| 13 | 13 | |
| 14 | -### 步骤 1:按 `docs/02 § 二` REQ 序 + MR state 定位当前模块 + 本模块 REQ 列表 | |
| 15 | - | |
| 16 | -> `MR:` 字段 × GitLab API state 的三组合判定规则见 `CLAUDE.md § ✅ 模块完成判定规则 § 模块状态语义`。 | |
| 17 | - | |
| 18 | -与 `coding-start` 步骤 3 同构(完成判定以 `MR:` 字段 + GitLab API `state` 为准;curl 调用,凭据读 `.env.local` 的 `GITLAB_API_URL` / `GITLAB_TOKEN` / `GITLAB_PROJECT_ID`): | |
| 19 | - | |
| 20 | -- 用 `Read` 读取 `docs/02-开发计划.md`,用 `Grep`(pattern `^\|\s*[0-9]+\s*\|\s*\*\*(REQ-[A-Z0-9]+-[0-9]+)\*\*\s*\|\s*(module_\w+)`)抽取 § 二 表格数据行,按行号升序得 `req_order[]`。 | |
| 21 | -- 若 `req_order` 为空 → 打印"⚠️ docs/02 § 二 REQ 开发顺序清单为空或无法解析,请检查"并停止。 | |
| 22 | -- 初始化 `module_merged[module_id → bool]` 空缓存。按序遍历 `req_order[]`: | |
| 23 | - - `module_merged[module_id]` 已缓存为 `true` → 跳过本 REQ。 | |
| 24 | - - `module_merged[module_id]` 已缓存为 `false` → `current_module = module_id`,结束遍历。 | |
| 25 | - - 未缓存 → 读取 docs/08 § 二 该模块条目的 ` - MR:` 字段: | |
| 26 | - - `MR: —` → `module_merged[module_id] = false`,`current_module = module_id`,结束遍历。 | |
| 27 | - - `MR: !<iid>` → 先 `Bash`: `set -a; . ./.env.local; set +a`,然后**分步校验 HTTP + 返回条数 + state 枚举**(语义与 `coding-start` 步骤 3 严格一致): | |
| 28 | - ```bash | |
| 29 | - mkdir -p .tmp | |
| 30 | - HTTP_CODE=$(curl -sS -o .tmp/mr.json -w '%{http_code}' \ | |
| 31 | - --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | |
| 32 | - "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests?iid=<iid>") | |
| 33 | - ``` | |
| 34 | - 硬停条件(任一触发 → 打印与 `coding-start` 同款诊断横幅后**停下**,不进入下游 skill): | |
| 35 | - 1. `HTTP_CODE != 200` | |
| 36 | - 2. `HTTP_CODE == 200` 但 `jq 'length' .tmp/mr.json != 1` | |
| 37 | - 3. state 不在 `{merged, opened, closed}` 里 | |
| 38 | - | |
| 39 | - 通过后按 state 分派: | |
| 40 | - - `merged` → 缓存 `true`,跳过本 REQ | |
| 41 | - - `opened` / `closed` → 缓存 `false`,`current_module = module_id`,结束遍历 | |
| 42 | -- **抽取本模块 REQ 序列 `req_list[]`**:从 `req_order[]` 取出所有 `module_id == current_module` 的项,按原序组成(同模块 REQ 必须连续,见 A5 约束)。 | |
| 43 | -- 用 `Read` 读取 docs/08 § 二 该模块行 + 后续缩进行,提取 `module_name` / `depends_on`(REQ 列表以 docs/02 为准,不再读取 docs/08)。 | |
| 44 | - | |
| 45 | -### 步骤 2:所有模块已完成短路 | |
| 46 | - | |
| 47 | -如果 `req_order[]` 全部遍历完 `current_module` 仍未赋值(即所有模块都 merged)→ 打印"所有模块已完成"摘要并停止。 | |
| 14 | +### 步骤 1:定位当前模块与本模块 REQ 列表 | |
| 15 | + | |
| 16 | +按 `docs/02 § 二 开发顺序清单` 的 REQ 顺序扫描,找到第一个所属模块尚未 merged 的模块作为 `current_module`,并抽取本模块 REQ 序列。 | |
| 17 | + | |
| 18 | +模块状态判定(`MR:` 字段 × GitLab API state 三种组合的语义和应对动作)参见 `CLAUDE.md § ✅ 模块完成判定规则 § 模块状态语义`。 | |
| 19 | + | |
| 20 | +找到 `current_module` 后,从 docs/02 § 二 的 REQ 列表里取出所有 `module_id == current_module` 的项,按原序得 `req_list[]`(A5 约束保证同模块 REQ 连续)。模块名、需求卡目录等其它字段由后续步骤按需从 `docs/08 § 二` 或 `docs/01-需求清单/` 取,不在本步骤预读。 | |
| 21 | + | |
| 22 | +约束: | |
| 23 | + | |
| 24 | +- GitLab 凭据从 `.env.local` 读取(`GITLAB_API_URL` / `GITLAB_TOKEN` / `GITLAB_PROJECT_ID`) | |
| 25 | +- API 异常(HTTP 非 2xx / 找不到 MR / state 非合法值)一律硬停,**禁止静默假设未 merged**,向用户打印诊断信息,引导核查上述凭据与 docs/08 的 iid | |
| 26 | +- 任何文件读取或解析失败 → 打印错误并停止 | |
| 27 | + | |
| 28 | +### 步骤 2:所有模块都完成时结束 | |
| 29 | + | |
| 30 | +如果步骤 1 没找到任何未 merged 的模块(即整个项目已做完),打印"所有模块已完成"提示用户,结束流程,不再进入后续步骤。 | |
| 48 | 31 | |
| 49 | 32 | ### 步骤 3:确保处于模块分支 |
| 50 | 33 | |
| 51 | -- `target_branch = module-<module_id>`(例 `module-module_sys`)。 | |
| 52 | -- 用 `Bash` 执行 `git branch --show-current` 得 `current_branch`。 | |
| 53 | -- `current_branch == target_branch` → 继续步骤 4。 | |
| 54 | -- 否则用 `Bash` 执行 `git rev-parse --verify <target_branch> 2>/dev/null`: | |
| 55 | - - 存在 → `git checkout <target_branch>` | |
| 56 | - - 不存在 → `git checkout -b <target_branch>` | |
| 57 | -- 若当前工作区有未提交改动且 checkout 失败 → 打印错误并停止(请用户手工处理 dirty state)。 | |
| 34 | +确保工作树位于 `target_branch = module-<module_id>`(例 `module-module_sys`)。 | |
| 35 | + | |
| 36 | +- 已在该分支 → 继续步骤 4 | |
| 37 | +- 该分支已存在但当前不在 → checkout 过去 | |
| 38 | +- 该分支不存在 → 先把工作树切到远程默认分支(main 或 master)并 fast-forward 同步,作为新分支的干净 base,再 `git checkout -b` 创建模块分支 | |
| 39 | + | |
| 40 | +任何错误(定位不到默认分支 / 切换前工作树脏 / 不能 fast-forward / checkout 失败)一律停下并打印诊断信息,不自动 stash、不强制覆盖。 | |
| 41 | + | |
| 42 | +### 步骤 4:计算已完成 REQ 集合 `done_reqs[]` | |
| 58 | 43 | |
| 59 | -### 步骤 4:计算已完成 REQ 集合 `done_reqs[]`(幂等断点恢复关键) | |
| 44 | +对 `req_list[]` 中每个 REQ,检查 `docs/superpowers/reviews/` 下是否存在该 REQ 的 review 文件、且其 `verdict` 字段为 `approve`。两条件都满足 → 收入 `done_reqs[]`,步骤 6 推进时跳过这些 REQ。 | |
| 60 | 45 | |
| 61 | -- 对 `req_list[]` 每个 `req_id`,用 `Glob` 查 `docs/superpowers/reviews/*-<req_id>.md`。 | |
| 62 | -- 命中后用 `Grep`(pattern `^verdict:\s*approve`,`-i` 不敏感)检查首部 verdict。 | |
| 63 | -- 两者都命中 → 加入 `done_reqs[]`。 | |
| 46 | +每次进入本 skill 都重新计算(不缓存),保证中断/重跑后能从最新进度继续。 | |
| 64 | 47 | |
| 65 | 48 | ### 步骤 5:渲染并打印模块横幅 |
| 66 | 49 | |
| 67 | -`Read ${CLAUDE_SKILL_DIR}/templates/module-start-banner-template.md`,填充槽位;`reqs[]` 每项的 `status` 字段根据 `done_reqs[]` 填 `x`(已完成)或空格(未完成)。 | |
| 50 | +按 `${CLAUDE_SKILL_DIR}/templates/module-start-banner-template.md` 渲染输出。 | |
| 68 | 51 | |
| 69 | -### 步骤 6:推进主循环 | |
| 52 | +### 步骤 6:派发 | |
| 70 | 53 | |
| 71 | -- 从 `req_list[]` 取第一个不在 `done_reqs[]` 中的 REQ 作为 `next_req`。 | |
| 72 | -- **没有 `next_req`**(全部完成)→ 调用 `Skill(test-gate)` 进入模块闸门。 | |
| 73 | -- **有 `next_req`** → 调用 `Skill(feature-brainstorm)` 启动该 REQ 的功能循环。功能循环链(brainstorm → plan → tdd → verify → review)完成后,`feature-review` 在 `verdict=approve` 分支会回调 `Skill(module-start)` —— 再次进入本 skill 时,步骤 4 会把刚通过的 REQ 加入 `done_reqs[]`,步骤 6 自动取下一 REQ,形成可重入推进。 | |
| 74 | -- 任何停止条件触发(中断触发 / 测试持续失败 / review 5 轮仍 request-changes)→ 停止本模块,不要静默跳下一 REQ。 | |
| 54 | +- 还有未完成 REQ → 调用 `Skill(feature-brainstorm)` 启动该 REQ 的功能循环 | |
| 55 | +- 本模块 REQ 全部完成 → 调用 `Skill(test-gate)` 进入模块测试闸门 | |
| 75 | 56 | |
| 76 | 57 | ## 参考 |
| 77 | 58 | |
| ... | ... | @@ -79,5 +60,4 @@ allowed-tools: Read Write Skill Glob Grep Bash(git branch *) Bash(git checkout * |
| 79 | 60 | - `docs/08-模块任务管理.md § 二`(模块元数据,含 `MR:` 字段;完成判定以 MR state 为准) |
| 80 | 61 | - `docs/superpowers/reviews/*.md`(REQ 级进度事实——verdict=approve 即完成) |
| 81 | 62 | - `${CLAUDE_SKILL_DIR}/templates/module-start-banner-template.md` |
| 82 | -- 下游:`feature-*`、`test-gate` | |
| 83 | -- 注:跨模块日志文件的创建由 `hooks/scripts/log-cross-module.sh` 在首次跨模块改动时按需完成,本 skill 不再做防御性初始化。 | |
| 63 | +- 下游:`feature-brainstorm`(每个 REQ)/ `test-gate`(本模块完成) | ... | ... |
skills/coding/module-start/templates/module-start-banner-template.md
| 1 | -## Module start — {{module_id}} ({{module_name}}) | |
| 1 | +## 当前模块:{{module_id}}({{module_name}}) | |
| 2 | 2 | |
| 3 | -- 模块依赖: {{depends_on}} | |
| 4 | -- 对应需求文件: docs/01-需求清单/{{req_file}} | |
| 5 | -- 分支: module-{{module_id}} | |
| 6 | -- 功能清单(`x` = 已完成 review approve): | |
| 3 | +- 分支:module-{{module_id}} | |
| 4 | +- 需求卡片目录:docs/01-需求清单/{{req_dir}}/ | |
| 5 | +- 功能清单(`x` = 已完成 review approve): | |
| 7 | 6 | {{#each reqs}} |
| 8 | 7 | - [{{status}}] {{req_id}} — {{title}} |
| 9 | 8 | {{/each}} |
| 10 | - | |
| 11 | -开始/恢复功能循环(Layer 3),本次处理清单中第一个未完成 REQ。触发中断 → 停;所有 REQ 完成 → 进入模块闸门。 | ... | ... |
skills/coding/mr-create/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: mr-create |
| 3 | -description: 模块报告完成后,验证当前分支为 module-<id> 且 worktree 干净,push 推代码和所有 evidence,创建 GitLab MR(报告嵌入描述),把 MR URL 追加到模块报告 § ⑫ 并 commit,把 MR iid 回写到 docs/08 该模块 `MR:` 字段并 commit,再次 push 同步。完成信号由 MR merged state 判定。停下等待人工审核。 | |
| 3 | +description: 模块报告完成后,把 module 分支推到远程并创建 GitLab MR,把 MR iid 回写 docs/08 + URL 回写报告 § ⑫,停下等人工 Approve + Merge。 | |
| 4 | 4 | user-invocable: false |
| 5 | -allowed-tools: Read Write Edit Skill Bash(git *) Bash(curl *) Bash(jq *) Bash(sed *) Bash(awk *) Bash(cat *) Bash(echo *) Bash(mkdir -p .tmp) Bash(mv .tmp/*) Bash(rm -f .tmp/*) | |
| 5 | +allowed-tools: Read Edit Bash(git *) Bash(bash *) | |
| 6 | 6 | --- |
| 7 | 7 | |
| 8 | 8 | **所有输出必须使用中文。** |
| ... | ... | @@ -11,173 +11,82 @@ allowed-tools: Read Write Edit Skill Bash(git *) Bash(curl *) Bash(jq *) Bash(se |
| 11 | 11 | |
| 12 | 12 | ## 前置条件 |
| 13 | 13 | |
| 14 | -- `module-report` 已生成报告文件并 commit 到 module 分支。 | |
| 15 | -- `test-gate` 返回了绿色,test-gate.md 已 commit 到 module 分支。 | |
| 16 | -- 当前分支 = `module-<module_id>`(由 `module-start` 步骤 3 负责切入)。 | |
| 14 | +- `module-report` 已生成报告并 commit 到 module 分支 | |
| 15 | +- `test-gate` 绿色,test-gate.md 已 commit 到 module 分支 | |
| 16 | +- 当前分支 = `module-<module_id>`(由 `module-start` 步骤 3 切入) | |
| 17 | 17 | |
| 18 | 18 | ## 执行步骤 |
| 19 | 19 | |
| 20 | -### 步骤 0:中断检查 | |
| 21 | - | |
| 22 | -调用 `Skill(interrupt-check)` → 触发则停止。 | |
| 23 | - | |
| 24 | 20 | ### 步骤 1:验证当前分支 |
| 25 | 21 | |
| 26 | -- `Bash`: `git branch --show-current` → `current_branch`。 | |
| 27 | -- `current_branch` 必须匹配 `module-*`;否则打印错误并**停止**(不自动创建分支——分支职责在 `module-start` 步骤 3)。 | |
| 28 | -- 从 `current_branch` 取 `module_id = current_branch` 去掉 `"module-"` 前缀。 | |
| 29 | - | |
| 30 | -### 步骤 2:验证 worktree 干净(防止 evidence 文件漏 commit) | |
| 22 | +`git branch --show-current` 必须匹配 `module-*`,否则停下报错(不自动建分支——分支职责在 `module-start` 步骤 3)。从分支名取 `module_id` = 去掉 `module-` 前缀。 | |
| 31 | 23 | |
| 32 | -- `Bash`: `git status --porcelain` | |
| 33 | -- 输出非空 → 打印 dirty 文件清单并**停止**: | |
| 34 | - ``` | |
| 35 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 36 | - [mr-create] ⚠️ worktree 不干净,无法 push | |
| 24 | +### 步骤 2:验证 worktree 干净 | |
| 37 | 25 | |
| 38 | - 以下文件有未提交改动: | |
| 39 | - <git status 输出> | |
| 26 | +`git status --porcelain` 输出非空 → 停下打印 dirty 文件清单: | |
| 40 | 27 | |
| 41 | - push 前所有 evidence 必须已 commit 到 module 分支。检查点: | |
| 42 | - - test-gate 步骤 3b 是否已 commit test-gate.md? | |
| 43 | - - module-report 步骤 5b 是否已 commit 模块报告 + cross-module log? | |
| 44 | - - 本 skill 前没人跑过额外的 Edit/Write? | |
| 45 | - | |
| 46 | - 修复方式:`git add <files> && git commit -m "..."`,然后重新运行 /erp-workflow:coding-start。 | |
| 47 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 48 | - ``` | |
| 28 | +``` | |
| 29 | +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 30 | + [mr-create] ⚠️ worktree 不干净,无法 push | |
| 49 | 31 | |
| 50 | -### 步骤 3:push 推送全部已 commit 内容 | |
| 32 | + <git status 输出> | |
| 51 | 33 | |
| 52 | -`git push -u origin <current_branch>` — **不要**使用 `--no-verify`(hook `deny-no-verify.sh` 会拦截)。 | |
| 34 | + push 前所有 evidence 必须已 commit。检查点: | |
| 35 | + - test-gate 步骤 3 是否已 commit test-gate.md? | |
| 36 | + - module-report 步骤 5 是否已 commit 报告 + cross-module log? | |
| 53 | 37 | |
| 54 | -此 push 包含:代码 commit(feature-tdd 产出)+ 模块 review fix commit(feature-review 产出)+ test-gate evidence commit(test-gate 产出)+ 模块完成报告 commit(module-report 产出)。 | |
| 38 | + 修复:git add <files> && git commit -m "...",然后重跑 /erp-workflow:coding-start。 | |
| 39 | +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 40 | +``` | |
| 55 | 41 | |
| 56 | -### 步骤 4:读取 MR 标题模板 | |
| 42 | +### 步骤 3:初次 push | |
| 57 | 43 | |
| 58 | -用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/mr-title-template.md`,填充 `module_id`、`module_name`,得到 MR 标题字符串(一行,较短,进上下文 OK)。 | |
| 44 | +`git push -u origin <current_branch>`——**禁用 `--no-verify`**。 | |
| 59 | 45 | |
| 60 | -### 步骤 5:生成 MR 描述文件(纯 shell,不读模块报告内容进上下文) | |
| 46 | +### 步骤 4:调脚本创建(或复用)MR | |
| 61 | 47 | |
| 62 | 48 | ```bash |
| 63 | -mkdir -p .tmp | |
| 64 | -DESC_FILE=.tmp/mr-desc.md | |
| 65 | -REPORT="docs/superpowers/module-reports/<date>-<module_id>.md" | |
| 66 | - | |
| 67 | -# 先用 sed 替换 description 模板的简单槽位 | |
| 68 | -sed -e "s|{{test_gate_conclusion}}|<值>|g" \ | |
| 69 | - -e "s|{{test_subagent_id}}|<值>|g" \ | |
| 70 | - -e "s|{{module_id}}|<值>|g" \ | |
| 71 | - -e "s|{{date}}|<值>|g" \ | |
| 72 | - "${CLAUDE_SKILL_DIR}/templates/mr-description-template.md" > "$DESC_FILE" | |
| 73 | - | |
| 74 | -# 把模块报告完整内容直接管道注入,替换 {{module_report_contents}} 所在行 | |
| 75 | -awk -v report="$REPORT" ' | |
| 76 | - /\{\{module_report_contents\}\}/ { while ((getline line < report) > 0) print line; close(report); next } | |
| 77 | - { print } | |
| 78 | -' "$DESC_FILE" > .tmp/mr-desc.final | |
| 79 | -mv .tmp/mr-desc.final "$DESC_FILE" | |
| 49 | +bash "${CLAUDE_SKILL_DIR}/scripts/create-mr.sh" <module_id> <current_branch> <YYYY-MM-DD> | |
| 80 | 50 | ``` |
| 81 | 51 | |
| 82 | -关键:**模块报告内容只经 awk 管道流过**,从不进入 LLM 上下文。 | |
| 52 | +脚本内部完成:加载 `.env.local` → 探测目标分支 → 从 docs/08 取 `module_name` 与 test-gate.md 取结论 → 渲染 description → 查已有 opened MR → 否则创建新 MR。 | |
| 83 | 53 | |
| 84 | -### 步骤 5.3:加载 GitLab 凭据并探测目标分支 | |
| 54 | +输出(stdout):单行 `<MR_IID> <MR_URL>`,由本步骤捕获供后续步骤使用。失败时脚本写诊断到 stderr 并 exit 1,本 skill 停下。 | |
| 85 | 55 | |
| 86 | -```bash | |
| 87 | -# 加载 .env.local 中的 GITLAB_API_URL / GITLAB_TOKEN / GITLAB_PROJECT_ID | |
| 88 | -set -a; . ./.env.local; set +a | |
| 89 | -for v in GITLAB_API_URL GITLAB_TOKEN GITLAB_PROJECT_ID; do | |
| 90 | - eval "val=\${$v:-}"; [ -n "$val" ] || { echo "[mr-create] ⚠️ .env.local 缺少 $v"; exit 1; } | |
| 91 | -done | |
| 92 | - | |
| 93 | -# 探测默认分支作为 target_branch(与 coding-start 同策略) | |
| 94 | -TARGET_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||') | |
| 95 | -[ -n "$TARGET_BRANCH" ] || TARGET_BRANCH=$(git branch -r --format='%(refname:short)' | grep -E '^origin/(main|master)$' | head -1 | sed 's|^origin/||') | |
| 96 | -[ -n "$TARGET_BRANCH" ] || { echo "[mr-create] ⚠️ 无法探测默认分支(origin/main 或 origin/master)"; exit 1; } | |
| 97 | -``` | |
| 56 | +### 步骤 5:回写 docs/08 MR 字段并 commit | |
| 98 | 57 | |
| 99 | -### 步骤 5.5:幂等守门——检查 source branch 是否已有 opened MR | |
| 58 | +**先于步骤 6 执行**——若步骤 6 失败,重跑时步骤 4 脚本能识别已有 MR + docs/08 已含 IID,状态一致。 | |
| 100 | 59 | |
| 101 | -防止部分失败后重试创建重复 MR;查询失败不吞,硬停避免误创第二个 MR: | |
| 60 | +`Edit docs/08-模块任务管理.md`:把当前模块的 ` - MR: —` 改为 ` - MR: !<MR_IID>`。 | |
| 102 | 61 | |
| 103 | 62 | ```bash |
| 104 | -mkdir -p .tmp | |
| 105 | -HTTP_CODE=$(curl -sS -o .tmp/existing.json -w '%{http_code}' \ | |
| 106 | - --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | |
| 107 | - "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests?source_branch=${current_branch}&state=opened") | |
| 108 | -if [ "$HTTP_CODE" != "200" ]; then | |
| 109 | - echo "[mr-create] ⚠️ 查询已有 opened MR 失败 (HTTP $HTTP_CODE)" >&2 | |
| 110 | - jq -r '.message // .error // .' .tmp/existing.json | head -c 200 >&2; echo >&2 | |
| 111 | - echo " 请核对 .env.local 的 GITLAB_API_URL / GITLAB_TOKEN / GITLAB_PROJECT_ID 后重跑" >&2 | |
| 112 | - rm -f .tmp/existing.json | |
| 113 | - exit 1 | |
| 114 | -fi | |
| 115 | -EXISTING_IID=$(jq -r '.[0].iid // empty' .tmp/existing.json) | |
| 116 | -EXISTING_URL=$(jq -r '.[0].web_url // empty' .tmp/existing.json) | |
| 117 | -rm -f .tmp/existing.json | |
| 63 | +git add docs/08-模块任务管理.md | |
| 64 | +git commit -m "chore(<module_id>): record MR !<MR_IID> in docs/08" | |
| 118 | 65 | ``` |
| 119 | 66 | |
| 120 | -- `EXISTING_IID` 非空 → 打印 `[mr-create] 检测到已有 opened MR: !${EXISTING_IID}`,**跳过步骤 6**,直接用 `EXISTING_IID` / `EXISTING_URL` 进入步骤 7。 | |
| 121 | -- `EXISTING_IID` 为空 → 正常走步骤 6 创建新 MR。 | |
| 67 | +### 步骤 6:追加 MR URL 到模块报告 § ⑫ 并 commit | |
| 122 | 68 | |
| 123 | -### 步骤 6:创建 MR(仅当 5.5 未命中时) | |
| 69 | +`Edit docs/superpowers/module-reports/<date>-<module_id>.md` 的 § ⑫,把 `{{mr_url}}` 替换为 `<MR_URL>`(已替换则追加一行)。 | |
| 124 | 70 | |
| 125 | 71 | ```bash |
| 126 | -TITLE="<步骤 4 得到的标题>" | |
| 127 | - | |
| 128 | -CREATE_RESP=$(curl -sS -X POST \ | |
| 129 | - --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | |
| 130 | - --header "Content-Type: application/json" \ | |
| 131 | - --data "$(jq -n \ | |
| 132 | - --arg src "$current_branch" \ | |
| 133 | - --arg tgt "$TARGET_BRANCH" \ | |
| 134 | - --arg title "$TITLE" \ | |
| 135 | - --rawfile desc "$DESC_FILE" \ | |
| 136 | - '{source_branch: $src, target_branch: $tgt, title: $title, description: $desc, remove_source_branch: false}')" \ | |
| 137 | - "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests") | |
| 138 | - | |
| 139 | -MR_IID=$(echo "$CREATE_RESP" | jq -r '.iid // empty') | |
| 140 | -MR_URL=$(echo "$CREATE_RESP" | jq -r '.web_url // empty') | |
| 141 | -[ -n "$MR_IID" ] || { echo "[mr-create] ⚠️ MR 创建失败,API 响应:"; echo "$CREATE_RESP" | jq . >&2; exit 1; } | |
| 142 | -rm -f "$DESC_FILE" | |
| 72 | +git add docs/superpowers/module-reports/<date>-<module_id>.md | |
| 73 | +git commit -m "docs(<module_id>): record MR !<MR_IID> link in module report" | |
| 143 | 74 | ``` |
| 144 | 75 | |
| 145 | -对应 5.5 命中时复用 `EXISTING_IID` / `EXISTING_URL` 作为 `MR_IID` / `MR_URL`。 | |
| 146 | - | |
| 147 | -### 步骤 7:回写 docs/08 的 MR 字段并 commit(durable state 先持久化) | |
| 148 | - | |
| 149 | -> 先写 docs/08 再追模块报告——这样如果步骤 8 失败,重跑时 5.5 能直接识别已有 MR 且 docs/08 已含 IID,避免状态不一致。 | |
| 150 | - | |
| 151 | -- `Edit docs/08-模块任务管理.md`:把当前模块条目的 ` - MR: —` 改为 ` - MR: !<MR_IID>`(只改一行)。docs/08 § 二 中 `MR:` 是唯一动态字段。 | |
| 152 | -- `Bash`: `git add docs/08-模块任务管理.md && git commit -m "chore(<module_id>): record MR !<MR_IID> in docs/08"`。 | |
| 153 | - | |
| 154 | -### 步骤 8:追加 MR URL 到模块报告 § ⑫ 并 commit | |
| 155 | - | |
| 156 | -- `Edit docs/superpowers/module-reports/<date>-<module_id>.md` 的 § ⑫:把 `{{mr_url}}` 占位替换为 `<MR_URL>`(若已替换过,追加一行)。 | |
| 157 | -- `Bash`: `git add docs/superpowers/module-reports/<date>-<module_id>.md && git commit -m "docs(<module_id>): record MR !<MR_IID> link in module report"`。 | |
| 158 | - | |
| 159 | -### 步骤 9:再次 push | |
| 160 | - | |
| 161 | -步骤 3 的 push 之后,步骤 7、8 又产生了两个新 commit(docs/08 MR iid + 模块报告 MR link)。必须再次 push 让 MR 自动更新 commit 列表 + diff: | |
| 162 | - | |
| 163 | -`Bash`: `git push origin <current_branch>` | |
| 164 | - | |
| 165 | -### 步骤 10:向会话打印 MR URL | |
| 76 | +### 步骤 7:再次 push 同步新 commits | |
| 166 | 77 | |
| 167 | -### 步骤 11:停止 — 等待人工 Approve + Merge | |
| 78 | +步骤 5、6 产生了两个新 commit,必须再 push 让 MR 自动更新 commit 列表 + diff: | |
| 168 | 79 | |
| 169 | -用户合并后再次运行 `/erp-workflow:coding-start`,入口会自动检测 MR merged → 探测默认分支(`main` 或 `master`)→ `git checkout <默认分支>` + `git pull --ff-only origin <默认分支>` 同步远程 → 派发下一模块。 | |
| 80 | +`git push origin <current_branch>` | |
| 170 | 81 | |
| 171 | -## 设计要点 | |
| 82 | +### 步骤 8:打印 MR URL,停下等 Approve + Merge | |
| 172 | 83 | |
| 173 | -- **默认分支(main 或 master)是 protected branch**,只能通过 MR 修改。MR diff 覆盖代码、模块报告、test-gate evidence、cross-module log,以及 docs/08 的 `MR: !<iid>` 字段。 | |
| 174 | -- **完成信号以 MR state 为准**:docs/08 § 二 中仅 `MR:` 字段在 `—` / `!<iid>` 之间变化,避免提前合并未完成模块的顺序错误。 | |
| 175 | -- **worktree 干净前置**(步骤 2):保证 test-gate.md、module-report.md、cross-module log.md 都已进 repo,审计证据完整;防止下次 coding-start 切回默认分支时遇到 dirty state。 | |
| 84 | +向会话打印 `<MR_URL>`,结束本 skill。用户在 GitLab merge 后再运行 `/erp-workflow:coding-start`,入口扫描到 `state=merged` 后自动推进下一模块。 | |
| 176 | 85 | |
| 177 | 86 | ## 参考 |
| 178 | 87 | |
| 88 | +- `${CLAUDE_SKILL_DIR}/scripts/create-mr.sh`(步骤 4 主流程脚本) | |
| 179 | 89 | - `${CLAUDE_SKILL_DIR}/templates/mr-title-template.md` |
| 180 | 90 | - `${CLAUDE_SKILL_DIR}/templates/mr-description-template.md` |
| 181 | 91 | - 上游:`module-report` |
| 182 | -- 守门:`interrupt-check` | |
| 183 | 92 | - 下游闸门:用户手工 MR Approve + Merge | ... | ... |
skills/coding/mr-create/scripts/create-mr.sh
0 → 100755
| 1 | +#!/usr/bin/env bash | |
| 2 | +# create-mr.sh — mr-create 主流程:渲染 description、查已有 MR / 创建新 MR | |
| 3 | +# | |
| 4 | +# 用法: | |
| 5 | +# bash create-mr.sh <module_id> <current_branch> <date> | |
| 6 | +# | |
| 7 | +# 输出(stdout,单行,由调用方读取): | |
| 8 | +# <MR_IID> <MR_URL> | |
| 9 | +# | |
| 10 | +# 失败:诊断写 stderr,exit 1。 | |
| 11 | +# | |
| 12 | +# 设计要点: | |
| 13 | +# - 模块报告整文只经 sed + awk 管道流入 description 与 GitLab API(curl --rawfile), | |
| 14 | +# 全程不进 LLM 上下文。 | |
| 15 | +# - 幂等:同一 source_branch 已有 opened MR 时,复用其 iid/url,不再创建。 | |
| 16 | + | |
| 17 | +set -euo pipefail | |
| 18 | + | |
| 19 | +MODULE_ID="${1:?usage: create-mr.sh <module_id> <current_branch> <date>}" | |
| 20 | +CURRENT_BRANCH="${2:?missing current_branch}" | |
| 21 | +DATE="${3:?missing date (YYYY-MM-DD)}" | |
| 22 | + | |
| 23 | +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) | |
| 24 | +TPL_DIR="$SCRIPT_DIR/../templates" | |
| 25 | +DESC_TPL="$TPL_DIR/mr-description-template.md" | |
| 26 | +TITLE_TPL="$TPL_DIR/mr-title-template.md" | |
| 27 | + | |
| 28 | +REPORT="docs/superpowers/module-reports/${DATE}-${MODULE_ID}.md" | |
| 29 | +TEST_GATE="docs/superpowers/module-reports/${MODULE_ID}-test-gate.md" | |
| 30 | + | |
| 31 | +[ -f "$REPORT" ] || { echo "[create-mr] ⚠️ 模块报告不存在: $REPORT" >&2; exit 1; } | |
| 32 | +[ -f "$TEST_GATE" ] || { echo "[create-mr] ⚠️ test-gate evidence 不存在: $TEST_GATE" >&2; exit 1; } | |
| 33 | + | |
| 34 | +# 1. 加载凭据 | |
| 35 | +[ -f .env.local ] || { echo "[create-mr] ⚠️ .env.local 不存在" >&2; exit 1; } | |
| 36 | +set -a; . ./.env.local; set +a | |
| 37 | +for v in GITLAB_API_URL GITLAB_TOKEN GITLAB_PROJECT_ID; do | |
| 38 | + eval "val=\${$v:-}" | |
| 39 | + [ -n "$val" ] || { echo "[create-mr] ⚠️ .env.local 缺少 $v" >&2; exit 1; } | |
| 40 | +done | |
| 41 | + | |
| 42 | +# 2. 探测目标分支(origin/HEAD → main → master) | |
| 43 | +TARGET_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || true) | |
| 44 | +[ -n "$TARGET_BRANCH" ] || TARGET_BRANCH=$(git branch -r --format='%(refname:short)' | grep -E '^origin/(main|master)$' | head -1 | sed 's|^origin/||' || true) | |
| 45 | +[ -n "$TARGET_BRANCH" ] || { echo "[create-mr] ⚠️ 无法探测默认分支(origin/main 或 origin/master)" >&2; exit 1; } | |
| 46 | + | |
| 47 | +# 3. 从 docs/08 § 二 提取 module_name | |
| 48 | +MODULE_NAME=$(awk -v mid="$MODULE_ID" ' | |
| 49 | + $0 ~ "^- "mid" " { sub("^- "mid" ", ""); print; exit } | |
| 50 | +' docs/08-模块任务管理.md) | |
| 51 | +[ -n "$MODULE_NAME" ] || { echo "[create-mr] ⚠️ docs/08 § 二 找不到模块 $MODULE_ID" >&2; exit 1; } | |
| 52 | + | |
| 53 | +# 4. 从 test-gate evidence 提取 conclusion + subagent_id | |
| 54 | +TEST_SUBAGENT_ID=$(awk '/^- 子会话: / { sub("^- 子会话: ", ""); print; exit }' "$TEST_GATE") | |
| 55 | +TEST_GATE_CONCLUSION=$(awk '/^结论: / { sub("^结论: ", ""); print; exit }' "$TEST_GATE" | awk '{print $1}') | |
| 56 | + | |
| 57 | +# 5. 渲染 MR 标题(单行,可进 LLM 上下文) | |
| 58 | +TITLE=$(cat "$TITLE_TPL") | |
| 59 | +TITLE="${TITLE//\{\{module_id\}\}/$MODULE_ID}" | |
| 60 | +TITLE="${TITLE//\{\{module_name\}\}/$MODULE_NAME}" | |
| 61 | + | |
| 62 | +# 6. 渲染 description(整篇模块报告,全程不进 LLM 上下文) | |
| 63 | +mkdir -p .tmp | |
| 64 | +DESC_FILE=.tmp/mr-desc.md | |
| 65 | + | |
| 66 | +sed -e "s|{{test_gate_conclusion}}|$TEST_GATE_CONCLUSION|g" \ | |
| 67 | + -e "s|{{test_subagent_id}}|$TEST_SUBAGENT_ID|g" \ | |
| 68 | + -e "s|{{module_id}}|$MODULE_ID|g" \ | |
| 69 | + -e "s|{{date}}|$DATE|g" \ | |
| 70 | + "$DESC_TPL" > "$DESC_FILE" | |
| 71 | + | |
| 72 | +awk -v report="$REPORT" ' | |
| 73 | + /\{\{module_report_contents\}\}/ { while ((getline line < report) > 0) print line; close(report); next } | |
| 74 | + { print } | |
| 75 | +' "$DESC_FILE" > .tmp/mr-desc.final | |
| 76 | +mv .tmp/mr-desc.final "$DESC_FILE" | |
| 77 | + | |
| 78 | +# 7. 幂等守门:查已有 opened MR | |
| 79 | +HTTP_CODE=$(curl -sS -o .tmp/existing.json -w '%{http_code}' \ | |
| 80 | + --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | |
| 81 | + "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests?source_branch=${CURRENT_BRANCH}&state=opened") | |
| 82 | + | |
| 83 | +if [ "$HTTP_CODE" != "200" ]; then | |
| 84 | + echo "[create-mr] ⚠️ 查询已有 MR 失败 (HTTP $HTTP_CODE)" >&2 | |
| 85 | + jq -r '.message // .error // .' .tmp/existing.json | head -c 200 >&2 | |
| 86 | + echo >&2 | |
| 87 | + rm -f .tmp/existing.json "$DESC_FILE" | |
| 88 | + exit 1 | |
| 89 | +fi | |
| 90 | + | |
| 91 | +EXISTING_IID=$(jq -r '.[0].iid // empty' .tmp/existing.json) | |
| 92 | +EXISTING_URL=$(jq -r '.[0].web_url // empty' .tmp/existing.json) | |
| 93 | +rm -f .tmp/existing.json | |
| 94 | + | |
| 95 | +if [ -n "$EXISTING_IID" ]; then | |
| 96 | + echo "[create-mr] 复用已有 opened MR: !$EXISTING_IID" >&2 | |
| 97 | + rm -f "$DESC_FILE" | |
| 98 | + echo "$EXISTING_IID $EXISTING_URL" | |
| 99 | + exit 0 | |
| 100 | +fi | |
| 101 | + | |
| 102 | +# 8. 创建新 MR(--rawfile desc 把 description 文件流入 jq → curl,不进 LLM 上下文) | |
| 103 | +CREATE_RESP=$(curl -sS -X POST \ | |
| 104 | + --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | |
| 105 | + --header "Content-Type: application/json" \ | |
| 106 | + --data "$(jq -n \ | |
| 107 | + --arg src "$CURRENT_BRANCH" \ | |
| 108 | + --arg tgt "$TARGET_BRANCH" \ | |
| 109 | + --arg title "$TITLE" \ | |
| 110 | + --rawfile desc "$DESC_FILE" \ | |
| 111 | + '{source_branch: $src, target_branch: $tgt, title: $title, description: $desc, remove_source_branch: false}')" \ | |
| 112 | + "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests") | |
| 113 | + | |
| 114 | +MR_IID=$(echo "$CREATE_RESP" | jq -r '.iid // empty') | |
| 115 | +MR_URL=$(echo "$CREATE_RESP" | jq -r '.web_url // empty') | |
| 116 | + | |
| 117 | +if [ -z "$MR_IID" ]; then | |
| 118 | + echo "[create-mr] ⚠️ MR 创建失败:" >&2 | |
| 119 | + echo "$CREATE_RESP" | jq . >&2 | |
| 120 | + rm -f "$DESC_FILE" | |
| 121 | + exit 1 | |
| 122 | +fi | |
| 123 | + | |
| 124 | +rm -f "$DESC_FILE" | |
| 125 | +echo "$MR_IID $MR_URL" | ... | ... |
skills/coding/test-gate/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: test-gate |
| 3 | -description: MR 创建前的唯一硬闸门。子会话执行 scripts/test.sh(setup-test-db.sh 清库后,Spring Boot 启动时 Flyway 自动 apply 当前 sql/migrations/V*.sql),任一失败则停止。 | |
| 3 | +description: MR 创建前的硬闸门。子会话跑 scripts/test.sh 全量测试,绿则进入 module-report,红则停下并按失败类型引导用户。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,71 +9,53 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) |
| 9 | 9 | |
| 10 | 10 | # test-gate |
| 11 | 11 | |
| 12 | -## 执行步骤 | |
| 12 | +模块所有 REQ 完成后的硬闸门:子会话跑 `./scripts/test.sh`(含本模块新增 + 已合并模块回归),按模板渲染证据并 commit 到 module 分支。绿色继续,红色停下,按失败类型给用户恢复路径。 | |
| 13 | 13 | |
| 14 | -1. **派发子会话**(`Agent`,general-purpose)运行 `./scripts/test.sh`: | |
| 14 | +## 执行步骤 | |
| 15 | 15 | |
| 16 | +1. 派发 Agent 子会话(general-purpose)运行 `./scripts/test.sh`,子会话只返回结构化 JSON(不输出描述文字): | |
| 17 | + ```json | |
| 18 | + { | |
| 19 | + "command": "./scripts/test.sh", | |
| 20 | + "exit_code": <int>, | |
| 21 | + "passed": <int>, | |
| 22 | + "failed": <int>, | |
| 23 | + "stdout_excerpt": "<最后 30 行,含 FAIL 摘要>" | |
| 24 | + } | |
| 16 | 25 | ``` |
| 17 | - 任务:运行项目本地测试闸门。不要修改任何代码或数据。步骤: | |
| 18 | - 1. cd 到仓库根目录。 | |
| 19 | - 2. 执行:./scripts/test.sh | |
| 20 | - 3. 仅返回 JSON:{"command":"./scripts/test.sh","exit_code":<int>,"passed":<int>,"failed":<int>,"stdout_excerpt":"<最后 30 行,包含 FAIL 摘要>"} | |
| 21 | - 不要输出任何描述性文字。 | |
| 22 | - ``` | |
| 23 | - | |
| 24 | -2. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/test-gate-result-template.md`,用结果填充槽位。 | |
| 25 | - | |
| 26 | -3. 写入 `docs/superpowers/module-reports/<module_id>-test-gate.md`。 | |
| 27 | - | |
| 28 | -3b. **commit evidence 到 module 分支**(确保 test-gate.md 随 MR 合并进默认分支,审计可追溯): | |
| 29 | - | |
| 26 | +2. 按 `${CLAUDE_SKILL_DIR}/templates/test-gate-result-template.md` 渲染证据,写入 `docs/superpowers/module-reports/<module_id>-test-gate.md`。 | |
| 27 | +3. Commit evidence 到 module 分支(保证证据随 MR 合并进默认分支可审计): | |
| 30 | 28 | ```bash |
| 31 | 29 | git add docs/superpowers/module-reports/<module_id>-test-gate.md |
| 32 | 30 | git commit -m "chore(<module_id>): add local test-gate evidence" |
| 33 | 31 | ``` |
| 34 | - | |
| 35 | -4. 如果 `exit_code = 0` → 交接给 `module-report`(输出 `test-gate: 通过`)。 | |
| 36 | - | |
| 37 | -5. 否则打印以下横幅并**停下**(不自动重试、不自动修复——失败分类需人工判断): | |
| 32 | +4. **`exit_code = 0`** → 调用 `Skill(module-report)`。 | |
| 33 | +5. **失败** → 打印以下横幅并停止,不调用下游 skill;不自动重试、不自动修复(失败分类需人工判断): | |
| 38 | 34 | |
| 39 | 35 | ``` |
| 40 | 36 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 41 | 37 | [test-gate] ⚠️ 未通过 |
| 42 | - | |
| 43 | - 失败清单: <失败测试清单(来自子会话 JSON)> | |
| 44 | - stdout 摘录: <最后 30 行 / FAIL 摘要> | |
| 38 | + 失败清单: <子会话 JSON 的 failed 摘要> | |
| 45 | 39 | 详细证据: docs/superpowers/module-reports/<module_id>-test-gate.md |
| 46 | 40 | |
| 47 | - 请根据失败类型选择下一步: | |
| 48 | - | |
| 49 | - ① 测试脆弱(flakey,偶发) | |
| 50 | - → 重新运行 /erp-workflow:coding-start | |
| 51 | - (module-start 幂等:reviews 已全 approve 会跳过功能循环,直接重新执行本闸门) | |
| 52 | - | |
| 53 | - ② 真有回归(某个 REQ 破坏了其他 REQ/已合并模块) | |
| 54 | - → 定位到具体 REQ,删除其 review 记录: | |
| 55 | - rm docs/superpowers/reviews/*-<REQ-id>.md | |
| 56 | - → 重新运行 /erp-workflow:coding-start | |
| 57 | - (module-start 把该 REQ 视为未完成,重走 brainstorm→...→review 循环修复) | |
| 58 | - | |
| 59 | - ③ 环境/依赖问题(DB 连不上、外部 API 失败、证书失效) | |
| 60 | - → 触发中断 #3(外部接口不可达) | |
| 61 | - → 调用 Skill(interrupt-check) 追加 Blocker 到本模块任一 plan 文件 | |
| 62 | - → 修复环境后重新运行 /erp-workflow:coding-start | |
| 41 | + 按失败类型选恢复路径: | |
| 42 | + ① 测试脆弱(偶发 flakey)→ 重跑 /erp-workflow:coding-start | |
| 43 | + (module-start 幂等:reviews 已全 approve 会跳过功能循环,直接重跑本闸门) | |
| 44 | + ② 真有回归(某 REQ 破坏其它 REQ 或已合并模块) | |
| 45 | + → rm docs/superpowers/reviews/*-<REQ-id>.md | |
| 46 | + → 重跑 /erp-workflow:coding-start(module-start 视该 REQ 未完成,重走功能循环) | |
| 47 | + ③ 环境/依赖问题(DB 连不上 / 外部 API 失败 / 证书失效) | |
| 48 | + → Skill(interrupt-check) 追加 Blocker 到任一 plan 文件 → 修复环境 → 重跑 | |
| 63 | 49 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 64 | 50 | ``` |
| 65 | 51 | |
| 66 | - 然后**停止**,不调用下游 skill(module-report / mr-create)。 | |
| 67 | - | |
| 68 | 52 | ## 护栏 |
| 69 | 53 | |
| 70 | -- **绝不**在主会话直接执行 `./scripts/test.sh`。 | |
| 71 | -- **绝不**通过 `git push --no-verify` 绕过(hook `deny-no-verify.sh` 会拦截)。 | |
| 72 | - | |
| 73 | -## 衔接 | |
| 74 | - | |
| 75 | -立即调用 `Skill(module-report)` 生成模块报告。 | |
| 54 | +- **绝不**在主会话直接执行 `./scripts/test.sh`,必须通过子会话 | |
| 55 | +- **绝不**通过 `git push --no-verify` 绕过(hook `deny-no-verify.sh` 会硬拦) | |
| 76 | 56 | |
| 77 | 57 | ## 参考 |
| 78 | 58 | |
| 79 | 59 | - `${CLAUDE_SKILL_DIR}/templates/test-gate-result-template.md` |
| 60 | +- 上游:`module-start`(本模块 REQ 全 approve 后派发到此) | |
| 61 | +- 下游:`module-report`(绿色时) | ... | ... |
skills/crosscut/coding-start/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: coding-start |
| 3 | -description: B 阶段(Coding)入口。先验证 Plan 已完成;按 docs/02 § 二 REQ 开发顺序清单扫描,对每个 REQ 所属模块用 docs/08 的 `MR:` 字段 + GitLab API state 判定是否完成——merged 跳过,`—` 或 opened/closed 选为当前模块并派发到 module-start。派发前自动探测默认分支(main / master)并 git checkout + pull --ff-only 保持 base 最新。 | |
| 3 | +description: B 阶段(Coding)入口。验证 Plan 已完成后派发到 module-start;模块定位、分支管理与远程同步全部由 module-start 负责。 | |
| 4 | 4 | user-invocable: true |
| 5 | -allowed-tools: Skill Read Glob Grep Bash(curl *) Bash(jq *) Bash(git branch *) Bash(git checkout *) Bash(git pull *) Bash(git status *) Bash(git symbolic-ref *) Bash(sed *) Bash(mkdir -p .tmp) Bash(rm -f .tmp/*) | |
| 5 | +allowed-tools: Skill Read Glob Grep Bash(cat *) | |
| 6 | 6 | --- |
| 7 | 7 | |
| 8 | 8 | **所有输出必须使用中文。** |
| ... | ... | @@ -11,157 +11,33 @@ B 阶段(Coding)的入口分发器。 |
| 11 | 11 | |
| 12 | 12 | ## 执行步骤 |
| 13 | 13 | |
| 14 | -### 步骤 1:确认 docs/08 存在 | |
| 15 | - | |
| 16 | -检查 `docs/08-模块任务管理.md`存在。 | |
| 17 | -- 不存在 → 打印"⚠️ 项目尚未初始化,请先运行 `/erp-workflow:plan-start`"并停下。 | |
| 18 | - | |
| 19 | -### 步骤 2:Plan 完成性检查 | |
| 20 | - | |
| 21 | -用 `Grep` 在 `docs/08-模块任务管理.md` 搜索 `^- \[ \] A[0-5]`(pattern 可以直接匹配父项未勾的情况;也可以用更宽的 `^[[:space:]]*- \[ \].*A[0-5]` 覆盖子项)。 | |
| 22 | - | |
| 23 | -- **命中任一A 阶段未勾选** → 打印并停下: | |
| 24 | - ``` | |
| 25 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 26 | - [coding-start] ⚠️ Plan 尚未完成 | |
| 27 | - | |
| 28 | - docs/08 § 一 还有未勾选项,请先运行: | |
| 29 | - /erp-workflow:plan-start | |
| 30 | - | |
| 31 | - 继续 Plan 阶段直到 A5 下游文档生成完成,再回来运行 coding-start。 | |
| 32 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 33 | - ``` | |
| 34 | - | |
| 35 | -- 无命中 → Plan 已完成,进入步骤 3。 | |
| 36 | - | |
| 37 | -### 步骤 3:按 docs/02 REQ 序 + MR state 找当前模块 | |
| 38 | - | |
| 39 | -> `MR:` 字段 × GitLab API state 的三组合判定规则见 `CLAUDE.md § ✅ 模块完成判定规则 § 模块状态语义`。 | |
| 40 | - | |
| 41 | -1. 用 `Read` 读取 `docs/02-开发计划.md` 的 § 二;用 `Grep`(pattern `^\|\s*[0-9]+\s*\|\s*\*\*(REQ-[A-Z0-9]+-[0-9]+)\*\*\s*\|\s*(module_\w+)`,`-n`)抽取所有表格数据行,按行号升序得 `req_order[]`,每项含 `req_id` + `module_id`。 | |
| 42 | -2. 若 `req_order` 为空 → 打印错误并**停下**(与原逻辑相同,略)。 | |
| 43 | -3. **初始化模块完成缓存 `module_merged[module_id → bool]`**(空)。 | |
| 44 | -4. 按序遍历 `req_order[]`: | |
| 45 | - - 若 `module_merged[module_id]` 已有缓存: | |
| 46 | - - `true` → 跳过本 REQ,继续下一个 | |
| 47 | - - `false` → `current_module = module_id`,结束遍历,进入步骤 4 | |
| 48 | - - 未缓存 → 读取 docs/08 § 二 该模块条目的 ` - MR:` 字段: | |
| 49 | - - `MR: —` → `module_merged[module_id] = false`;`current_module = module_id`,结束遍历 | |
| 50 | - - `MR: !<iid>` → 先 `Bash`: `set -a; . ./.env.local; set +a` 加载凭据,然后**分步校验 HTTP 状态 + 返回条数 + state 枚举**(v3 API 用 iid 过滤列表): | |
| 51 | - ```bash | |
| 52 | - mkdir -p .tmp | |
| 53 | - HTTP_CODE=$(curl -sS -o .tmp/mr.json -w '%{http_code}' \ | |
| 54 | - --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ | |
| 55 | - "${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/merge_requests?iid=<iid>") | |
| 56 | - ``` | |
| 57 | - **硬停条件**(触发任一则打印诊断横幅并**停下**,不派发;任何"查不到就假设未 merged"的静默处理都禁止): | |
| 58 | - 1. `HTTP_CODE != 200`:API 不可达 / token 错 / URL 错 | |
| 59 | - 2. `HTTP_CODE == 200` 但 `jq 'length' .tmp/mr.json != 1`:docs/08 记了 `!<iid>` 但远程查不到对应 MR(数据不一致) | |
| 60 | - 3. `state = jq -r '.[0].state' .tmp/mr.json` 不在 `{merged, opened, closed}` 三个合法值里 | |
| 61 | - | |
| 62 | - 诊断横幅模板: | |
| 63 | - ``` | |
| 64 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 65 | - [coding-start] ⚠️ 无法确定 MR !<iid>(模块 <module_id>)的状态 | |
| 66 | - | |
| 67 | - 原因: <HTTP <code> | 查不到 MR | state=<异常值>> | |
| 68 | - API : ${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID} | |
| 69 | - body: <jq -r '.message // .error // .' .tmp/mr.json | head -c 200> | |
| 70 | - | |
| 71 | - 请按下列顺序核查: | |
| 72 | - 1. .env.local 的 GITLAB_TOKEN 是否有效(在 GitLab Profile → Private token 页检查) | |
| 73 | - 2. GITLAB_API_URL 前缀 / v3 路径 / host 是否匹配你的 GitLab 部署 | |
| 74 | - 3. GITLAB_PROJECT_ID 是否是该项目的 URL-encoded 路径或数字 ID | |
| 75 | - 4. docs/08 记的 iid 是否存在于 GitLab 项目的 MR 列表 | |
| 14 | +### 步骤 0:打印 B 阶段整体流程图 | |
| 76 | 15 | |
| 77 | - 修正后重跑 /erp-workflow:coding-start。 | |
| 78 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 79 | - ``` | |
| 80 | - 清理:`rm -f .tmp/mr.json`。 | |
| 81 | - | |
| 82 | - 校验通过后按 state 分派: | |
| 83 | - - `merged` → `module_merged[module_id] = true`,跳过本 REQ | |
| 84 | - - `opened` / `closed` → `module_merged[module_id] = false`;`current_module = module_id`,结束遍历 | |
| 85 | -5. 全遍历完仍无命中(所有模块都 merged) → 打印: | |
| 86 | - ``` | |
| 87 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 88 | - [coding-start] ✅ 所有模块已完成 | |
| 89 | - | |
| 90 | - docs/02 § 二 清单中每个 REQ 所属模块的 MR 都已 merged。项目结束。 | |
| 91 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 92 | - ``` | |
| 93 | - 并**停下**。 | |
| 94 | -6. 命中后:记录 `current_module` 的 `MR:` 字段值(`—` / `!<iid>-opened` / `!<iid>-closed` / `!<iid>-查不到`,用于步骤 5 横幅展示)。 | |
| 95 | - | |
| 96 | -### 步骤 4:探测默认分支 + 切回并同步远程(准备 module-start 切新分支的干净 base) | |
| 97 | - | |
| 98 | -4.0 **探测默认分支**: | |
| 16 | +每次入口都先展示总图,再做后续校验和派发: | |
| 99 | 17 | |
| 100 | 18 | ```bash |
| 101 | -DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||') | |
| 102 | -if [ -z "$DEFAULT_BRANCH" ]; then | |
| 103 | - # HEAD 未设置,回退:在 main / master 里取第一个实际存在的远程分支 | |
| 104 | - DEFAULT_BRANCH=$(git branch -r --format='%(refname:short)' | grep -E '^origin/(main|master)$' | head -1 | sed 's|^origin/||') | |
| 105 | -fi | |
| 19 | +cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/coding-start/banners/flow-overview.txt" | |
| 106 | 20 | ``` |
| 107 | 21 | |
| 108 | -- `DEFAULT_BRANCH` 为空 → 打印错误并**停下**(既无 `origin/main` 也无 `origin/master`,说明远程未就绪,提示用户先完成 Plan 的首次 push 或 `git remote set-head origin -a` 设置 HEAD)。 | |
| 109 | - | |
| 110 | -4.1 **切回 + 同步**: | |
| 111 | - | |
| 112 | -- `Bash`: `git branch --show-current` → `current_branch`。 | |
| 113 | -- 若 `current_branch != $DEFAULT_BRANCH`: | |
| 114 | - - `Bash`: `git status --porcelain`;非空 → 打印: | |
| 115 | - ``` | |
| 116 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 117 | - [coding-start] ⚠️ 当前分支 <current_branch> 有未提交改动,无法切换到 <DEFAULT_BRANCH> | |
| 118 | - | |
| 119 | - <git status 输出> | |
| 120 | - | |
| 121 | - 请手工处理后重新运行 /erp-workflow:coding-start。 | |
| 122 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 123 | - ``` | |
| 124 | - 并**停下**。 | |
| 125 | - - `Bash`: `git checkout "$DEFAULT_BRANCH"`。 | |
| 126 | -- `Bash`: `git pull --ff-only origin "$DEFAULT_BRANCH"`。 | |
| 127 | - - 失败(非 fast-forward / 网络 / 冲突)→ 打印错误横幅: | |
| 128 | - ``` | |
| 129 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 130 | - [coding-start] ⚠️ 同步 <DEFAULT_BRANCH> 失败 | |
| 131 | - | |
| 132 | - 本地 <DEFAULT_BRANCH> 无法 fast-forward 到 remote。请手工处理: | |
| 133 | - git status | |
| 134 | - git log <DEFAULT_BRANCH>..origin/<DEFAULT_BRANCH> # 远程领先的 commit | |
| 135 | - git log origin/<DEFAULT_BRANCH>..<DEFAULT_BRANCH> # 本地未推的 commit | |
| 136 | - 修复后重新运行 /erp-workflow:coding-start。 | |
| 137 | - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 138 | - ``` | |
| 139 | - 并**停下**。 | |
| 22 | +### 步骤 1:确认 docs/08 存在 | |
| 140 | 23 | |
| 141 | -### 步骤 5:打印横幅并分发 | |
| 24 | +检查 `docs/08-模块任务管理.md`存在。 | |
| 25 | +- 不存在 → 打印"⚠️ 项目尚未初始化,请先运行 `/erp-workflow:plan-start`"并停下。 | |
| 142 | 26 | |
| 143 | -``` | |
| 144 | -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 145 | - [coding-start] | |
| 146 | - 阶段:B 编码 | |
| 147 | - 当前模块:<current_module> | |
| 148 | - MR 状态:<未建 | opened | closed | 查不到> | |
| 149 | - 下一步:invoke module-start | |
| 150 | -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | |
| 151 | -``` | |
| 27 | +### 步骤 2:Plan 完成性检查 | |
| 152 | 28 | |
| 153 | -然后立即用 `Skill` 工具调用 `module-start`。 | |
| 29 | +读取 `docs/08-模块任务管理.md § 一`,判断 A0~A5 是否全部勾选(含子项)。 | |
| 154 | 30 | |
| 155 | -> 分发前先调用 `interrupt-check`;触发中断则停在此不分发。 | |
| 31 | +- 任一未勾选 → 提示用户先运行 `/erp-workflow:plan-start` 完成 A 阶段,并停下 | |
| 32 | +- 全部勾选 → 进入步骤 3 | |
| 156 | 33 | |
| 157 | -## 设计要点 | |
| 34 | +### 步骤 3:派发到 module-start | |
| 158 | 35 | |
| 159 | -- **完成判定直接读取 `MR:` 字段 + GitLab API state**(curl,`PRIVATE-TOKEN` 头):MR 未 merge 前 docs/08 没有任何"已完成"标记;用户提前触发 coding-start 时,步骤 3 扫描到 MR opened 仍会选中当前模块,module-start 会 `git checkout module-<id>` 回到原分支继续,不会跳到下一模块。 | |
| 160 | -- **每次派发前都 pull 默认分支**:代码同步的同时,也保证 module-start 切出新分支时 base 新鲜。默认分支由步骤 4.0 探测(main 或 master),不硬编码。 | |
| 36 | +打印一句 `[coding-start] → invoke module-start` 后立即用 `Skill` 工具调用 `module-start`。当前模块的定位、MR 状态、分支管理与 REQ 列表全部由 `module-start` 自行处理。 | |
| 161 | 37 | |
| 162 | 38 | ## 参考 |
| 163 | 39 | |
| 164 | -- `docs/02-开发计划.md § 二 开发顺序清单`(分发追踪) | |
| 165 | -- `docs/08-模块任务管理.md § 二`(模块元数据,含 `MR:` 字段) | |
| 166 | -- `CLAUDE.md`(项目指令) | |
| 40 | +- `docs/08-模块任务管理.md § 一`(A0~A5 进度勾选,本 skill 步骤 2 读取) | |
| 41 | +- `module-start`(下游:模块定位 + REQ 列表 + 推进) | |
| 167 | 42 | - `plan-start`(姊妹入口,A 阶段) |
| 43 | +- `CLAUDE.md`(项目指令) | ... | ... |
skills/crosscut/coding-start/banners/flow-overview.txt
0 → 100644
| 1 | +┌────────────────────────────────────────────────────────┐ | |
| 2 | +│ 🛠️ 阶段 B:编码(按模块循环) │ | |
| 3 | +│ │ | |
| 4 | +│ coding-start │ | |
| 5 | +│ (Plan 完成校验) │ | |
| 6 | +│ ↓ │ | |
| 7 | +│ module-start │ | |
| 8 | +│ (定位当前模块 + 推进未完成 REQ) │ | |
| 9 | +│ ↓ │ | |
| 10 | +│ ┌─ 功能循环(每个 REQ)─────────────┐ │ | |
| 11 | +│ │ feature-brainstorm │ │ | |
| 12 | +│ │ ↓ │ │ | |
| 13 | +│ │ feature-plan │ │ | |
| 14 | +│ │ ↓ │ │ | |
| 15 | +│ │ feature-tdd │ │ | |
| 16 | +│ │ ↓ │ │ | |
| 17 | +│ │ feature-verify │ │ | |
| 18 | +│ │ ↓ │ │ | |
| 19 | +│ │ feature-review │ │ | |
| 20 | +│ │ ├ approve → 回 module-start │ | |
| 21 | +│ │ └ request-changes ↺ 重跑 verify │ | |
| 22 | +│ │ (自修复 ≤5 轮) │ | |
| 23 | +│ └────────────────────────────────────┘ │ | |
| 24 | +│ ↓ (本模块所有 REQ approve) │ | |
| 25 | +│ test-gate (子会话跑 ./scripts/test.sh) │ | |
| 26 | +│ ↓ │ | |
| 27 | +│ module-report (生成 12 节模块完成报告) │ | |
| 28 | +│ ↓ │ | |
| 29 | +│ mr-create (push + 创建 GitLab MR) │ | |
| 30 | +│ ↓ │ | |
| 31 | +│ 停下,等待人工 Approve + Merge │ | |
| 32 | +│ ↺ 用户重跑 coding-start │ | |
| 33 | +│ ╰──→ 回到 module-start(下一模块) │ | |
| 34 | +└────────────────────────────────────────────────────────┘ | ... | ... |
skills/crosscut/cross-module-log/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: cross-module-log |
| 3 | -description: 为 log-cross-module.sh hook 自动追加到跨模块改动日志中的条目批量补填「原因」和「影响评估」。**仅由 module-report § ⑦ 硬验收时调用**,CC 编辑中途不主动调用(节省 LLM 调用次数)。 | |
| 3 | +description: 批量补填跨模块改动日志中 hook 留下的 `TBD(CC 补)`(原因 / 影响评估两列)。仅由 module-report § ⑦ 硬验收时调用。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Edit Bash(git branch *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,29 +9,23 @@ allowed-tools: Read Write Edit Bash(git branch *) |
| 9 | 9 | |
| 10 | 10 | # cross-module-log |
| 11 | 11 | |
| 12 | -## 说明 | |
| 13 | - | |
| 14 | -软规则 S2 执行:对**非当前模块**文件的每次编辑(无论目标模块是否已 MR merged)必须记录原因 + 影响评估。 | |
| 15 | - | |
| 16 | -**本日志由 hook + skill 协作维护**——hook 自动落存根(4 列机械数据),CC 不主动处理 TBD;由 `module-report` § ⑦ 硬验收阶段统一调用本 skill,一次性推断补齐「原因 / 影响评估」两列。**不需要人工填写**,人工只在最终看 MR 描述时复核。 | |
| 12 | +软规则 S2 自动留痕:hook `log-cross-module.sh` 在每次对**非当前模块**文件的改动时追加 4 列存根(时间戳 / 目标模块 / 文件 / 改动摘要),「原因」「影响评估」两列写 `TBD(CC 补)`。本 skill 在 `module-report § ⑦` 硬验收阶段一次性推断补齐两列;CC 编辑代码中途不主动调用(节省 LLM 调用次数)。 | |
| 17 | 13 | |
| 18 | 14 | ## 执行步骤 |
| 19 | 15 | |
| 20 | -1. 确定当前模块(从当前 git 分支名推导:`git branch --show-current` → `module-<module_id>` → 取 `module_id`。`module-start` 步骤 3 保证本 skill 执行时一定处于 `module-*` 分支)。 | |
| 21 | -2. 打开 `docs/superpowers/module-reports/<current>-cross-module.md`。**文件不存在 → 打印 `cross-module-log: 无跨模块改动,跳过` 并退出**(hook 是日志文件的唯一创建者;文件缺失意味着本模块周期内 hook 从未追加过条目,没有 TBD 需要补)。 | |
| 22 | -3. 找到「原因」或「影响评估」列中含 `TBD(CC 补)` 的行。 | |
| 23 | -4. 对每个 TBD 行,CC **自主推断**填写以下两列(基于当前 session 的改动上下文 + REQ 卡片 + 目标模块的代码): | |
| 24 | - - **原因**:为什么要修改目标模块的代码?当前模块的哪个需求迫使这样做? | |
| 25 | - - **影响评估**:目标模块的哪些 API / 行为 / 调用方可能受影响?其现有测试是否仍然有效?是否需要新测试?(1-3 句话) | |
| 26 | -5. 编辑该行;保持时间戳 / 目标模块 / 文件 / 改动摘要列不变。 | |
| 27 | -6. 输出确认:`cross-module-log: 更新了 N 行`。 | |
| 28 | - | |
| 29 | -## 下游 | |
| 30 | - | |
| 31 | -填充后的日志会被 `module-report` 原文嵌入到 `module-report-template.md` § ⑦。 | |
| 16 | +1. 从 `git branch --show-current` 取 `module_id`(去掉 `module-` 前缀;`module-start` 步骤 3 保证此时在 `module-*` 分支)。 | |
| 17 | +2. 打开 `docs/superpowers/module-reports/<module_id>-cross-module.md`。文件不存在 → 输出 `cross-module-log: 无跨模块改动,跳过` 并结束(hook 是日志文件唯一创建者;缺失意味着本模块周期内没有跨模块改动)。 | |
| 18 | +3. 用 `Edit` 逐行替换「原因」或「影响评估」列含 `TBD(CC 补)` 的行: | |
| 19 | + - **保持时间戳 / 目标模块 / 文件 / 改动摘要四列不变** | |
| 20 | + - **原因**:为什么要改目标模块的代码?当前模块的哪个 REQ 迫使这样做? | |
| 21 | + - **影响评估**:目标模块的哪些 API / 行为 / 调用方可能受影响?现有测试是否仍有效?是否需要新测试?(1-3 句话) | |
| 22 | + - 推断依据:当前 session 的改动上下文 + REQ 卡片 + 目标模块代码 | |
| 23 | +4. 输出 `cross-module-log: 更新了 N 行`。 | |
| 32 | 24 | |
| 33 | 25 | ## 参考 |
| 34 | 26 | |
| 35 | -- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md`(仅供 `hooks/scripts/log-cross-module.sh` 创建日志文件时渲染表头;本 skill 自身不再读取) | |
| 36 | -- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-row-template.md`(行结构参考,hook 内联拼接行字符串,本 skill 直接编辑已有行) | |
| 37 | -- `CLAUDE.md` § 🟡 软规则 S2 | |
| 27 | +- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-template.md`(hook 创建日志文件时渲染表头,本 skill 不读) | |
| 28 | +- `${CLAUDE_SKILL_DIR}/templates/cross-module-log-row-template.md`(行结构参考,hook 拼接用;本 skill 直接 Edit 已有行) | |
| 29 | +- `CLAUDE.md § 🟡 软规则 S2` | |
| 30 | +- 上游:`module-report § ⑦`(唯一调用方) | |
| 31 | +- 下游:`module-report` 把补齐后的日志原文嵌入 § ⑦ | ... | ... |
skills/crosscut/interrupt-check/SKILL.md
| 1 | 1 | --- |
| 2 | 2 | name: interrupt-check |
| 3 | -description: 在每个功能循环步骤和生成重要制品前运行。检查 CLAUDE.md 中的 3 项中断清单,触发任一项则追加 Blocker 到计划文件并停止。 | |
| 3 | +description: 检查 CLAUDE.md § 🚩 中断机制 3 项是否触发,触发则追加 Blocker 到当前 plan 文件并停下,等用户决策。 | |
| 4 | 4 | user-invocable: false |
| 5 | 5 | allowed-tools: Read Write Bash(mysql *) |
| 6 | 6 | --- |
| ... | ... | @@ -9,19 +9,18 @@ allowed-tools: Read Write Bash(mysql *) |
| 9 | 9 | |
| 10 | 10 | # interrupt-check |
| 11 | 11 | |
| 12 | -## 说明 | |
| 12 | +核对 CLAUDE.md § 🚩 中断机制中的 3 项是否触发;任一触发 → 按模板渲染 Blocker 块追加到当前 plan 文件,**停下,用户决策前不调用任何下游 skill**。 | |
| 13 | 13 | |
| 14 | -验证 CLAUDE.md § 🚩 中断机制中的 3 项均未触发。触发任一项则中断。 | |
| 14 | +> 需求歧义 / schema 缺口 / 技术栈外组件引入等场景不在本清单,由各 feature-* 的 `AskUserQuestion` 流程处理。 | |
| 15 | 15 | |
| 16 | -> 需求歧义 / schema 缺口 / 技术栈外组件引入等场景由各 feature-* skill 的 `AskUserQuestion` 流程承接,不进入本清单,不会在这里触发。 | |
| 16 | +## 调用方(现役) | |
| 17 | 17 | |
| 18 | -## 调用时机 | |
| 18 | +- **feature-tdd 步骤 3.e**:同一测试连续 >10 次修复失败时主动调用(命中条件 1) | |
| 19 | +- **test-gate 失败横幅 ③**:环境/依赖问题时引导用户手工调用(命中条件 3) | |
| 19 | 20 | |
| 20 | -- 每个功能循环步骤开始前(3.1-3.5) | |
| 21 | -- 生成模块级制品前(模块报告、MR 描述) | |
| 22 | -- 用户请求涉及外部依赖、环境凭据变更时 | |
| 21 | +其它 skill 均已不再预防性调用本 skill;条件 2 / 3 在自然出错路径上由各 skill 自身的诊断信息覆盖,本 skill 主要承接条件 1 的硬触发与用户主动登记。 | |
| 23 | 22 | |
| 24 | -## 检查清单(3 项 — 权威来源:CLAUDE.md) | |
| 23 | +## 检查清单(权威来源:`CLAUDE.md § 🚩 中断机制`) | |
| 25 | 24 | |
| 26 | 25 | 1. **测试反复失败** — 同一功能中同一测试连续 10 次修复失败 |
| 27 | 26 | 2. **要改密钥/账密/包名** — 涉及 `docs/07-环境配置.md` 中的人工填写字段 |
| ... | ... | @@ -29,21 +28,12 @@ allowed-tools: Read Write Bash(mysql *) |
| 29 | 28 | |
| 30 | 29 | ## 执行步骤 |
| 31 | 30 | |
| 32 | -1. 读取当前功能的规格/计划文件路径(从对话或 `docs/08` 获取)。 | |
| 33 | -2. 逐项检查 3 个中断。如果全部未触发 → 输出 `interrupt-check: 通过`,退出。 | |
| 34 | -3. 触发时: | |
| 35 | - - 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/interrupt-block-template.md` | |
| 36 | - - 填充槽位(interrupt_number、interrupt_name、description、affected_scope、recommendation、decision_needed) | |
| 37 | - - 将渲染后的块追加到当前计划文件:`docs/superpowers/plans/YYYY-MM-DD-<REQ-id>.md` | |
| 38 | - - 向会话打印一句话摘要 + 指向计划文件的路径 | |
| 39 | - - **停止** — 在用户决策前不调用任何后续 skill | |
| 40 | - | |
| 41 | -## 输出 | |
| 42 | - | |
| 43 | -`interrupt-check: 通过`(仅会话),或一个 `## 🚩 Blocker` 块追加到功能计划文件。 | |
| 31 | +1. 逐项核对 3 个中断条件。**全部未触发** → 输出 `interrupt-check: 通过`,结束。 | |
| 32 | +2. **触发任一** → 按 `${CLAUDE_SKILL_DIR}/templates/interrupt-block-template.md` 渲染 Blocker 块,追加到当前 plan 文件(典型路径 `docs/superpowers/plans/<YYYY-MM-DD>-<REQ-id>.md`;test-gate 场景下追加到本模块任一已有 plan 文件)。 | |
| 33 | +3. 向会话打印一句话摘要 + 指向 plan 文件路径,**停下**。 | |
| 44 | 34 | |
| 45 | 35 | ## 参考 |
| 46 | 36 | |
| 47 | 37 | - `${CLAUDE_SKILL_DIR}/templates/interrupt-block-template.md` |
| 48 | -- `CLAUDE.md` § 🚩 中断机制(权威 3 条) | |
| 49 | -- `CLAUDE.md` § 🟡 软规则(S2 跨模块改动,不触发中断但需留痕) | |
| 38 | +- `CLAUDE.md § 🚩 中断机制`(权威 3 条) | |
| 39 | +- `CLAUDE.md § 🟡 软规则`(S2 跨模块改动不触发中断;由 `cross-module-log` 处理) | ... | ... |
skills/crosscut/plan-start/SKILL.md
| ... | ... | @@ -36,10 +36,10 @@ docs/08 § 一 是**Plan 阶段进度追踪**(A0~A5 çš„ checkbox)。§ äºŒçš |
| 36 | 36 | |
| 37 | 37 | ### 2.1 Plan å·²å®Œæˆ |
| 38 | 38 | |
| 39 | -A 阶段所有 checkbox å‡ `[x]`ã€‚æ— åŽç» skill,本æ¥éª¤**è‡ªè¡Œæ‰“å°æµç¨‹å›¾**,然åŽ**åœä¸‹**: | |
| 39 | +A 阶段所有 checkbox å‡ `[x]`ã€‚æ— åŽç» skill,本æ¥éª¤**å…ˆæ‰“å°æ•´ä½“æµç¨‹å›¾**,å†è¾“å‡ºå®Œæˆæ¨ªå¹…,然åŽ**åœä¸‹**: | |
| 40 | 40 | |
| 41 | 41 | ```bash |
| 42 | -cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/plan-start/banners/flow-done.txt" | |
| 42 | +cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/plan-start/banners/flow-overview.txt" | |
| 43 | 43 | ``` |
| 44 | 44 | |
| 45 | 45 | å†å‘ç”¨æˆ·è¾“å‡ºå®Œæˆæ¨ªå¹…: |
| ... | ... | @@ -66,6 +66,12 @@ cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/plan-start/banners/flow-done.txt" |
| 66 | 66 | |
| 67 | 67 | ### 2.2 æ£å¸¸æ´¾å‘(`åŽç»` éžç©ºï¼‰ |
| 68 | 68 | |
| 69 | +å…ˆæ‰“å°æ•´ä½“æµç¨‹å›¾ï¼Œå†æ‰“å°åˆ†å‘通知: | |
| 70 | + | |
| 71 | +```bash | |
| 72 | +cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/plan-start/banners/flow-overview.txt" | |
| 73 | +``` | |
| 74 | + | |
| 69 | 75 | 打å°ç®€çŸåˆ†å‘通知: |
| 70 | 76 | |
| 71 | 77 | ``` | ... | ... |
skills/crosscut/plan-start/banners/flow-done.txt renamed to skills/crosscut/plan-start/banners/flow-overview.txt
skills/plan/downstream-gen/SKILL.md
| ... | ... | @@ -123,7 +123,7 @@ cp "${CLAUDE_SKILL_DIR}/templates/docs-10-header-template.md" docs/10-验收检æ |
| 123 | 123 | 1. å®¡æ ¸ docs/01~10 + CLAUDE.md + sql/migrations/V1 + å„ scripts/* |
| 124 | 124 | |
| 125 | 125 | 2. 把全部 Plan 产物 commit: |
| 126 | - git add -A && git commit -m "chore: plan phase A0~A5 done" | |
| 126 | + git add -A && git commit -m "chore: plan phase done" | |
| 127 | 127 | |
| 128 | 128 | 3. 推到远程,按仓库状æ€äºŒé€‰ä¸€ï¼š |
| 129 | 129 | ... | ... |