# .githooks — 零依赖 mockup handoff conformance gate（F62-b）

消费仓库 commit mockup handoff 文档时，强制它引用一份**真跑过、通过、且新鲜**的
conformance report，堵住"手打 `Integrity audit: I1=pass` 编造证据"这个洞。

- `pre-commit` — 纯 POSIX sh，扫 staged `docs/handoffs/*.md`，把含 `<!-- mockup-handoff -->`
  标记的交给校验脚本。
- `audit-mockup-handoff-evidence.mjs` — 校验逻辑（TVU 真源 `scripts/audit-mockup-handoff-evidence.mjs`
  的自包含镜像）。**唯一硬依赖是 node**，不需要 husky / npm install。

## 为什么需要它

mockup handoff 文档的真实落点是**消费仓库**的 `docs/handoffs/`，但那道校验闸此前只挂在
`tvu-design-system` 自己的 `.husky/pre-commit`——消费仓库从没 scaffold 它。变体 B（Design-only）
连 npm/husky 基建都没有。本套 hook 用 git 原生的 `core.hooksPath`，让**两个变体都能启用**，
且变体 B 不用装任何 npm 生态。

## 启用

### 变体 B（Design-only，无 npm/husky）—— 推荐用 core.hooksPath

```bash
git config core.hooksPath .githooks
```

一条命令，git 原生支持。之后 commit 会自动跑本目录 `pre-commit`。（`core.hooksPath` 是本地
repo 配置，不随 clone 自动带上——每个 clone 各自跑一次；团队 clone 后 setup skill 会提示。）

### 变体 A（Design+Code，已装 husky）—— 追加到 .husky/pre-commit

husky 把 `core.hooksPath` 指向 `.husky/`，和 `.githooks` **二选一**。变体 A 已经用 husky 跑
lint-staged 等闸，所以**不要**切 hooksPath，改为把下面这段追加到 `.husky/pre-commit`
（复用同一个校验脚本，零重复逻辑）：

```sh
# --- TVU mockup handoff conformance gate (F62-b) ---
STAGED_HANDOFFS=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^docs/handoffs/.*\.md$' || true)
if [ -n "$STAGED_HANDOFFS" ]; then
  echo "▶ pre-commit: mockup handoff conformance evidence"
  node .githooks/audit-mockup-handoff-evidence.mjs $STAGED_HANDOFFS || exit 1
fi
```

## 怎么产出被引用的 report

handoff 里写一行（大小写不敏感）：

```
Conformance report: docs/handoffs/.conformance/<fileKey>-<nodes>-<timestamp>.json
```

这份 JSON 由 TVU 设计系统的 conformance 审计落盘：

```bash
# 变体 A（已装 TVU DS 包）：
npx --no-install audit-mockup-conformance --file <fileKey> --node <ids> \
  --report docs/handoffs/.conformance/<fileKey>-<nodes>-<ts>.json
# 或直接调包内脚本：
node node_modules/@nancyzeng0210/tvu-design-system/scripts/audit-mockup-conformance.mjs \
  --file <fileKey> --report <path>

# 变体 B（无 npm，但本地有 tvu-design-system sibling clone）：
node ../../tvu-design-system/scripts/audit-mockup-conformance.mjs \
  --file <fileKey> --node <ids> --report docs/handoffs/.conformance/<...>.json
```

report 含 `fileKey / nodeIds / subAudits[].exitCode / timestamp`。gate 校验：文件存在 + 可解析 +
每个子审计 `exitCode=0` + `timestamp` 距 commit ≤ 24h。

> 只有带 `<!-- mockup-handoff -->` 标记的 handoff 受本闸约束；普通 session 交接 handoff 不受影响。

## 降级行为

| 情况 | 行为 |
|---|---|
| 无 staged `docs/handoffs/*.md` | 直接放行（no-op） |
| handoff 无 `<!-- mockup-handoff -->` 标记 | 放行（不是 mockup 交付，不强制 report） |
| **node 缺失**（变体 B 极简环境） | **fail-open**：大声告警"conformance 未经机器验证，靠 owner design-walkthrough 兜底" + 放行。设 `TVU_HANDOFF_GATE_STRICT=1` 改为阻断 |
| 引用了 report 但文件缺失/非法 JSON | 阻断 |
| report 有子审计 exitCode≠0 | 阻断 |
| report 超 24h 时效窗口 | 阻断 |

**已知残余缺口**：若消费产品既不 `git commit` handoff（纯 Figma 交付、AI 全程不碰本地 repo）、
也不装 node，机械 chokepoint 全部落空，只能靠 owner 人工走查（design-walkthrough）。这是产出物
第一落点是 Figma 节点树（非 git）的架构性上限，不假装靠"文档要求 AI 自觉"补上。
