gitlink-cli/skills/gitlink-gatekeeper/SKILL.md

18 KiB
Raw Permalink Blame History

name version description metadata
gitlink-gatekeeper 1.0.0 Policy-as-Code 的 PR 合并门禁:按版本化的 gatekeeper.yaml 策略对一个 Pull Request 聚合多路信号、算出 0-100 评分卡、给出三态裁决PASS / REQUEST_CHANGES / COMMENT并把结论作为结构化评论回写、打标签。当用户需要判断 PR 能否合并、做合并门禁/质量闸门、生成可复现的 PR 裁决,或问『这个 PR 达标了吗』时触发。默认 dry-run绝不自动合并。
requires cliHelp
bins
gitlink-cli
gitlink-cli pr --help

gitlink-gatekeeperPolicy-as-Code PR 合并门禁)

CRITICAL — 开始前必须先阅读 gitlink-shared/SKILL.md,其中包含认证、权限处理和 GitLink API 注意事项PR list state 过滤不准、主分支是 master、issue 更新需带回 subject/description 等)。 CRITICAL — 任何写操作(回写评论、打标签、合并)前,必须向用户复述将要做什么并得到确认;默认 dry-run 不写任何东西。绝不默认自动合并。 CRITICAL — GitLink 操作只能用 gitlink-cli。禁止用 ghGitHub CLI操作 GitLink 资源。gh 仅适用于 GitHub 平台。

前置条件: 先阅读 gitlink-shared/SKILL.md 了解认证和全局参数;本 Skill 的全字段策略与算法细节见同目录 REFERENCE.md,故障排查见 TROUBLESHOOTING.md


定位与产物

gitlink-gatekeeper 是一个可复现的 PR 合并门禁:团队把合并标准写进版本化的 gatekeeper.yamlgatekeeper 据此对一个 PR 算出透明的 0-100 评分卡,给出三态裁决,并把结论回写。与 gitlink-code-review(只产出主观评论、无阈值、无结论)相比,本 Skill 的核心是可裁决、可审计、可复现:同一策略 + 同一 PR → 同一裁决。

裁决 emoji 触发条件 回写标签
PASS 无硬门禁失败 且 total ≥ thresholds.pass gatekeeper:pass
REQUEST_CHANGES 命中任一硬门禁,或 total < thresholds.request_changes gatekeeper:needs-changes
COMMENT 💬 介于两阈值之间且无硬门禁失败 gatekeeper:review

GitLink 原生 PR review 的 statuscommon/approved/rejectedrejected 即「请求修改」的等价。gitlink-gatekeeper 刻意把所有自动裁决以建议性的 common 评论回写(评分卡标题明确标注三态裁决),把强语义的 approved/rejected 留给人工授意;裁决状态通过标签(gatekeeper:needs-changes 等)承载——这正是本作品复用 label 命令组的原因。


工作流概览

阶段 操作 AI Agent 角色
① 加载策略 gatekeeper.yaml,找不到则回退内置默认策略 解析 / 校验 / 回退
② 采集上下文 拉 PR 元信息、变更文件、diff、commits、CI 状态 执行 CLI 命令采集数据
③ 产出发现 逐文件审查,按 severity 分级标记问题 AI 分析,输出发现列表
④ 逐维评分 五维各算 0..weight 得分,相加得 total 确定性计算(非主观)
⑤ 硬门禁 逐项判定 hard_gates,命中即拦截 布尔判定
⑥ 裁决 由硬门禁 + total + 阈值得出三态 确定性计算
⑦ 渲染评分卡 按模板渲染 Markdown 评分卡 模板填充
⑧ 回写(受 --apply dry-run 仅打印;--apply 才回写评论 + 打标签 写操作,需确认
⑨ 安全规则 默认 dry-run绝不自动合并 守门

详细工作流

Step ① 加载策略 gatekeeper.yaml

策略默认从仓库根目录 gatekeeper.yaml 读取,可用 --policy <path> 指定。找不到时回退到下文内置默认策略(务必在评分卡 footer 注明用的是哪个策略来源)。

加载后做两项校验(不通过则报错并停止):

  • weights 五项之和必须 = 100review_findings + test_coverage + pr_hygiene + commit_quality + ci_status)。
  • thresholds.passthresholds.request_changes 为 0-100 整数,且 pass ≥ request_changes

内置默认策略(与 examples/gatekeeper.yaml 一致回退时使用policy 标记为 <built-in default>@v1

version: 1
weights:
  review_findings: 40
  test_coverage:   20
  pr_hygiene:      15
  commit_quality:  15
  ci_status:       10
hard_gates:
  forbid_blocker_findings: true
  require_ci_pass: true
  require_tests_for_src_changes: true
  require_linked_issue: false
  max_changed_files: 80
severity_penalty: { blocker: 100, major: 25, minor: 5, nit: 1 }
thresholds: { pass: 85, request_changes: 60 }
labels:
  pass: "gatekeeper:pass"
  request_changes: "gatekeeper:needs-changes"
  comment: "gatekeeper:review"
source_globs: ["**/*.go", "**/*.py", "**/*.js", "**/*.ts", "**/*.rs", "**/*.java"]
test_globs:   ["**/*_test.go", "**/test_*.py", "**/*.test.*", "**/*.spec.*", "tests/**"]
behavior:
  dry_run_default: true
  post_comment: true
  apply_label: true
  auto_merge: false
  merge_method: squash

Step ② 采集 PR 上下文CLI 命令映射)

用以下命令采集一个 PR 的全部输入。始终带 --format json 便于解析--owner/--repo 在 git 仓库目录下可自动从 remote 解析,否则显式传入。

步骤 数据 命令
PR 元信息 标题/描述/作者/关联 issue gitlink-cli pr +view -i <id> --format json
变更文件 文件路径列表 gitlink-cli pr +files -i <id> --format json
Diff 变更内容供 AI 审查 gitlink-cli pr +diff -i <id> --format json
commits commit 列表(消息供 commit_quality gitlink-cli pr +commits -i <id> --format json
CI 状态 构建结果 gitlink-cli ci +builds --format json

实测注意:pr +filespr +diff 底层都打 /pulls/:id/files——+files 取路径列表,+diff 取含 patch 的同一份数据按需取用即可。commit 列表使用 pr +commits。CI 通过/失败需从 builds 返回的 status 字段判断;无 build 记录时按「CI 未知」处理(见 §3.5)。

PR=42
gitlink-cli pr +view  -i "$PR" --format json   # title / body / 关联 issue
gitlink-cli pr +files -i "$PR" --format json   # changed files
gitlink-cli pr +diff  -i "$PR" --format json   # diff供 AI 审查)
gitlink-cli pr +commits -i "$PR" --format json
gitlink-cli ci +builds --format json

Step ③ AI 按 severity 分级产出发现

对 diff 逐文件审查(检查项参考 gitlink-code-review:安全红线、错误处理、并发、资源管理、魔法数字、测试覆盖等),产出结构化发现列表,每条带:

{ severity: blocker|major|minor|nit, file: "<path>", line: <n>, message: "<问题描述>" }

severity 判定准则(与 §3.1 扣分表对应):

severity 含义 典型例子
blocker 必须拦截,单条即清零本维度并触发硬门禁 硬编码密钥/Token、SQL/命令注入、路径遍历、不安全反序列化、XSS
major 严重缺陷,强烈建议修 未处理错误/异常吞掉、资源泄漏、并发竞态、明显逻辑错误
minor 一般问题 风格偏离、轻微复杂度、缺注释、魔法数字
nit 吹毛求疵 命名小瑕疵、格式建议

控制信噪比:宁可少而准。同时记录 Strengths(做得好的点),用于评分卡 ✅ Strengths 区。

Step ④ 按 SSOT 算法逐维评分(确定性)

每维产出 0..weights[dim],五维相加得 total ∈ 0..100这是确定性计算,不是再次主观打分——同输入必同分。

4.1 review_findings默认 40

penalty = Σ severity_penalty[finding.severity]          # 对所有发现求和
score   = weights.review_findings * max(0, 1 - penalty / weights.review_findings)

扣分累计达本维度权重即扣到 0blocker 单条penalty=100即清零本维度。

4.2 test_coverage默认 20

changed_src   = 变更文件中匹配 source_globs 的数量
changed_tests = 变更文件中匹配 test_globs 的数量
if changed_src == 0:        score = weights.test_coverage      # 无源码改动,不扣
elif changed_tests == 0:    score = 0
else: ratio = min(1, changed_tests / changed_src)
      score = round(weights.test_coverage * (0.5 + 0.5*ratio)) # 有测试至少拿一半

4.3 pr_hygiene默认 15三项各占 1/3

  • 描述非空且长度 ≥ 30 字符 → +1/3
  • 关联了 IssuePR body 含 #<n> 或 API 标记)→ +1/3
  • 体量适中(changed_files ≤ max_changed_files/2)→ +1/3超过一半但未超上限 → +1/6
score = round(weights.pr_hygiene * 命中比例)

4.4 commit_quality默认 15

conforming = 符合 Conventional Commits (type(scope): subject) 的 commit 数
total_c    = commit 总数
score      = round(weights.commit_quality * conforming / total_c)   # total_c=0 给满分

4.5 ci_status默认 10

CI 通过 → weights.ci_status
CI 失败 → 0
CI 未知/无 → round(weights.ci_status * 0.5)

Step ⑤ 硬门禁评估

hard_gates 逐项判定,命中任一 → hard_gate_failed = true(记下命中的门禁名供评分卡 ⛔ Hard gate failures 区):

门禁 命中条件
forbid_blocker_findings 为 true 且存在 blocker 发现
require_ci_pass 为 true 且 CI 明确失败failingunknown/无 build 记录不触发本门禁
require_tests_for_src_changes 为 true 且 changed_src > 0changed_tests == 0
require_linked_issue 为 true 且未关联 Issue
max_changed_files > 0changed_files > max_changed_files

Step ⑥ 裁决计算

if hard_gate_failed:                        verdict = REQUEST_CHANGES
elif total >= thresholds.pass:              verdict = PASS
elif total <  thresholds.request_changes:   verdict = REQUEST_CHANGES
else:                                       verdict = COMMENT

Step ⑦ 渲染评分卡(回写到 PR 的 Markdown

按下列模板填充emojiPASS=REQUEST_CHANGES=COMMENT=💬)。无硬门禁失败时省略 区;各发现区无内容时省略。

## 🛡️ Gatekeeper Report — PR #<id> <title>

**Verdict: <emoji> <PASS|REQUEST_CHANGES|COMMENT>**  ·  Score: <total>/100  ·  policy: <policy_path>@v<version>

| Dimension | Weight | Score | Notes |
|-----------|:------:|:-----:|-------|
| Review findings | 40 | <s>/40 | <n blocker / n major / n minor / n nit> |
| Test coverage   | 20 | <s>/20 | <changed_src> src / <changed_tests> test files |
| PR hygiene      | 15 | <s>/15 | <desc / linked issue / size 命中情况> |
| Commit quality  | 15 | <s>/15 | <conforming>/<total> conventional |
| CI status       | 10 | <s>/10 | <passing/failing/unknown> |

### ⛔ Hard gate failures (<n>)
- `<gate>`: <说明>

### 🔴 Must fix (<n>)
- [<severity>] <message><file>:<line>

### 🟡 Should fix (<n>)
- ...

### 🔵 Nits (<n>)

### ✅ Strengths
- <做得好的点>

### Next steps
1. <按裁决给出的最高优先级行动>
---
*Generated by gitlink-gatekeeper · policy-as-code PR gate · re-run after changes*

Step ⑧ 回写dry-run默认vs --apply

默认 dry-run:不传 --apply 时,只把评分卡打印给用户,不回写、不打标签、不合并。 --apply 才写:写操作前先向用户复述「将向 PR # 回写评分卡评论 + 打标签 <label>」,确认后执行。

回写评论(二选一,优先用 pr +review 因其自带 --dry-run 预览):

# 方式 A作为 PR review 回写status 用 common即 GitLink 的 COMMENT 等价)
gitlink-cli pr +review -i "$PR" --status common --content "$(cat scorecard.md)"

# 方式 B作为普通 PR 评论回写
gitlink-cli pr +comment -i "$PR" --body "$(cat scorecard.md)"

⚠️ pr +review --status 只接受 common / approved / rejected。评分卡评论一律用 common(不要因为裁决=PASS 就 approvedAPPROVE 是更强的批准语义,需用户显式授意)。裁决信息已写在评分卡标题里,状态由标签承载。

打标签GitLink 无「直接给 PR 挂标签」的命令,标签挂在 PR 背后的 Issue 上):

# 1) 确保裁决对应的标签存在(首次需创建;已存在则跳过)
gitlink-cli label +list --format json                      # 查现有标签拿 id
gitlink-cli label +create --name "gatekeeper:needs-changes" --color "#D73A4A" \
  --description "PR gate: changes requested"                # 不存在才创建

# 2) 取 PR 背后的 issue idpr +view 返回里有 issue 对象)
ISSUE_ID=$(gitlink-cli pr +view -i "$PR" --format json | jq -r '.data.issue.id')

# 3) 通过 Issue 更新挂标签(必须带回当前 subject/description否则会被清空——见 gitlink-shared
gitlink-cli api POST /:owner/:repo/issues/$ISSUE_ID --body '{
  "issue_tag_ids": [<tag_id>],
  "done_ratio": 0,
  "subject": "<原始标题>",
  "description": "<原始描述>"
}'

Step ⑨ 安全规则(硬性,不可绕过)

  • 默认 dry-run:不传 --apply 一律只打印,不产生任何写副作用。
  • 绝不默认自动合并behavior.auto_merge 默认 false;即便策略里设为 true,也必须同时满足 verdict == PASS 命令显式带 --apply 才允许合并,且合并前再次向用户复述确认。
  • 合并命令(仅在上述全部条件满足时):
    # merge_method 来自策略 behavior.merge_methodmerge|rebase|squash
    gitlink-cli pr +merge -i "$PR" --method squash
    

    注意 CLI 标志是 --method(底层 API 字段才是 do)。

  • 不回显 Token遵循 gitlink-shared/SKILL.md 的认证与 API 注意事项401 引导重登、403 查权限)。
  • REQUEST_CHANGES 永不触发合并COMMENT/PASS 默认也不合并,除非满足自动合并三条件。

完整示例

示例 1dry-run默认安全不写任何东西

# 在目标仓库目录下,对 PR #42 跑门禁,仅预览评分卡
PR=42
gitlink-cli pr +view  -i "$PR" --format json
gitlink-cli pr +files -i "$PR" --format json
gitlink-cli pr +diff  -i "$PR" --format json
gitlink-cli pr +commits -i "$PR" --format json
gitlink-cli ci +builds --format json
# → AI 产出发现 → 按 §4 评分 → §5 硬门禁 → §6 裁决 → §7 渲染评分卡
# → dry-run仅把评分卡打印给用户结尾提示「如需回写到 PR请加 --apply」

预期产出(节选):

**Verdict: ❌ REQUEST_CHANGES**  ·  Score: 58/100  ·  policy: gatekeeper.yaml@v1
⛔ Hard gate failures (1)
- require_tests_for_src_changes: 改了 3 个源码文件但没有新增测试
dry-run未回写、未打标签、未合并

示例 2--apply用户确认后回写评论 + 打标签)

PR=42
# …(同上采集 + 评分,得到裁决=REQUEST_CHANGES渲染出 scorecard.md)…

# 写操作前向用户复述:将向 PR #42 回写评分卡评论 + 打标签 gatekeeper:needs-changes
# 用户确认后:

# 1) 回写评分卡review 形式,自带 dry-run 可先预演)
gitlink-cli pr +review -i "$PR" --status common --dry-run --content "$(cat scorecard.md)"  # 预演
gitlink-cli pr +review -i "$PR" --status common          --content "$(cat scorecard.md)"  # 实际回写

# 2) 打标签 gatekeeper:needs-changes按 Step ⑧ 取/建 tag_id 与 issue_id 后)
gitlink-cli api POST /:owner/:repo/issues/$ISSUE_ID --body '{
  "issue_tag_ids": [101], "done_ratio": 0,
  "subject": "feat: add rate limiter", "description": "<原始描述>"
}'

# 注意:裁决=REQUEST_CHANGES → 绝不合并。
# 即便裁决=PASS也只有在 policy.auto_merge=true 且本次显式 --apply 时才允许:
#   gitlink-cli pr +merge -i "$PR" --method squash

策略预设

examples/ 提供三套可直接 --policy 引用的预设:

预设 特点 适用
gatekeeper.yaml 默认平衡策略pass 85 / rc 60 大多数仓库
gatekeeper.strict.yaml 高阈值、require_linked_issue: true、更小 max_changed_files 核心库 / 发布分支
gatekeeper.lenient.yaml 低阈值、关掉部分硬门禁 早期项目 / 文档仓库
gitlink-cli pr +view -i 42 --format json   # 采集后用严格策略评分(评分逻辑同上,仅阈值/门禁不同)
# 评分时加载:--policy examples/gatekeeper.strict.yaml

最佳实践

  1. 先 dry-run 再 --apply:永远先看评分卡内容,确认无误再回写。
  2. 策略进版本库:把 gatekeeper.yaml 提交到仓库,让裁决标准对所有贡献者透明、可审计。
  3. 可复现优先:评分是确定性的——若两次裁决不同,先查是不是 PR 内容或策略变了而非「AI 心情」。
  4. 控制发现数量:最严重的 3-5 条比 20 条琐碎问题更有价值nit 折叠展示。
  5. 大 PR 分段处理pr +diff 输出可能很大Agent 应分段读取 diff 再汇总发现。
  6. 标签复用:同一仓库的三个 gatekeeper:* 标签建一次即可,后续只更新挂载关系。

注意事项

  • PR review/评论提交后会通知关注该 PR 的参与者,评分卡内容保持专业、可操作。
  • --state 过滤 PR 列表不精确,需用 pull_request_status 字段客户端判断0=open,1=merged,2=closed
  • GitLink 主分支是 master(非 main);合并方式由策略 merge_method 决定。
  • 对 draft PR 应提示用户先标记为 Ready for Review 再门禁。
  • 端到端「PR 看门人闭环」(路由建议 reviewer → 裁决 → 回写 + 为 REQUEST_CHANGES 自动建 tracking issue的完整工作流形态见配套独立仓库 recorder/gitlink-gatekeeper策略字段与算法见 REFERENCE.md