Commit a6f9c34db2c6c668d718a081634a77616db2adb6

Authored by zichun
1 parent cc94a5f4

edit

.claude-plugin/plugin.json
@@ -2,5 +2,5 @@ @@ -2,5 +2,5 @@
2 "name": "erp-workflow", 2 "name": "erp-workflow",
3 "description": "ERP 项目全流程框架:阶段 A 计划(一次性) + 阶段 B 编码(模块循环 + 功能循环),含完整 skill 流水线 + 守门 hook + 软规则留痕。专为后端 Spring Boot + 前端 React + MySQL 8 的 ERP/管理类系统设计。", 3 "description": "ERP 项目全流程框架:阶段 A 计划(一次性) + 阶段 B 编码(模块循环 + 功能循环),含完整 skill 流水线 + 守门 hook + 软规则留痕。专为后端 Spring Boot + 前端 React + MySQL 8 的 ERP/管理类系统设计。",
4 "version": "0.1.0", 4 "version": "0.1.0",
5 - "skills": ["./skills/plan", "./skills/coding", "./skills/crosscut"] 5 + "skills": ["./skills/plan", "./skills/coding", "./skills/crosscut", "./skills/internal"]
6 } 6 }
README.md
@@ -164,7 +164,7 @@ erp-workflow-plugin/ @@ -164,7 +164,7 @@ erp-workflow-plugin/
164 |---|---|---| 164 |---|---|---|
165 | `erp-plan-start` | **A 阶段入口**。读取 docs/08 § 一 找第一个未勾 A 子项 → 派发对应 A skill;A 全部完成时提示运行 coding-start | **用户手动**运行 `/erp-workflow:erp-plan-start` | 165 | `erp-plan-start` | **A 阶段入口**。读取 docs/08 § 一 找第一个未勾 A 子项 → 派发对应 A skill;A 全部完成时提示运行 coding-start | **用户手动**运行 `/erp-workflow:erp-plan-start` |
166 | `erp-coding-start` | **B 阶段入口**。先验证 Plan 已完成;**按 `docs/02 § 二` REQ 序扫描**,对每个 REQ 所属模块查询 `MR: 字段 + glab mr view state`:`merged` 跳过;`—`/opened/closed/查不到 选为当前模块。派发前 `git checkout main + git pull --ff-only` 同步远程 base,然后调用 `erp-module-start` | **用户手动**运行 `/erp-workflow:erp-coding-start` | 166 | `erp-coding-start` | **B 阶段入口**。先验证 Plan 已完成;**按 `docs/02 § 二` REQ 序扫描**,对每个 REQ 所属模块查询 `MR: 字段 + glab mr view state`:`merged` 跳过;`—`/opened/closed/查不到 选为当前模块。派发前 `git checkout main + git pull --ff-only` 同步远程 base,然后调用 `erp-module-start` | **用户手动**运行 `/erp-workflow:erp-coding-start` |
167 -| `erp-red-flag-check` | 检查 CLAUDE.md 的 6 项红旗清单;命中则追加 Blocker 到计划文件并停下 | 功能循环各步骤和生成重要制品前自动调用 | 167 +| `erp-red-flag-check` | 检查 CLAUDE.md 的 3 项红旗清单;命中则追加 Blocker 到计划文件并停下 | 功能循环各步骤和生成重要制品前自动调用 |
168 | `erp-cross-module-log` | 给 `log-cross-module.sh` 追加的跨模块改动存根补「原因 / 影响评估」 | 用户看到 hook 提示后调用;`erp-module-start` 初始化日志文件时也会用其模板 | 168 | `erp-cross-module-log` | 给 `log-cross-module.sh` 追加的跨模块改动存根补「原因 / 影响评估」 | 用户看到 hook 提示后调用;`erp-module-start` 初始化日志文件时也会用其模板 |
169 169
170 ## Templates 清单(38 份) 170 ## Templates 清单(38 份)
skills/coding/erp-feature-brainstorm/SKILL.md
@@ -11,7 +11,7 @@ allowed-tools: Read Write Skill Bash(mysql *) @@ -11,7 +11,7 @@ allowed-tools: Read Write Skill Bash(mysql *)
11 11
12 ## 说明 12 ## 说明
13 13
14 -针对一个 REQ-XXX-NNN,委托 `superpowers:brainstorming` 进行头脑风暴,再把输出填入标准规格模板,产出单页功能规格。 14 +针对一个 REQ-XXX-NNN,委托本插件的 `superpower-brainstorming`(superpowers:brainstorming 的本地 fork,已剥掉 approval gates)进行头脑风暴,再把输出填入标准规格模板,产出单页功能规格。
15 15
16 ## 执行步骤 16 ## 执行步骤
17 17
@@ -20,7 +20,7 @@ allowed-tools: Read Write Skill Bash(mysql *) @@ -20,7 +20,7 @@ allowed-tools: Read Write Skill Bash(mysql *)
20 - 当前 REQ-XXX-NNN(从对话中获取,或 `docs/08` 当前模块下一个未完成的 REQ)。 20 - 当前 REQ-XXX-NNN(从对话中获取,或 `docs/08` 当前模块下一个未完成的 REQ)。
21 - REQ 卡片:`docs/01-需求清单/<module>.md` 中对应的 REQ-XXX-NNN 节。 21 - REQ 卡片:`docs/01-需求清单/<module>.md` 中对应的 REQ-XXX-NNN 节。
22 - 相关数据表(从 `docs/03` 或实时 mysql 命令行查询)。 22 - 相关数据表(从 `docs/03` 或实时 mysql 命令行查询)。
23 -3. 委托 `superpowers:brainstorming`,以 REQ 卡片 + schema 引用作为上下文 23 +3. 委托本插件 `superpower-brainstorming`,以 REQ 卡片 + schema 引用作为上下文;把步骤 4 推导出的落盘路径作为 caller-provided path 传入
24 4. 推导路径:`docs/superpowers/specs/$(date +%F)-<REQ-id>.md`。如已存在,征求用户确认后覆盖。 24 4. 推导路径:`docs/superpowers/specs/$(date +%F)-<REQ-id>.md`。如已存在,征求用户确认后覆盖。
25 5. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md`,从头脑风暴输出填充槽位: 25 5. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md`,从头脑风暴输出填充槽位:
26 - `goal`、`input`、`output`、`rules`、`constraints`、`schema_refs`、`api_refs`、`acceptance` 26 - `goal`、`input`、`output`、`rules`、`constraints`、`schema_refs`、`api_refs`、`acceptance`
@@ -35,5 +35,5 @@ allowed-tools: Read Write Skill Bash(mysql *) @@ -35,5 +35,5 @@ allowed-tools: Read Write Skill Bash(mysql *)
35 ## 参考 35 ## 参考
36 36
37 - `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md` 37 - `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md`
38 -- 委托:`superpowers:brainstorming` 38 +- 委托:`superpower-brainstorming`(本插件 `skills/internal/superpower-brainstorming/`,superpowers:brainstorming 的无门 fork)
39 - 守门:`erp-red-flag-check` 39 - 守门:`erp-red-flag-check`
skills/coding/erp-feature-plan/SKILL.md
@@ -16,7 +16,7 @@ allowed-tools: Read Write Grep Skill @@ -16,7 +16,7 @@ allowed-tools: Read Write Grep Skill
16 - 当前 REQ-XXX-NNN 及其规格文件 `docs/superpowers/specs/YYYY-MM-DD-<REQ>.md`(规格不存在则报错)。 16 - 当前 REQ-XXX-NNN 及其规格文件 `docs/superpowers/specs/YYYY-MM-DD-<REQ>.md`(规格不存在则报错)。
17 - 相关代码指针(已有的待修改文件,通过 Grep 发现)。 17 - 相关代码指针(已有的待修改文件,通过 Grep 发现)。
18 - `docs/04-技术规范.md` 和 `docs/09-项目目录结构.md`(编码规范 + 目录规范)。 18 - `docs/04-技术规范.md` 和 `docs/09-项目目录结构.md`(编码规范 + 目录规范)。
19 -3. 委托 `superpowers:writing-plans`,以规格 + 代码指针 + 规范作为上下文 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`。 20 4. 推导路径:`docs/superpowers/plans/$(date +%F)-<REQ-id>.md`。
21 5. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md`,填充 `files[]`、`tasks[]`、`commits[]`。 21 5. 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md`,填充 `files[]`、`tasks[]`、`commits[]`。
22 6. 强制要求:每个任务有失败测试标识、实现路径和完成标准。 22 6. 强制要求:每个任务有失败测试标识、实现路径和完成标准。
@@ -30,5 +30,5 @@ allowed-tools: Read Write Grep Skill @@ -30,5 +30,5 @@ allowed-tools: Read Write Grep Skill
30 ## 参考 30 ## 参考
31 31
32 - `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md` 32 - `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md`
33 -- 委托:`superpowers:writing-plans` 33 +- 委托:`superpower-writing-plans`(本插件 `skills/internal/superpower-writing-plans/`,superpowers:writing-plans 的无门 fork)
34 - 守门:`erp-red-flag-check` 34 - 守门:`erp-red-flag-check`
skills/coding/erp-feature-review/SKILL.md
@@ -11,7 +11,7 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *) @@ -11,7 +11,7 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *)
11 11
12 ## 执行步骤 12 ## 执行步骤
13 13
14 -1. 委托 `superpowers:code-reviewer`,以该 REQ 的 diff(`git diff <feature-start>..HEAD`)和规格作为输入。 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`。 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`。 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. 写入报告。 17 4. 写入报告。
@@ -36,6 +36,6 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *) @@ -36,6 +36,6 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *)
36 36
37 - `${CLAUDE_SKILL_DIR}/templates/feature-review-template.md` 37 - `${CLAUDE_SKILL_DIR}/templates/feature-review-template.md`
38 - Fix commit 格式与 `erp-feature-tdd` 的 `commit-message-template.md` 对齐(`fix(<scope>): <subject> <req_id>`) 38 - Fix commit 格式与 `erp-feature-tdd` 的 `commit-message-template.md` 对齐(`fix(<scope>): <subject> <req_id>`)
39 -- 委托:`superpowers:code-reviewer` 39 +- 委托:`superpower-code-reviewer`(本插件 `agents/superpower-code-reviewer.md`,superpowers:code-reviewer 的本地 fork)
40 - 上游:`erp-feature-verify` 40 - 上游:`erp-feature-verify`
41 - 下游:`erp-module-start`(approve)或 `erp-feature-verify`(request-changes) 41 - 下游:`erp-module-start`(approve)或 `erp-feature-verify`(request-changes)
skills/coding/erp-feature-review/templates/feature-review-template.md
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 req_id: {{req_id}} 2 req_id: {{req_id}}
3 date: {{date}} 3 date: {{date}}
4 round: {{round}} 4 round: {{round}}
5 -reviewer: superpowers:code-reviewer 5 +reviewer: superpower-code-reviewer
6 --- 6 ---
7 7
8 # Review: {{req_id}} — round {{round}} 8 # Review: {{req_id}} — round {{round}}
skills/coding/erp-feature-tdd/SKILL.md
@@ -23,7 +23,7 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) @@ -23,7 +23,7 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *)
23 b. **派发子会话**(通过 `Agent`,general-purpose)运行测试并确认失败;子会话只返回 `{command, exit_code, failing_assertion}`。主会话**不直接**运行测试。 23 b. **派发子会话**(通过 `Agent`,general-purpose)运行测试并确认失败;子会话只返回 `{command, exit_code, failing_assertion}`。主会话**不直接**运行测试。
24 c. 在 `impl_file` 处实现最小代码使测试通过。 24 c. 在 `impl_file` 处实现最小代码使测试通过。
25 d. **再次派发子会话**运行测试并确认通过。 25 d. **再次派发子会话**运行测试并确认通过。
26 - e. 持续失败(同一测试 >10 次修复尝试)→ 调用 `erp-red-flag-check`(红旗 #4)。 26 + e. 持续失败(同一测试 >10 次修复尝试)→ 调用 `erp-red-flag-check`(红旗 #1)。
27 f. 暂存变更并使用 `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` 提交;`scope` 匹配任务的模块,`subject` ≤50 字符,`req_id` 必填。 27 f. 暂存变更并使用 `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` 提交;`scope` 匹配任务的模块,`subject` ≤50 字符,`req_id` 必填。
28 5. 所有任务完成后 → 交接给 `erp-feature-verify`。 28 5. 所有任务完成后 → 交接给 `erp-feature-verify`。
29 29
@@ -41,5 +41,5 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) @@ -41,5 +41,5 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *)
41 ## 参考 41 ## 参考
42 42
43 - `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` 43 - `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md`
44 -- 委托:`superpowers:test-driven-development` 44 +- 原则参考:`superpowers:test-driven-development`(本插件未镜像;仅作为 TDD 原则手册参考,不做运行时 invoke — 本 skill 已把"子会话跑测试 + REQ tag commit"流程直接写死)
45 - 守门:`erp-red-flag-check` 45 - 守门:`erp-red-flag-check`
skills/coding/erp-feature-verify/SKILL.md
@@ -37,4 +37,4 @@ allowed-tools: Skill Read Write Agent @@ -37,4 +37,4 @@ allowed-tools: Skill Read Write Agent
37 ## 参考 37 ## 参考
38 38
39 - `${CLAUDE_SKILL_DIR}/templates/feature-verify-evidence-template.md` 39 - `${CLAUDE_SKILL_DIR}/templates/feature-verify-evidence-template.md`
40 -- 委托:`superpowers:verification-before-completion` 40 +- 原则参考:`superpowers:verification-before-completion`(本插件未镜像;仅作为"证据先于断言"原则参考,不做运行时 invoke — 本 skill 已把子会话派发 + 模板渲染证据流程直接写死)
skills/coding/erp-local-test-gate/SKILL.md
@@ -25,7 +25,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) @@ -25,7 +25,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *)
25 25
26 3. 写入 `docs/superpowers/module-reports/<module_id>-test-gate.md`。 26 3. 写入 `docs/superpowers/module-reports/<module_id>-test-gate.md`。
27 27
28 -3b. **commit evidence 到 module 分支**(确保 test-gate.md 随 MR 合并进 main,审计可追溯): 28 +3b. **commit evidence 到 module 分支**(确保 test-gate.md 随 MR 合并进默认分支,审计可追溯):
29 29
30 ```bash 30 ```bash
31 git add docs/superpowers/module-reports/<module_id>-test-gate.md 31 git add docs/superpowers/module-reports/<module_id>-test-gate.md
@@ -57,7 +57,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) @@ -57,7 +57,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *)
57 (module-start 把该 REQ 视为未完成,重走 brainstorm→...→review 循环修复) 57 (module-start 把该 REQ 视为未完成,重走 brainstorm→...→review 循环修复)
58 58
59 ③ 环境/依赖问题(DB 连不上、外部 API 失败、证书失效) 59 ③ 环境/依赖问题(DB 连不上、外部 API 失败、证书失效)
60 - → 命中红旗 #6(外部接口不可达) 60 + → 命中红旗 #3(外部接口不可达)
61 → 调用 Skill(erp-red-flag-check) 追加 Blocker 到本模块任一 plan 文件 61 → 调用 Skill(erp-red-flag-check) 追加 Blocker 到本模块任一 plan 文件
62 → 修复环境后重新运行 /erp-workflow:erp-coding-start 62 → 修复环境后重新运行 /erp-workflow:erp-coding-start
63 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 63 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
skills/coding/erp-module-report/SKILL.md
@@ -31,7 +31,7 @@ allowed-tools: Read Write Glob Grep Skill Bash(git diff *) Bash(git log *) Bash( @@ -31,7 +31,7 @@ allowed-tools: Read Write Glob Grep Skill Bash(git diff *) Bash(git log *) Bash(
31 - § ⑧ 必须列举所有偏离规格之处;如果没有,明确写"无偏离"。 31 - § ⑧ 必须列举所有偏离规格之处;如果没有,明确写"无偏离"。
32 5. 写入 `docs/superpowers/module-reports/$(date +%F)-<module_id>.md`。 32 5. 写入 `docs/superpowers/module-reports/$(date +%F)-<module_id>.md`。
33 33
34 -5b. **commit 模块报告 + cross-module 日志到 module 分支**(确保审计证据随 MR 合并进 main;erp-mr-create 的 worktree clean 前置条件依赖此步): 34 +5b. **commit 模块报告 + cross-module 日志到 module 分支**(确保审计证据随 MR 合并进默认分支;erp-mr-create 的 worktree clean 前置条件依赖此步):
35 35
36 ```bash 36 ```bash
37 git add docs/superpowers/module-reports/$(date +%F)-<module_id>.md 37 git add docs/superpowers/module-reports/$(date +%F)-<module_id>.md
skills/coding/erp-mr-create/SKILL.md
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 name: erp-mr-create 2 name: erp-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-<id> 且 worktree 干净,push 推代码和所有 evidence,创建 GitLab MR(报告嵌入描述),把 MR URL 追加到模块报告 § ⑫ 并 commit,把 MR iid 回写到 docs/08 该模块 `MR:` 字段并 commit,再次 push 同步。完成信号由 MR merged state 判定。停下等待人工审核。
4 user-invocable: false 4 user-invocable: false
5 -allowed-tools: Read Write Edit Skill Bash(git *) Bash(glab *) Bash(sed *) Bash(awk *) Bash(cat *) Bash(echo *) Bash(mkdir -p .tmp) Bash(mv .tmp/*) Bash(rm -f .tmp/*) 5 +allowed-tools: Read Write Edit Skill Bash(git *) Bash(glab *) Bash(jq *) Bash(sed *) Bash(awk *) Bash(cat *) Bash(echo *) Bash(mkdir -p .tmp) Bash(mv .tmp/*) Bash(rm -f .tmp/*)
6 --- 6 ---
7 7
8 **所有输出必须使用中文。** 8 **所有输出必须使用中文。**
@@ -81,7 +81,20 @@ mv .tmp/mr-desc.final &quot;$DESC_FILE&quot; @@ -81,7 +81,20 @@ mv .tmp/mr-desc.final &quot;$DESC_FILE&quot;
81 81
82 关键:**模块报告内容只经 awk 管道流过**,从不进入 LLM 上下文。 82 关键:**模块报告内容只经 awk 管道流过**,从不进入 LLM 上下文。
83 83
84 -### 步骤 6:创建 MR 84 +### 步骤 5.5:幂等守门——检查 source branch 是否已有 opened MR
  85 +
  86 +防止部分失败后重试创建重复 MR:
  87 +
  88 +```bash
  89 +EXISTING_JSON=$(glab mr list --source-branch "<current_branch>" --state opened -F json 2>/dev/null)
  90 +EXISTING_IID=$(echo "$EXISTING_JSON" | jq -r '.[0].iid // empty')
  91 +EXISTING_URL=$(echo "$EXISTING_JSON" | jq -r '.[0].web_url // empty')
  92 +```
  93 +
  94 +- `EXISTING_IID` 非空 → 打印 `[erp-mr-create] 检测到已有 opened MR: !${EXISTING_IID}`,**跳过步骤 6**,直接用 `EXISTING_IID` / `EXISTING_URL` 进入步骤 7。
  95 +- `EXISTING_IID` 为空 → 正常走步骤 6 创建新 MR。
  96 +
  97 +### 步骤 6:创建 MR(仅当 5.5 未命中时)
85 98
86 ```bash 99 ```bash
87 TITLE_FILE=.tmp/mr-title.txt 100 TITLE_FILE=.tmp/mr-title.txt
@@ -90,20 +103,23 @@ glab mr create --title &quot;$(cat &quot;$TITLE_FILE&quot;)&quot; --description &quot;$(cat &quot;$DESC_FILE&quot;) @@ -90,20 +103,23 @@ glab mr create --title &quot;$(cat &quot;$TITLE_FILE&quot;)&quot; --description &quot;$(cat &quot;$DESC_FILE&quot;)
90 rm -f .tmp/mr-title.txt .tmp/mr-desc.md 103 rm -f .tmp/mr-title.txt .tmp/mr-desc.md
91 ``` 104 ```
92 105
93 -### 步骤 7:追加 MR URL 到模块报告 § ⑫ 并 commit 106 +解析返回的 `IID` 和 `URL`,赋给 `MR_IID` / `MR_URL`(对应 5.5 命中时复用 `EXISTING_IID` / `EXISTING_URL`)。
  107 +
  108 +### 步骤 7:回写 docs/08 的 MR 字段并 commit(durable state 先持久化)
  109 +
  110 +> 先写 docs/08 再追模块报告——这样如果步骤 8 失败,重跑时 5.5 能直接识别已有 MR 且 docs/08 已含 IID,避免状态不一致。
94 111
95 -- 解析返回的 MR URL 和 IID(`<iid>`)。  
96 -- `Edit docs/superpowers/module-reports/<date>-<module_id>.md` 的 § ⑫:把 `{{mr_url}}` 占位替换为实际 URL(若已替换过,追加一行)。  
97 -- `Bash`: `git add docs/superpowers/module-reports/<date>-<module_id>.md && git commit -m "docs(<module_id>): record MR !<iid> link in module report"`。 112 +- `Edit docs/08-模块任务管理.md`:把当前模块条目的 ` - MR: —` 改为 ` - MR: !<MR_IID>`(只改一行)。docs/08 § 二 中 `MR:` 是唯一动态字段。
  113 +- `Bash`: `git add docs/08-模块任务管理.md && git commit -m "chore(<module_id>): record MR !<MR_IID> in docs/08"`。
98 114
99 -### 步骤 8:回写 docs/08 的 MR 字段并 commit 115 +### 步骤 8:追加 MR URL 到模块报告 § ⑫ 并 commit
100 116
101 -- `Edit docs/08-模块任务管理.md`:把当前模块条目的 ` - MR: —` 改为 ` - MR: !<iid>`(只改一行)。docs/08 § 二 中 `MR:` 是唯一动态字段。  
102 -- `Bash`: `git add docs/08-模块任务管理.md && git commit -m "chore(<module_id>): record MR !<iid> in docs/08"`。 117 +- `Edit docs/superpowers/module-reports/<date>-<module_id>.md` 的 § ⑫:把 `{{mr_url}}` 占位替换为 `<MR_URL>`(若已替换过,追加一行)。
  118 +- `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"`。
103 119
104 ### 步骤 9:再次 push 120 ### 步骤 9:再次 push
105 121
106 -步骤 3 的 push 之后,步骤 7 和 8 又产生了两个新 commit(MR link + docs/08 MR iid)。必须再次 push 让 MR 自动更新 commit 列表 + diff: 122 +步骤 3 的 push 之后,步骤 7、8 又产生了两个新 commit(docs/08 MR iid + 模块报告 MR link)。必须再次 push 让 MR 自动更新 commit 列表 + diff:
107 123
108 `Bash`: `git push origin <current_branch>` 124 `Bash`: `git push origin <current_branch>`
109 125
@@ -111,13 +127,13 @@ rm -f .tmp/mr-title.txt .tmp/mr-desc.md @@ -111,13 +127,13 @@ rm -f .tmp/mr-title.txt .tmp/mr-desc.md
111 127
112 ### 步骤 11:停止 — 等待人工 Approve + Merge 128 ### 步骤 11:停止 — 等待人工 Approve + Merge
113 129
114 -用户合并后再次运行 `/erp-workflow:erp-coding-start`,入口会自动检测 MR merged → `git checkout main` + `git pull --ff-only` 同步远程 + 派发下一模块。 130 +用户合并后再次运行 `/erp-workflow:erp-coding-start`,入口会自动检测 MR merged → 探测默认分支(`main` 或 `master`)→ `git checkout <默认分支>` + `git pull --ff-only origin <默认分支>` 同步远程 → 派发下一模块。
115 131
116 ## 设计要点 132 ## 设计要点
117 133
118 -- **main 是 protected branch**,只能通过 MR 修改。MR diff 覆盖代码、模块报告、test-gate evidence、cross-module log,以及 docs/08 的 `MR: !<iid>` 字段。 134 +- **默认分支(main 或 master)是 protected branch**,只能通过 MR 修改。MR diff 覆盖代码、模块报告、test-gate evidence、cross-module log,以及 docs/08 的 `MR: !<iid>` 字段。
119 - **完成信号以 MR state 为准**:docs/08 § 二 中仅 `MR:` 字段在 `—` / `!<iid>` 之间变化,避免提前合并未完成模块的顺序错误。 135 - **完成信号以 MR state 为准**:docs/08 § 二 中仅 `MR:` 字段在 `—` / `!<iid>` 之间变化,避免提前合并未完成模块的顺序错误。
120 -- **worktree 干净前置**(步骤 2):保证 test-gate.md、module-report.md、cross-module log.md 都已进 repo,审计证据完整;防止下次 coding-start `git checkout main` 遇到 dirty state。 136 +- **worktree 干净前置**(步骤 2):保证 test-gate.md、module-report.md、cross-module log.md 都已进 repo,审计证据完整;防止下次 coding-start 切回默认分支时遇到 dirty state。
121 137
122 ## 参考 138 ## 参考
123 139
skills/coding/erp-mr-create/templates/mr-description-template.md
@@ -15,4 +15,4 @@ @@ -15,4 +15,4 @@
15 ## 审核入口 15 ## 审核入口
16 16
17 - 本 MR = 模块 `{{module_id}}` 的唯一人工介入点 17 - 本 MR = 模块 `{{module_id}}` 的唯一人工介入点
18 -- Approve + Merge 后,下次用户运行 `/erp-workflow:erp-coding-start` 时入口会自动扫描到 `glab mr view state=merged`,`git pull --ff-only` 同步 main 并推进下一模块 18 +- Approve + Merge 后,下次用户运行 `/erp-workflow:erp-coding-start` 时入口会自动扫描到 `glab mr view state=merged`,探测默认分支后 `git pull --ff-only` 同步并推进下一模块
skills/crosscut/erp-coding-start/SKILL.md
1 --- 1 ---
2 name: erp-coding-start 2 name: erp-coding-start
3 -description: B 阶段(Coding)入å£ã€‚å…ˆéªŒè¯ Plan 已完æˆï¼›æŒ‰ docs/02 § 二 REQ å¼€å‘é¡ºåºæ¸…啿‰«æï¼Œå¯¹æ¯ä¸ª REQ 所属模å—用 docs/08 çš„ `MR:` 字段 + `glab mr view state` 判定是å¦å®Œæˆâ€”—merged 跳过,`—` 或 opened/closed é€‰ä¸ºå½“å‰æ¨¡å—å¹¶æ´¾å‘到 erp-module-start。派å‘å‰å…ˆ git checkout main + pull --ff-only ä¿æŒ base 最新。 3 +description: B 阶段(Coding)入å£ã€‚å…ˆéªŒè¯ Plan 已完æˆï¼›æŒ‰ docs/02 § 二 REQ å¼€å‘é¡ºåºæ¸…啿‰«æï¼Œå¯¹æ¯ä¸ª REQ 所属模å—用 docs/08 çš„ `MR:` 字段 + `glab mr view state` 判定是å¦å®Œæˆâ€”—merged 跳过,`—` 或 opened/closed é€‰ä¸ºå½“å‰æ¨¡å—å¹¶æ´¾å‘到 erp-module-start。派å‘å‰è‡ªåŠ¨æŽ¢æµ‹é»˜è®¤åˆ†æ”¯ï¼ˆmain / master)并 git checkout + pull --ff-only ä¿æŒ base 最新。
4 user-invocable: true 4 user-invocable: true
5 -allowed-tools: Skill Read Glob Grep Bash(glab mr *) Bash(git branch *) Bash(git checkout *) Bash(git pull *) Bash(git status *) 5 +allowed-tools: Skill Read Glob Grep Bash(glab mr *) Bash(git branch *) Bash(git checkout *) Bash(git pull *) Bash(git status *) Bash(git symbolic-ref *) Bash(sed *)
6 --- 6 ---
7 7
8 **所有输出必须使用中文。** 8 **所有输出必须使用中文。**
9 9
10 -B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ 按 docs/02 § 二 REQ åº + MR state 定ä½å½“剿¨¡å— → 切回 main å¹¶åŒæ­¥è¿œç¨‹ → æ´¾å‘ erp-module-start**。ä¸ç›´æŽ¥ç”Ÿæˆä»»ä½•文件。开å‘顺åºä»¥ `docs/02 § 二` 为准;完æˆåˆ¤å®šä»¥ `docs/08 æ¡ç›®çš„ MR: 字段 + glab mr view state` 为准。 10 +B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ 按 docs/02 § 二 REQ åº + MR state 定ä½å½“剿¨¡å— → åˆ‡å›žé»˜è®¤åˆ†æ”¯å¹¶åŒæ­¥è¿œç¨‹ → æ´¾å‘ erp-module-start**。ä¸ç›´æŽ¥ç”Ÿæˆä»»ä½•文件。开å‘顺åºä»¥ `docs/02 § 二` 为准;完æˆåˆ¤å®šä»¥ `docs/08 æ¡ç›®çš„ MR: 字段 + glab mr view state` 为准。默认分支(`main` 或 `master`)在步骤 4 自动探测,ä¸ç¡¬ç¼–ç ã€‚
11 11
12 ## 执行步骤 12 ## 执行步骤
13 13
@@ -60,14 +60,28 @@ B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ æŒ @@ -60,14 +60,28 @@ B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ æŒ
60 å¹¶**åœä¸‹**。 60 å¹¶**åœä¸‹**。
61 6. 命中åŽï¼šè®°å½• `current_module` çš„ `MR:` 字段值(`—` / `!<iid>-opened` / `!<iid>-closed` / `!<iid>-查ä¸åˆ°`,用于步骤 5 横幅展示)。 61 6. 命中åŽï¼šè®°å½• `current_module` çš„ `MR:` 字段值(`—` / `!<iid>-opened` / `!<iid>-closed` / `!<iid>-查ä¸åˆ°`,用于步骤 5 横幅展示)。
62 62
63 -### 步骤 4:切回 main + åŒæ­¥è¿œç¨‹ï¼ˆå‡†å¤‡ module-start 切新分支的干净 base) 63 +### 步骤 4:探测默认分支 + åˆ‡å›žå¹¶åŒæ­¥è¿œç¨‹ï¼ˆå‡†å¤‡ module-start 切新分支的干净 base)
  64 +
  65 +4.0 **探测默认分支**:
  66 +
  67 +```bash
  68 +DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
  69 +if [ -z "$DEFAULT_BRANCH" ]; then
  70 + # HEAD 未设置,回退:在 main / master 里å–第一个实际存在的远程分支
  71 + DEFAULT_BRANCH=$(git branch -r --format='%(refname:short)' | grep -E '^origin/(main|master)$' | head -1 | sed 's|^origin/||')
  72 +fi
  73 +```
  74 +
  75 +- `DEFAULT_BRANCH` 为空 → 打å°é”™è¯¯å¹¶**åœä¸‹**(既无 `origin/main` 也无 `origin/master`,说明远程未就绪,æç¤ºç”¨æˆ·å…ˆå®Œæˆ Plan 的首次 push 或 `git remote set-head origin -a` 设置 HEAD)。
  76 +
  77 +4.1 **切回 + åŒæ­¥**:
64 78
65 - `Bash`: `git branch --show-current` → `current_branch`。 79 - `Bash`: `git branch --show-current` → `current_branch`。
66 -- 若 `current_branch != main`: 80 +- 若 `current_branch != $DEFAULT_BRANCH`:
67 - `Bash`: `git status --porcelain`ï¼›éžç©º → 打å°ï¼š 81 - `Bash`: `git status --porcelain`ï¼›éžç©º → 打å°ï¼š
68 ``` 82 ```
69 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” 83 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
70 - [erp-coding-start] âš ï¸ å½“å‰åˆ†æ”¯ <current_branch> 有未æäº¤æ”¹åŠ¨ï¼Œæ— æ³•åˆ‡æ¢åˆ° main 84 + [erp-coding-start] âš ï¸ å½“å‰åˆ†æ”¯ <current_branch> 有未æäº¤æ”¹åŠ¨ï¼Œæ— æ³•åˆ‡æ¢åˆ° <DEFAULT_BRANCH>
71 85
72 <git status 输出> 86 <git status 输出>
73 87
@@ -75,17 +89,17 @@ B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ æŒ @@ -75,17 +89,17 @@ B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ æŒ
75 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” 89 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
76 ``` 90 ```
77 å¹¶**åœä¸‹**。 91 å¹¶**åœä¸‹**。
78 - - `Bash`: `git checkout main`。  
79 -- `Bash`: `git pull --ff-only origin main`。 92 + - `Bash`: `git checkout "$DEFAULT_BRANCH"`。
  93 +- `Bash`: `git pull --ff-only origin "$DEFAULT_BRANCH"`。
80 - å¤±è´¥ï¼ˆéž fast-forward / 网络 / 冲çªï¼‰â†’ 打å°é”™è¯¯æ¨ªå¹…: 94 - å¤±è´¥ï¼ˆéž fast-forward / 网络 / 冲çªï¼‰â†’ 打å°é”™è¯¯æ¨ªå¹…:
81 ``` 95 ```
82 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” 96 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
83 - [erp-coding-start] âš ï¸ åŒæ­¥ main 失败 97 + [erp-coding-start] âš ï¸ åŒæ­¥ <DEFAULT_BRANCH> 失败
84 98
85 - 本地 main 无法 fast-forward 到 remote。请手工处ç†ï¼š 99 + 本地 <DEFAULT_BRANCH> 无法 fast-forward 到 remote。请手工处ç†ï¼š
86 git status 100 git status
87 - git log main..origin/main # 远程领先的 commit  
88 - git log origin/main..main # 本地未推的 commit 101 + git log <DEFAULT_BRANCH>..origin/<DEFAULT_BRANCH> # 远程领先的 commit
  102 + git log origin/<DEFAULT_BRANCH>..<DEFAULT_BRANCH> # 本地未推的 commit
89 ä¿®å¤åŽé‡æ–°è¿è¡Œ /erp-workflow:erp-coding-start。 103 ä¿®å¤åŽé‡æ–°è¿è¡Œ /erp-workflow:erp-coding-start。
90 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” 104 â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”
91 ``` 105 ```
@@ -110,7 +124,7 @@ B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ æŒ @@ -110,7 +124,7 @@ B 阶段(Coding)的入å£åˆ†å‘器。èŒè´£ï¼š**éªŒè¯ Plan å·²å®Œæˆ â†’ æŒ
110 ## 设计è¦ç‚¹ 124 ## 设计è¦ç‚¹
111 125
112 - **完æˆåˆ¤å®šç›´æŽ¥è¯»å– `MR:` 字段 + `glab mr view state`**:MR 未 merge å‰ docs/08 没有任何"已完æˆ"标记;用户æå‰è§¦å‘ coding-start 时,步骤 3 扫æåˆ° MR opened ä»ä¼šé€‰ä¸­å½“剿¨¡å—,module-start 会 `git checkout module-<id>` 回到原分支继续,ä¸ä¼šè·³åˆ°ä¸‹ä¸€æ¨¡å—。 126 - **完æˆåˆ¤å®šç›´æŽ¥è¯»å– `MR:` 字段 + `glab mr view state`**:MR 未 merge å‰ docs/08 没有任何"已完æˆ"标记;用户æå‰è§¦å‘ coding-start 时,步骤 3 扫æåˆ° MR opened ä»ä¼šé€‰ä¸­å½“剿¨¡å—,module-start 会 `git checkout module-<id>` 回到原分支继续,ä¸ä¼šè·³åˆ°ä¸‹ä¸€æ¨¡å—。
113 -- **æ¯æ¬¡æ´¾å‘å‰éƒ½ pull main**:代ç åŒæ­¥çš„åŒæ—¶ï¼Œä¹Ÿä¿è¯ module-start 切出新分支时 base 新鲜。 127 +- **æ¯æ¬¡æ´¾å‘å‰éƒ½ pull 默认分支**:代ç åŒæ­¥çš„åŒæ—¶ï¼Œä¹Ÿä¿è¯ module-start 切出新分支时 base 新鲜。默认分支由步骤 4.0 探测(main 或 master),ä¸ç¡¬ç¼–ç ã€‚
114 128
115 ## å‚考 129 ## å‚考
116 130
skills/crosscut/erp-plan-start/SKILL.md
@@ -64,10 +64,18 @@ A 阶段所有 checkbox 均 `[x]`。因无下游 A skill 接手,本步骤**自 @@ -64,10 +64,18 @@ A 阶段所有 checkbox 均 `[x]`。因无下游 A skill 接手,本步骤**自
64 64
65 ⚠️ 进入 B 阶段前必须完成: 65 ⚠️ 进入 B 阶段前必须完成:
66 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/* 66 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/*
67 - 2. 把全部 Plan 产物 commit 到 main 分支: 67 +
  68 + 2. 把全部 Plan 产物 commit:
68 git add -A && git commit -m "chore: plan phase A0~A5 done" 69 git add -A && git commit -m "chore: plan phase A0~A5 done"
69 - 3. 建 Plan MR 提交团队审核,合并后 main 成为 B 阶段每个模块分支的基线  
70 - 4. MR 合并后再运行 /erp-workflow:erp-coding-start 进入 B 阶段 70 +
  71 + 3. 推到远程:
  72 + git remote add origin <gitlab-url> # 若尚未添加
  73 + git push --no-verify -u origin master
  74 + # 首次 push 用 --no-verify 跳过 pre-push 的 test.sh;
  75 + # 本地 DB 尚未就位、scripts/test.sh 此时必然失败,属正常
  76 + # push 完成后到 GitLab UI 把 master(或 main)设为 protected
  77 +
  78 + 4. main(或 master)就绪后,再运行 /erp-workflow:erp-coding-start
71 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
72 ``` 80 ```
73 81
skills/crosscut/erp-red-flag-check/SKILL.md
1 --- 1 ---
2 name: erp-red-flag-check 2 name: erp-red-flag-check
3 -description: 在每个功能循环步骤和生成重要制品前运行。检查 CLAUDE.md 中的 6 项红旗清单,命中任一项则追加 Blocker 到计划文件并停止。 3 +description: 在每个功能循环步骤和生成重要制品前运行。检查 CLAUDE.md 中的 3 项红旗清单,命中任一项则追加 Blocker 到计划文件并停止。
4 user-invocable: false 4 user-invocable: false
5 allowed-tools: Read Write Bash(mysql *) 5 allowed-tools: Read Write Bash(mysql *)
6 --- 6 ---
@@ -11,27 +11,26 @@ allowed-tools: Read Write Bash(mysql *) @@ -11,27 +11,26 @@ allowed-tools: Read Write Bash(mysql *)
11 11
12 ## 说明 12 ## 说明
13 13
14 -验证 CLAUDE.md § 🚩 静默执行红旗清单中的 6 项均未触发。命中任一项则中断。 14 +验证 CLAUDE.md § 🚩 静默执行红旗清单中的 3 项均未触发。命中任一项则中断。
  15 +
  16 +> 需求歧义 / schema 缺口 / 技术栈外组件引入等场景由各 feature-* skill 的 `AskUserQuestion` 流程承接,不进入本清单,不会在这里命中。
15 17
16 ## 调用时机 18 ## 调用时机
17 19
18 - 每个功能循环步骤开始前(3.1-3.5) 20 - 每个功能循环步骤开始前(3.1-3.5)
19 - 生成模块级制品前(模块报告、MR 描述) 21 - 生成模块级制品前(模块报告、MR 描述)
20 -- 用户请求涉及 schema 冲突、技术栈边界或外部依赖 22 +- 用户请求涉及外部依赖、环境凭据变更
21 23
22 -## 检查清单(6 项 — 权威来源:CLAUDE.md) 24 +## 检查清单(3 项 — 权威来源:CLAUDE.md)
23 25
24 -1. **需求与 schema 冲突** — 所需字段/表在实时 schema 中不存在  
25 -2. **需求本身歧义** — 存在两种或以上合理解读  
26 -3. **超技术栈边界** — 需要 CLAUDE.md 技术栈表以外的框架/中间件  
27 -4. **测试反复失败** — 同一功能中同一测试连续 10 次修复失败  
28 -5. **要改密钥/账密/包名** — 涉及 `docs/07-环境配置.md` 中的人工填写字段  
29 -6. **外部接口不可达** — 第三方 API / 证书 / 网络问题 26 +1. **测试反复失败** — 同一功能中同一测试连续 10 次修复失败
  27 +2. **要改密钥/账密/包名** — 涉及 `docs/07-环境配置.md` 中的人工填写字段
  28 +3. **外部接口不可达** — 第三方 API / 证书 / 网络问题
30 29
31 ## 执行步骤 30 ## 执行步骤
32 31
33 1. 读取当前功能的规格/计划文件路径(从对话或 `docs/08` 获取)。 32 1. 读取当前功能的规格/计划文件路径(从对话或 `docs/08` 获取)。
34 -2. 逐项检查 6 个红旗。如果全部未命中 → 输出 `red-flag-check: 通过`,退出。 33 +2. 逐项检查 3 个红旗。如果全部未命中 → 输出 `red-flag-check: 通过`,退出。
35 3. 命中时: 34 3. 命中时:
36 - 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/red-flag-block-template.md` 35 - 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/red-flag-block-template.md`
37 - 填充槽位(flag_number、flag_name、description、affected_scope、recommendation、decision_needed) 36 - 填充槽位(flag_number、flag_name、description、affected_scope、recommendation、decision_needed)
@@ -46,5 +45,5 @@ allowed-tools: Read Write Bash(mysql *) @@ -46,5 +45,5 @@ allowed-tools: Read Write Bash(mysql *)
46 ## 参考 45 ## 参考
47 46
48 - `${CLAUDE_SKILL_DIR}/templates/red-flag-block-template.md` 47 - `${CLAUDE_SKILL_DIR}/templates/red-flag-block-template.md`
49 -- `CLAUDE.md` § 🚩 静默执行红旗清单(权威 6 条) 48 +- `CLAUDE.md` § 🚩 静默执行红旗清单(权威 3 条)
50 - `CLAUDE.md` § 🟡 软规则(S2 跨模块改动,不触发红旗但需留痕) 49 - `CLAUDE.md` § 🟡 软规则(S2 跨模块改动,不触发红旗但需留痕)
skills/crosscut/erp-red-flag-check/templates/red-flag-block-template.md
1 ## 🚩 Blocker 1 ## 🚩 Blocker
2 2
3 -**红旗编号**: {{flag_number}} (1-6,对应 CLAUDE.md 🚩 静默执行红旗清单) 3 +**红旗编号**: {{flag_number}} (1-3,对应 CLAUDE.md 🚩 静默执行红旗清单)
4 **红旗名称**: {{flag_name}} 4 **红旗名称**: {{flag_name}}
5 **问题描述**: {{description}} 5 **问题描述**: {{description}}
6 **影响范围**: {{affected_scope}} 6 **影响范围**: {{affected_scope}}
skills/plan/erp-db-design-gen/SKILL.md
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 name: erp-db-design-gen 2 name: erp-db-design-gen
3 description: A4 DB 设计生成 + REQ 回填——用 mysql CLI 反查 schema 生成 docs/03-数据库设计文档.md;然后用 Grep 在 docs/01-需求清单/*.md 定位 TBD(A4 自动补) 替换为实际表/字段引用。 3 description: A4 DB 设计生成 + REQ 回填——用 mysql CLI 反查 schema 生成 docs/03-数据库设计文档.md;然后用 Grep 在 docs/01-需求清单/*.md 定位 TBD(A4 自动补) 替换为实际表/字段引用。
4 user-invocable: false 4 user-invocable: false
5 -allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(source *) 5 +allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(set *) Bash(. .env.local)
6 --- 6 ---
7 7
8 **所有输出必须使用中文。** 8 **所有输出必须使用中文。**
@@ -36,10 +36,10 @@ allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(source *) @@ -36,10 +36,10 @@ allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(source *)
36 36
37 ### A. 查询实时 schema 37 ### A. 查询实时 schema
38 38
39 -用 `Bash` source `.env.local` 后查询 39 +用 `Bash` 加载 `.env.local` 后查询(用 `set -a; . ...; set +a` 替代 `source`,避免密码中的 shell 特殊字符被展开)
40 40
41 ```bash 41 ```bash
42 -source .env.local 42 +set -a; . .env.local; set +a
43 MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD} -N -B" 43 MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD} -N -B"
44 44
45 # 表列表 + 注释 45 # 表列表 + 注释
skills/plan/erp-db-init/SKILL.md
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 name: erp-db-init 2 name: erp-db-init
3 description: A3 验证 MySQL 连接 + 导出当前 schema 为 `sql/migrations/V1__initial_schema.sql`(Flyway 初始 migration,DDL only)+ 导出当前数据为 `sql/seed-data.sql`(INSERT only)。不执行任何 DDL。 3 description: A3 验证 MySQL 连接 + 导出当前 schema 为 `sql/migrations/V1__initial_schema.sql`(Flyway 初始 migration,DDL only)+ 导出当前数据为 `sql/seed-data.sql`(INSERT only)。不执行任何 DDL。
4 user-invocable: false 4 user-invocable: false
5 -allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(mysqldump *) Bash(source *) Bash(sed *) Bash(cat *) Bash(rm sql/migrations/*.raw) Bash(rm sql/*.raw) Bash(grep *) 5 +allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(mysqldump *) Bash(set *) Bash(. .env.local) Bash(sed *) Bash(cat *) Bash(rm sql/migrations/*.raw) Bash(rm sql/*.raw) Bash(grep *)
6 --- 6 ---
7 7
8 **所有输出必须使用中文。** 8 **所有输出必须使用中文。**
@@ -47,12 +47,15 @@ allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(mysql @@ -47,12 +47,15 @@ allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(mysql
47 47
48 ### B. 验证 MySQL 连接 48 ### B. 验证 MySQL 连接
49 49
50 -用 `Bash` 执行(先 `source .env.local` 加载变量): 50 +用 `Bash` 执行(先加载 `.env.local` 变量,用 `set -a; . ...; set +a` 避免 shell 展开密码中的特殊字符):
51 51
52 ```bash 52 ```bash
53 -source .env.local && mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -e "USE \`$DB_SCHEMA\`; SHOW TABLES;" 53 +set -a; . .env.local; set +a
  54 +mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -e "USE \`$DB_SCHEMA\`; SHOW TABLES;"
54 ``` 55 ```
55 56
  57 +> 注:密码中含 `$`、`` ` ``、空格、`!` 等字符时,`.env.local` 需用单引号包裹值,例如 `DB_PASSWORD='p@ss$w0rd!'`。`set -a; . file; set +a` 与 `source` 行为一致但更明确地声明"按 KEY=VALUE 加载并导出",配合单引号可避免展开。
  58 +
56 - **成功** → 记录表列表,继续步骤 C。 59 - **成功** → 记录表列表,继续步骤 C。
57 - **失败** → 向用户输出具体错误(认证失败 / schema 不存在 / 主机不可达等),提示检查 `.env.local`,**停下**。 60 - **失败** → 向用户输出具体错误(认证失败 / schema 不存在 / 主机不可达等),提示检查 `.env.local`,**停下**。
58 61
@@ -61,7 +64,9 @@ source .env.local &amp;&amp; mysql -h&quot;$DB_HOST&quot; -P&quot;$DB_PORT&quot; -u&quot;$DB_USER&quot; -p&quot;$DB_PASSWOR @@ -61,7 +64,9 @@ source .env.local &amp;&amp; mysql -h&quot;$DB_HOST&quot; -P&quot;$DB_PORT&quot; -u&quot;$DB_USER&quot; -p&quot;$DB_PASSWOR
61 用 `Bash` 执行 mysqldump(**仅 DDL,不含数据**): 64 用 `Bash` 执行 mysqldump(**仅 DDL,不含数据**):
62 65
63 ```bash 66 ```bash
64 -mkdir -p sql/migrations && source .env.local && mysqldump --no-data --skip-comments -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" > sql/migrations/V1__initial_schema.sql.raw 67 +mkdir -p sql/migrations
  68 +set -a; . .env.local; set +a
  69 +mysqldump --no-data --skip-comments -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" > sql/migrations/V1__initial_schema.sql.raw
65 ``` 70 ```
66 71
67 如果 `mysqldump` 命令不存在,向用户输出具体错误(提示安装 `mysql-client` / `brew install mysql-client` 等)并**停下**(mysql 和 mysqldump 是 A0 `erp-project-init` 的前置依赖,正常情况下应已就绪)。 72 如果 `mysqldump` 命令不存在,向用户输出具体错误(提示安装 `mysql-client` / `brew install mysql-client` 等)并**停下**(mysql 和 mysqldump 是 A0 `erp-project-init` 的前置依赖,正常情况下应已就绪)。
@@ -73,7 +78,7 @@ mkdir -p sql/migrations &amp;&amp; source .env.local &amp;&amp; mysqldump --no-data --skip-comme @@ -73,7 +78,7 @@ mkdir -p sql/migrations &amp;&amp; source .env.local &amp;&amp; mysqldump --no-data --skip-comme
73 ```bash 78 ```bash
74 PROJECT="<从 CLAUDE.md 读到的项目名称>" 79 PROJECT="<从 CLAUDE.md 读到的项目名称>"
75 TS="$(date -u +%FT%TZ)" 80 TS="$(date -u +%FT%TZ)"
76 -source .env.local 81 +set -a; . .env.local; set +a
77 { 82 {
78 sed -e "s|{{project_name}}|$PROJECT|g" \ 83 sed -e "s|{{project_name}}|$PROJECT|g" \
79 -e "s|{{timestamp}}|$TS|g" \ 84 -e "s|{{timestamp}}|$TS|g" \
@@ -94,7 +99,8 @@ rm sql/migrations/V1__initial_schema.sql.raw @@ -94,7 +99,8 @@ rm sql/migrations/V1__initial_schema.sql.raw
94 用 `Bash` 执行 mysqldump(**仅数据,INSERT only**): 99 用 `Bash` 执行 mysqldump(**仅数据,INSERT only**):
95 100
96 ```bash 101 ```bash
97 -source .env.local && mysqldump --no-create-info --complete-insert --skip-comments --skip-extended-insert \ 102 +set -a; . .env.local; set +a
  103 +mysqldump --no-create-info --complete-insert --skip-comments --skip-extended-insert \
98 -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" > sql/seed-data.sql.raw 104 -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" > sql/seed-data.sql.raw
99 ``` 105 ```
100 106
@@ -103,7 +109,7 @@ source .env.local &amp;&amp; mysqldump --no-create-info --complete-insert --skip-comment @@ -103,7 +109,7 @@ source .env.local &amp;&amp; mysqldump --no-create-info --complete-insert --skip-comment
103 ```bash 109 ```bash
104 PROJECT="<从 CLAUDE.md 读到的项目名称>" 110 PROJECT="<从 CLAUDE.md 读到的项目名称>"
105 TS="$(date -u +%FT%TZ)" 111 TS="$(date -u +%FT%TZ)"
106 -source .env.local 112 +set -a; . .env.local; set +a
107 { 113 {
108 sed -e "s|{{project_name}}|$PROJECT|g" \ 114 sed -e "s|{{project_name}}|$PROJECT|g" \
109 -e "s|{{timestamp}}|$TS|g" \ 115 -e "s|{{timestamp}}|$TS|g" \
@@ -146,7 +152,7 @@ rm sql/seed-data.sql.raw @@ -146,7 +152,7 @@ rm sql/seed-data.sql.raw
146 152
147 ## 不变量 153 ## 不变量
148 154
149 -- 密码通过 `.env.local` 经 `source` 注入,不硬编码在命令里 155 +- 密码通过 `.env.local` 以 `set -a; . .env.local; set +a` 方式导出到环境变量,不硬编码在命令里;特殊字符需用单引号包裹
150 - 本 skill **只** 产生 V1 initial migration 和 seed-data.sql 快照;后续 V2/V3 及业务数据变化由 B 阶段 `erp-feature-tdd` 在 REQ 实现时写入。 156 - 本 skill **只** 产生 V1 initial migration 和 seed-data.sql 快照;后续 V2/V3 及业务数据变化由 B 阶段 `erp-feature-tdd` 在 REQ 实现时写入。
151 157
152 ## 参考 158 ## 参考
skills/plan/erp-downstream-gen/SKILL.md
@@ -156,10 +156,27 @@ docs/08 已由 A0 erp-project-init 创建(含 Plan 进度骨架)。本步骤 @@ -156,10 +156,27 @@ docs/08 已由 A0 erp-project-init 创建(含 Plan 进度骨架)。本步骤
156 156
157 ⚠️ 进入 B 阶段前必须完成: 157 ⚠️ 进入 B 阶段前必须完成:
158 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/* 158 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/*
159 - 2. 把全部 Plan 产物 commit 到 main 分支: 159 +
  160 + 2. 把全部 Plan 产物 commit:
160 git add -A && git commit -m "chore: plan phase A0~A5 done" 161 git add -A && git commit -m "chore: plan phase A0~A5 done"
161 - 3. 建 Plan MR 提交团队审核,合并后 main 成为 B 阶段每个模块分支的基线  
162 - 4. MR 合并后再运行 /erp-workflow:erp-coding-start 进入 B 阶段 162 +
  163 + 3. 推到远程,按仓库状态二选一:
  164 +
  165 + 情况 A — 远程仓库是全新的(尚无 main / master):
  166 + git remote add origin <gitlab-url> # 若尚未添加
  167 + git push --no-verify -u origin master
  168 + # 首次 push 用 --no-verify 跳过 pre-push 的 test.sh;
  169 + # 本地 DB 尚未就位、scripts/test.sh 此时必然失败,属正常
  170 + # push 完成后到 GitLab UI 把 master(或 main)设为 protected
  171 +
  172 + 情况 B — 远程已有 main 需要走 MR 审核:
  173 + git checkout -b plan-init
  174 + git push -u origin plan-init
  175 + # 在 GitLab 打开 plan-init → main 的 MR,审核并合并
  176 +
  177 + 4. main(或 master)就绪后,再运行 /erp-workflow:erp-coding-start
  178 + 进入 B 阶段。届时 .env.local 应指向本地 MySQL(非共享远程 DB),
  179 + 否则 B 阶段每次测试闸门都会因 setup-test-db.sh 的防护失败。
163 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 180 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
164 ``` 181 ```
165 182
skills/plan/erp-project-init/templates/CLAUDE-template.md
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 4. 自动 git push(.githooks/pre-push 会再执行一遍 scripts/test.sh 作为硬闸门) 63 4. 自动 git push(.githooks/pre-push 会再执行一遍 scripts/test.sh 作为硬闸门)
64 + glab mr create,报告嵌入 MR 描述 64 + glab mr create,报告嵌入 MR 描述
65 5. 人工 review MR → Approve + Merge(人工动作,唯一人工介入点) 65 5. 人工 review MR → Approve + Merge(人工动作,唯一人工介入点)
66 -6. 下次用户运行 `/erp-workflow:erp-coding-start` 时,入口自动检测到本模块 `glab mr view state=merged` → `git checkout main && git pull --ff-only` 同步远程 → 扫描下一模块并派发 66 +6. 下次用户运行 `/erp-workflow:erp-coding-start` 时,入口自动检测到本模块 `glab mr view state=merged`,探测默认分支(main / master)后 `git checkout <默认分支> && git pull --ff-only` 同步远程 → 扫描下一模块并派发
67 7. 你**不需要**手工 Edit docs/08(模块元数据不变;下次 coding-start 自动扫描 MR state 判定是否完成) 67 7. 你**不需要**手工 Edit docs/08(模块元数据不变;下次 coding-start 自动扫描 MR state 判定是否完成)
68 ``` 68 ```
69 69
@@ -96,7 +96,7 @@ @@ -96,7 +96,7 @@
96 96
97 两层嵌套循环的详细步骤**全部固化到 skills**,CLAUDE.md 不展开。入口调 `/erp-workflow:erp-coding-start`,自动分发: 97 两层嵌套循环的详细步骤**全部固化到 skills**,CLAUDE.md 不展开。入口调 `/erp-workflow:erp-coding-start`,自动分发:
98 98
99 -- **模块循环(外层,Layer 2)** → `erp-module-start` → `erp-local-test-gate`(commit test-gate.md)→ `erp-module-report`(commit 模块报告 + cross-module log)→ `erp-mr-create`(worktree clean 校验 → push → 创建 MR → 追加 MR URL 到报告并 commit → 写 `MR: !<iid>` 到 docs/08 并 commit → 再次 push)→ 人工 Approve+Merge → 下次运行 `/erp-workflow:erp-coding-start` 扫描到本模块 `glab mr view merged` → `git pull --ff-only main` → 推进下一模块 99 +- **模块循环(外层,Layer 2)** → `erp-module-start` → `erp-local-test-gate`(commit test-gate.md)→ `erp-module-report`(commit 模块报告 + cross-module log)→ `erp-mr-create`(worktree clean 校验 → push → 创建 MR → 追加 MR URL 到报告并 commit → 写 `MR: !<iid>` 到 docs/08 并 commit → 再次 push)→ 人工 Approve+Merge → 下次运行 `/erp-workflow:erp-coding-start` 扫描到本模块 `glab mr view merged` → 探测默认分支并 `git pull --ff-only` → 推进下一模块
100 - **功能循环(内层,Layer 3,每个 REQ-XXX-NNN 走一遍)** → `erp-feature-brainstorm` → `erp-feature-plan` → `erp-feature-tdd` → `erp-feature-verify` → `erp-feature-review` 100 - **功能循环(内层,Layer 3,每个 REQ-XXX-NNN 走一遍)** → `erp-feature-brainstorm` → `erp-feature-plan` → `erp-feature-tdd` → `erp-feature-verify` → `erp-feature-review`
101 101
102 **本地测试闸门**: `erp-local-test-gate` 是 MR 前的唯一硬闸门,子会话执行 `scripts/test.sh`:脚本先由 `setup-test-db.sh` 清空库 → build / lint → 执行测试(Spring Boot 启动时 Flyway 自动 apply `sql/migrations/V*.sql`)→ e2e → 再次清库。详见 § 🧪 自测要求。`.githooks/pre-push` 在 push 时会再次执行 `scripts/test.sh` 作为兜底;`git push --no-verify` 被 hook `deny-no-verify.sh` 硬拦截。本项目不配置 GitLab CI/CD。 102 **本地测试闸门**: `erp-local-test-gate` 是 MR 前的唯一硬闸门,子会话执行 `scripts/test.sh`:脚本先由 `setup-test-db.sh` 清空库 → build / lint → 执行测试(Spring Boot 启动时 Flyway 自动 apply `sql/migrations/V*.sql`)→ e2e → 再次清库。详见 § 🧪 自测要求。`.githooks/pre-push` 在 push 时会再次执行 `scripts/test.sh` 作为兜底;`git push --no-verify` 被 hook `deny-no-verify.sh` 硬拦截。本项目不配置 GitLab CI/CD。
@@ -214,12 +214,11 @@ refactor(common): 统一响应格式包装 @@ -214,12 +214,11 @@ refactor(common): 统一响应格式包装
214 214
215 | # | 红旗 | 例子 | 215 | # | 红旗 | 例子 |
216 | - | --- | --- | 216 | - | --- | --- |
217 -| 1 | **需求与 schema 不一致** | REQ 提到的字段/表在现有 schema 里不存在 → 先向用户确认「加一个 migration 新增 / 修正 REQ 理解」;得到答复后继续,**不走下方 Blocker 文件流程** |  
218 -| 2 | **需求本身歧义** | spec 中某规则有两种合理解读,CC 无法判断 |  
219 -| 3 | **超技术栈边界** | 需要引入 `docs/tech-stack.md` 以外的框架 / 中间件 |  
220 -| 4 | **测试反复失败** | 同一测试同一功能内连续 **10 次**修复失败 |  
221 -| 5 | **要改密钥 / 账密 / 包名** | `docs/07-环境配置.md` 里由人工标注必须填的字段 |  
222 -| 6 | **外部接口不可达** | 第三方 API 无法连接、证书失效等环境问题 | 217 +| 1 | **测试反复失败** | 同一测试同一功能内连续 **10 次**修复失败 |
  218 +| 2 | **要改密钥 / 账密 / 包名** | `docs/07-环境配置.md` 里由人工标注必须填的字段 |
  219 +| 3 | **外部接口不可达** | 第三方 API 无法连接、证书失效等环境问题 |
  220 +
  221 +> 其余需要人类判断的场景(需求歧义 / schema 缺口 / 技术栈外组件引入)一律走普通 `AskUserQuestion` Q&A,不升格为红旗、不写 Blocker 文件。见下方「📘 技术栈外组件的处理规则」与各 feature-* skill 的 Q&A 流程。
223 222
224 **命中红旗时的固定动作:** 223 **命中红旗时的固定动作:**
225 224
@@ -232,7 +231,7 @@ refactor(common): 统一响应格式包装 @@ -232,7 +231,7 @@ refactor(common): 统一响应格式包装
232 ```markdown 231 ```markdown
233 ## ⚠️ 需要人工决策 232 ## ⚠️ 需要人工决策
234 233
235 -**红旗编号**: [1–6 中的某一条] 234 +**红旗编号**: [1–3 中的某一条]
236 **问题描述**: [详细描述] 235 **问题描述**: [详细描述]
237 **影响范围**: [影响哪些模块 / 功能] 236 **影响范围**: [影响哪些模块 / 功能]
238 **我的建议**: [初步判断,可选] 237 **我的建议**: [初步判断,可选]
@@ -241,6 +240,18 @@ refactor(common): 统一响应格式包装 @@ -241,6 +240,18 @@ refactor(common): 统一响应格式包装
241 240
242 --- 241 ---
243 242
  243 +## 📘 技术栈外组件的处理规则
  244 +
  245 +brainstorm / plan 阶段若发现需要 `docs/04-技术规范.md § 零` 技术栈表之外的框架、中间件或关键库:
  246 +
  247 +1. 用 `AskUserQuestion` 询问用户(选项至少含:接受引入 / 换方案 / 拒绝)
  248 +2. **接受** → 同会话直接在 `docs/04 § 零` 追加一行 → 继续流程
  249 +3. **换方案 / 拒绝** → 视为常规歧义澄清,继续 Q&A 收敛
  250 +
  251 +不写 Blocker 文件,不中断流程。
  252 +
  253 +---
  254 +
244 ## 🟡 软规则(允许继续,但有强制后续动作) 255 ## 🟡 软规则(允许继续,但有强制后续动作)
245 256
246 以下情况 **不触发中断**,CC 可自行继续推进,但必须在约定位置留痕,模块完成时统一审计。漏留痕 = 红旗。 257 以下情况 **不触发中断**,CC 可自行继续推进,但必须在约定位置留痕,模块完成时统一审计。漏留痕 = 红旗。
skills/plan/erp-skeleton-gen/SKILL.md
@@ -101,7 +101,7 @@ cp &quot;${CLAUDE_SKILL_DIR}/templates/scripts-setup-test-db-template.sh&quot; scripts/set @@ -101,7 +101,7 @@ cp &quot;${CLAUDE_SKILL_DIR}/templates/scripts-setup-test-db-template.sh&quot; scripts/set
101 101
102 ```bash 102 ```bash
103 #!/usr/bin/env bash 103 #!/usr/bin/env bash
104 -# scripts/test.sh —— 合并到 main 前的唯一硬闸门。 104 +# scripts/test.sh —— 合并到默认分支(main / master)前的唯一硬闸门。
105 # 顺序:setup-db → build → lint → unit+integration → e2e → reset-db 105 # 顺序:setup-db → build → lint → unit+integration → e2e → reset-db
106 # 由 .githooks/pre-push 和 erp-local-test-gate skill(通过子会话)调用。 106 # 由 .githooks/pre-push 和 erp-local-test-gate skill(通过子会话)调用。
107 107
skills/plan/erp-skeleton-gen/templates/env-local-template
1 -DB_HOST=【人工填写:MySQL host,例如 localhost】 1 +# .env.local — 本地开发凭据(入 .gitignore,不提交)
  2 +#
  3 +# 规则:
  4 +# 1. 值含 `$`、反引号、空格、`!` 等 shell 特殊字符时,必须用单引号包裹:
  5 +# DB_PASSWORD='p@ss$w0rd!'
  6 +# 否则 `set -a; . .env.local; set +a` 会做变量展开导致密码错乱。
  7 +# 2. DB_HOST 建议保持 localhost / 127.0.0.1;非本地 host 会被 scripts/setup-test-db.sh 防护拒绝。
  8 +# 3. DB_SCHEMA 建议命名含 test / _dev / _local / _ci,避免与生产库同名。
  9 +
  10 +DB_HOST=【人工填写:MySQL host,推荐 localhost】
2 DB_PORT=【人工填写:MySQL port,默认 3306】 11 DB_PORT=【人工填写:MySQL port,默认 3306】
3 DB_USER=【人工填写:开发账号名】 12 DB_USER=【人工填写:开发账号名】
4 -DB_PASSWORD=【人工填写:对应密码】  
5 -DB_SCHEMA=【人工填写:schema 名,例如 erp_dev】 13 +DB_PASSWORD=【人工填写:对应密码,含特殊字符时用单引号包裹】
  14 +DB_SCHEMA=【人工填写:schema 名,推荐含 test/_dev/_local,例如 erp_dev】
6 JWT_SECRET=【人工填写:JWT 签名密钥,256+ bit 随机串】 15 JWT_SECRET=【人工填写:JWT 签名密钥,256+ bit 随机串】
skills/plan/erp-skeleton-gen/templates/scripts-setup-test-db-template.sh
@@ -7,14 +7,44 @@ @@ -7,14 +7,44 @@
7 # - scripts/test.sh 开头:清空库,让 Spring 启动时 Flyway 从 V1 开始重放所有 migration 7 # - scripts/test.sh 开头:清空库,让 Spring 启动时 Flyway 从 V1 开始重放所有 migration
8 # - scripts/test.sh 结尾:清空库,避免测试遗留污染下次运行 8 # - scripts/test.sh 结尾:清空库,避免测试遗留污染下次运行
9 # - 手动调试时:reset 到零状态 9 # - 手动调试时:reset 到零状态
  10 +#
  11 +# 防护:本脚本只允许在本地 host + 测试库名上执行;非预期目标会被拒绝,
  12 +# 避免 .env.local 误指向 staging/prod 时触发不可逆 DROP。
10 13
11 set -euo pipefail 14 set -euo pipefail
12 15
13 -source "$(dirname "$0")/../.env.local" 16 +ENV_FILE="$(dirname "$0")/../.env.local"
  17 +[ -f "$ENV_FILE" ] || { echo "[setup-test-db] ⚠️ .env.local 不存在($ENV_FILE)" >&2; exit 1; }
  18 +
  19 +# 用 set -a 加载,让 KEY=VALUE 导出为环境变量;密码中含特殊字符时 .env.local 请用单引号包裹
  20 +set -a; . "$ENV_FILE"; set +a
  21 +
  22 +# 防护 1:只允许本地 host(localhost / 127.0.0.1 / ::1)
  23 +case "${DB_HOST:-}" in
  24 + localhost|127.0.0.1|::1) ;;
  25 + *)
  26 + echo "[setup-test-db] ⚠️ 拒绝在非本地 host (${DB_HOST}) 上执行 DROP DATABASE" >&2
  27 + echo " 如确需对远程 DB 重置(少见,常属误配),请显式声明:TEST_DB_ALLOW_REMOTE=1 $0" >&2
  28 + [ "${TEST_DB_ALLOW_REMOTE:-0}" = "1" ] || exit 1
  29 + ;;
  30 +esac
  31 +
  32 +# 防护 2:schema 名需像测试/开发库(含 test / _dev / _local),否则要求显式确认
  33 +case "${DB_SCHEMA:-}" in
  34 + *test*|*_dev|*_local|*_ci)
  35 + ;;
  36 + *)
  37 + echo "[setup-test-db] ⚠️ schema '${DB_SCHEMA}' 不像测试库(期望命名含 test / _dev / _local / _ci)" >&2
  38 + echo " 如确为期望行为,请显式声明:TEST_DB_ALLOW_PROD_NAME=1 $0" >&2
  39 + [ "${TEST_DB_ALLOW_PROD_NAME:-0}" = "1" ] || exit 1
  40 + ;;
  41 +esac
  42 +
  43 +# 防护 3:显式 banner,让人看见自己在 drop 什么
  44 +echo "[setup-test-db] 即将 DROP + CREATE \`${DB_SCHEMA}\` on ${DB_HOST}:${DB_PORT}"
14 45
15 MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD}" 46 MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD}"
16 47
17 -echo "[setup-test-db] drop + create database ${DB_SCHEMA}"  
18 $MYSQL_CMD -e "DROP DATABASE IF EXISTS \`${DB_SCHEMA}\`; CREATE DATABASE \`${DB_SCHEMA}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" 48 $MYSQL_CMD -e "DROP DATABASE IF EXISTS \`${DB_SCHEMA}\`; CREATE DATABASE \`${DB_SCHEMA}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
19 49
20 echo "[setup-test-db] done — schema will be applied by Flyway when Spring Boot starts" 50 echo "[setup-test-db] done — schema will be applied by Flyway when Spring Boot starts"