代码从任务到上线怎么流转:分支和提交怎么写、PR 怎么开、评审怎么做、CI 门禁怎么排、版本怎么发、依赖怎么升级,并且能在 GitHub、GitLab、Gitea 之间迁移。
主干必须设为受保护分支:禁止直接推送、强推和删除,改动只能经 PR 合入
怎么检查:检查平台的分支保护或规则集设置;GitHub 上同时勾选「不允许绕过」,让管理员也受约束
为什么、来源
为什么:只要还有一条绕过 PR 的路,评审和必过检查就都可以被跳过;强推还会让别人手里的历史失效。
- GitHub 默认允许管理员绕过分支保护,要显式打开「Do not allow bypassing the above settings」。
- 确需紧急绕过时,由有绕过权限的人操作,并在事后补记录。
- 机器人账号(包括 Agent)同样受这条约束,见 23。
默认用短分支的主干开发;长期分支只留给要并行维护的发布线
怎么检查:评审分支模型时看是否有长期存在、定期同步的功能或 develop 分支
为什么、来源
为什么:分支活得越久,合并冲突和集成风险越大;主干开发要求小步合入、持续集成。
- 主干开发不是不建分支,而是分支短命、合入频繁。
- 有多个版本要同时打补丁(如 SDK 的 2.x 和 3.x)时,开
release/2.x 这类维护分支,只接受修复。
分支名用「类型/任务编号-简述」,如 feat/PROJ-142-export-report
怎么检查:推送钩子或 CI 用正则检查分支名,不符合时提示
为什么、来源
为什么:一眼能看出分支做什么、对应哪个任务,便于清理和追溯。
- 类型和 Conventional Commits 一致:feat、fix、chore、docs、refactor 等;另有
release/ 和 hotfix/。
- 前缀不要细分太多;任务编号以项目统一的编号为准。
功能分支上可以多次小提交;默认用 squash 合并,要保留提交历史的仓库改用 rebase 或 merge 并写进贡献说明
怎么检查:检查平台允许的合并方式设置
为什么、来源
为什么:开发中允许临时提交,评审以整个 PR 的 diff 为单位;squash 后主干历史一条 PR 一个提交,便于回滚和生成变更日志。
- 需要逐个提交可追溯的库(如每个提交都要能独立构建、二分查找),可以选 rebase 合并,但这时每个提交都要符合 FLW-005。
用 squash 合并的仓库,最终提交信息(PR 标题)必须符合 Conventional Commits,破坏性变更必须明确标注
怎么检查:CI 对 PR 标题跑 commitlint;破坏性变更检查标题带 ! 或正文有 BREAKING CHANGE:
为什么、来源
为什么:版本号推断和变更日志草稿都从这条信息来;它写错,后面的自动化全错。
- 例:
feat(api): add export endpoint;破坏性变更写 feat(api)!: drop v1 export 或在正文写 BREAKING CHANGE: ...。
- 规范只给
feat(次版本)、fix(修订号)和破坏性变更(主版本)规定了版本含义;refactor、perf 等不要假定会触发发版。
- 只检查最终信息,不强求每个临时提交合规,避免双重负担。
提交信息只用来生成变更日志草稿;面向用户的部分由人改写后再发布
怎么检查:发布评审时检查变更日志里面向用户的条目是否经过改写,而不是原样的提交标题
为什么、来源
为什么:提交信息是写给开发者的,直接贴给用户会充满内部术语和无关条目。
- 工具(release-please、Changesets 等)按提交类型分组生成草稿;人工删掉内部改动、合并同类项、用用户能懂的话重写。
- 变更日志的结构和写法见 05。
暂存时只 git add 明确的路径,不用 git add -A 或 git add .;提交前核对暂存清单和未跟踪文件
怎么检查:提交前看 git status --short 和 git diff --cached --name-only;Agent 的权限设置里禁止 git add -A / git add .
为什么、来源
为什么:一把全加最容易把个人文件、生成的密钥、别人的改动一起提交上去。
- 仓库根目录常有不该提交的本地文件(调试脚本、截图、环境变量备份),
.gitignore 兜不全。
- 有多个 Agent 同时工作时尤其重要,见 23。
必过检查必须在服务端 CI 上运行并作为合并条件;本地钩子只做提前反馈,不能替代 CI
怎么检查:分支保护里配置必过检查;核对每项本地钩子检查在 CI 里都有对应项
为什么、来源
为什么:本地钩子可以被 --no-verify 跳过,环境也各不相同,只有服务端检查拦得住。
- 不得用
--no-verify、忽略 CI 失败、临时取消必过检查等方式绕过;确需例外的,走负责人审批并留记录。
是否强制签名提交按审计要求决定;开启前先实测机器人提交和 squash 合并
怎么检查:在测试仓库开启签名要求,跑一遍机器人提交、squash 合并、本地合并,确认都能通过
为什么、来源
为什么:签名只证明提交来自谁的密钥,不证明代码可信;而且贸然开启会卡住现有流程。
- GitHub:开启「Require signed commits」后,PR 头分支上只要有未签名提交,就可能挡住 squash 合并,即使最终的 squash 提交会由 GitHub 签名;解决办法是重写并签名这些提交。本地 squash 再推送的,结果提交必须自己签名。
- 签名与来源证明的整体要求见 25。
一个可独立评审、可独立验收的改动一个 PR;大功能拆成多个 PR
怎么检查:CI 统计改动行数和文件数(排除生成代码、锁文件、纯格式化),超过参考值时提示作者说明为什么不拆
参考值:Google 的经验是约 100 行通常合适、1000 行通常偏大;这不是统一阈值,各团队按自己的评审数据校准,只提示不阻断。
为什么、来源
为什么:小 PR 评审得更仔细、合得更快、出问题更好回滚。
- 「一个功能一个 PR」不是指一个月的功能只准开一个巨型 PR。
- 有依赖关系的切片可以用堆叠 PR(下一个 PR 基于上一个的分支)。
PR 关联任务编号和验收标准;错别字、例行依赖更新这类小改动可以免关联
怎么检查:CI 检查 PR 描述里有任务链接,或带有约定的豁免标签
为什么、来源
为什么:评审者要知道改动为了什么、做到什么程度算完;但给错字修复也补 issue 是无谓负担。
- 豁免范围要在贡献说明里写清,豁免的 PR 仍要走评审和必过检查。
PR 描述写清目标、改动、不在范围内的内容、风险、验证结果、没验证的项、回滚或修复办法
怎么检查:用平台的 PR 模板固定这些栏目,CI 检查栏目非空
为什么、来源
为什么:评审者靠这些信息判断能不能合、出了问题怎么退;不写就只能靠猜。
- 栏目建议:任务、目标与验收标准、改动、明确不做的、验证(只写实际运行过的)、兼容性与风险、回滚或向前修复、希望评审重点看的地方。
- GitHub 放
.github/pull_request_template.md,GitLab 放 .gitlab/merge_request_templates/,Gitea 放 .gitea/;内容保持一致。
- Agent 提交的 PR 要求更严,见 AGT-015。
验证结果必须如实记录:每项检查标「通过、失败、未运行、已跳过」并附命令和证据位置;没跑的写原因,写了原因也不能豁免必过检查
怎么检查:评审时把 PR 里写的结果和 CI 在最终提交上的记录对照;抽查命令和证据链接
为什么、来源
为什么:虚报的「通过」会让评审失去依据,问题直接进主干。
- 状态名用词表的证据状态:「未运行」是该跑但没能跑(写原因),「已跳过」是不适用(写为什么不适用)。
- 证据写准确的命令、退出码、CI 运行链接;不拿别人的或旧提交上的结果冒充。
还没准备好评审的 PR 用草稿状态(Draft)打开
怎么检查:评审者只处理已标记为可评审的 PR
为什么、来源
为什么:早开草稿能让 CI 先跑、让别人知道有人在改;又不会过早占用评审者。
合并到主干前必须有至少一名非作者批准;平台不能强制审批时,把合并权限只给评审人并人工核对
怎么检查:支持的平台在分支保护里设必需批准数并禁止作者自批;GitLab 免费版等不支持强制审批的,在受保护分支里把「允许合并」限定给维护者角色,定期抽查合并记录
为什么、来源
为什么:作者自己看不出的问题,换个人一眼就能发现;自审自合等于没有评审。
- GitLab 免费版的审批是可选的,挡不住无批准合并;强制的必需审批要 Premium 或 Ultimate。不要在免费版上写「必须审批」却没有任何强制手段。
- 同一个机器人令牌不能既当作者又当批准人;不同的机器人账号也不一定真独立,要看背后是谁在控制。
人工评审看需求是否达成、数据和状态语义、接口兼容、异常流程和回滚可行性;格式、lint、类型检查交给工具
怎么检查:评审清单列出这些看点;CI 里有对应的格式和 lint 检查
为什么、来源
为什么:人的注意力有限,花在工具能查的东西上,真正的问题就会漏掉。
AI 评审的批准不能作为合并的唯一批准;必需批准里至少有一名承担责任的人
怎么检查:检查 AI 评审工具的「允许批准」设置;开启时,分支保护的必需批准数要多出一名人工,或对关键路径要求 CODEOWNERS 里的人批准
为什么、来源
为什么:AI 评审会误判正确的实现、和写代码的 AI 有共同盲点;能计入规则不等于应该单独放行。
- GitHub Copilot 代码评审默认只留「评论」,不计入必需批准;管理员可以在企业、组织或仓库级开启批准,开启后它的批准和同事的批准一样能满足必需审批规则。新提交推上来后它的批准会失效。
- AI 评审适合找风险线索、给可验证的修改建议。研究显示开发者拒绝的 AI 评审意见多于接受的,不能拿「AI 评审通过」当质量保证。
- 用云端 AI 评审意味着代码出域,内网项目要先确认是否允许,见 27。
阻断性评审意见必须解决后才能合并;有新提交时旧批准失效,受影响的检查重跑
怎么检查:分支保护里开启「合并前需解决所有讨论」和「新提交后撤销旧批准」
为什么、来源
为什么:不解决的阻断意见等于没提;批准后又偷偷改的代码没人看过。
- 非阻断的建议可以开 issue 后续跟进,在评论里注明链接。
评审及时给出首次有效回应,并把时效作为团队目标统计;超时不能自动批准
怎么检查:统计从「可评审」到首次有效评审的时间
参考值:参考 Google 的做法,首次回应最长不超过一个工作日。
为什么、来源
为什么:评审拖得越久,作者切换上下文的成本越高,PR 越容易变大。
用 CODEOWNERS 指定路径负责人,并在分支保护里要求负责人批准;CODEOWNERS、CI 配置、发布配置本身也要有负责人
怎么检查:检查分支保护开启了「需要代码负责人批准」;检查 CODEOWNERS 覆盖自身、CI 目录和发布配置
为什么、来源
为什么:CODEOWNERS 文件本身只负责自动指派评审人,不开强制就不算门禁;门禁配置没人管,作者就能改门禁给自己放行。
- 三个平台语法不同:GitHub 和 GitLab 用各自的通配模式,Gitea 用 Go 正则。不要把同一份文件直接拷到另一个平台;可以维护一份中立的负责人清单,用小脚本分别生成。
- Gitea 从 28 起支持「所有匹配的 CODEOWNERS 规则都获得负责人批准前禁止合并」。
- Agent 不得修改这些文件,见 AGT-011。
涉及权限、凭据、计费、客户数据、数据库迁移、发布和门禁配置的改动,必须由对应负责人额外批准
怎么检查:把这些路径写进 CODEOWNERS 并要求负责人批准;无法按路径识别的,用风险标签触发额外审批
为什么、来源
为什么:这类改动影响不可逆或跨系统,出错的代价远大于多等一个批准。
- 可以按风险分级定合并权限(示意):
- 低:文档错字、自动生成的低风险文件,CI 通过后可以试行自动合并,但要留记录、可撤销。
- 一般:普通功能和修复,一名独立评审人。
- 较高:公共接口、共享依赖、数据库结构、跨模块,负责人批准并在最新主干上验证兼容。
- 高:本条列出的范围,承担实际责任的人批准;发布和门禁修改单独授权。
必过检查清单写在仓库里,检查名唯一且固定,并限定由哪个 CI 应用上报
怎么检查:脚本比对仓库声明的检查清单和分支保护里的必过检查;GitHub 上为每项必过检查指定来源应用
为什么、来源
为什么:检查名重复或来源不限,任何能写状态的集成都能伪造一个「通过」。
- 检查名跨平台保持一致,如
ci/lint、ci/build、ci/tests、ci/policy,各平台再做映射。
- GitHub 上指定了来源应用后,其他人或集成上报的同名状态不会被接受。
必过检查的结果必须对应待合并的最新提交;用接口合并时带上 head 提交编号作为条件
怎么检查:分支保护要求分支与主干同步或启用合并队列;合并接口传入期望的 head SHA,不一致就失败
为什么、来源
为什么:检查跑完后又有新提交或主干变了,旧的「通过」就不再说明问题。
- GitHub 合并 PR 的接口可以传
sha,head 不匹配时拒绝合并;其他平台用类似的条件参数,或在合并前重新读取状态。
- 低吞吐的仓库,「合并前要求与主干同步 + 串行合并」就够用,不必上合并队列。
CI 的检查逻辑写在仓库内的脚本里,平台 YAML 只负责触发、权限和运行环境;本地能跑同一条命令
怎么检查:评审时看 CI 配置是否只调用 scripts/ci/* 这类脚本,没有大段内联逻辑
为什么、来源
为什么:检查逻辑绑死在某个平台的 YAML 里,本地复现不了,换平台要重写;Gitea Actions 也只是大体兼容 GitHub Actions。
- 推荐结构:
scripts/ci/check-format、check-build、check-tests、check-policy;.github/workflows/、.gitlab-ci.yml、.gitea/workflows/ 各自薄适配。
- 只抽象稳定的命令行脚本,不要自己发明一套跨平台工作流语言。
CI 分层:PR 上跑决定能否合并的快速检查,合并后或定时跑耗时长的回归,发版时再跑发布门禁
怎么检查:评审 CI 配置时核对每层的触发条件和是否为必过
为什么、来源
为什么:把所有检查都压在 PR 上会拖慢每次合并;只在发版时检查又发现得太晚。
- 分层示意:本地预检 → PR 必过检查 → 合并候选(启用合并队列时)→ 合并后或定时 → 打 tag 发版。
- 合并后才跑的检查,只有当它是发布门槛时才阻断发版。测试怎么分层见 04。
启用合并队列(或合并列车)时,必过检查必须在队列生成的合并候选上运行
怎么检查:GitHub 的工作流要监听 merge_group 事件;第三方 CI 要对 gh-readonly-queue/ 开头的分支运行;GitLab 合并列车按官方配置开启合并结果流水线
为什么、来源
为什么:两个 PR 各自通过、合在一起坏掉,正是合并队列要防的;检查不在合并候选上跑,队列就白开了。
- GitHub 的工作流不监听
merge_group 时,PR 进队列后必过检查不会触发,合并会失败。
- GitLab 合并列车只在 Premium 和 Ultimate 提供;Gitea 不要假定有同等能力。
- 合并队列只在主干繁忙、经常出现「各自绿、合起来红」时才上;平时用 FLW-023 的做法。
CI 缓存只放能重新生成的依赖和中间结果,不放密钥,不当正式制品用;要传给后续步骤的产物用制品(artifact)机制
怎么检查:评审 CI 配置的缓存路径,确认不含凭据文件和发布产物;缓存键包含锁文件哈希和运行环境版本
为什么、来源
为什么:缓存可能被其他分支的流水线读写,内容不可信;放了密钥会泄露,拿来当制品无法追溯。
同一分支上过期的 PR 流水线可以自动取消;发布、数据库迁移、共享集成环境的任务要串行,且取消不能留下半成品
怎么检查:检查 CI 配置的并发设置:GitHub 用 concurrency,GitLab 用 workflow: rules 去重和资源组
为什么、来源
为什么:取消过期流水线能省资源;但发布和迁移被并发或中途取消,可能产出损坏的制品或数据。
有对外接口、SDK 或库的项目用语义化版本(SemVer);按时间交付、没有公共接口的产品可以用日历版本(CalVer)
怎么检查:评审版本策略时看项目是否有被别人依赖的接口
为什么、来源
为什么:SemVer 的价值在于向依赖方表达兼容性;没有依赖方的内部应用硬套 SemVer,主版本号就失去意义。
- 接口本身的版本策略(URL 版本、OpenAPI 版本)见 16。
每次发布绑定固定的源提交、版本号和制品摘要;发布记录写明兼容性变化、批准人和回滚或修复办法
怎么检查:发布流水线生成发布清单(版本、源提交、构建记录、制品名、sha256、渠道、说明、回滚办法),缺项时失败
为什么、来源
为什么:出了问题要能回答「用户装的到底是哪份代码构建出来的」,并且知道怎么退。
- 部署、制品库和运行态回滚见 26;签名与来源证明见 25。
- 数据库回滚不承诺一键完成,原则见 17。
已发布的版本不得改内容:不重写或删除已发布的 tag,不覆盖同名制品;有问题发新版本
怎么检查:平台上开启 tag 保护;制品库关闭同版本覆盖
为什么、来源
为什么:同一个版本号对应不同内容,就没人能确定外面装的是哪一份。
- SemVer 明确规定:版本发布后内容不得修改,任何修改都要发新版本。
回滚代码用 git revert 生成新提交,不强推主干;回滚合并提交时明确指定主线
怎么检查:主干禁止强推(FLW-001);评审回滚 PR 时看是否为 revert 提交
为什么、来源
为什么:revert 保留了「做过什么、又撤了什么」的记录;强推会让别人基于旧历史的工作出错。
依赖更新用机器人自动开 PR 并跑必过检查,按更新级别分级处理;默认不自动合并主版本升级
怎么检查:检查依赖机器人的配置:主版本更新不开自动合并;对配置文件跑官方的配置校验
为什么、来源
为什么:手工升级容易漏、容易拖;主版本升级常有不兼容改动,要人看。
- 补丁版本和开发依赖可以在 CI 通过后自动合并;主版本升级逐个评审。
- 断网环境里机器人需要内网的包仓库和元数据源,否则只能看到已知版本。
一个仓库只用一个本地 Git 钩子管理工具,钩子里跑的命令和 CI 一致
怎么检查:检查仓库里是否同时存在多个钩子配置(如 lefthook.yml 和 .pre-commit-config.yaml)
为什么、来源
为什么:两套钩子工具会重复执行、互相覆盖安装;钩子和 CI 跑的命令不同,本地通过 CI 照样失败。
仓库模板的更新走版本化的 PR;已有项目按版本主动升级,不悄悄改动它们的规则
怎么检查:模板仓库打版本 tag;项目里记录所用模板版本,CI 提示有新版本
为什么、来源
为什么:静默修改会让旧项目突然多出失败的检查,也说不清规则是什么时候变的。
issue 用统一的核心字段:负责人、优先级、状态、验收标准、风险;优先级和工作量分开记
怎么检查:issue 模板固定这些字段;定期统计缺字段的 issue
为什么、来源
为什么:字段统一才能跨平台迁移不丢信息;把「小」当成「急」会让优先级失真。
- 优先级用 P0–P3,状态用「待澄清、就绪、进行中、待评审、待验收、已完成、已取消」,定义见 06 和公共词表。
- 关键语义写在 issue 正文或通用字段里,不只依赖某个平台的专属字段类型。
- 任务状态以任务系统为准;PR 合并是触发状态变化的事件,不是状态本身。
流程要求能由平台或 CI 强制的就配置强制;平台不支持的能力要写明替代做法,不能当作已强制
怎么检查:列出贡献说明里的每条「必须」,逐项对应到分支保护、CI 检查或人工审查点;对不上的标出来
为什么、来源
为什么:只写在文档里的规则没人兜底;把做不到的门禁写成「必须」,会让人误以为已经有保护。
- 各平台差异举例:GitLab 免费版审批不强制、合并列车要付费版;Gitea 的 CODEOWNERS 用 Go 正则;Gitea Actions 与 GitHub Actions 不完全一致。
- 自动化脚本遇到平台不支持的功能,要明确报「不支持」并阻断或降级,不能静默当作通过。
要在内网或断网环境交付的项目,交付前必须在断网条件下跑通构建、CI 和发布
怎么检查:定期在禁止外网的 runner 上跑完整流水线,依赖只从内部镜像拉取
为什么、来源
为什么:外网能跑不代表断网能跑;Actions、包仓库、钩子依赖、证书任何一项没镜像都会在现场失败。
- 要准备的:内部 Git 服务、runner、包仓库镜像、CI 动作镜像、内部 CA、钩子和发布工具的安装包。
- 引入云端 AI 评审或代码助手前,确认代码和上下文是否允许出域,见 27。
按变更边界选单仓或多仓:经常一起改、一起发布的放一个仓库;权限、生命周期、客户隔离不同的分开
怎么检查:架构评审时看跨仓的原子改动频率和权限边界
为什么、来源
为什么:拆错了要么频繁跨仓协调,要么权限和发布互相牵连;不能只因为 Agent 多就拆仓。
- 多个包共享版本和发布节奏的,用 monorepo 加工作区(如 pnpm workspace)和 Changesets。
- 多仓共享的协议要显式发布成版本化的包,不跨仓引用未发布的源码。
并行任务要改同一个高争用文件(迁移、锁文件、接口契约、CI 配置)时,先约定负责人和合并顺序,或先合入契约 PR
怎么检查:开工前在任务里写明会改的高争用文件;评审时看是否有两个进行中的 PR 改了同一个
为什么、来源
为什么:这些文件冲突了很难自动合并,合错了会在运行时才出问题。
- 多 Agent 并行时的工作树隔离和任务登记见 23;迁移文件的约定见 17。
没有符合条件的规则。
分支策略
主干开发 + 短分支
备选:主干 + 受限的 release 维护分支(需要同时给多个已发布版本打补丁)
不推荐:所有项目强制 GitFlow
提交信息规范与检查
Conventional Commits,用 commitlint 只检查 PR 标题或 squash 后的提交 · commitlint 为 MIT
备选:团队自定的格式(不从提交信息生成版本和变更日志)
不推荐:完全自由文本却又想自动推断版本
合并方式
squash 合并
备选:rebase 合并(库要保留每个提交、每个提交都要能独立构建);merge 提交(要保留分支结构,如合并长期维护分支)
不推荐:直接推送主干
本地 Git 钩子
Lefthook · Lefthook、pre-commit 均为 MIT(待核实)
备选:pre-commit(以 Python 为主的仓库,或要直接用 pre-commit 生态里的现成钩子(它会为每个钩子管理独立的运行环境))
不推荐:同一个仓库两个都装;手写 .git/hooks 脚本;只靠本地钩子没有 CI 兜底
依赖更新机器人
自托管 Renovate · Renovate 为 AGPL-3.0,作为独立运行的工具使用;修改后对外提供网络服务时的义务见 27
备选:Dependabot(只用 GitHub,接受它的生态和配置能力)
不推荐:每个项目手写升级脚本;默认自动合并主版本升级
变更日志草稿与发布自动化
单个包、以 Conventional Commits 为输入:release-please · release-please 为 Apache-2.0;Changesets、semantic-release、GoReleaser 开源版为 MIT(待核实)
备选:Changesets(JS/TS monorepo,多个包独立发版);semantic-release(想要全自动发布,团队提交纪律成熟);GoReleaser(Go 命令行工具,要多平台二进制、校验和和打包(见 11))
不推荐:一套巨大的跨语言发布框架包打天下
版本号方案
SemVer
备选:CalVer(按时间交付、没有被依赖的公共接口)
不推荐:所有项目统一用日历版本
代码托管平台
按场景选:私有化、多团队用 GitLab 自托管版;轻量、断网用 Gitea;公网开源协作用 GitHub · Gitea 为 MIT;GitLab 分开源的社区版和商业的企业版
不推荐:假定三个平台的高级功能完全一样
主干串行验证
合并前要求与主干同步 + 带 head 提交编号条件合并
备选:GitHub 合并队列 / GitLab 合并列车(主干繁忙,经常出现各自通过、合起来失败)
不推荐:偶发冲突就自己写分布式合并队列
跨平台路径负责人
各平台原生 CODEOWNERS + 分支保护里的负责人批准
备选:一份中立的负责人清单,用小脚本生成各平台的 CODEOWNERS(同一套规则要在多个平台上维护)
不推荐:同一份 CODEOWNERS 直接拷到另一个平台;以为有 CODEOWNERS 文件就等于强制了
仓库模板
轻量的模板仓库 + 版本化的初始化清单
备选:Copier 或 Cookiecutter(有明确的变量替换需求,或要把模板更新同步回已有项目)
不推荐:为十几个文件先造一套脚手架服务
✕ 错误做法:把「一个功能一个 PR」理解成一个月的功能只开一个巨型 PR
✓ 应该:一个可评审、可验收的切片一个 PR,大功能拆多个
✕ 错误做法:AI 写的代码交给另一个 AI 评审通过就合并
✓ 应该:AI 评审作补充,加上可执行的检查和承担责任的人工批准
✕ 错误做法:只在 AGENTS.md 或贡献说明里写「禁止直接推 main」
✓ 应该:在平台上开分支保护并禁止绕过
✕ 错误做法:本地钩子全绿就当作 CI 全绿
✓ 应该:必过检查在服务端 CI 上跑
✕ 错误做法:拿旧提交上的绿灯合并新代码
✓ 应该:合并时校验 head 提交编号;主干繁忙时用合并队列
✕ 错误做法:每个临时提交都要求精美的提交信息,同时又用 squash 合并
✓ 应该:只严格检查最终的 PR 标题
✕ 错误做法:在 GitLab 免费版上写「必须审批才能合并」却没有任何强制
✓ 应该:列出部署版本的能力清单,做不到的写替代做法或升级平台
✕ 错误做法:发布出错就删 tag 重发
✓ 应该:发一个新版本修正,保留旧版本
✕ 错误做法:把构建缓存当正式交付物
✓ 应该:用制品机制传递,带摘要
✕ 错误做法:全靠公网的 Actions 和包仓库,却承诺断网交付
✓ 应该:内部镜像 + 断网演练
✕ 错误做法:把提交日志原样贴成给用户看的更新说明
✓ 应该:提交信息生成草稿,人工改写面向用户的部分
✕ 错误做法:自己写一套跨平台工作流语言替代 GitHub、GitLab、Gitea 的 CI
✓ 应该:只抽象稳定的命令行脚本,平台 YAML 做薄适配