# 评分卡样例（scorecard-sample）

> 这是 `gitlink-gatekeeper` 渲染到 PR 评论的 **评分卡** 的样例输出，严格套用 [`REFERENCE.md`](../REFERENCE.md) 第 6 节模板。
> 本样例对应一个 **REQUEST_CHANGES**（含硬门禁失败）案例，用于展示模板的全部分区（硬门禁、Must fix、Should fix、Nits、Strengths、Next steps 同时出现）。
> 数值来自默认策略 `gatekeeper.yaml`（权重 40/20/15/15/10，blocker=100/major=25/minor=5/nit=1，pass=85、request_changes=60），可按第 3–5 节算法逐项复算。

---

## 输入快照（用于复算）

| 信号 | 值 |
|------|----|
| 变更文件总数 `changed_files` | 6 |
| 匹配 `source_globs` 的源码文件 `changed_src` | 4 |
| 匹配 `test_globs` 的测试文件 `changed_tests` | 0 |
| AI 审查发现 | 0 blocker / 1 major / 2 minor / 1 nit |
| PR 描述 | 非空，长度 64 字符（≥30） |
| 关联 Issue | 否（body 无 `#<n>`） |
| commits | 共 4 条，符合 Conventional Commits 的 3 条 |
| CI 状态 | passing |

## 逐维复算

- **review_findings（权重 40）**：`penalty = 25(major) + 2×5(minor) + 1(nit) = 36`；
  `score = 40 × max(0, 1 − 36/40) = 40 × 0.1 = 4`
- **test_coverage（权重 20）**：`changed_src=4 > 0` 且 `changed_tests=0` → `score = 0`
- **pr_hygiene（权重 15）**：描述≥30 ✓(+1/3)；关联 Issue ✗(0)；体量 `changed_files=6 ≤ max_changed_files/2 = 40` ✓(+1/3) → 命中比例 `2/3`；
  `score = round(15 × 2/3) = round(10.0) = 10`
- **commit_quality（权重 15）**：`conforming/total = 3/4`；`score = round(15 × 3/4) = round(11.25) = 11`
- **ci_status（权重 10）**：passing → `score = 10`
- **总分**：`4 + 0 + 10 + 11 + 10 = 35`

## 硬门禁判定

| 门禁 | 配置 | 命中？ |
|------|------|:------:|
| `forbid_blocker_findings` | true | 否（无 blocker） |
| `require_ci_pass` | true | 否（CI passing） |
| `require_tests_for_src_changes` | true | **是**（changed_src=4>0 且 changed_tests=0） |
| `require_linked_issue` | false | —（未启用） |
| `max_changed_files` | 80 | 否（6 ≤ 80） |

`hard_gate_failed = true` → 裁决直接 **REQUEST_CHANGES**（无视总分；本例总分 35 < 60 亦指向 REQUEST_CHANGES，结论一致）。

---

## 渲染的评分卡（回写到 PR 的正文）

```markdown
## 🛡️ Gatekeeper Report — PR #128 feat(auth): add refresh-token rotation

**Verdict: ❌ REQUEST_CHANGES**  ·  Score: 35/100  ·  policy: gatekeeper.yaml@v1

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

### ⛔ Hard gate failures (1)
- `require_tests_for_src_changes`: 改动了 4 个源码文件，但本 PR 未包含任何测试文件

### 🔴 Must fix (1)
- [major] 刷新令牌轮换未对旧令牌做失效处理，存在重放风险 — internal/auth/refresh.go:88

### 🟡 Should fix (2)
- [minor] `rotateToken` 缺少超时上下文，可能阻塞请求 — internal/auth/refresh.go:54
- [minor] 错误信息直接回显内部字段名，建议脱敏 — internal/auth/handler.go:131

### 🔵 Nits (1)
- [nit] 导出函数 `NewRotator` 缺少 doc 注释 — internal/auth/refresh.go:22

### ✅ Strengths
- commit 历史清晰，3/4 符合 Conventional Commits
- CI 全绿，PR 描述完整

### Next steps
1. **补测试**：为改动的 4 个源码文件添加单元测试，解除 `require_tests_for_src_changes` 硬门禁
2. 修复 major 重放风险后重新触发 gatekeeper
---
*Generated by gitlink-gatekeeper · policy-as-code PR gate · re-run after changes*
```

---

> **复算提示**：把上表「输入快照」的数值代入 `REFERENCE.md` 第 3 节公式即可重现每一维得分；硬门禁按第 4 节逐项比对；最终裁决按第 5 节判定树得出。同策略 + 同输入 → 同评分卡（确定性）。
