--- name: 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(curl *) Bash(jq *) Bash(sed *) Bash(awk *) Bash(cat *) Bash(echo *) Bash(mkdir -p .tmp) Bash(mv .tmp/*) Bash(rm -f .tmp/*) --- **所有输出必须使用中文。** # mr-create ## 前置条件 - `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) - `Bash`: `git status --porcelain` - 输出非空 → 打印 dirty 文件清单并**停止**: ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [mr-create] ⚠️ worktree 不干净,无法 push 以下文件有未提交改动: 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。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` ### 步骤 3:push 推送全部已 commit 内容 `git push -u origin ` — **不要**使用 `--no-verify`(hook `deny-no-verify.sh` 会拦截)。 此 push 包含:代码 commit(feature-tdd 产出)+ 模块 review fix commit(feature-review 产出)+ test-gate evidence commit(test-gate 产出)+ 模块完成报告 commit(module-report 产出)。 ### 步骤 4:读取 MR 标题模板 用 `Read` 读取 `${CLAUDE_SKILL_DIR}/templates/mr-title-template.md`,填充 `module_id`、`module_name`,得到 MR 标题字符串(一行,较短,进上下文 OK)。 ### 步骤 5:生成 MR 描述文件(纯 shell,不读模块报告内容进上下文) ```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" ``` 关键:**模块报告内容只经 awk 管道流过**,从不进入 LLM 上下文。 ### 步骤 5.3:加载 GitLab 凭据并探测目标分支 ```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.5:幂等守门——检查 source branch 是否已有 opened MR 防止部分失败后重试创建重复 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 ``` - `EXISTING_IID` 非空 → 打印 `[mr-create] 检测到已有 opened MR: !${EXISTING_IID}`,**跳过步骤 6**,直接用 `EXISTING_IID` / `EXISTING_URL` 进入步骤 7。 - `EXISTING_IID` 为空 → 正常走步骤 6 创建新 MR。 ### 步骤 6:创建 MR(仅当 5.5 未命中时) ```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" ``` 对应 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 ### 步骤 11:停止 — 等待人工 Approve + Merge 用户合并后再次运行 `/erp-workflow:coding-start`,入口会自动检测 MR merged → 探测默认分支(`main` 或 `master`)→ `git checkout <默认分支>` + `git pull --ff-only origin <默认分支>` 同步远程 → 派发下一模块。 ## 设计要点 - **默认分支(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。 ## 参考 - `${CLAUDE_SKILL_DIR}/templates/mr-title-template.md` - `${CLAUDE_SKILL_DIR}/templates/mr-description-template.md` - 上游:`module-report` - 守门:`interrupt-check` - 下游闸门:用户手工 MR Approve + Merge