From a6f9c34db2c6c668d718a081634a77616db2adb6 Mon Sep 17 00:00:00 2001 From: zichun Date: Tue, 21 Apr 2026 15:06:35 +0800 Subject: [PATCH] edit --- .claude-plugin/plugin.json | 2 +- README.md | 2 +- skills/coding/erp-feature-brainstorm/SKILL.md | 6 +++--- skills/coding/erp-feature-plan/SKILL.md | 4 ++-- skills/coding/erp-feature-review/SKILL.md | 4 ++-- skills/coding/erp-feature-review/templates/feature-review-template.md | 2 +- skills/coding/erp-feature-tdd/SKILL.md | 4 ++-- skills/coding/erp-feature-verify/SKILL.md | 2 +- skills/coding/erp-local-test-gate/SKILL.md | 4 ++-- skills/coding/erp-module-report/SKILL.md | 2 +- skills/coding/erp-mr-create/SKILL.md | 42 +++++++++++++++++++++++++++++------------- skills/coding/erp-mr-create/templates/mr-description-template.md | 2 +- skills/crosscut/erp-coding-start/SKILL.md | 40 +++++++++++++++++++++++++++------------- skills/crosscut/erp-plan-start/SKILL.md | 14 +++++++++++--- skills/crosscut/erp-red-flag-check/SKILL.md | 23 +++++++++++------------ skills/crosscut/erp-red-flag-check/templates/red-flag-block-template.md | 2 +- skills/plan/erp-db-design-gen/SKILL.md | 6 +++--- skills/plan/erp-db-init/SKILL.md | 22 ++++++++++++++-------- skills/plan/erp-downstream-gen/SKILL.md | 23 ++++++++++++++++++++--- skills/plan/erp-project-init/templates/CLAUDE-template.md | 29 ++++++++++++++++++++--------- skills/plan/erp-skeleton-gen/SKILL.md | 2 +- skills/plan/erp-skeleton-gen/templates/env-local-template | 15 ++++++++++++--- skills/plan/erp-skeleton-gen/templates/scripts-setup-test-db-template.sh | 34 ++++++++++++++++++++++++++++++++-- 23 files changed, 198 insertions(+), 88 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index cf92522..d5dbc67 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,5 +2,5 @@ "name": "erp-workflow", "description": "ERP 项目全流程框架:阶段 A 计划(一次性) + 阶段 B 编码(模块循环 + 功能循环),含完整 skill 流水线 + 守门 hook + 软规则留痕。专为后端 Spring Boot + 前端 React + MySQL 8 的 ERP/管理类系统设计。", "version": "0.1.0", - "skills": ["./skills/plan", "./skills/coding", "./skills/crosscut"] + "skills": ["./skills/plan", "./skills/coding", "./skills/crosscut", "./skills/internal"] } diff --git a/README.md b/README.md index 9e9b159..a408df1 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ erp-workflow-plugin/ |---|---|---| | `erp-plan-start` | **A 阶段入口**。读取 docs/08 § 一 找第一个未勾 A 子项 → 派发对应 A skill;A 全部完成时提示运行 coding-start | **用户手动**运行 `/erp-workflow:erp-plan-start` | | `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` | -| `erp-red-flag-check` | 检查 CLAUDE.md 的 6 项红旗清单;命中则追加 Blocker 到计划文件并停下 | 功能循环各步骤和生成重要制品前自动调用 | +| `erp-red-flag-check` | 检查 CLAUDE.md 的 3 项红旗清单;命中则追加 Blocker 到计划文件并停下 | 功能循环各步骤和生成重要制品前自动调用 | | `erp-cross-module-log` | 给 `log-cross-module.sh` 追加的跨模块改动存根补「原因 / 影响评估」 | 用户看到 hook 提示后调用;`erp-module-start` 初始化日志文件时也会用其模板 | ## Templates 清单(38 份) diff --git a/skills/coding/erp-feature-brainstorm/SKILL.md b/skills/coding/erp-feature-brainstorm/SKILL.md index 2029758..1cf429b 100644 --- a/skills/coding/erp-feature-brainstorm/SKILL.md +++ b/skills/coding/erp-feature-brainstorm/SKILL.md @@ -11,7 +11,7 @@ allowed-tools: Read Write Skill Bash(mysql *) ## 说明 -针对一个 REQ-XXX-NNN,委托 `superpowers:brainstorming` 进行头脑风暴,再把输出填入标准规格模板,产出单页功能规格。 +针对一个 REQ-XXX-NNN,委托本插件的 `superpower-brainstorming`(superpowers:brainstorming 的本地 fork,已剥掉 approval gates)进行头脑风暴,再把输出填入标准规格模板,产出单页功能规格。 ## 执行步骤 @@ -20,7 +20,7 @@ allowed-tools: Read Write Skill Bash(mysql *) - 当前 REQ-XXX-NNN(从对话中获取,或 `docs/08` 当前模块下一个未完成的 REQ)。 - REQ 卡片:`docs/01-需求清单/.md` 中对应的 REQ-XXX-NNN 节。 - 相关数据表(从 `docs/03` 或实时 mysql 命令行查询)。 -3. 委托 `superpowers:brainstorming`,以 REQ 卡片 + schema 引用作为上下文。 +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` @@ -35,5 +35,5 @@ allowed-tools: Read Write Skill Bash(mysql *) ## 参考 - `${CLAUDE_SKILL_DIR}/templates/feature-spec-template.md` -- 委托:`superpowers:brainstorming` +- 委托:`superpower-brainstorming`(本插件 `skills/internal/superpower-brainstorming/`,superpowers:brainstorming 的无门 fork) - 守门:`erp-red-flag-check` diff --git a/skills/coding/erp-feature-plan/SKILL.md b/skills/coding/erp-feature-plan/SKILL.md index 4afd9a3..1b0a8cb 100644 --- a/skills/coding/erp-feature-plan/SKILL.md +++ b/skills/coding/erp-feature-plan/SKILL.md @@ -16,7 +16,7 @@ allowed-tools: Read Write Grep Skill - 当前 REQ-XXX-NNN 及其规格文件 `docs/superpowers/specs/YYYY-MM-DD-.md`(规格不存在则报错)。 - 相关代码指针(已有的待修改文件,通过 Grep 发现)。 - `docs/04-技术规范.md` 和 `docs/09-项目目录结构.md`(编码规范 + 目录规范)。 -3. 委托 `superpowers:writing-plans`,以规格 + 代码指针 + 规范作为上下文。 +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. 强制要求:每个任务有失败测试标识、实现路径和完成标准。 @@ -30,5 +30,5 @@ allowed-tools: Read Write Grep Skill ## 参考 - `${CLAUDE_SKILL_DIR}/templates/feature-plan-template.md` -- 委托:`superpowers:writing-plans` +- 委托:`superpower-writing-plans`(本插件 `skills/internal/superpower-writing-plans/`,superpowers:writing-plans 的无门 fork) - 守门:`erp-red-flag-check` diff --git a/skills/coding/erp-feature-review/SKILL.md b/skills/coding/erp-feature-review/SKILL.md index 8ebdadf..e84be23 100644 --- a/skills/coding/erp-feature-review/SKILL.md +++ b/skills/coding/erp-feature-review/SKILL.md @@ -11,7 +11,7 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *) ## 执行步骤 -1. 委托 `superpowers:code-reviewer`,以该 REQ 的 diff(`git diff ..HEAD`)和规格作为输入。 +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. 写入报告。 @@ -36,6 +36,6 @@ allowed-tools: Read Write Edit Skill Agent Bash(git add *) Bash(git commit *) - `${CLAUDE_SKILL_DIR}/templates/feature-review-template.md` - Fix commit 格式与 `erp-feature-tdd` 的 `commit-message-template.md` 对齐(`fix(): `) -- 委托:`superpowers:code-reviewer` +- 委托:`superpower-code-reviewer`(本插件 `agents/superpower-code-reviewer.md`,superpowers:code-reviewer 的本地 fork) - 上游:`erp-feature-verify` - 下游:`erp-module-start`(approve)或 `erp-feature-verify`(request-changes) diff --git a/skills/coding/erp-feature-review/templates/feature-review-template.md b/skills/coding/erp-feature-review/templates/feature-review-template.md index a9b6216..27fa018 100644 --- a/skills/coding/erp-feature-review/templates/feature-review-template.md +++ b/skills/coding/erp-feature-review/templates/feature-review-template.md @@ -2,7 +2,7 @@ req_id: {{req_id}} date: {{date}} round: {{round}} -reviewer: superpowers:code-reviewer +reviewer: superpower-code-reviewer --- # Review: {{req_id}} — round {{round}} diff --git a/skills/coding/erp-feature-tdd/SKILL.md b/skills/coding/erp-feature-tdd/SKILL.md index 641ab0d..9a74487 100644 --- a/skills/coding/erp-feature-tdd/SKILL.md +++ b/skills/coding/erp-feature-tdd/SKILL.md @@ -23,7 +23,7 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) b. **派发子会话**(通过 `Agent`,general-purpose)运行测试并确认失败;子会话只返回 `{command, exit_code, failing_assertion}`。主会话**不直接**运行测试。 c. 在 `impl_file` 处实现最小代码使测试通过。 d. **再次派发子会话**运行测试并确认通过。 - e. 持续失败(同一测试 >10 次修复尝试)→ 调用 `erp-red-flag-check`(红旗 #4)。 + e. 持续失败(同一测试 >10 次修复尝试)→ 调用 `erp-red-flag-check`(红旗 #1)。 f. 暂存变更并使用 `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` 提交;`scope` 匹配任务的模块,`subject` ≤50 字符,`req_id` 必填。 5. 所有任务完成后 → 交接给 `erp-feature-verify`。 @@ -41,5 +41,5 @@ allowed-tools: Read Write Edit Agent Skill Bash(git add *) Bash(git commit *) ## 参考 - `${CLAUDE_SKILL_DIR}/templates/commit-message-template.md` -- 委托:`superpowers:test-driven-development` +- 原则参考:`superpowers:test-driven-development`(本插件未镜像;仅作为 TDD 原则手册参考,不做运行时 invoke — 本 skill 已把"子会话跑测试 + REQ tag commit"流程直接写死) - 守门:`erp-red-flag-check` diff --git a/skills/coding/erp-feature-verify/SKILL.md b/skills/coding/erp-feature-verify/SKILL.md index 7496cb1..aea6773 100644 --- a/skills/coding/erp-feature-verify/SKILL.md +++ b/skills/coding/erp-feature-verify/SKILL.md @@ -37,4 +37,4 @@ allowed-tools: Skill Read Write Agent ## 参考 - `${CLAUDE_SKILL_DIR}/templates/feature-verify-evidence-template.md` -- 委托:`superpowers:verification-before-completion` +- 原则参考:`superpowers:verification-before-completion`(本插件未镜像;仅作为"证据先于断言"原则参考,不做运行时 invoke — 本 skill 已把子会话派发 + 模板渲染证据流程直接写死) diff --git a/skills/coding/erp-local-test-gate/SKILL.md b/skills/coding/erp-local-test-gate/SKILL.md index d912597..a044ebe 100644 --- a/skills/coding/erp-local-test-gate/SKILL.md +++ b/skills/coding/erp-local-test-gate/SKILL.md @@ -25,7 +25,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) 3. 写入 `docs/superpowers/module-reports/-test-gate.md`。 -3b. **commit evidence 到 module 分支**(确保 test-gate.md 随 MR 合并进 main,审计可追溯): +3b. **commit evidence 到 module 分支**(确保 test-gate.md 随 MR 合并进默认分支,审计可追溯): ```bash git add docs/superpowers/module-reports/-test-gate.md @@ -57,7 +57,7 @@ allowed-tools: Read Write Skill Agent Bash(git add *) Bash(git commit *) (module-start 把该 REQ 视为未完成,重走 brainstorm→...→review 循环修复) ③ 环境/依赖问题(DB 连不上、外部 API 失败、证书失效) - → 命中红旗 #6(外部接口不可达) + → 命中红旗 #3(外部接口不可达) → 调用 Skill(erp-red-flag-check) 追加 Blocker 到本模块任一 plan 文件 → 修复环境后重新运行 /erp-workflow:erp-coding-start ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/skills/coding/erp-module-report/SKILL.md b/skills/coding/erp-module-report/SKILL.md index 05c8ec4..6e125aa 100644 --- a/skills/coding/erp-module-report/SKILL.md +++ b/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( - § ⑧ 必须列举所有偏离规格之处;如果没有,明确写"无偏离"。 5. 写入 `docs/superpowers/module-reports/$(date +%F)-.md`。 -5b. **commit 模块报告 + cross-module 日志到 module 分支**(确保审计证据随 MR 合并进 main;erp-mr-create 的 worktree clean 前置条件依赖此步): +5b. **commit 模块报告 + cross-module 日志到 module 分支**(确保审计证据随 MR 合并进默认分支;erp-mr-create 的 worktree clean 前置条件依赖此步): ```bash git add docs/superpowers/module-reports/$(date +%F)-.md diff --git a/skills/coding/erp-mr-create/SKILL.md b/skills/coding/erp-mr-create/SKILL.md index ef5319a..c72e949 100644 --- a/skills/coding/erp-mr-create/SKILL.md +++ b/skills/coding/erp-mr-create/SKILL.md @@ -2,7 +2,7 @@ name: erp-mr-create description: 模块报告完成后,验证当前分支为 module- 且 worktree 干净,push 推代码和所有 evidence,创建 GitLab MR(报告嵌入描述),把 MR URL 追加到模块报告 § ⑫ 并 commit,把 MR iid 回写到 docs/08 该模块 `MR:` 字段并 commit,再次 push 同步。完成信号由 MR merged state 判定。停下等待人工审核。 user-invocable: false -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/*) +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/*) --- **所有输出必须使用中文。** @@ -81,7 +81,20 @@ mv .tmp/mr-desc.final "$DESC_FILE" 关键:**模块报告内容只经 awk 管道流过**,从不进入 LLM 上下文。 -### 步骤 6:创建 MR +### 步骤 5.5:幂等守门——检查 source branch 是否已有 opened MR + +防止部分失败后重试创建重复 MR: + +```bash +EXISTING_JSON=$(glab mr list --source-branch "" --state opened -F json 2>/dev/null) +EXISTING_IID=$(echo "$EXISTING_JSON" | jq -r '.[0].iid // empty') +EXISTING_URL=$(echo "$EXISTING_JSON" | jq -r '.[0].web_url // empty') +``` + +- `EXISTING_IID` 非空 → 打印 `[erp-mr-create] 检测到已有 opened MR: !${EXISTING_IID}`,**跳过步骤 6**,直接用 `EXISTING_IID` / `EXISTING_URL` 进入步骤 7。 +- `EXISTING_IID` 为空 → 正常走步骤 6 创建新 MR。 + +### 步骤 6:创建 MR(仅当 5.5 未命中时) ```bash TITLE_FILE=.tmp/mr-title.txt @@ -90,20 +103,23 @@ glab mr create --title "$(cat "$TITLE_FILE")" --description "$(cat "$DESC_FILE") rm -f .tmp/mr-title.txt .tmp/mr-desc.md ``` -### 步骤 7:追加 MR URL 到模块报告 § ⑫ 并 commit +解析返回的 `IID` 和 `URL`,赋给 `MR_IID` / `MR_URL`(对应 5.5 命中时复用 `EXISTING_IID` / `EXISTING_URL`)。 + +### 步骤 7:回写 docs/08 的 MR 字段并 commit(durable state 先持久化) + +> 先写 docs/08 再追模块报告——这样如果步骤 8 失败,重跑时 5.5 能直接识别已有 MR 且 docs/08 已含 IID,避免状态不一致。 -- 解析返回的 MR URL 和 IID(``)。 -- `Edit docs/superpowers/module-reports/-.md` 的 § ⑫:把 `{{mr_url}}` 占位替换为实际 URL(若已替换过,追加一行)。 -- `Bash`: `git add docs/superpowers/module-reports/-.md && git commit -m "docs(): record MR ! link in module report"`。 +- `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:回写 docs/08 的 MR 字段并 commit +### 步骤 8:追加 MR URL 到模块报告 § ⑫ 并 commit -- `Edit docs/08-模块任务管理.md`:把当前模块条目的 ` - MR: —` 改为 ` - MR: !`(只改一行)。docs/08 § 二 中 `MR:` 是唯一动态字段。 -- `Bash`: `git add docs/08-模块任务管理.md && git commit -m "chore(): record MR ! in docs/08"`。 +- `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(MR link + docs/08 MR iid)。必须再次 push 让 MR 自动更新 commit 列表 + diff: +步骤 3 的 push 之后,步骤 7、8 又产生了两个新 commit(docs/08 MR iid + 模块报告 MR link)。必须再次 push 让 MR 自动更新 commit 列表 + diff: `Bash`: `git push origin ` @@ -111,13 +127,13 @@ rm -f .tmp/mr-title.txt .tmp/mr-desc.md ### 步骤 11:停止 — 等待人工 Approve + Merge -用户合并后再次运行 `/erp-workflow:erp-coding-start`,入口会自动检测 MR merged → `git checkout main` + `git pull --ff-only` 同步远程 + 派发下一模块。 +用户合并后再次运行 `/erp-workflow:erp-coding-start`,入口会自动检测 MR merged → 探测默认分支(`main` 或 `master`)→ `git checkout <默认分支>` + `git pull --ff-only origin <默认分支>` 同步远程 → 派发下一模块。 ## 设计要点 -- **main 是 protected branch**,只能通过 MR 修改。MR diff 覆盖代码、模块报告、test-gate evidence、cross-module log,以及 docs/08 的 `MR: !` 字段。 +- **默认分支(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 `git checkout main` 遇到 dirty state。 +- **worktree 干净前置**(步骤 2):保证 test-gate.md、module-report.md、cross-module log.md 都已进 repo,审计证据完整;防止下次 coding-start 切回默认分支时遇到 dirty state。 ## 参考 diff --git a/skills/coding/erp-mr-create/templates/mr-description-template.md b/skills/coding/erp-mr-create/templates/mr-description-template.md index ebb483c..850d425 100644 --- a/skills/coding/erp-mr-create/templates/mr-description-template.md +++ b/skills/coding/erp-mr-create/templates/mr-description-template.md @@ -15,4 +15,4 @@ ## 审核入口 - 本 MR = 模块 `{{module_id}}` 的唯一人工介入点 -- Approve + Merge 后,下次用户运行 `/erp-workflow:erp-coding-start` 时入口会自动扫描到 `glab mr view state=merged`,`git pull --ff-only` 同步 main 并推进下一模块 +- Approve + Merge 后,下次用户运行 `/erp-workflow:erp-coding-start` 时入口会自动扫描到 `glab mr view state=merged`,探测默认分支后 `git pull --ff-only` 同步并推进下一模块 diff --git a/skills/crosscut/erp-coding-start/SKILL.md b/skills/crosscut/erp-coding-start/SKILL.md index ccef567..8750a97 100644 --- a/skills/crosscut/erp-coding-start/SKILL.md +++ b/skills/crosscut/erp-coding-start/SKILL.md @@ -1,13 +1,13 @@ --- name: erp-coding-start -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 最新。 +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 最新。 user-invocable: true -allowed-tools: Skill Read Glob Grep Bash(glab mr *) Bash(git branch *) Bash(git checkout *) Bash(git pull *) Bash(git status *) +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 *) --- **所有输出必须使用中文。** -B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → 按 docs/02 § 二 REQ 序 + MR state 定位当前模块 → 切回 main 并同步远程 → 派发 erp-module-start**。不直接生成任何文件。开发顺序以 `docs/02 § 二` 为准;完成判定以 `docs/08 条目的 MR: 字段 + glab mr view state` 为准。 +B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → 按 docs/02 § 二 REQ 序 + MR state 定位当前模块 → 切回默认分支并同步远程 → 派发 erp-module-start**。不直接生成任何文件。开发顺序以 `docs/02 § 二` 为准;完成判定以 `docs/08 条目的 MR: 字段 + glab mr view state` 为准。默认分支(`main` 或 `master`)在步骤 4 自动探测,不硬编码。 ## 执行步骤 @@ -60,14 +60,28 @@ B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → 并**停下**。 6. 命中后:记录 `current_module` 的 `MR:` 字段值(`—` / `!-opened` / `!-closed` / `!-查不到`,用于步骤 5 横幅展示)。 -### 步骤 4:切回 main + 同步远程(准备 module-start 切新分支的干净 base) +### 步骤 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 +``` + +- `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 != main`: +- 若 `current_branch != $DEFAULT_BRANCH`: - `Bash`: `git status --porcelain`;非空 → 打印: ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - [erp-coding-start] ⚠️ 当前分支 有未提交改动,无法切换到 main + [erp-coding-start] ⚠️ 当前分支 有未提交改动,无法切换到 @@ -75,17 +89,17 @@ B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` 并**停下**。 - - `Bash`: `git checkout main`。 -- `Bash`: `git pull --ff-only origin main`。 + - `Bash`: `git checkout "$DEFAULT_BRANCH"`。 +- `Bash`: `git pull --ff-only origin "$DEFAULT_BRANCH"`。 - 失败(非 fast-forward / 网络 / 冲突)→ 打印错误横幅: ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - [erp-coding-start] ⚠️ 同步 main 失败 + [erp-coding-start] ⚠️ 同步 失败 - 本地 main 无法 fast-forward 到 remote。请手工处理: + 本地 无法 fast-forward 到 remote。请手工处理: git status - git log main..origin/main # 远程领先的 commit - git log origin/main..main # 本地未推的 commit + git log ..origin/ # 远程领先的 commit + git log origin/.. # 本地未推的 commit 修复后重新运行 /erp-workflow:erp-coding-start。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` @@ -110,7 +124,7 @@ B 阶段(Coding)的入口分发器。职责:**验证 Plan 已完成 → ## 设计要点 - **完成判定直接读取 `MR:` 字段 + `glab mr view state`**:MR 未 merge 前 docs/08 没有任何"已完成"标记;用户提前触发 coding-start 时,步骤 3 扫描到 MR opened 仍会选中当前模块,module-start 会 `git checkout module-` 回到原分支继续,不会跳到下一模块。 -- **每次派发前都 pull main**:代码同步的同时,也保证 module-start 切出新分支时 base 新鲜。 +- **每次派发前都 pull 默认分支**:代码同步的同时,也保证 module-start 切出新分支时 base 新鲜。默认分支由步骤 4.0 探测(main 或 master),不硬编码。 ## 参考 diff --git a/skills/crosscut/erp-plan-start/SKILL.md b/skills/crosscut/erp-plan-start/SKILL.md index 9c7654d..817f06d 100644 --- a/skills/crosscut/erp-plan-start/SKILL.md +++ b/skills/crosscut/erp-plan-start/SKILL.md @@ -64,10 +64,18 @@ A 阶段所有 checkbox 均 `[x]`。因无下游 A skill 接手,本步骤**自 ⚠️ 进入 B 阶段前必须完成: 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/* - 2. 把全部 Plan 产物 commit 到 main 分支: + + 2. 把全部 Plan 产物 commit: git add -A && git commit -m "chore: plan phase A0~A5 done" - 3. 建 Plan MR 提交团队审核,合并后 main 成为 B 阶段每个模块分支的基线 - 4. MR 合并后再运行 /erp-workflow:erp-coding-start 进入 B 阶段 + + 3. 推到远程: + git remote add origin # 若尚未添加 + git push --no-verify -u origin master + # 首次 push 用 --no-verify 跳过 pre-push 的 test.sh; + # 本地 DB 尚未就位、scripts/test.sh 此时必然失败,属正常 + # push 完成后到 GitLab UI 把 master(或 main)设为 protected + + 4. main(或 master)就绪后,再运行 /erp-workflow:erp-coding-start ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` diff --git a/skills/crosscut/erp-red-flag-check/SKILL.md b/skills/crosscut/erp-red-flag-check/SKILL.md index a6e20e4..6a6008d 100644 --- a/skills/crosscut/erp-red-flag-check/SKILL.md +++ b/skills/crosscut/erp-red-flag-check/SKILL.md @@ -1,6 +1,6 @@ --- name: erp-red-flag-check -description: 在每个功能循环步骤和生成重要制品前运行。检查 CLAUDE.md 中的 6 项红旗清单,命中任一项则追加 Blocker 到计划文件并停止。 +description: 在每个功能循环步骤和生成重要制品前运行。检查 CLAUDE.md 中的 3 项红旗清单,命中任一项则追加 Blocker 到计划文件并停止。 user-invocable: false allowed-tools: Read Write Bash(mysql *) --- @@ -11,27 +11,26 @@ allowed-tools: Read Write Bash(mysql *) ## 说明 -验证 CLAUDE.md § 🚩 静默执行红旗清单中的 6 项均未触发。命中任一项则中断。 +验证 CLAUDE.md § 🚩 静默执行红旗清单中的 3 项均未触发。命中任一项则中断。 + +> 需求歧义 / schema 缺口 / 技术栈外组件引入等场景由各 feature-* skill 的 `AskUserQuestion` 流程承接,不进入本清单,不会在这里命中。 ## 调用时机 - 每个功能循环步骤开始前(3.1-3.5) - 生成模块级制品前(模块报告、MR 描述) -- 用户请求涉及 schema 冲突、技术栈边界或外部依赖时 +- 用户请求涉及外部依赖、环境凭据变更时 -## 检查清单(6 项 — 权威来源:CLAUDE.md) +## 检查清单(3 项 — 权威来源:CLAUDE.md) -1. **需求与 schema 冲突** — 所需字段/表在实时 schema 中不存在 -2. **需求本身歧义** — 存在两种或以上合理解读 -3. **超技术栈边界** — 需要 CLAUDE.md 技术栈表以外的框架/中间件 -4. **测试反复失败** — 同一功能中同一测试连续 10 次修复失败 -5. **要改密钥/账密/包名** — 涉及 `docs/07-环境配置.md` 中的人工填写字段 -6. **外部接口不可达** — 第三方 API / 证书 / 网络问题 +1. **测试反复失败** — 同一功能中同一测试连续 10 次修复失败 +2. **要改密钥/账密/包名** — 涉及 `docs/07-环境配置.md` 中的人工填写字段 +3. **外部接口不可达** — 第三方 API / 证书 / 网络问题 ## 执行步骤 1. 读取当前功能的规格/计划文件路径(从对话或 `docs/08` 获取)。 -2. 逐项检查 6 个红旗。如果全部未命中 → 输出 `red-flag-check: 通过`,退出。 +2. 逐项检查 3 个红旗。如果全部未命中 → 输出 `red-flag-check: 通过`,退出。 3. 命中时: - 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/red-flag-block-template.md` - 填充槽位(flag_number、flag_name、description、affected_scope、recommendation、decision_needed) @@ -46,5 +45,5 @@ allowed-tools: Read Write Bash(mysql *) ## 参考 - `${CLAUDE_SKILL_DIR}/templates/red-flag-block-template.md` -- `CLAUDE.md` § 🚩 静默执行红旗清单(权威 6 条) +- `CLAUDE.md` § 🚩 静默执行红旗清单(权威 3 条) - `CLAUDE.md` § 🟡 软规则(S2 跨模块改动,不触发红旗但需留痕) diff --git a/skills/crosscut/erp-red-flag-check/templates/red-flag-block-template.md b/skills/crosscut/erp-red-flag-check/templates/red-flag-block-template.md index 8d1278e..b12dc35 100644 --- a/skills/crosscut/erp-red-flag-check/templates/red-flag-block-template.md +++ b/skills/crosscut/erp-red-flag-check/templates/red-flag-block-template.md @@ -1,6 +1,6 @@ ## 🚩 Blocker -**红旗编号**: {{flag_number}} (1-6,对应 CLAUDE.md 🚩 静默执行红旗清单) +**红旗编号**: {{flag_number}} (1-3,对应 CLAUDE.md 🚩 静默执行红旗清单) **红旗名称**: {{flag_name}} **问题描述**: {{description}} **影响范围**: {{affected_scope}} diff --git a/skills/plan/erp-db-design-gen/SKILL.md b/skills/plan/erp-db-design-gen/SKILL.md index 9e662aa..aef92fe 100644 --- a/skills/plan/erp-db-design-gen/SKILL.md +++ b/skills/plan/erp-db-design-gen/SKILL.md @@ -2,7 +2,7 @@ name: erp-db-design-gen description: A4 DB 设计生成 + REQ 回填——用 mysql CLI 反查 schema 生成 docs/03-数据库设计文档.md;然后用 Grep 在 docs/01-需求清单/*.md 定位 TBD(A4 自动补) 替换为实际表/字段引用。 user-invocable: false -allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(source *) +allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(set *) Bash(. .env.local) --- **所有输出必须使用中文。** @@ -36,10 +36,10 @@ allowed-tools: Read Write Edit Grep Glob Skill Bash(mysql *) Bash(source *) ### A. 查询实时 schema -用 `Bash` source `.env.local` 后查询: +用 `Bash` 加载 `.env.local` 后查询(用 `set -a; . ...; set +a` 替代 `source`,避免密码中的 shell 特殊字符被展开): ```bash -source .env.local +set -a; . .env.local; set +a MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD} -N -B" # 表列表 + 注释 diff --git a/skills/plan/erp-db-init/SKILL.md b/skills/plan/erp-db-init/SKILL.md index 4fac7e9..5a30dbe 100644 --- a/skills/plan/erp-db-init/SKILL.md +++ b/skills/plan/erp-db-init/SKILL.md @@ -2,7 +2,7 @@ name: erp-db-init description: A3 验证 MySQL 连接 + 导出当前 schema 为 `sql/migrations/V1__initial_schema.sql`(Flyway 初始 migration,DDL only)+ 导出当前数据为 `sql/seed-data.sql`(INSERT only)。不执行任何 DDL。 user-invocable: false -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 *) +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 *) --- **所有输出必须使用中文。** @@ -47,12 +47,15 @@ allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(mysql ### B. 验证 MySQL 连接 -用 `Bash` 执行(先 `source .env.local` 加载变量): +用 `Bash` 执行(先加载 `.env.local` 变量,用 `set -a; . ...; set +a` 避免 shell 展开密码中的特殊字符): ```bash -source .env.local && mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -e "USE \`$DB_SCHEMA\`; SHOW TABLES;" +set -a; . .env.local; set +a +mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -e "USE \`$DB_SCHEMA\`; SHOW TABLES;" ``` +> 注:密码中含 `$`、`` ` ``、空格、`!` 等字符时,`.env.local` 需用单引号包裹值,例如 `DB_PASSWORD='p@ss$w0rd!'`。`set -a; . file; set +a` 与 `source` 行为一致但更明确地声明"按 KEY=VALUE 加载并导出",配合单引号可避免展开。 + - **成功** → 记录表列表,继续步骤 C。 - **失败** → 向用户输出具体错误(认证失败 / schema 不存在 / 主机不可达等),提示检查 `.env.local`,**停下**。 @@ -61,7 +64,9 @@ source .env.local && mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWOR 用 `Bash` 执行 mysqldump(**仅 DDL,不含数据**): ```bash -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 +mkdir -p sql/migrations +set -a; . .env.local; set +a +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 ``` 如果 `mysqldump` 命令不存在,向用户输出具体错误(提示安装 `mysql-client` / `brew install mysql-client` 等)并**停下**(mysql 和 mysqldump 是 A0 `erp-project-init` 的前置依赖,正常情况下应已就绪)。 @@ -73,7 +78,7 @@ mkdir -p sql/migrations && source .env.local && mysqldump --no-data --skip-comme ```bash PROJECT="<从 CLAUDE.md 读到的项目名称>" TS="$(date -u +%FT%TZ)" -source .env.local +set -a; . .env.local; set +a { sed -e "s|{{project_name}}|$PROJECT|g" \ -e "s|{{timestamp}}|$TS|g" \ @@ -94,7 +99,8 @@ rm sql/migrations/V1__initial_schema.sql.raw 用 `Bash` 执行 mysqldump(**仅数据,INSERT only**): ```bash -source .env.local && mysqldump --no-create-info --complete-insert --skip-comments --skip-extended-insert \ +set -a; . .env.local; set +a +mysqldump --no-create-info --complete-insert --skip-comments --skip-extended-insert \ -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" > sql/seed-data.sql.raw ``` @@ -103,7 +109,7 @@ source .env.local && mysqldump --no-create-info --complete-insert --skip-comment ```bash PROJECT="<从 CLAUDE.md 读到的项目名称>" TS="$(date -u +%FT%TZ)" -source .env.local +set -a; . .env.local; set +a { sed -e "s|{{project_name}}|$PROJECT|g" \ -e "s|{{timestamp}}|$TS|g" \ @@ -146,7 +152,7 @@ rm sql/seed-data.sql.raw ## 不变量 -- 密码通过 `.env.local` 经 `source` 注入,不硬编码在命令里。 +- 密码通过 `.env.local` 以 `set -a; . .env.local; set +a` 方式导出到环境变量,不硬编码在命令里;特殊字符需用单引号包裹。 - 本 skill **只** 产生 V1 initial migration 和 seed-data.sql 快照;后续 V2/V3 及业务数据变化由 B 阶段 `erp-feature-tdd` 在 REQ 实现时写入。 ## 参考 diff --git a/skills/plan/erp-downstream-gen/SKILL.md b/skills/plan/erp-downstream-gen/SKILL.md index b9ba33a..6cf127f 100644 --- a/skills/plan/erp-downstream-gen/SKILL.md +++ b/skills/plan/erp-downstream-gen/SKILL.md @@ -156,10 +156,27 @@ docs/08 已由 A0 erp-project-init 创建(含 Plan 进度骨架)。本步骤 ⚠️ 进入 B 阶段前必须完成: 1. 人工通读 docs/01~10 + CLAUDE.md + sql/migrations/V1 + 各 scripts/* - 2. 把全部 Plan 产物 commit 到 main 分支: + + 2. 把全部 Plan 产物 commit: git add -A && git commit -m "chore: plan phase A0~A5 done" - 3. 建 Plan MR 提交团队审核,合并后 main 成为 B 阶段每个模块分支的基线 - 4. MR 合并后再运行 /erp-workflow:erp-coding-start 进入 B 阶段 + + 3. 推到远程,按仓库状态二选一: + + 情况 A — 远程仓库是全新的(尚无 main / master): + git remote add origin # 若尚未添加 + git push --no-verify -u origin master + # 首次 push 用 --no-verify 跳过 pre-push 的 test.sh; + # 本地 DB 尚未就位、scripts/test.sh 此时必然失败,属正常 + # push 完成后到 GitLab UI 把 master(或 main)设为 protected + + 情况 B — 远程已有 main 需要走 MR 审核: + git checkout -b plan-init + git push -u origin plan-init + # 在 GitLab 打开 plan-init → main 的 MR,审核并合并 + + 4. main(或 master)就绪后,再运行 /erp-workflow:erp-coding-start + 进入 B 阶段。届时 .env.local 应指向本地 MySQL(非共享远程 DB), + 否则 B 阶段每次测试闸门都会因 setup-test-db.sh 的防护失败。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` diff --git a/skills/plan/erp-project-init/templates/CLAUDE-template.md b/skills/plan/erp-project-init/templates/CLAUDE-template.md index 8a337b0..eb9d38a 100644 --- a/skills/plan/erp-project-init/templates/CLAUDE-template.md +++ b/skills/plan/erp-project-init/templates/CLAUDE-template.md @@ -63,7 +63,7 @@ 4. 自动 git push(.githooks/pre-push 会再执行一遍 scripts/test.sh 作为硬闸门) + glab mr create,报告嵌入 MR 描述 5. 人工 review MR → Approve + Merge(人工动作,唯一人工介入点) -6. 下次用户运行 `/erp-workflow:erp-coding-start` 时,入口自动检测到本模块 `glab mr view state=merged` → `git checkout main && git pull --ff-only` 同步远程 → 扫描下一模块并派发 +6. 下次用户运行 `/erp-workflow:erp-coding-start` 时,入口自动检测到本模块 `glab mr view state=merged`,探测默认分支(main / master)后 `git checkout <默认分支> && git pull --ff-only` 同步远程 → 扫描下一模块并派发 7. 你**不需要**手工 Edit docs/08(模块元数据不变;下次 coding-start 自动扫描 MR state 判定是否完成) ``` @@ -96,7 +96,7 @@ 两层嵌套循环的详细步骤**全部固化到 skills**,CLAUDE.md 不展开。入口调 `/erp-workflow:erp-coding-start`,自动分发: -- **模块循环(外层,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: !` 到 docs/08 并 commit → 再次 push)→ 人工 Approve+Merge → 下次运行 `/erp-workflow:erp-coding-start` 扫描到本模块 `glab mr view merged` → `git pull --ff-only main` → 推进下一模块 +- **模块循环(外层,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: !` 到 docs/08 并 commit → 再次 push)→ 人工 Approve+Merge → 下次运行 `/erp-workflow:erp-coding-start` 扫描到本模块 `glab mr view merged` → 探测默认分支并 `git pull --ff-only` → 推进下一模块 - **功能循环(内层,Layer 3,每个 REQ-XXX-NNN 走一遍)** → `erp-feature-brainstorm` → `erp-feature-plan` → `erp-feature-tdd` → `erp-feature-verify` → `erp-feature-review` **本地测试闸门**: `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): 统一响应格式包装 | # | 红旗 | 例子 | | - | --- | --- | -| 1 | **需求与 schema 不一致** | REQ 提到的字段/表在现有 schema 里不存在 → 先向用户确认「加一个 migration 新增 / 修正 REQ 理解」;得到答复后继续,**不走下方 Blocker 文件流程** | -| 2 | **需求本身歧义** | spec 中某规则有两种合理解读,CC 无法判断 | -| 3 | **超技术栈边界** | 需要引入 `docs/tech-stack.md` 以外的框架 / 中间件 | -| 4 | **测试反复失败** | 同一测试同一功能内连续 **10 次**修复失败 | -| 5 | **要改密钥 / 账密 / 包名** | `docs/07-环境配置.md` 里由人工标注必须填的字段 | -| 6 | **外部接口不可达** | 第三方 API 无法连接、证书失效等环境问题 | +| 1 | **测试反复失败** | 同一测试同一功能内连续 **10 次**修复失败 | +| 2 | **要改密钥 / 账密 / 包名** | `docs/07-环境配置.md` 里由人工标注必须填的字段 | +| 3 | **外部接口不可达** | 第三方 API 无法连接、证书失效等环境问题 | + +> 其余需要人类判断的场景(需求歧义 / schema 缺口 / 技术栈外组件引入)一律走普通 `AskUserQuestion` Q&A,不升格为红旗、不写 Blocker 文件。见下方「📘 技术栈外组件的处理规则」与各 feature-* skill 的 Q&A 流程。 **命中红旗时的固定动作:** @@ -232,7 +231,7 @@ refactor(common): 统一响应格式包装 ```markdown ## ⚠️ 需要人工决策 -**红旗编号**: [1–6 中的某一条] +**红旗编号**: [1–3 中的某一条] **问题描述**: [详细描述] **影响范围**: [影响哪些模块 / 功能] **我的建议**: [初步判断,可选] @@ -241,6 +240,18 @@ refactor(common): 统一响应格式包装 --- +## 📘 技术栈外组件的处理规则 + +brainstorm / plan 阶段若发现需要 `docs/04-技术规范.md § 零` 技术栈表之外的框架、中间件或关键库: + +1. 用 `AskUserQuestion` 询问用户(选项至少含:接受引入 / 换方案 / 拒绝) +2. **接受** → 同会话直接在 `docs/04 § 零` 追加一行 → 继续流程 +3. **换方案 / 拒绝** → 视为常规歧义澄清,继续 Q&A 收敛 + +不写 Blocker 文件,不中断流程。 + +--- + ## 🟡 软规则(允许继续,但有强制后续动作) 以下情况 **不触发中断**,CC 可自行继续推进,但必须在约定位置留痕,模块完成时统一审计。漏留痕 = 红旗。 diff --git a/skills/plan/erp-skeleton-gen/SKILL.md b/skills/plan/erp-skeleton-gen/SKILL.md index 0a51a49..a6320e7 100644 --- a/skills/plan/erp-skeleton-gen/SKILL.md +++ b/skills/plan/erp-skeleton-gen/SKILL.md @@ -101,7 +101,7 @@ cp "${CLAUDE_SKILL_DIR}/templates/scripts-setup-test-db-template.sh" scripts/set ```bash #!/usr/bin/env bash -# scripts/test.sh —— 合并到 main 前的唯一硬闸门。 +# scripts/test.sh —— 合并到默认分支(main / master)前的唯一硬闸门。 # 顺序:setup-db → build → lint → unit+integration → e2e → reset-db # 由 .githooks/pre-push 和 erp-local-test-gate skill(通过子会话)调用。 diff --git a/skills/plan/erp-skeleton-gen/templates/env-local-template b/skills/plan/erp-skeleton-gen/templates/env-local-template index ebe16fb..1a03adc 100644 --- a/skills/plan/erp-skeleton-gen/templates/env-local-template +++ b/skills/plan/erp-skeleton-gen/templates/env-local-template @@ -1,6 +1,15 @@ -DB_HOST=【人工填写:MySQL host,例如 localhost】 +# .env.local — 本地开发凭据(入 .gitignore,不提交) +# +# 规则: +# 1. 值含 `$`、反引号、空格、`!` 等 shell 特殊字符时,必须用单引号包裹: +# DB_PASSWORD='p@ss$w0rd!' +# 否则 `set -a; . .env.local; set +a` 会做变量展开导致密码错乱。 +# 2. DB_HOST 建议保持 localhost / 127.0.0.1;非本地 host 会被 scripts/setup-test-db.sh 防护拒绝。 +# 3. DB_SCHEMA 建议命名含 test / _dev / _local / _ci,避免与生产库同名。 + +DB_HOST=【人工填写:MySQL host,推荐 localhost】 DB_PORT=【人工填写:MySQL port,默认 3306】 DB_USER=【人工填写:开发账号名】 -DB_PASSWORD=【人工填写:对应密码】 -DB_SCHEMA=【人工填写:schema 名,例如 erp_dev】 +DB_PASSWORD=【人工填写:对应密码,含特殊字符时用单引号包裹】 +DB_SCHEMA=【人工填写:schema 名,推荐含 test/_dev/_local,例如 erp_dev】 JWT_SECRET=【人工填写:JWT 签名密钥,256+ bit 随机串】 diff --git a/skills/plan/erp-skeleton-gen/templates/scripts-setup-test-db-template.sh b/skills/plan/erp-skeleton-gen/templates/scripts-setup-test-db-template.sh index 23c02a9..669b762 100644 --- a/skills/plan/erp-skeleton-gen/templates/scripts-setup-test-db-template.sh +++ b/skills/plan/erp-skeleton-gen/templates/scripts-setup-test-db-template.sh @@ -7,14 +7,44 @@ # - scripts/test.sh 开头:清空库,让 Spring 启动时 Flyway 从 V1 开始重放所有 migration # - scripts/test.sh 结尾:清空库,避免测试遗留污染下次运行 # - 手动调试时:reset 到零状态 +# +# 防护:本脚本只允许在本地 host + 测试库名上执行;非预期目标会被拒绝, +# 避免 .env.local 误指向 staging/prod 时触发不可逆 DROP。 set -euo pipefail -source "$(dirname "$0")/../.env.local" +ENV_FILE="$(dirname "$0")/../.env.local" +[ -f "$ENV_FILE" ] || { echo "[setup-test-db] ⚠️ .env.local 不存在($ENV_FILE)" >&2; exit 1; } + +# 用 set -a 加载,让 KEY=VALUE 导出为环境变量;密码中含特殊字符时 .env.local 请用单引号包裹 +set -a; . "$ENV_FILE"; set +a + +# 防护 1:只允许本地 host(localhost / 127.0.0.1 / ::1) +case "${DB_HOST:-}" in + localhost|127.0.0.1|::1) ;; + *) + echo "[setup-test-db] ⚠️ 拒绝在非本地 host (${DB_HOST}) 上执行 DROP DATABASE" >&2 + echo " 如确需对远程 DB 重置(少见,常属误配),请显式声明:TEST_DB_ALLOW_REMOTE=1 $0" >&2 + [ "${TEST_DB_ALLOW_REMOTE:-0}" = "1" ] || exit 1 + ;; +esac + +# 防护 2:schema 名需像测试/开发库(含 test / _dev / _local),否则要求显式确认 +case "${DB_SCHEMA:-}" in + *test*|*_dev|*_local|*_ci) + ;; + *) + echo "[setup-test-db] ⚠️ schema '${DB_SCHEMA}' 不像测试库(期望命名含 test / _dev / _local / _ci)" >&2 + echo " 如确为期望行为,请显式声明:TEST_DB_ALLOW_PROD_NAME=1 $0" >&2 + [ "${TEST_DB_ALLOW_PROD_NAME:-0}" = "1" ] || exit 1 + ;; +esac + +# 防护 3:显式 banner,让人看见自己在 drop 什么 +echo "[setup-test-db] 即将 DROP + CREATE \`${DB_SCHEMA}\` on ${DB_HOST}:${DB_PORT}" MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD}" -echo "[setup-test-db] drop + create database ${DB_SCHEMA}" $MYSQL_CMD -e "DROP DATABASE IF EXISTS \`${DB_SCHEMA}\`; CREATE DATABASE \`${DB_SCHEMA}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" echo "[setup-test-db] done — schema will be applied by Flyway when Spring Boot starts" -- libgit2 0.22.2