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