mcpField
M
merge-discipline

merge-discipline

github.com/fudesign2008开发659 次安装今天更新
代码仓库

关于 merge-discipline

Merge Discipline 是一项在合并到受保护分支前执行检查的共享技能,供开发工作流使用。它按顺序处理 OpenSpec 归档、变基与冲突、覆盖率、PR 代码审查及 squash 决策,并依赖 pr-code-review。

主要功能

  1. 01检查 OpenSpec 变更是否归档
  2. 02检测目标分支落后与冲突
  3. 03按项目偏好执行覆盖率门控
  4. 04通过 pr-code-review 审查 PR
  5. 05判断 squash 并固定提交尖端

适用场景

  1. 01合并受保护分支前检查 OpenSpec 变更
  2. 02目标分支前进时评估是否需要变基
  3. 03按项目配置决定是否运行覆盖率分析
  4. 04在合并 PR 前执行代码审查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fudesign2008/open-skills --skill merge-discipline

安装后在 Claude Code 中输入/merge-discipline确认已加载。

附带文件evals/evals.jsonreference.md

详细介绍

合并规范

内部共享技能——合并时规范的唯一事实来源。分为五部分,按顺序执行 A → B → C → R → D:OpenSpec 归档关联门控(Part A)+ rebase/冲突预检查(Part B)+ 覆盖率门控(Part C)+ PR 代码审查(Part R,强依赖 pr-code-review)+ squash 决策与 tip 固定(Part D)。引用此技能的工作流必须在 frontmatter dependencies 中声明此依赖,若缺失则在启动时中止。

前置技能检查

加载时,扫描 frontmatter dependencies。若有任何技能缺失 → 打印下方的 Missing Notice 并中止(不得静默降级)。

Missing Notice
⚠️ merge-discipline is missing a strong dependency and cannot run

【Missing skill(s)】
- <name>: <one-line purpose>

【Why it's needed】
merge-discipline strongly depends on:
- `pr-code-review`: Part R — multi-perspective PR review with ≥80 confidence filter before tip-pin merge

【Install】
Install each missing skill by name (preferred):
  npx skills add FuDesign2008/open-skills -g --skill <name> --yes
Example for this skill's dependency:
  npx skills add FuDesign2008/open-skills -g --skill pr-code-review --yes
Or install every open-skills skill:
  npx skills add FuDesign2008/open-skills -g --skill '*' --yes

Re-trigger merge after installing.

列出多个缺失技能时,针对每个缺失名称分别打印一行 npx skills add … --skill <name> --yes(不要只显示 '*')。

适用场景

任何合并到受保护分支的操作(glab mr merge / gh pr merge / git merge <target>)——无论是来自工作流的分支收尾决策、用户直接发出的合并命令(“merge MR” / “合并”),还是 AI 准备发起合并调用。“保留分支” / “继续开发”不触发此流程。

使用 feature-branch-closeout 的宿主由该技能负责收尾菜单;本技能仅在选择合并后运行(或在收到直接合并命令时运行)。不要在此重新定义完整菜单。

合并时的执行顺序:Part A(归档门控)→ Part B(rebase)→ Part C(覆盖率)→ Part R(PR 代码审查)→ Part D(squash 决策 + tip 固定)→ 合并。 Part A 首先运行,以免归档工作被推迟到 rebase/CI 之后。rebase 会改变源 tip,因此必须在新的 tip 上重新运行 Parts C/R/D。


Part A — OpenSpec 归档关联门控

防止先合并、后归档:实现已进入受保护分支,但 OpenSpec 增量规范/归档仍只存在于后续文档 PR 中。

关联判定(任一命中即为关联)
  1. Diff 命中:PR 或分支 diff 包含 openspec/changes/<name>/ 下的路径,且不在 openspec/changes/archive/ 下。
  2. 会话命中:与会话绑定的 OpenSpec 变更名称仍在 openspec list 中显示为 active(未归档)。
决策表
状态操作
未关联放行 → Part B
已关联且变更已归档(仅位于 openspec/changes/archive/… 下,且不在 openspec list 中)放行 → Part B
已关联且变更仍为活跃阻止合并。指出变更名称。要求在同一源 tip 上完成归档(同步主规范 + 移至 openspec/changes/archive/),然后从 Part A 重新进入合并流程。不要运行 Part B/C/D,也不要运行合并命令。
硬性规则
  • 用户直接发出的合并命令必须执行此 Part——不得隐式跳过。
  • 用户只能通过明确留痕的跳过操作强制继续(使用下方模板);默认操作是阻止。
  • 变更仍有关联且处于活跃状态时,不要建议在合并后单独提交归档 MR(仅在 Part D Strategy B 中作为恢复方案)。
检测(示例)
# Diff hit (GitHub example — adapt for platform)
gh pr diff <id> --name-only | grep -E '^openspec/changes/[^/]+/' | grep -v '^openspec/changes/archive/'

# Session hit
openspec list --json   # active names; compare to session-bound change
留痕模板

位置:PR 描述和 design.md Verification Notes(如有)。

情况模板
用户明确跳过【OpenSpec archive 门控跳过】用户显式跳过,关联 change 仍为 active。时间:<ISO>。决策人:用户。change:<name>。
隐式遗漏(未经过门控便已合并)【OpenSpec archive 门控漏跑】合并已发生但 Part A 未运行。时间:<ISO>。漏跑阶段:<合并前/合并后>。
风险信号
  • 因为“第 8 阶段已经说好稍后归档”或“合并后再开一个文档 PR”而合并。
  • openspec/changes/<name>/ 仍在 PR diff 中,却在直接收到“merge MR”命令时跳过 Part A。
  • 将 Strategy B 视为仍处于打开状态且有关联的 MR 的理想路径。

B 部分 — Rebase/冲突预检

防止出现盲目合并到不断变化的目标这一问题:等用户说“合并”时,目标分支(release/*、main 等)通常已经有变动——队友已经合入了自己的工作。如果不检查就合并,要么在平台上失败(mergeable=false),要么悄无声息地合入到最新提交之前。本部分会让 AI 把这种情况告知用户并提出 rebase,而不是让用户得记得主动询问。

检测
git fetch origin <target-branch>
BEHIND=$(git rev-list --count HEAD..origin/<target-branch>)
BASE=$(git merge-base HEAD origin/<target-branch>)
CONFLICTS=$(git merge-tree "$BASE" HEAD origin/<target-branch> | grep -c "^changed in both")

<target-branch> = MR/PR 的基分支(通过平台的 CLI 查询),或用户指定的合并目标。

决策矩阵
状态操作
BEHIND=0 且 CONFLICTS=0无问题 — 继续执行 C 部分
BEHIND>0 且 CONFLICTS=0报告“目标分支领先 N 个提交;rebase 可顺利应用” → 等待用户确认
CONFLICTS>0报告“目标分支领先 N 个提交,约有 M 个文件冲突” → 等待用户确认

未经确认不得自动 rebase——rebase 会重写源分支并触发完整的 CI 重新运行;是否进行由用户决定。

确认后 — 执行 rebase(仅源分支)

源分支是用户个人的修复分支(fix/jira-fix-…),团队策略允许对其执行强制推送。本部分绝不会重写受保护的目标分支。

git rebase origin/<target-branch>
# On conflict: delegate to git-conflict-resolve skill (mode=rebase — per-commit resolution)
git push --force-with-lease origin <source-branch>

如果 git-conflict-resolve 不可用,或用户中止操作,则停止——将工作树留在 rebase 中途(使用 git rebase --abort 退出),由人工解决。

范围界定 — 本部分不等待 CI

--force-with-lease 推送后,本部分结束:报告“已 rebase,PR 已更新,CI 正在重新运行”,并将控制权交还给工作流。CI 门禁和实际合并仍由工作流的常规收尾流程负责(C 部分 → D 部分 → 合并)——和任何评审后合并一样,用户会在 CI 通过后回来。本部分是 rebase 修复工具,不是协调通过 CI 并完成合并的工具。

循环次数上限

每次合并尝试最多触发一次 rebase。如果等待 CI 时目标分支再次发生变动,下一次合并尝试会从 A 部分重新进入并再次检测——有次数限制,不会无限循环。

危险信号
  • 因为“CI 已经通过”而跳过本部分直接合并(通过的是旧版本上的 CI;目标分支已发生变动)。
  • 执行 rebase 后,在新版本通过 D 部分的祖先检查之前就声称已完成合并。

C 部分 — 覆盖率门禁

有合并意图时,C 部分启动。默认不会自动运行分析器。

1. 确定项目偏好

依次扫描 AGENTS.md 和 CLAUDE.md(以第一个匹配项为准),查找符合以下格式的行(键不区分大小写):

coverage-gate:\s*(always|never|ask)\b

值行为
(未设置)视为 ask
ask每次合并时询问用户:此次合并要运行覆盖率检查,还是跳过?不得自动运行
always不经询问,直接执行门禁步骤(取决于分析器是否可用)
never跳过分析器;写入项目偏好留痕;继续执行 R 部分
2. 预检测(仅当决定运行时)

如果 test-coverage-analyzer 可用,则继续执行门禁步骤。如果未找到,则输出“门禁不可用:未检测到 test-coverage-analyzer”,写入环境缺口留痕,并让用户决定是否继续合并。

3. 门控步骤(仅当决策为 run 时)

独立的 Bash 权限——运行分析器脚本:

  1. 构造 --base(按顺序尝试,命中后停止):

    • MR/PR:gh pr view --json baseRefName -q .baseRefName / glab mr view <iid> -F json | jq .target_branch → --base <target>(纯分支名;脚本的 validate_ref 会添加 origin/)
    • 失败 / detached HEAD / 无远程仓库 → 省略 --base,依赖脚本的 5 级回退;警告“无显式 base,MR 可能会将差异误判为 0”
    • 多仓库 MR → 对每个仓库分别运行门控,并为每个仓库指定自己的 --base;任何仓库失败 → 整体暂停
  2. 调用脚本(先阅读 test-coverage-analyzer SKILL.md,确认参数约定): python3 "<SKILL_DIR>/scripts/analyze_coverage.py" "<project-root>" [--base <target>]

  3. 决策矩阵:

    结果🤖 自动👤 手动
    ✅ 已生成报告 + 覆盖率达到阈值继续到 Part R(PR 代码审查)提示通过,等待用户重新确认
    ⚠️ 覆盖率低于阈值暂停,输出报告,等待用户(强制继续/添加测试/中止)同左
    💥 崩溃 / 未生成报告 / 退出码为 1视为门控失败,暂停同左
    📭 无测试代码 / 通过率为 0%展示报告,暂停并等待用户判断同左
    🕳️ 应该运行,但门控未运行且已合并(隐式漏跑)暂停合并,重新运行门控;若已合并,则记录漏跑留痕同左

应该运行 = 偏好为 always,或偏好为 ask 且用户选择运行。偏好为 never 或用户在 ask 下明确选择跳过,不属于隐式漏跑。

留痕模板

位置:PR 描述和 design.md 的 Verification Notes。

情况模板
用户明确跳过【覆盖率门控跳过】用户显式跳过,未运行 test-coverage-analyzer。时间:<ISO>。决策人:用户。
项目偏好为 never【覆盖率门控跳过】工程偏好 coverage-gate: never。时间:<ISO>。决策人:项目配置。
环境缺失(未找到 skill)【覆盖率门控跳过】未检测到 test-coverage-analyzer skill,门控不可用。时间:<ISO>。决策人:系统(环境缺漏)。
隐式漏跑【覆盖率门控漏跑】合并已发生但门控未运行(应跑未跑)。时间:<ISO>。漏跑阶段:<合并前/合并后>。

Part R — PR 代码审查(强依赖)

防止未经 PR 审查就合并:覆盖率/CI 可能显示绿色,但差异中仍可能包含高置信度缺陷,而多视角、双轴审查能够发现这些缺陷。

1. 解析 pr-review-gate 偏好

依次扫描 AGENTS.md 和 CLAUDE.md(以首次匹配为准),查找:

pr-review-gate:\s*(always|never|ask|non-code-light|auto)\b

值行为
(未设置)按 auto 处理(根据内容匹配深度——表面分类 + 规模/风险升级)
always以 depth=full 运行 pr-code-review
auto根据内容匹配深度:分类表面(§2)+ 规模/风险升级(§2.5)——未触发升级条件 → light;触发任一升级条件 → full(包括应用代码)
never跳过 pr-code-review;记录项目偏好留痕;继续到 Part D
ask询问用户本次合并要选择 full / light / skip;不得自动选择;skip 需要用户明确选择跳过的留痕
non-code-light对 PR 表面进行分类(§2);非应用代码 → depth=light;应用代码 → 使用与 auto 相同的规模/风险阶梯
2. 分类 PR 表面(需要时)

当偏好为 non-code-light、auto 或未设置时(或用户在 ask 下选择 light 时),使用 reference.md「非应用代码表面」中的允许/拒绝表,对打开的 PR/MR 三点式变更路径进行分类。

  • 所有路径均在允许列表中,且没有路径在拒绝列表中 → 非应用代码
  • 存在任何在拒绝列表中的路径(或同时包含两类路径)→ 应用代码
2.5 规模/风险升级

当偏好为 auto(或未设置)时,在表面分类之后应用;或者当偏好为 non-code-light 且表面属于应用代码时应用。(non-code-light + 非应用代码无需使用此表,保持 light。)命中任意一项,即强制设置 depth=full。

信号来源规则
大型差异三点差异统计变更行总数 > 400 或 变更文件数 > 20 → full
破坏性变更信号PR 标题/描述/提交消息,不区分大小写匹配 migration、schema、breaking、API contract、deprecat → full
失败后重新进入此 tip 上的 Part R 历史记录此 tip 上先前有 Part R 失败,修复后重新提交审核 → full
敏感路径/关键词变更路径 + 标题/描述/提交,与 reference.md「敏感路径和关键词」对照匹配任意 glob 或关键词 → full

未命中升级条件 → light(包括应用代码)。阈值和敏感项表是可针对仓库调整的常量(参见 reference.md「基于内容匹配的深度阶梯」)。

3. 运行或跳过
  1. 当需要运行审核时,确认 frontmatter 依赖 pr-code-review 可用(加载时已运行前置检查)。
  2. 如果偏好为 never(或 ask + 用户跳过):写入留痕 → Part D(不要加载 pr-code-review)。
  3. 否则,根据选定的 depth(full 或 light)对即将合并的开放 PR/MR 加载 pr-code-review(遵循该技能 — Standards∥Spec,置信度 ≥80)。
  4. 决策矩阵:
结果操作
通过 — 两个维度均未保留 ≥80 的 Critical/Important 问题继续 Part D
失败 — 任一维度存在 ≥80 的 Critical/Important 问题阻止进入 Part D。 仅在源 tip 上修复,不得视为解锁条件。可选择:(1) 在源 tip 上修复,然后必须在新 tip 上重新运行 Part R(§3.5);或 (2) 用户明确跳过/接受残留并写入留痕。仅当 Part A/B 关联或变基规则要求时,才从 Part A 重新进入 — 不得以此替代重新审核。
技能不符合条件而跳过(已关闭/草稿/本次会话已审核)只有当 PR 仍是待合并对象,且此 tip 上存在先前 ≥80 分的双维度干净审核时,本 Part 才视为通过;否则暂停并询问用户。修复后在之前的 tip 上进行的干净审核不计入。
3.5 失败 → 修复 → 重新审核,直到通过(轮次上限)

审核运行失败且 Agent 应用了源 tip 修复时(而非明确跳过/接受并留痕):

  1. 将本次合并尝试中的每次 pr-code-review 调用计为一轮(初次失败为第 1 轮)。
  2. 修复提交到 tip 后,重新解析深度(§1–2.5;如果命中失败后重新进入信号 → full),并在进入 Part D 之前对新 tip 重新运行 pr-code-review。
  3. 只有当某轮通过(任一维度均无 ≥80 的 Critical/Important 问题),或用户明确跳过/接受残留并写入留痕时,才转至 Part D。
  4. 上限 = 3 轮。 如果第 3 轮仍失败:暂停并询问用户 — 继续(恰好再进行一轮审核;若失败则再次暂停)/接受残留阻塞并写入留痕/中止合并。不要自动进入 Part D。
  5. 从未运行 Part R 的偏好 never/ask 跳过路径不进入此循环。
留痕
情况模板
用户明确跳过【PR code-review 门控跳过】用户显式跳过 Part R(pr-code-review)。时间:<ISO>。决策人:用户。PR:<url or id>。
达到轮次上限后接受残留【PR code-review 残留接受】用户在 Part R 轮次封顶后接受残留 ≥80 Critical/Important。时间:<ISO>。决策人:用户。PR:<url or id>。轮次:<n>。
项目偏好为 never【PR code-review 门控跳过】工程偏好 pr-review-gate: never。时间:<ISO>。决策人:项目配置。
使用轻量路径可选:在审核评论中添加备注:pr-review-gate: <non-code-light|auto>; surface=<non-application-code|application-code>; scale=<ok|escalated>; sensitive=<ok|hit>;depth=<light|full>
危险信号
  • 因为“CI 是绿色的”或“coverage-gate never”而跳过 Part R(跳过 coverage 不等于跳过 Part R)
  • 未经分类便将未设置的 pr-review-gate 视为一刀切的审查深度——未设置 ≡ auto(根据内容匹配:基础审查 + 按规模/风险升级);一刀切的 full/light/skip 均禁止
  • 未加载 pr-code-review,却把 Claude Code /code-review 插件当作替代方案
  • 将 Standards 和 Spec 合并成一个排序列表,并把“整体看起来不错”视为 Part R 通过
  • 在 auto 或 non-code-light 模式下,将 application-code 一概视为 full(full 需要命中升级条件,或偏好设置为 always)
  • 修复 Part R 发现的问题后,未在当前 tip 上重新进行双轴无问题审查,就进入 Part D (除非有明确的 skip/accept-residual 留痕)

Part D — Squash 决策 + tip 锁定(Part R 通过后、合并前)

Part D 负责审查通过到执行合并命令之间的所有事项:squash 决策(步骤 0)可防止默认采用合并策略,tip 锁定(步骤 1–4)可防止合并时使用过期 tip。(事后分析:docs/mr-merge-stale-tip-archive-miss-incident.md。)

步骤 0 — Squash 决策(必须在执行合并命令前完成)

防止默认采用合并策略:GitLab 的“接受合并请求时 squash 提交”复选框和 GitHub 的 squash 合并方式,都必须作为选项展示并由用户确认,绝不能使用平台默认值或由 AI 静默选择。若不同选择会导致不同结果,就需要提示用户;若可行策略不足两个,便没有可供选择的事项,因此步骤 0 应直接结束,而不是提问。

  1. 列出即将合并的 tip 上的提交(即步骤 1 将锁定的同一个 tip):

    gh pr view <id> --json commits        # GitHub
    glab mr commits <id>                  # GitLab (or the MR commits API)
    
  2. 折叠预检查——若符合以下任一条件,说明结论并给出一行理由,然后跳过后续步骤,直接执行(用户仍可根据已说明的结论进行覆盖):

    • 比 base 恰好多出一个提交 → “单个提交:不 squash”(无需合并多个提交)。
    • 仓库/平台策略仅允许一种合并方式 → 采用该方式,并说明这是强制策略。
  3. 分类并提出建议(两个或更多提交)——依据决策表,说明建议及其理由:

    提交历史建议
    原子提交且各自有价值(功能 / 可独立审查的增强 / 分别归档)不 squash — 合并提交可保留历史
    琐碎提交的累积(fixup / typo / wip / CI 重试噪声,没有独立价值)Squash — 合并为一个提交
    源分支将继续接收开发工作倾向于不 squash — squash 会切断与目标分支共享的提交图,并导致后续合并产生冲突
  4. 询问并等待——展示建议,并要求用户明确选择(squash / 不 squash)。对于两个可行策略,绝不自动择一:直接合并命令和自动模式的托管平台工作流都必须在此暂停(此询问是合并流程的子步骤,不是模式回退)。用户的明确选择优先于建议。

  5. 按选定策略执行——将策略传入步骤 1 的合并命令(gh pr merge <id> --merge|--squash … / glab mr merge <id> [--squash] …,标志依据所用 CLI 版本)。若平台不支持 squash:说明这一限制,并使用可用方式合并。

步骤 1–4 — 锁定提示

防止过期提示合并竞态:在合并前几秒推送的归档/修复提交未能进入目标分支,因为合并会快进到推送前的提示(其流水线已通过),而刚推送的提交仍留在源分支上。

  1. 锁定合并修订版本。

    MERGE_SHA=$(git rev-parse origin/<source-branch>)   # or the SHA returned by push
    

    使用 glab mr merge <id> --sha "$MERGE_SHA" -y(GitLab)或 gh pr merge <id> --match-head-commit "$MERGE_SHA"(GitHub;较早的文档可能写作 --sha——请使用你的 gh 支持的标志),并采用步骤 0 中选择的合并方式进行合并。禁止不锁定提示的直接合并。 如果平台 CLI 没有锁定提示的标志:请等待该提示的流水线通过后再合并,并将步骤 3 视为强制后备措施。

  2. 不要轻信即时出现的 Pipeline succeeded。 如果刚在合并前推送了新提交,立即出现的 Pipeline succeeded 几乎肯定是旧提示的结果。请确认结果的 sha 与刚推送的提示一致,或依赖步骤 1 中的提示锁定。

  3. 合并后的祖先检查(强制)。

    git fetch origin <target>
    git merge-base --is-ancestor "$MERGE_SHA" origin/<target> && echo OK || echo MISSING
    

    缺少 → 刚推送的提交(归档/规格同步/修复)未进入目标分支。不要声称已完成,不要继续进行 Jira 回写。请打开补录 MR(cherry-pick),或暂停并等待用户处理。

  4. 双策略与后备方案。

    • 策略 A(默认): MR 仍处于打开且可合并状态;任何关联的 OpenSpec 变更均已归档(或不存在关联) → 在锁定提示的情况下,在同一提示上合并实现和归档(执行步骤 1–3)。
    • 策略 B(仅用于恢复): 实现 MR 已意外合并,而归档仍待处理 → 为归档单独打开文档 MR;明确列出“归档待处理 !N”并留痕;绝不可假装归档已进入目标分支。如果 MR 仍处于打开状态且关联的是活动变更,则绝不可建议采用此策略——这种情况属于 A 部分拦截项,而非策略 B。
合并后工作区同步(祖先检查通过后)

合并只完成了整个流程的一半——在开始下一个任务之前,本地工作区必须返回集成分支。根据 MR 的基分支元数据确定目标分支(gh pr view <id> --json baseRefName / GitLab target_branch),复用已确定的基分支 B 部分;绝不要假设是 main 或 master,因为实际目标通常是 develop、release/* 或集成分支。

  1. 目标分支在本地存在 → 切换到该分支并快进:git checkout <target> && git pull --ff-only origin <target>;报告一条同步信息(分支 + 新提示)。
  2. 目标分支在本地不存在 → 提供获取它的命令(git fetch origin <target>:<target>);必须报告本地副本缺失,不得静默跳过。
  3. 无法快进(历史已分叉) → 报告分叉情况并将决定交给用户;不得自行变基或强制更新。
  4. 明确询问是否删除已合并的源分支(本地和远程)作为后续操作——清理工作仍由 feature-branch-closeout 负责。

模式生命周期

在 ask 模式下询问用户,或在用户选择启用后运行分析器,这些操作本身不会触发“自动恢复为手动模式”(合并流程的子步骤)。门控暂停(A 部分拦截项/低于阈值/崩溃/应运行隐式遗漏/等待用户回答 ask/R 部分失败)= 合并流程中断,按既有规则恢复为手动模式。B 部分的变基执行遵循相同规则:用户确认的变基是合并流程的子步骤(本身不会触发恢复为手动模式);未解决的冲突或已中止的变基会中断合并流程并恢复为手动模式。

集成指南(用于引用工作流)

  • 在自己的正文中保留: 阶段顺序行(例如 archive → branch-closeout → merge-discipline(A→B→C→R→D) → merge → writeback)、指向此技能的一行说明,以及 1-2 个关键红旗。不要将各部分步骤复制到正文中。
  • 委托给此技能: 全部五个部分——A(归档关卡)、B(变基)、C(覆盖率)、R(通过 pr-code-review 进行 PR 代码审查)、D(固定 tip)。
  • 合并前检查清单(唯一来源): reference.md「合并前检查清单」/“合并前检查清单”。引用此指南的工作流只保留一句指引——不要将检查清单粘贴到工作流的 reference.md 中。
  • 缺少强依赖项: 中止并逐个技能打印 npx skills add FuDesign2008/open-skills -g --skill <name> --yes(参见“先决技能检查”)。

评论

支持 Markdown