关于 pr-landing-discipline
pr-landing-discipline 是供变更作者使用的技能,规范 PR 或补丁从评审到落地的流程。它定义草稿与就绪状态、评论分流和收敛检查,并默认由人工决定合并。
主要功能
- 01提交待处理时保持草稿状态
- 02同一轮处理评审评论
- 03CI 与评审线程收敛门槛
- 04检查 PR 内容中的个人信息
- 05处理机器人评审意见的规则
- 06默认由人工决定合并
适用场景
- 01管理经历多轮评审的 PR
- 02检查 PR 是否达到合并就绪状态
- 03处理机器人评论而避免推测性修改
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vivswan/ skills --skill pr-landing-discipline安装后在 Claude Code 中输入/pr-landing-discipline确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.mdREADME.md.codex-plugin/plugin.jsonagents/openai.yaml详细介绍
PR 落地规范
PR 在合并前都属于进行中的工作。提交尚未完成时保持草稿;一旦收敛,立即将其设为就绪。每一轮评审都在出现的同一周期内完成分流,依据声明的目的核对代码行数,并默认由人类决定是否合并。
这些规则适用于任何将变更从“已打开”推进到“已落地”的会话:通过评审轮次直至合并的 PR,或直接推送的补丁。
下文中的“作者”指准备变更的人,无论是人类还是 Agent,也无论是独自工作还是在多 Agent 会话中工作。撰写 PR 正文或 issue 是 /pr-and-issue-discipline 技能发挥作用的环节。本技能从 PR 创建后开始适用。
适用时机
- 开放状态的 PR 刚收到一轮评审意见
- PR 收敛,或已就绪的 PR 出现需要新提交的工作
- 变更即将落地,无论是通过合并 PR 还是直接推送
- 决定由谁合并已收敛的 PR
草稿规范
- 每个 PR 都以草稿状态打开,并在整个评审过程中保持草稿状态。
- 一旦收敛,立即设为就绪:绝不批量处理,也绝不延后。只有在重新阅读
/pr-and-issue-discipline技能规定的 PR 正文后,才能将其设为就绪;因为正文是在 PR 打开时撰写的,而差异此后已有变动。 - 一旦就绪的 PR 出现需要新提交的工作,立即将其改回草稿状态,并在开始修复轮次前完成此操作(例如,出现新的有效评审意见或门禁检查发现问题)。
- 草稿状态反映待处理的提交。 收敛状态决定是否可以提出合并。若新评论只需回复,不会让就绪的 PR 重新变为草稿,因为回复并解决评论会在同一周期内完成,无需新提交。只有满足下文完整的收敛定义时,才会提出合并 PR。
已收敛是指评审已按 /rubber-duck-review 技能中的定义收敛(第 7 步是唯一的定义),并且满足 PR 特定标准:CI 全部通过,且所有评审线程均已解决(已修复或已答复)。
全部通过是指 PR 上的每一项检查,无论是否为必需检查,也包括依赖链中每个 PR 上的检查。若基础分支未重新设定目标而导致仍有检查失败,即使必需门禁已通过,也不具备就绪资格。
设为就绪还要求 PR 发布的内容不包含任何 PII。标题、正文、提交消息、差异中新增的行,以及评审、issue 和 CI 评论,都不能让读者了解作者是谁、如何工作,或其机器如何配置。PII 的定义、替代内容和合同例外,均以 /pr-and-issue-discipline 技能中的脱敏规则为准。
以下 grep 检查会查找标识符,以及被替换的真实数据夹具中的数值。引用真实配置、日志或转录内容时,应由评审者阅读,并作为阻塞性问题处理。检查会从 PR 本身读取全部内容,而不是从本地 HEAD 读取;并使用包含固定字符串的 needle 文件进行 grep:
- 作者的真实标识符,每行一个,包括作者曾公开使用过的所有写法。下方六项是姓名、雇主、登录名、用户名、电子邮件和机器名的占位符。
- 真实数据夹具中的来源描述短语(
measured from、real logs)。 - 当 PR 替换的夹具来自真实数据测量时: 整个旧文件中的每个完整精度比率,以及所有 4 位或更多位数的整数,避免旧数值进入新夹具、正文或评论。
旧文件通过 PR 合并基点上的仓库 API 读取:比较端点会指出文件,内容端点则会提供该位置的文件内容。因此,不会从本地获取任何内容,被保留的数值同样会成为 needle。
n=<pr number>; out="$(mktemp)"; diff="$(mktemp)"; shas="$(mktemp)"; old="$(mktemp)"; needles="$(mktemp)"
fixture='<path/to/replaced-fixture.json>' # only when a real-data fixture is being replaced; with none, drop this line and every line that uses $fixture, $mb, $old, or $head
printf '%s\n' 'Real Name' 'ExampleCorp' 'real-login' 'realuser' '[email protected]' 'MacBook-Pro' 'measured from' 'real logs' > "$needles"
head="$(gh pr view "$n" --json headRefOid -q .headRefOid)" &&
mb="$(gh api "repos/{owner}/{repo}/compare/$(gh pr view "$n" --json baseRefOid -q .baseRefOid)...$head" -q .merge_base_commit.sha)" &&
gh api -H 'Accept: application/vnd.github.raw+json' "repos/{owner}/{repo}/contents/$fixture?ref=$mb" > "$old" &&
{ grep -oE '[0-9]+\.[0-9]{3,}|[0-9]{4,}' "$old" >> "$needles" || test $? -eq 1; } && # no distinctive figure in the old file adds no needles and is not a stop; exit 2 (a read error) is
gh api -H 'Accept: application/vnd.github.raw+json' "repos/{owner}/{repo}/contents/$fixture?ref=$head" > "$out" &&
gh api --paginate "repos/{owner}/{repo}/pulls/$n/commits" -q '.[].sha' > "$shas" &&
test "$(wc -l < "$shas")" -eq "$(gh api "repos/{owner}/{repo}/pulls/$n" -q .commits)" && # the listing stops at 250 commits; a short list is a stop, not a sample
xargs -I{} gh api -H 'Accept: application/vnd.github.diff' "repos/{owner}/{repo}/commits/{}" < "$shas" > "$diff" &&
{ gh pr view "$n" --json title,body -q '.title, .body' &&
gh api --paginate "repos/{owner}/{repo}/pulls/$n/commits" -q '.[].commit.message' &&
sed -n '/^+/p' "$diff" &&
gh api --paginate "repos/{owner}/{repo}/pulls/$n/reviews" -q '.[].body' &&
gh api --paginate "repos/{owner}/{repo}/pulls/$n/comments" -q '.[].body' &&
gh api --paginate "repos/{owner}/{repo}/issues/$n/comments" -q '.[].body' &&
xargs -I{} gh api --paginate "repos/{owner}/{repo}/commits/{}/comments" -q '.[].body' < "$shas"; } >> "$out" &&
{ grep -n -i -F -f "$needles" "$out"; echo "grep exit $?"; }
rm -f "$out" "$diff" "$shas" "$old" "$needles"
grep exit 1 表示检查通过。grep exit 0 会列出命中项;将每项判定为问题前,都必须先阅读。允许的命中项以 /pr-and-issue-discipline 技能的 references/redaction.md 中“保留内容”一节所列项目为准。
若某个 4 位或更多位数的 needle 与无关行中的年份、sha 片段或端口号相符,可在明确说明这一判读后排除。其他所有命中项都属于阻塞性问题。若命中的是来源描述短语,则说明对应的夹具存在问题,必须通过手动编写夹具来修复。
如果没有 grep exit 这一行,就说明链路中某处读取失败了,而运行结果也没有说明 PR 的情况。Fixture 路径错误会导致 404,读取被截断则会返回非零退出码。提交列表比 PR 的提交数量短,会导致其 test 失败;任何提交读取失败,xargs 都会以非零状态退出。
如果旧 fixture 中没有指纹模式匹配的图,就不会添加任何待查项。此时只能通过逐眼对比替换文件与旧文件来检查,因为 grep 没有任何独特内容可供查找。
该代码块读取的内容及原因:
- 每个提交自身新增的行,绝不只看最终 diff。某个 fixture 在一个提交中新增、下一个提交中删除,仍然是由第一个提交发布的;rebase 或合并提交也会保留两者。只读新增行,因为会脱敏 PII 的 PR 中,被删除的行按定义就包含 PII。
- PR 头部版本中的整个新 fixture,而不只是更改过的行。替换文件中原封不动保留的旧图,正是指纹要捕捉的漏网内容。
- 所有评论:评审、issue 和提交评论,包括 CI 评论。 如果 CI 评论包含从真实数据衍生出的图,就应视为对发布该评论的检查本身的发现,而不仅仅是对 PR 的发现。
持续跟进,直到评论收敛
开放的 PR 在合并前都是进行中的工作。机器人评审者(例如 Copilot 代码评审)和人工评审者会在每次推送后留下评论。对每个 PR,循环处理,直到没有新情况:
- 每次推送都通过 PR 订阅获取 CI 结果(见下文的 Companion Gates)。
- 评审到达时,必须在它出现的同一轮处理 EVERY 条评论,绝不攒在一起处理:
- 有效发现必须在同一轮修复。
- 无效或不适用于此处的评论,要回复说明原因并解决其讨论串。没有需要回应内容的评论(例如赞扬、重述 diff)无需回复,直接解决。
- 推送修复前,正文必须按
/pr-and-issue-discipline技能的规定进行每轮编辑:原地修改,覆盖计数,并重新运行大小检查。 - 推送修复后,重新开始循环:监看新的 CI,对更改后的内容重新设门禁,机器人也可能再次评审。
机器人评论仅供参考。 非人工评审者(Copilot 代码评审、任何评审机器人)对仓库评审说明的遵循并不一致,而且许多仓库根本没有评审说明。因此,作者应按照这些说明本应采用的筛选标准来审查每条机器人评论:只针对能够证明的缺陷发表评论。
-
值得修复: 评论指出具体输入或状态,以及此 diff 会导致的错误输出、崩溃或数据丢失。必须在本轮修复。
-
推测性加固: 无法触达代码的恶意调用者、“考虑添加验证”、“增强健壮性”、单用户工具中的竞态,或针对代码永远不会收到的输入所做的检查。用一句话拒绝并记录,不予实现(
/rubber-duck-review技能的第 6 步针对有外部用户或存在恶意输入面的仓库设有例外,此时由用户设定标准)。无论评论如何措辞,只要指出了输入及其造成的损害,就属于第一类。
-
手写库已有功能: 如果评论指出仓库中已有的库或工具及其覆盖范围,就应按
/code-standards技能的规定替换。包括本次更改新增、移动、触及、复用或依赖的代码。大型库或适用性不明确的情况交由负责人处理,并暂缓 PR。绝不能因为依赖负担而不采用。未指出库的评论归入下一类。 -
仓库的 linter 和 formatter 不会强制执行的风格、命名或结构意见,以及表达相同含义的改写: 用一句话拒绝。若该意见已由 linter 报告,则无需回复,直接解决,因为失败检查已经说明问题。
-
重述 diff、赞扬、总结: 直接解决,无需回复。
这一句拒绝本身就是讨论串的收敛。机器人给出的总结判定(“建议更改”)不是门禁,而且机器人不能替代 Companion Gates 下的阻塞性评审。 返工预算。 如果连续多轮每轮只发现一个问题(大约进行到第 10 轮时),就停止逐个修复问题。列出反复出现的问题类别,一次性全面处理变更中每个类别,然后继续循环。有一次经过 35 轮才收敛,但列出问题类别后,只需几轮批量处理就完成了。
通过 GraphQL 读取讨论串状态,绝不要根据评论时间戳判断(即使讨论串没有新评论,也仍可能尚未解决):
reviewThreads(first: 100) { nodes { isResolved } pageInfo { hasNextPage endCursor } }
当 hasNextPage 为 true 时,使用 after: <endCursor> 翻页。固定读取第一页并不代表拿到了完整集合。
草稿不会自动触发的机器人审查,需要显式请求(例如,在草稿 PR 中将 Copilot 添加为审查者;如果仓库提供了设置,优先选择平衡或高推理)。每次都能成功注册的方式,是使用 GraphQL mutation 和 Copilot 的机器人 ID:
pr_id="$(gh pr view <n> --json id --jq .id)"
gh api graphql -f query='mutation($pr: ID!) { requestReviews(input: {pullRequestId: $pr, botIds: ["BOT_kgDOCnlnWA"], union: true}) { clientMutationId } }' -f pr="$pr_id"
REST 审查者端点是备用方案。它要求登录名必须精确为 Copilot;但有些时候,它会在反复请求后返回 201,requested_reviewers 却为空,时间线中也没有事件;copilot-pull-request-reviewer[bot] 则总是如此无操作。
无论采用哪种方式,GraphQL reviewRequests 都不会显示待处理的 Copilot 请求,因此只有议题时间线能确认请求是否已注册。两轮之间绝不轮询。如果已安装 /watch-github 技能并且已订阅该 PR,每条新评论或审查都会以消息的形式送达,并注明作者和发布位置。
一轮的实际情况:
- “空清单也会通过验证”:有效。在同一轮中通过回归测试修复。
- “脚本未接入文档”:这是有意安排的顺序。回复了计划(合并后统一更新文档,接入所有脚本),随后解决。
- “跟随符号链接”:拆分处理。经实证确认后,修复了保留叶节点一致性的部分;依据已记录的设计理由,拒绝了可能逃逸的部分。
- Copilot 提出的“应用清单前先验证其来源”,针对的是一个在 CI 中只有一个调用方、且读取由同一工作流写入的文件的脚本:作为推测性加固予以拒绝。用一句话回复后解决。该轮的“建议更改”结论并未阻止合并。
- 对于一个本身不产生任何行为的删除变更,有人提出“添加回归测试”:予以拒绝。评论没有指出任何测试可以锁定、而源文件又未说明的事实(这是
/rubber-duck-review技能的一贯测试问题);正文中的普查就是证明。回复了规则后解决。
配套门槛
- 每次推送后,PR 都会保持订阅状态,CI 结果会以消息形式送达。 如果已安装,
/watch-github技能会定义订阅方式。MERGE 根据 PR 的合并提交进行判断,该提交会作为主线分支的新顶端报告。执行gh pr merge后,git rev-parse HEAD仍指向主题分支的顶端,而 squash 或 merge 提交只存在于主线上。 - 在任何内容合入之前,都要进行能够阻止合入的独立审查。 审查范围仅限于确切要合入的内容,绝不审查工作树:提交后审查分支或 PR 差异(
base...HEAD),提交前则审查暂存区差异。如果已安装,/rubber-duck-review技能会定义审查及其收敛方式。 - 门槛会检查正文中的库说明。 如果差异新增、移动、触及、复用或依赖某个库形类别(由
/code-standards技能的references/design.md负责维护的列表),则会被拒绝,除非## How(在测量形态中为## Reasoning;没有 PR 时则为合入报告)包含符合/pr-and-issue-discipline技能所述格式的**Library:**项:列出一个库及其涵盖内容,或者说明搜索过程以及为何没有合适选项。 - 门槛或审查者确认的库必须成为后续任务(一个 issue 或本次会话的任务列表),或者在 PR 中完成,绝不能只记录为尚未构建的条目。大型库或适用性不明确时,应先向负责人询问,任务则根据其答复安排。
- 推送前的本地运行应有针对性。 运行类型检查(如果该语言支持),以及变更和验证所涉及的测试;长时间运行时要明确设置超时,并且只能通过你启动的 PID 停止进程,绝不能按模式停止。完整测试套件以及 lint 加构建检查应交由 CI 判断;CI 已会运行时,绝不要在本地重复运行。
- 提交前钩子只能作为检查门槛: 它能做和不能做的事情,以
/code-standards技能的“钩子只检查,绝不提交”标准为准。 - 当 CI 分钟数按量计费或供应紧张时(例如,按量计费套餐中的私有仓库),应改为在推送前于本地运行一次完整检查。不确定属于哪种情况时,询问用户,或者询问向你介绍情况的负责人。
合入前的行数核算
变更合入之前,作者应按文件类型查看新增和删除行数,并核对总数是否符合变更所述目的。目的由标题的类型和正文的首个标题确定;没有 PR 时,则由提交说明的类型确定。
计数只是信号,不是判决。它能发现一种重写:规模与被替换内容一样大,而每位审查者都认为它很干净;计数会促使人们追问原因。
变更是否优秀取决于判断;优秀的变更无论计数如何都会合入。但绝不能在意外情况尚未解释清楚时合入。
git diff --cached --numstat # landing a still-uncommitted patch
git diff --numstat "<base-remote>/<base>...HEAD" # a branch; the remote the PR merges into, never a fork remote
gh api --paginate "repos/<owner>/<repo>/pulls/<n>/files" --jq '.[] | "\(.additions)\t\(.deletions)\t\(.filename)"' # an open or landed PR
按类型汇总各行。类型由仓库的分类方式决定:源代码、脚本、测试、文档、工作流以及生成文件(锁文件、快照、渲染输出)。
生成文件和二进制文件行(两列均为 -)应注明并从所有总数中排除。重命名行({old => new})只计算修改过的行,因此应保持重命名检测开启。
下方的 TARGET 是目的所作用的类型:库变更对应源代码,脚本重写对应脚本,CI 变更对应工作流。这些行是常见形式,应按审查者的理解方式阅读,而不是当作铁律。若目的不符合任何一种形式,则按其自身情况判断。
| 声明的目的 | 预期形态 | 何时需要追问 |
|---|---|---|
| 简化、整合、弃用 | 目标代码净减少;测试仅因移除行为而减少 | 目标代码持平或增加 |
| Bug 修复 | 目标代码改动集中在缺陷位置;测试因回归用例而增加 | 目标代码增长远超缺陷位置,或测试增加 0 |
| 功能 | 目标代码和测试均增加 | 测试增加 0 |
| 保持行为不变的重构 | 现有测试原样通过(可以调整结构或加强测试) | 现有测试现在预期不同的输出:行为变了,因此目的描述不准确,标题应使用可见的类型,而不是 refactor(/pr-and-issue-discipline 技能的标题规则) |
| 纯移动 | 重命名行,加上仅涉及移动路径的编辑(导入、工作流和清单路径、注释、移动文件中的相对路径);移动前后的导出项统计和测试数量相同(/code-standards 技能的 references/structure.md) | 移动的文件包含路径调整之外的编辑,或有导出项或测试消失 |
| 文档或契约变更 | 仅更改文档和契约文件 | 可执行代码发生更改 |
如果某一行提到测试增加 0,说明已有哪个测试覆盖了这项变更即可作答:修复让现有断言现在能够锁定问题;功能则由已有的基于数据的测试套件覆盖。对于本身不涉及行为的删除,如果正文给出了统计数据(被删除内容的 grep 计数及前后对比),测试增加 0 无需解释。/code-standards 技能的 references/tests.md 规定了统计方法。
为了让这一行有内容可写而编写的测试,正是这项统计要捕捉的问题,绝不是对问题的回答。
不匹配之处是作者要在合并前回答的问题,而不是阻止合并的理由。要么缩减变更,要么认定变更本身合理,并按便于核查的粒度解释增长(重写按函数,批量处理按文件)。
为了让变更更好而增加代码行数,是合理的解释。仅仅说“更整洁”则不行。
有两种解释反复出现,都需要核实,而不能直接接受。一种是分阶段切换,删除工作放在一个明确标出的同级 PR 中完成(合并时一起查看两个 PR 的总和)。另一种是目的描述中未提及的保护条件或规则(此时标题就错了:先修正标题,再适用它现在所属的那一行)。
如何处理这些统计数据,取决于它们说明了什么。符合对应行预期的计数无需解释。如果要附上计数,应放在 PR 正文的技术细节部分。
与目的相悖的计数属于第一部分内容。读者的预期(整合会缩小代码)是错的,因此应根据 /pr-and-issue-discipline 技能的可读性规则,在用户能看到的说明部分给出计数和原因,并在提出变更时告知用户。若没有 PR,则两者都写入合并报告。
## Line accounting
- **Source +41 -12, tests +30 -0.** The consolidation grew source by 29: the merged entry point carries a guard the three old paths each skipped. The three paths themselves go with the sibling PR #N (-88), so the pair is net -59.
生产环境中的一个案例:重写发布脚本并弃用构建分支调用链时,删掉了 18 个函数,又增加了 18 个,脚本大小保持不变。评审者认为所有替代代码都很简洁。只有计数提出了疑问,答案是:实际上什么都没有弃用。
对其前后的 68 个 PR 进行审查,每个 PR 调用一次 pulls/<n>/files,发现没有其他 PR 大小不变。审查发现,有四个 PR 增加了标题未提及的保护条件,或将删除工作分阶段放到了同级 PR 中。
谁来合并
默认由人来合并。PR 的目的是在主线合并前设置人工关卡:作者负责准备 PR(推送、检查全部通过、提交“可以合并”的报告),再由人来合并。
如果由合并队列负责排序,作者准备好的操作就是将已收敛的 PR 加入队列。入队不等于已合并,因此要持续关注,直到提交真正落地。还有两种固定例外,但都必须经过用户授权:
- 只是一个简单的机械性修正。 几行改动,不改变任何行为、流程或规程(类型收窄、拼写错误修正、没有语义影响的重命名),只要所有门禁都通过,就直接合并。人工审核环节留给值得人类关注的改动。若不确定是否算“简单”,那就不算。
- 流水线因等待合并而受阻。 当已收敛的 PR 阻塞了排队中的工作,而人类没有采取行动时,就合并它,并在下一份报告中说明。作者本可自行完成合并却让工作闲置等待,这才是问题;发送通知则能保留人类的监督。
仓库定义的长期批准标签(例如 merge-when-green),对单个 PR 的整个生命周期持续生效。 由批准者(仓库授权其授予批准的人,如所有者或仓库指定的维护者)应用此标签,绝不能由 Agent 应用;之后的任何推送或强制推送都不会使其失效。没有此标签的 PR,或没有定义该标签的仓库,均遵循上述规则。
该标签授予操作者的是合并权限,而不是对内容的认可。批准者认可了 PR 的构想和形式,这种认可可能是在创作时或更早作出的,而且批准者可能并未阅读代码。因此,操作者须针对即将合入的确切 head 代替批准者进行审核:
- 按上述定义,head 已收敛
- 根据目的(见上文)核对行数
- 对照 diff 重新阅读 PR 正文
- 具有阻止权限的门禁审核已针对这个 head 运行
每个 PR 都要进行相同的检查,但阅读深度有所不同:拼写错误修正只需快速浏览 diff,而重写发布脚本则要从头到尾阅读 diff。本技能中的门禁是用于作出判断的工具箱,并非标签解锁的检查清单。合并前须有两项确认:
label='<approval label>' # the label the repository defines, e.g. merge-when-green
head="$(gh pr view <n> --json headRefOid --jq .headRefOid)" # first: a push after this fails the merge below
label="$label" gh api --paginate "repos/<owner>/<repo>/issues/<n>/timeline" --jq '.[]
| select((.event == "labeled" or .event == "unlabeled") and .label.name == env.label)
| "\(.created_at) \(.event) \(.actor.login)"' # jq reads the label from the environment, so any label name is safe
1. the LAST line is a labeled event by an authorised approver
-> labeled by anyone else: remove the label, report who set it, an approver re-applies it
-> unlabeled, or no line at all: no approval; the rules above apply
2. the operator's gate review ran on THIS head and its findings are triaged
-> the head moved since the review: re-read head, re-review the head with the delta named, then merge
折叠 PR 与标签。 批准涵盖的是批准者在该 PR 上认可的构想和形式。将另一个 PR 折叠进带标签的 PR,会引入批准者从未认可的内容。
因此,折叠前就存在的标签,只有在每个被折叠进来的 PR 都按其各自的时间线通过检查 1(即标签由批准者设置,而非仅仅存在某个标签)时才能保留。否则,操作者应移除标签并说明原因,待批准者阅读折叠后的内容后重新应用标签。
折叠后由批准者设置的标签即为该项批准,与被折叠 PR 原先带有什么标签无关。新的合并 PR 与其他 PR 一样,初始时不带标签。
PR 自身的审核轮次(同一分支上的新提交)不算折叠,因此标签会保留。优先选择堆叠 PR 或新建 PR,而非折叠进带标签的 PR。
操作者仅在以下两种情况下移除标签:该标签由非授权批准者设置;或折叠带入了未经批准的内容。检查未通过、有未解决的讨论、门禁发现问题或 head 发生变化,都不会移除标签。它们会暂缓合并,而非撤销批准。
两项确认均通过且 head 已收敛:使用仓库的合并方式(--squash、--merge 或 --rebase)以及 --match-head-commit "$head" 进行合并,这样若合并期间发生并发推送,合并就会失败,而不会将其合入,然后报告结果。
检查 1 未通过:移除标签并应用上述规则。检查 2 或收敛检查未通过:依据 Draft Discipline 对发现的问题进行分类处理(需要提交修复的问题会使 PR 重新变为草稿),保留标签,并在报告中说明原因。
落地操作须以退出状态为条件,绝不串联执行;这既适用于合并 PR,也适用于直接推送。读取门禁自身的结论并停止,只有在门禁本身报告通过后,才通过单独的命令执行落地操作。通过意味着门禁的退出代码和结论都通过;审核门禁只有在其发现的问题均已分类处理后才算通过,而不能仅凭进程以 0 退出就认定通过。
tail gate.log; git merge && git push # WRONG: the merge runs whatever the log said
tail gate.log && git merge && git push # WRONG: && conditions on tail printing the log,
# not on the gate's verdict; a red log still merges