---
name: doc-review
description: >
  当用户要求审查模块文档是否"够不够实现"、PRD/数据模型/API/UI 文档是否完整、
  或在动代码前对一组刚写完的文档做实现就绪度审查时使用。
  与 docs-main 的"一致性检查"（对不对）正交，本 skill 关注"完整性"（够不够）。
  与 plan-review 的"实施方案审查"正交，本 skill 关注"文档本身够不够支撑实现"。
  触发短语：审文档、审一下文档、文档够不够、PRD 审查、文档审查、
  文档 review、doc review、实现就绪度、文档完整性、再看一眼这几份文档。
---

# 文档审查技能（doc-review）

## 定位

对**已写完的一组模块文档**（新模块全套 / 大改后的 ≥ 3 份文档）做"实现就绪度"审查——
**不是审计划是否合理**（那是 `plan-review`），**也不是审文档之间是否一致**（那是 `docs-main` §3 的 5-cell 矩阵），
而是审：**照这份文档从 0 写代码，能不能写出来？哪里会卡？**

## 何时使用 / 不该使用

**使用本 skill**：
- 新模块 PRD + data-model + API + UI + 测试 一整套刚写完，准备开干代码前
- 模块大重构（≥ 3 份文档同步改动）后，准备 merge 前
- 用户说"再看一眼这几份文档"、"审一下"、"够不够实现"

**不要用本 skill**：
- 单字段 tweak / 单文档小改 → 用 `docs-main` §3 的 5-cell 矩阵就够
- 审"实施方案 / 架构 / 数据流是否合理" → 用 `plan-review`
- 审 PR 代码 → 用 `code-review`

## 与现有 skill 的边界

| Skill | 审什么 | 输出 |
|---|---|---|
| `docs-main` §3 | 文档之间一致性（drift / 死链 / seed 漂移）| 差异表 |
| `plan-review` | 实施方案 / 架构 / 数据流 / 性能 | 方案改进建议 |
| **`doc-review`** | **文档够不够支撑从 0 实现** | 🔴/🟡/🟢 分级发现清单 |

三者正交，按需组合。`doc-review` 不替代另外两个。

## 核心工作流

### Step 0 — Recon（如尚未做过）

如本次工作首次接触模块，先按 `docs-main` §0 的侦察清单做项目惯例侦察（10 分钟）。
已经写过文档的人通常侦察过了，可跳过——但**对照侦察清单逐项确认文档里相关条目**：
- 权限码格式有没有写对？
- 审计装饰器名引用对了吗？
- schema 文件名 / 路径符合项目惯例吗？
- 用了正确的前端状态库 / 错误页模式吗？

这些是 lens 之外的"项目惯例类"低垂果实，必须先扫一遍。

### Step 1 — Lens 批判审查（4 通用 + 2 条件 Lens E/F）

**关键原则**：换 lens > 加轮次。**每个 lens 跑一次 ≥ 同一 lens 跑两次**。
free-association"再看一眼"是无效的——必须用 4 个通用 lens 的 prompt 各跑一次；**如果文档涉及容灾 / 备份 / 多副本 / RPO/RTO**，额外跑 Lens E；**如果文档涉及 AI 类功能 / LLM 调用 / 用户面服务**，额外跑 Lens F——4 通用 lens 抓不到这两类缺陷。

---

#### Lens A — 实现者走查（最重要，单独最高价值）

**Prompt**：
> "我是接手 PR 的初级 dev，从 0 照这份文档写代码。每一步该怎么写？哪里缺细节？"

**走查路径**：
1. 后端：从 controller 路由 → DTO 校验 → service 调用外部 API → 数据库写入 → 响应封装。**每一步**问"代码怎么写？"
2. 前端：从路由 → 数据获取 → 状态管理 → 组件 → 错误态 / 加载态 / 空数据态。
3. 数据库：从 Prisma schema → migration SQL → 索引 → seed → 数据回填。

**典型抓得到的坑**：
- 外部 API 的请求参数清单（如 Graph `$select` 该选哪些字段）
- 分页 / 翻页 / nextLink / cursor 处理
- 静态映射表（如 SKU displayName）的来源
- 库 / 框架选型（CSV parser、date 库、状态管理库）
- 批量操作大小（每多少条 commit / chunk）
- 响应头细节（Content-Disposition for download / Cache-Control / ETag）
- organizationId / actorId 的注入路径
- Prisma 不支持的特性（partial unique index、citext、custom types）
- migration SQL 多语句执行顺序
- 权限点 seed / 角色挂接 实施清单

---

#### Lens B — 外部 API / 依赖对抗

**Prompt**：
> "把所有外部依赖（第三方 API / SDK / 服务）的怪癖列一遍：返回格式、分页、限流、字段缺失、数据延迟、字符大小写、脱敏。"

**走查路径**：
1. 列出所有外部依赖（OAuth provider、消息队列、文件存储、第三方 SaaS API）
2. 对每个依赖问 8 个问题：
   - 返回格式：JSON / CSV / XML / 二进制？
   - 字段命名规范：camelCase / snake_case / PascalCase？大小写敏感吗？
   - 分页：cursor / nextLink / offset？默认 / 最大每页多少？
   - 限流：429？Retry-After？每多少时间多少次？
   - 字段缺失：哪些字段会 null？哪些 license / scope 才返回？
   - 数据延迟：实时 vs 离线统计？延迟多久？
   - 脱敏 / 隐私设置：是否默认混淆 PII？需要外部 admin 配置开关？
   - 错误码 / 状态码：哪些会被 retry？哪些直接 fail？

**典型抓得到的坑**（以 Microsoft Graph 为例）：
- 活动报告 24-48 小时延迟（不是实时）
- UPN 默认脱敏（M365 admin center 开关，不开就废）
- Reports endpoint 返 CSV 不是 JSON
- SubscribedSku 没有人类可读 displayName
- UPN 大小写不保证一致
- `signInActivity` 需要 P1/P2 license
- `$select` 必填否则字段不返
- `@odata.nextLink` 翻页

---

#### Lens C — 失败模式走查

**Prompt**：
> "遍历每一个步骤，假设它在最坏时刻崩溃：进程重启、DB 临时挂、上游半路 429、用户连点两次。状态留下什么？怎么恢复？"

**走查路径**：
1. 长时间运行的操作（同步 / 导出 / 批处理）：进程 OOM 后状态？
2. 多步事务：第 N 步失败后，前 N-1 步的状态怎么处理？
3. 并发：同一操作同时触发两次会怎样？
4. 上游崩溃：依赖的外部 API 超时 / 5xx / 限流，本地状态怎么处理？
5. 用户误操作：连点按钮 / 刷新页面 / 关浏览器，前端状态怎么处理？

**典型抓得到的坑**：
- 长时间任务的 status 卡 running（需要 startup hook 恢复）
- 中途失败的事务性策略（回滚 / 不回滚 / 重试）
- 互斥锁的兜底（partial unique index 必须配启动恢复）
- 并发触发的去重（mutex / idempotency key）
- 启动 hook 的防御性 try/catch（不能拖死整个 backend 启动）
- 前端 mutation pending 时禁用按钮 / debounce

---

#### Lens D — 跨文档字段 diff

**Prompt**：
> "PRD 里承诺的每一个字段 / 操作 / 状态，机械对照到 API / data-model / UI / 测试场景。逐项打钩，缺失的标记。"

**走查路径**：
1. 列 PRD §"功能清单"或§"详细需求"中所有字段 / 操作
2. 对每一项，**机械检查**：
   - data-model 里有对应字段吗？
   - API 响应里返回了吗？
   - UI 文档里展示了吗？
   - 测试场景覆盖了吗？
3. 列 UI 文档里所有可见状态（loading / success / empty / error / partial）：每个状态都有规格吗？
4. 列错误码：API 里出现的所有错误码 → UI 里有对应文案吗？

**典型抓得到的坑**：
- 列表列数 PRD vs UI 不一致（PRD 列了 13 列，UI 只画 8 列）
- license 字段 PRD 提到但 API 响应没返
- `mail` 字段 data-model 有但 API 响应没返
- 错误码 API 定义了但 UI 没说怎么提示
- UI 状态规格不完整（只写了 success 没写 loading/empty/error）
- URL 状态持久化范围不明（哪些进 URL 哪些不进）
- i18n key 命名约定缺失

> 📌 这 cell 与 `docs-main` §3 的 5-cell 矩阵中的 cell ① 重叠但更聚焦"完整性"。
> docs-main 的 cell ① 查"链接 / frontmatter / 数字断言"等 hygiene；
> 本 lens D 查"PRD 承诺 vs 其他文档兑现"。

---

#### Lens E — 灾难协同（运维/容灾/备份/DR 类文档**必跑**）

**触发条件**：文档主题或内容命中以下任意一项 → 4 通用 lens 跑完后**必须**额外跑此 lens：

- backup / disaster / recovery / DR / failover / replication / restore 关键词
- 涉及 RPO / RTO 指标
- 涉及多源、多副本、多区域、多机协同
- 涉及"机房挂 / 区域故障 / 灾难 / 误删 / 演练"等场景

**为什么需要单独 lens**：4 通用 lens 的 prompt 偏业务模块（DTO / 字段 / 状态机 / 错误码），跟着思考时**看不见**"数据源前提 / 时序协同 / 优先级"这类运维语义的缺陷。Lens A/B/C/D 都覆盖不到——已有 case 实测漏抓 4 个真缺陷（详见参考 `.learnings/2026-05-14-doc-review-dr-lens-gap.md`）。

**Prompt**：
> "灾难不是单点的。**多个 source 同时挂**、**多个维度同时备份但时间错开**、**同一指标在不同数据源下含义不同**——这些跨维度交互文档是否声明清楚？"

**走查清单**（7 项，逐项打钩）：

1. **RPO/RTO 的数据源前提**：每个 RPO/RTO 数字明确写"哪个 repo / 哪个副本"。本地 + 异地两层副本时**必须拆列**，不能含混合为单一数字
2. **多维度备份的时序偏移**：DB / 附件 / Configs 等是否在同一时刻备份？如果错开（典型如 04:30 / 05:00 / 05:30）→ 一致性模型如何处理？(a) 接受最终一致 + 启动清扫 (b) FS 快照同时点 (c) 应用层 quiesce —— 选哪个、为什么？
3. **inventory 完整性**：所有要备份的对象（模块/表/路径/字段）是否**穷举**？不能依赖"调研中"占位 —— 漏一项 = 永久静默丢
4. **多 source 同时受灾的恢复优先级**：列 T0/T1/T2/T3 分级 + RTO 预算分配。**T0 通常是元基础设施**（备份枢纽自身、密钥、监控告警系统）
5. **元基础设施的循环依赖**：备份枢纽自己的备份去哪？监控系统自己被谁监控？告警系统挂时怎么告警？—— 不能 self-referential
6. **加密策略 + restore 端权限隔离的端到端覆盖**：传输 + 静态 + 跨地域复制 + 备份介质本身是否**全程**加密？此外**含破坏性语句的 dump 是否有 restore 端目标机白名单 / wrapper 隔离**——`pg_dumpall --clean --if-exists` 等 dump 内含 `DROP DATABASE IF EXISTS` / `DROP ROLE IF EXISTS`，**禁止**任何路径直接 `psql -f` 到任意 host，必须走 wrapper 强制目标机白名单（拒绝 prod hostname）+ dump 文件名带 `-DESTRUCTIVE-DO-NOT-APPLY-TO-PROD` 标注。不能停在"备份是加密的"或"恢复 SOP 文档化了"就 OK
7. **演练频率 vs 真实事故概率 + 失败升级阶梯**：演练越密成本越高、越稀越接近"备份不算完成"——明确写演练频率 + 失败处理（核心不变量：演练失败 = 备份不算完成）。**且必须有升级阶梯**：失败后 T+24h / T+72h / T+7d / T+30d 各级触发什么动作 / 升级到谁 / 谁有决策权 / cron 禁用期间的代偿机制（手工 dump 谁顶？写 incident 到哪？）——否则"失败 = 不算完成"会变成静默失效（cron 被禁几周没人察觉）

**典型抓得到的坑**：
- RPO ≤ 5min（本地 repo）vs RPO ≤ 24h（异地副本）—— 混为一谈
- DB 04:30 / 附件 05:30 —— restore 后 orphan refs 无应对方案
- "platform_tickets 附件 + 其他模块" —— "其他模块"没穷举 = 永久静默丢
- 多机受灾 4h 预算 —— 没分级 = 灾难时拍脑袋
- 备份枢纽自己的密钥放在备份枢纽 —— 循环依赖
- 跨地域复制开关未启用 —— 区域故障时副本同归于尽
- "每月演练 1 次" —— 但没写失败如何处理 / 失败时备份是否仍算成立
- `pg_dumpall --clean --if-exists` dump 任由 dispatcher / 人工 `psql -f` —— 误打到 prod = 整库 wipe（必须 wrapper + 目标机白名单）
- "演练失败 = 备份不算完成" 写了但没升级阶梯 —— cron 被禁几周没人察觉 = 静默失效

---

#### Lens F — AI quality / 可观测性（AI 类 / 用户面模块**必跑**）

**触发条件**：文档主题或内容命中以下任意一项 → 4 通用 lens 跑完后**必须**额外跑此 lens：

- 调用 LLM / AI service / 自研 prompt（OpenAI / Anthropic / 自部署模型）
- prompt 模板 / system prompt / few-shot example 出现在文档或代码
- 用户面交互（生成内容 / 翻译 / 摘要 / 推荐 / 搜索）
- 任何"输出质量靠主观判断"的功能

**为什么需要单独 lens**：4 通用 lens 是为传统 CRUD + 外部 API 同步类模块设计的，AI 类模块特有的审查面（prompt eval / LLM 成本 / system prompt 的 i18n / 运行时可观测性）**不在 4 lens 的 prompt 视野里**。已有 case 实测：`flow-diagram` 模块第一轮 4-lens 跑完全部 finding 修齐后，用户主动起的第二轮抓到 1 🔴 + 5 🟡 都是 AI/可观测性维度的（详见参考 `.learnings/2026-05-01-doc-review-needs-ai-and-observability-lens.md`）。

**Prompt**：
> "AI 输出对了吗、对了多少？运行时谁在看？换语言怎么办？挂了谁知道？—— 这四问，文档是否答得上来？"

**走查清单**（7 项，逐项打钩）：

1. **prompt eval / 黄金样本**：是否定义 ≥ 5 个**有期望输出**的黄金 case？prompt 改动时如何回归验证（自动跑 + 比对 / 人工抽检 / 阈值告警）？无 eval = 改 prompt 像盲改代码
2. **system prompt 的 i18n**：用户 locale 是 `en-US` 时，system prompt 是否切到英文（不是输出语言切但 prompt 仍中文，否则 LLM 倾向输出中文）？默认 fallback 是哪种语言？文档有明示
3. **LLM 频控 / 防滥用**：单用户 N 分钟内最多调用几次？超限怎么处理（429 / 排队 / 降级）？成本上限（token / USD）触发告警？无频控的 AI endpoint = 被公开后 5 分钟就能拖垮 quota
4. **token / cost 可观测性**：每次调用 input/output token、cache 命中、估算 cost、latency 是否落 metric？按 model / channel / user 维度可聚合？没有 = 出问题时连"调了多少次"都不知道
5. **运行时 metric / 告警**：错误率（model 5xx / 内容审核失败）、p95 latency、成本/min 是否有告警？阈值多少？告警通道是哪？无告警 = 故障靠用户报错才知道
6. **失败降级与重试**：LLM 超时 / 限流 / 内容审核拒绝 / 模型 deprecated 时的降级路径？是否会**写脏数据**（半结构化 JSON 解析失败也落库）？
7. **a11y / 包容性**：键盘导航 / 屏幕阅读器 / 高对比度 / 老年用户 / 多语言用户群体是否能用？这维度对所有用户面模块都重要，AI 类尤其（输出是主要交互）

**典型抓得到的坑**：

- system prompt 写"用中文回答"硬编码 → en-US 用户看到中英混杂
- prompt 改了但无 eval → 上线后输出退化没人发现
- AI endpoint 无频控 → 公开 1 天 token 烧光被 Anthropic 限流
- token usage 没埋点 → 成本飘高才发现某个 prompt 每次烧 50k token
- LLM 超时无降级 → 整页 spinner 转 60 秒后白屏
- 内容审核拒绝时 service 抛 500 而不是返"请换种说法" → 用户黑盒看不懂
- 部门预设配色 / 标签的 key 只有中文 → 切英文 locale 显示中文 key

**反模式**：

- ❌ "AI 输出质量靠人工抽检" —— 没机制就是没机制，不算审查通过
- ❌ "成本观测先不做，跑起来再说" —— LLM 调用是 OpEx，没成本观测 = 失控

---

### Step 2 — 输出格式

按严重性分三级输出，**附复现命令 / 文档行号 / 修改建议**：

```
## 🔴 必须改（不修会浪费一整个 debug session）
### 1. <findings 标题>
- 位置：docs/modules/{module}/01-prd.md:42
- 问题：<具体描述>
- 证据：<grep 命令 / 引用 / 对比>
- 修法：<具体改成什么>

## 🟡 该改（跨文档不一致 / 漏洞）
...

## 🟢 可选优化
...
```

**严重性判定标准**：

- 🔴 **必须改**：照原文档实现会**直接报错 / 永远 403 / 数据丢失 / 整个方案失效**
- 🟡 **该改**：照原文档实现能跑起来但有 bug / UX 差 / 后续埋坑
- 🟢 **可选**：实现细节 / 命名 / 文风层面，不影响正确性

### Step 3 — 收敛建议

跑完所有适用 lens（业务文档 = 4 通用；DR/Ops 文档 = 4 通用 + Lens E）后，**主动告诉用户"可以开干了"或"还需再审一轮"**。

**何时建议停审**：
- 本轮 🔴 ≤ 1 项 + 🟡 ≤ 2 项 + 用户明确要求开始实现
- 已经审过 ≥ 2 轮，新发现都是 🟢 级
- 剩余问题需要打开 IDE 看代码才能定（如具体 DTO 形状 / logger 格式）

**何时建议继续**：
- 🔴 ≥ 2 项（说明文档有结构性缺漏）
- 跨多份文档的字段 diff 明显有 cascading 影响（一改 cascade 一片）
- DR/Ops 文档**没**跑 Lens E —— 必须补跑（Lens A-D 抓不到运维语义缺陷）

> 💡 **重要**：不鼓励无限审下去。同一 lens 跑两次信号增量 ≈ 0，凭直觉"再看一眼"是反模式。如果适用的 lens 都跑过且收敛，**主动建议停审**。

## 输入要求

执行本 skill 时用户应提供：
- 要审的文档路径或 module 名（如 `docs/modules/ops-center/`）
- （可选）业务背景 / 已知关注点

如未提供，用 `ls docs/modules/` 列出候选请用户选择。

## 输出要求

- 按 🔴/🟡/🟢 三级输出 findings 清单
- 每项 finding 附：位置 / 问题 / 证据 / 修法
- 跑完 4 lens 后主动给"是否继续审"的建议
- 不擅自改文档——发现问题报告给用户，由用户决定是否调用 `docs-main` 或 Edit 修

## 反模式（避免）

- ❌ 不做 Step 0 项目惯例侦察直接开审 → 错失 50% 真坑
- ❌ "再看一眼"式 free-association → 同 lens 重复跑、覆盖度低
- ❌ 跑超过 4 轮还在挖小毛病 → 边际收益归零，浪费用户时间
- ❌ 把 🟢 级和 🔴 级混在一起平铺 → 用户难判优先级
- ❌ 审完直接擅自修文档 → 越权，应由用户决策后再用 `docs-main` 修

## 触发关联

- 上游：`docs-main`（先写完文档）/ `plan-feature`（先有 PRD）
- 下游：`docs-main`（修文档）/ `backend-main` / `frontend-main` / `database-main`（开始实现）

## 参考

- `.learnings/2026-04-30-doc-review-multi-lens.md` — 本 skill 的诞生故事，详述 5 轮审查 vs 1 轮 4-lens 的对比
- `.learnings/2026-05-14-doc-review-dr-lens-gap.md` — Lens E 的诞生故事：4 lens 漏抓 4 个真缺陷的复盘 + 触发条件 + 7 项 checklist 由来
- `docs-main` SKILL.md §0 — 项目惯例侦察清单
- `docs-main` SKILL.md §3 — 5-cell 一致性矩阵（与本 skill 的 Lens D 互补）
