# 裁决记录 — PASS

> 模拟 `gitlink-gatekeeper` 在一个真实风格 PR 上的完整执行记录：采集 → 逐维算分 → 硬门禁 → 裁决 → 渲染评分卡 → dry-run 回写命令。
> 策略：默认 `gatekeeper.yaml`（权重 40/20/15/15/10，severity_penalty blocker=100/major=25/minor=5/nit=1，thresholds pass=85 / request_changes=60，max_changed_files=80）。
> 命令均来自 `REFERENCE.md` 第 7 节 CLI 映射，已对照 `gitlink-cli pr/ci/label` 真实 shortcut 核验。

**场景**：`Gitlink/forgeplus` 仓库 PR #214，给搜索模块增加分页参数校验，附带单元测试，CI 全绿。

---

## ① 采集上下文

```bash
# PR 元信息（标题/描述/作者/关联）
gitlink-cli pr +view -i 214 --owner Gitlink --repo forgeplus --format json

# 变更文件列表
gitlink-cli pr +files -i 214 --owner Gitlink --repo forgeplus --format json

# diff（供 AI 审查）
gitlink-cli pr +diff -i 214 --owner Gitlink --repo forgeplus --format json

# commits
gitlink-cli api GET /Gitlink/forgeplus/pulls/214/commits --format json

# CI 状态
gitlink-cli ci +builds --owner Gitlink --repo forgeplus --format json
```

**采集结果摘要**

| 信号 | 值 |
|------|----|
| 标题 | `feat(search): validate pagination params` |
| 描述 | 非空，长度 142 字符（≥30），且含 `Closes #198` |
| `changed_files` | 5 |
| `changed_src`（匹配 `source_globs`） | 3（`search.go` / `paginate.go` / `params.go`） |
| `changed_tests`（匹配 `test_globs`） | 2（`paginate_test.go` / `params_test.go`） |
| commits | 4 条，全部 `type(scope): subject` 形式 → conforming=4 |
| CI | passing |
| AI 审查发现 | 0 blocker / 0 major / 1 minor / 2 nit |

---

## ② 逐维算分（公式代入，可复算）

**review_findings（权重 40）** — `REFERENCE.md` §3.1
```
penalty = 1×5(minor) + 2×1(nit) = 7
score   = 40 × max(0, 1 − 7/40) = 40 × (33/40) = 40 × 0.825 = 33
```

**test_coverage（权重 20）** — §3.2
```
changed_src=3 > 0, changed_tests=2 > 0
ratio = min(1, 2/3) = 0.6667
score = round(20 × (0.5 + 0.5×0.6667)) = round(20 × 0.8333) = round(16.667) = 17
```

**pr_hygiene（权重 15）** — §3.3
```
描述≥30 ✓ (+1/3)
关联 Issue（body 含 #198） ✓ (+1/3)
体量 changed_files=5 ≤ max_changed_files/2 = 40 ✓ (+1/3)
命中比例 = 3/3 = 1
score = round(15 × 1) = 15
```

**commit_quality（权重 15）** — §3.4
```
conforming/total = 4/4
score = round(15 × 4/4) = 15
```

**ci_status（权重 10）** — §3.5
```
CI passing → score = 10
```

**总分**
```
total = 33 + 17 + 15 + 15 + 10 = 90
```

---

## ③ 硬门禁判定（§4）

| 门禁 | 配置 | 命中？ |
|------|------|:------:|
| `forbid_blocker_findings` | true | 否（0 blocker） |
| `require_ci_pass` | true | 否（passing） |
| `require_tests_for_src_changes` | true | 否（changed_src=3>0 但 changed_tests=2>0） |
| `require_linked_issue` | false | —（未启用） |
| `max_changed_files` | 80 | 否（5 ≤ 80） |

`hard_gate_failed = false`。

---

## ④ 裁决（§5）

```
hard_gate_failed = false
total = 90 ≥ thresholds.pass(85)
→ verdict = PASS
```

---

## ⑤ 渲染的评分卡

```markdown
## 🛡️ Gatekeeper Report — PR #214 feat(search): validate pagination params

**Verdict: ✅ PASS**  ·  Score: 90/100  ·  policy: gatekeeper.yaml@v1

| Dimension | Weight | Score | Notes |
|-----------|:------:|:-----:|-------|
| Review findings | 40 | 33/40 | 0 blocker / 0 major / 1 minor / 2 nit |
| Test coverage   | 20 | 17/20 | 3 src / 2 test files |
| PR hygiene      | 15 | 15/15 | desc ✓ / linked issue #198 ✓ / size ✓ |
| Commit quality  | 15 | 15/15 | 4/4 conventional |
| CI status       | 10 | 10/10 | passing |

### 🟡 Should fix (1)
- [minor] `validatePage` 对负数与零分两个分支处理，可合并 — paginate.go:41

### 🔵 Nits (2)
- [nit] 导出函数 `ValidateParams` 缺少 doc 注释 — params.go:18
- [nit] 测试用例命名建议统一为 `TestXxx_Case` — params_test.go:7

### ✅ Strengths
- 源码改动均有对应测试，覆盖良好
- commit 全部符合 Conventional Commits，关联了 Issue #198
- CI 全绿

### Next steps
1. 评分达标且无硬门禁失败，可合并；如需自动合并请在策略中开启 `auto_merge` 并显式带 `--apply`
2. 两个 nit 可在后续提交一并处理，不阻塞合并
---
*Generated by gitlink-gatekeeper · policy-as-code PR gate · re-run after changes*
```

---

## ⑥ 将执行的回写命令（dry-run 展示）

> 默认 `behavior.dry_run_default: true` → 不传 `--apply` 时只打印以下命令，不真正回写。
> `behavior.auto_merge: false` → 即便裁决 PASS 也**不会**触发合并。

```bash
# [dry-run] 1) 把评分卡作为评论回写到 PR（与 workflow 脚本一致，走 pr +comment）
gitlink-cli pr +comment -i 214 -b "$(cat scorecard.md)" --owner Gitlink --repo forgeplus
#   也可作为评审记录回写：gitlink-cli pr +review -i 214 --status common --content "$(cat scorecard.md)"
#   （GitLink review status 为 common/approved/rejected；gatekeeper 自动裁决统一用建议性的 common，
#    强语义 approved/rejected 留给人工授意。）

# [dry-run] 2) 打 PASS 标签（labels.pass = "gatekeeper:pass"）
#   2a. 确保标签存在（首次需创建；已存在则跳过）
gitlink-cli label +create -n "gatekeeper:pass" -c "#2EA043" \
  -d "Gatekeeper verdict: PASS" --owner Gitlink --repo forgeplus
#   2b. 标签挂到 PR 背后的 issue（GitLink PR 复用 issue 标签体系）。
#       注意：用 PR 关联的 issue.id（非 PR 号），先从 pr +view 取：
ISSUE_ID=$(gitlink-cli pr +view -i 214 --owner Gitlink --repo forgeplus --format json | jq -r '.data.issue.id')
gitlink-cli api POST /Gitlink/forgeplus/issues/$ISSUE_ID --body '{
  "issue_tag_ids": [<gatekeeper:pass 的 tag_id>],
  "done_ratio": 0,
  "subject": "<PR 原标题>",
  "description": "<PR 原描述>"
}'
#   也可用 gitlink-cli issue +update，它会自动保留 subject/description，免手动回传。

# 合并默认禁用（auto_merge=false）。仅当策略 auto_merge=true 且裁决=PASS 且显式 --apply 时才会执行：
#   gitlink-cli pr +merge -i 214 --method squash --owner Gitlink --repo forgeplus
```

> 实际写回需追加 `--apply`，且 Agent 会先向用户复述「将回写 1 条评论 + 打 1 个 gatekeeper:pass 标签，不合并」并等待确认。
