Community编程与开发github.com

chrislacey89/预合并审查

实现验证后的主流水线审查步骤。用于创建带有溯源信息的PR,并在合并前进行架构审查。不适用于QA验收、规划或实现工作。

预合并审查 是什么?

该技能是AI代理在代码合并前的关键审查步骤,专门用于在实现验证通过后、合并至主分支前创建PR并执行架构审查。它支持三种运行模式:作者模式(默认)为当前分支创建PR并输出审查建议;审查者模式针对他人创建的PR生成草稿评论;循环模式则将所有发现记录到持久化存储中,确保审查结果不会在会话结束后丢失。技能的核心价值在于通过结构化的架构审查(基于review-checklist.md)确保代码变更与项目架构原则一致,同时不会自动修复代码或阻止合并,而是提供咨询性发现。它支持GitHub Issue和Sidecar两种后端,能够智能判断审查范围(完整分支或增量变更),并通过Review Currency Marker避免重复审查。该技能特别适合需要严格架构审查的团队,尤其是在异步执行(AFK)场景下,确保审查发现能被追踪和处理。

兼容平台✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/chrislacey89/skills/tree/HEAD/pre-merge

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

预合并审查 是做什么的?

创建链接回PRD和切片问题的GitHub PR,然后对照项目的架构原则审查完整差异。生成咨询性发现——不会阻止合并、自动修复代码或创建问题。

调用位置

这是一个主流水线技能,用于实现验证通过后、合并至主分支之前,或在接手他人PR进行审查时使用。

当分支已准备好创建PR、进行架构审查和最终的计划到代码一致性检查时,使用 /pre-merge。当你审查的不是自己创建的PR时,使用 /pre-merge --pr <number>。

不要将其用作实现验证、QA验收或重构规划的替代品。它假定工作已经构建完毕并准备审查。

模式

/pre-merge 以三种模式之一运行。所有三种模式都重用 review-checklist.md 中定义的架构审查维度;它们的不同之处在于消费什么和产生什么。

  • 作者模式(默认)——在你的分支上调用,不带 --pr 参数。该技能创建PR(阶段2)并将发现作为咨询性建议打印到终端(阶段4),然后交还控制权——它不会自己进行任何修复提交,也不会开启第二轮审查。 这是 /execute 步骤6在有人值守路径上自动调用的模式。它读取的内容由分支状态决定,而不是由标志决定: 首次审查读取整个切片,即整个分支,除非切片的工作项记录了起始点;针对已带有审查货币戳记且其头部随后发生移动的PR重新运行时,会将戳记后的提交作为审查的主题,并且不会重新读取之前审查覆盖的内容。阶段1步骤4做出该决定并打印出来。增量是主题,而不是审查者可以读取的限制:review-checklist.md 决定了审查者读取的内容,其中包括增量周围的代码树。
  • 审查者模式——使用 /pre-merge --pr <number> 针对不是你创建的PR进行调用。该技能跳过PR创建(PR已存在),并生成草稿评论文本(阶段4)供你审查和发布,该文本按照 references/comment-craft.md(5P门、三重R、评论信号、MMG交换)的结构组织。
  • 循环模式——以 /pre-merge --loop 方式调用,或在 /execute 在无人值守运行中交回控制权时自动进入。与作者模式类似地运行阶段1-4,但阶段1步骤4的范围决策除外:循环模式始终读取整个分支,从不获取后戳增量,然后继续进入阶段5:它将每一条发现记录在持久分类账中——记录在PR正文中,或工作项上,位于 references/state-backend.md § 审查记录存放位置所指出的位置——并附加支持或反驳每一条发现的证据,然后交回给操作者。在此模式下,发现不再是终端输出;它们成为带有所有者的持久化行。循环模式不修复任何内容,也不进行任何提交——操作者决定如何处理每个发现(参见阶段5 § 为什么循环不修复)。

循环模式的退出条件是每个发现都有所有者——绝不是审查结果干净。 “零发现”是一个禁止的终止信号,这与阶段4现有的最低发现数量保护一致,该保护的存在是因为在非平凡的差异上出现近乎空的审查意味着审查提前结束,而不是代码完美无瑕。一个在干净审查上终止的循环将可靠地产生干净的审查而别无其他(Meadows,《寻求错误目标》;Leveson:永远不要奖励低事件计数——要奖励报告本身)。

如果你正在用户的工作分支之外的分支上运行,并且没有提供 --pr,请询问一次用户是否意味着针对特定的PR编号使用审查者模式,而不是猜测——自动检测可以节省一次按键,但错误分类模式会产生本应是本地建议的草稿评论,反之亦然。

使用时机

  • 作者模式: QA通过后、合并功能分支到主分支之前;在Ralph完成AFK执行并且你已验证行为之后;或者对于任何你想在合并前进行审查的分支,即使没有完整的流水线运行。
  • 审查者模式: 当队友或外部贡献者打开一个PR,并且你希望将 review-checklist.md 中的完整架构审查应用于他们的差异并生成建设性的评论文本时。
  • 循环模式: 当发现必须在产生它们的会话之后仍然存在时——来自 /execute 的AFK交接,或者任何你将分多次审查且不想每次都重新推导的分支。在你付出的成本是丢失发现的线索,而不是进行修复时使用它。

执行流程

阶段1:收集上下文

作者模式:

  1. 询问PRD问题编号。 如果此更改未经过完整流水线,则接受“none”。在从AFK /execute 交接进入循环模式时,不要询问——从交接中获取问题编号(/execute 步骤6传递了它),如果缺失则视为“none”。此时无人可以回答,因此在此处的无条件问题会导致运行在其第一步就挂起。在Sidecar后端上,答案是 W-<n> 工作项ID(步骤2)。此处的“none”意味着阶段2无法在PR上记录内容,并且审查笔记和循环模式分类账没有归属地:references/state-backend.md § 审查记录存放位置说明了它们会发生什么。

  2. 如果提供了PRD:

    gh issue view <number>
    gh issue list --search "in:body #<prd-number>" --state all --json number,title,state,body --limit 100
    

    从每个切片问题的正文中解析边界映射(Produces/Consumes部分)。

    在Sidecar后端上(bash "$SC" backend slices 返回 sidecar,依据 references/state-backend.md;下面的块会询问它,并在拒绝时停止,而不是回退到 gh issue),PRD及其切片是工作项,步骤1中询问的编号是 W-<n> ID:此分支实现的切片,由 /execute 传递,或者其PRD。没有 gh issue 命令运行;溯源关系和边界映射来自工作项:

    SC="<此技能目录>/references/sidecar.sh"
    W="W-<n>"
    case "$(bash "$SC" backend slices)" in
      sidecar) ;;
      github) echo "后端是github:使用上面的问题块读取溯源关系"; exit 0 ;;
      *) echo "溯源关系中止:sidecar.sh backend slices 失败;不猜测github路径" >&2; exit 1 ;;
    esac
    J="$(bash "$SC" work view "$W" --json)" || { echo "溯源关系中止:无法读取 $W" >&2; exit 1; }
    case "$(jq -r '.kind // empty' <<<"$J")" in
      slice) PRD="$(jq -r '.parent // empty' <<<"$J")" ;;
      prd)   PRD="$W" ;;
      *) echo "溯源关系中止:$W 没有 kind" >&2; exit 1 ;;
    esac
    SLICES="$W"
    if [ -n "$PRD" ]; then
      bash "$SC" work view "$PRD" --json | jq -r '"# \(.id) (prd, \(.state))\n\(.body)"'
      SLICES="$(bash "$SC" work list --kind slice --parent "$PRD" --state all)" \
        || { echo "溯源关系中止:无法列出 $PRD 的切片" >&2; exit 1; }
    fi
    while IFS= read -r S; do
      [ -n "$S" ] || continue
      bash "$SC" work view "$S" --json | jq -r '"# \(.id) (slice, \(.state))\n\(.body)"'
    done <<<"$SLICES"
    

    从这些正文中解析边界映射,方式与从问题正文解析完全相同。保留 $W:阶段2将PR记录在其上。

  3. 检测基础分支并评估差异:

    SC="<此技能目录>/references/sidecar.sh"
    # $BASE_REF 是范围端点;$BASE_BRANCH 是 `--base` 接受的名称。
    BASE_REF="$(bash "$SC" base-ref)" || { echo "base-ref 失败:不猜测基础分支" >&2; exit 1; }
    BASE_BRANCH="$(bash "$SC" base-ref --name)" || { echo "base-ref --name 失败:不猜测基础分支" >&2; exit 1; }
    git diff "$BASE_REF...HEAD" --stat
    git log --oneline "$BASE_REF..HEAD"
    

    base-ref 在有声明的 base_ref 时打印它,否则打印默认的 origin/HEAD。在非零退出时不会打印任何引用:停止并传递其标准错误,而不是猜测基础分支。对于堆叠PR切片,此处设置的变量不会到达后续块:将兄弟切片的分支名称(PR将目标的上游分支)放入每个消费该名称的块的 BASE_BRANCH= 占位符中(你的后端的阶段2块和阶段4戳记块)。如果与基础分支没有差异,则告诉用户没有需要审查的内容并停止。

    残留: $BASE_REF 仅在最近一次 git fetch 后才是最新的。当 origin/<base> 无法解析时,base-ref 会针对本地分支进行测量并在标准错误上说明;如果计数看起来不正确,执行 git fetch 并重新运行。

  4. 决定范围——整个切片,还是后戳增量。 此决策仅适用于作者模式。循环模式跳过它,并无条件地将 SCOPE_FROM 设置为 $BASE_REF,根据阶段3的循环模式说明,每次调用时读取整个分支。首次审查读取整个切片:sidecar.sh range 给出其左端点,当步骤2命名的工作项在分支上仍有记录时,左端点是切片的记录起始点,否则是 $BASE_REF,因此在 github 后端上,切片是整个分支。针对此技能已经戳记过的PR(其头部随后发生了移动)重新运行——一个 /fix-findings 轮次,或故意删除锁定后的人工修复——会将后戳增量作为其主题。戳记位于 backend stamp 所指示的位置,依据 references/state-backend.md:PR正文或Sidecar。使用与 /closeout 步骤2相同的表达式读取PR正文;scripts/test-review-currency-marker.sh 固定了两个字节相同的戳记,并且 scripts/test-delta-review-scope.sh 针对其命名的每个分支状态在两个后端上运行此块与一个夹具:

    SC="<此技能目录>/references/sidecar.sh"
    case "$(bash "$SC" backend stamp)" in
      sidecar)
        REVIEWED_SHA=$(bash "$SC" stamp read "$(git branch --show-current)") ;;
      github)
        REVIEWED_SHA=$(gh pr list --head "$(git branch --show-current)" --json body -q '.[0].body // ""' \
          | sed -n 's/.*<!-- reviewed-at: \([0-9a-f]\{40\}\) -->.*/\1/p' | tail -1) ;;
      *) echo "范围中止:sidecar.sh backend stamp 失败;不猜测github路径" >&2; exit 1 ;;
    esac
    W="<步骤2命名的 W-<n>,在github后端上为空>"
    BASE_REF="$(bash "$SC" base-ref)" || { echo "范围中止:sidecar.sh base-ref 失败" >&2; exit 1; }
    RANGE="$(bash "$SC" range ${W:+"$W"})" || { echo "范围中止:sidecar.sh range 失败" >&2; exit 1; }
    SLICE_FROM="${RANGE%%..*}"
    SCOPE_FROM="$SLICE_FROM"
    if [ -n "$REVIEWED_SHA" ] \
      && git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \
      && [ "$REVIEWED_SHA" != "$(git rev-parse HEAD)" ] \
      && [ -z "$(git log --merges --format=%H "$REVIEWED_SHA..HEAD")" ]; then
      SCOPE_FROM="$REVIEWED_SHA"
    elif [ -n "$REVIEWED_SHA" ] \
      && ! git merge-base --is-ancestor "$REVIEWED_SHA" HEAD 2>/dev/null \
      && [ "$(git rev-parse -q --verify "$REVIEWED_SHA^{tree}")" = "$(git rev-parse 'HEAD^{tree}')" ]; then
      SCOPE_FROM="HEAD"
    fi
    if [ "$SCOPE_FROM" = HEAD ]; then
      echo "审查范围:无,仅历史重写——HEAD 携带在 $REVIEWED_SHA 审查过的代码树"
    elif [ "$SCOPE_FROM" = "$BASE_REF" ]; then
      echo "审查范围:整个分支,从 $BASE_REF 开始"
    elif [ "$SCOPE_FROM" = "$SLICE_FROM" ]; then
      echo "审查范围:整个切片,从 $SLICE_FROM($W 的记录起始点)开始"
    else
      echo "审查范围:后戳增量,从 $SCOPE_FROM 开始"
    fi
    git diff "$SCOPE_FROM...HEAD" --stat
    git log --oneline "$SCOPE_FROM..HEAD"
    

    四种状态会回退到整个切片,每种都是有目的的:没有戳记(首次审查,或手动创建的PR);戳记不是 HEAD 的祖先且 HEAD 不再携带其代码树(分支被重写且内容随之改变,或者被审查的提交已消失——/closeout 将此称为更强的分歧信号,并且它不是更小的审查);戳记等于 HEAD(没有新的内容可以限定范围);以及戳记之后的合并提交(后戳增量将携带基础分支自身的更改,而这些更改不是该分支上任何人编写的)。除非 range 找到了记录起始点,否则整个切片就是整个分支,当 range 找到一个因折叠或变基而被重写掉的记录起始点时,它会在标准错误上说明并回答 $BASE_REF,而不会重新锚定起始点。该决策会被打印出来,以便终端读取和PR对读取的内容说同样的话。

    保留审查过代码树的重写并非分歧。 当被戳记的提交不是 HEAD 的祖先但仍然存在于本地并且携带与 HEAD 相同的代码树时,历史发生了变化而内容没有:阶段4的交接所提供的折叠(git rebase -i --autosquash)正是产生这种情况。该块打印 review scope: none 并将 SCOPE_FROM 设置为 "HEAD"。跳过阶段3以及阶段4中除戳记(在 HEAD 处重新戳记)和交接菜单之外的所有内容。戳记证明审查过的内容,而代码树哈希就是那个确切的内容。如果被戳记的提交在本地不可用,则无法比较代码树,重写将采用上述的整个分支回退。

    在增量重新运行时,增量是主题,并且代码树仍然可读。 向每个阶段3审查者简要介绍后戳提交声称关闭的发现——本次会话中之前的输出,循环模式写入的分类账(位于 references/state-backend.md § 审查记录存放位置所指出的位置),或提交消息本身——以及任何存在的 /fix-findings 打断器裁决,以及 bash "$SC" profile 输出的逐字内容,这些是阶段3的契约授予每个审查者的。每个提交的第一个问题是修复是否关闭了其发现所命名的每个站点的发现,这是修复完整性维度;第二个问题是…

(文档已截断以供翻译)

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

chrislacey89/closeout

Primary pipeline tail step after /pre-merge, when a reviewed PR is ready to land and you want the workspace back to a clean base. Use to merge, switch off the worktree before removing it, prune the merged branch, pull base, and verify a clean end state. Triggers on 'close out', 'merge and clean up', 'get back to a clean main/base', 'tear down the worktree'. Not for capturing lessons (that's /compound), or closing the PRD and any other open slices and research-artifact hygiene (that's the cleanup prose in SYSTEM-OVERVIEW Step 9).

chrislacey89/compound

Primary pipeline knowledge-capture step that closes the compounding loop. Capture a durable lesson in docs/solutions/ onto the open PR before /closeout merges (the default), or post-merge when the lesson only surfaces during/after merge. Use so future /research and /write-a-prd runs improve. Not for trivial edits with no reusable project-level learning, and not for the merge/teardown itself (that's /closeout).

chrislacey89/excavate

User-invoked side-route for onboarding to a legacy repo with no docs/solutions/, research archive, or written decisions. Mines the last 12 months of git history and, when gh is available, PR bodies and review threads, into ranked hotspots, cited recovered decisions, and a who-to-ask map, then offers a PR of docs/solutions/ entries. Not for repos that already compound, not for judging people, not model-invoked.

chrislacey89/execute

Primary pipeline execution step after /prd-to-issues or for clearly scoped implementation work. Use to build, verify, and commit a concrete slice, delegating to /tdd for backend work and behavior-heavy frontend logic when red-green-refactor will reduce risk. Not for shaping or pre-merge review.

相关技能