# N6: QA 评估

AI 动态决策是否触发 `cm-qa-engineer`，不按固定间隔。

QA 开始前解析 `tester` 角色并记录 `decision`/`phase: route`；浏览器走查另外解析
`browser_qa`。路由元数据只说明请求的工具/模型别名，正式命令、逻辑核验和浏览器
证据仍必须由本地 QA 合同实际产生。

同时读取有效配置的 workflow profile 与 `policies.tests`：`java-backend` 优先
commands/logic 及 API、数据库回归；`web-frontend` 优先 commands/browser；
`cm-default` 按 logic→commands→browser。`policies.tests` 只关闭未被规格要求的可选
类型；`test-cases.json` 中 blocking case 即使类型未列出也必须执行，环境不可用则
`BLOCKED`，不得把配置当作跳过已审批验收的许可。

## 评分（1-5 分，总分 ≥ 8 触发）

| 维度     | 1 分                 | 5 分                  |
| -------- | -------------------- | --------------------- |
| 变更范围 | 单文件小改动         | 跨多模块/多项目       |
| 风险等级 | 纯 UI 文案           | 数据库/支付/认证      |
| 累积变更 | 上次 QA 后 1 个 task | 上次 QA 后 5+ 个 task |
| 功能边界 | 模块内部实现         | 完整用户可感知功能    |

## 必须触发（无需打分）

- 当前 feature 所有 task 完成
- API 接口变更（**收尾合并豁免**：下一个任务就是本 feature 最后一个任务时，可合并至 feature 级 QA 一次执行——避免背靠背双跑全量；合并决策记运行日志 `decision` 事件。实跑教训：后端 feature 几乎每任务都改 API,逐任务触发 QA 成本失衡）
- 数据库 migration
- 认证/授权/支付逻辑
- 连续 5 个 task 未触发过 QA

## 跳过

- 纯文档/注释/配置格式
- 仅新增类型定义（未实现）
- 上一个 task 刚触发过 QA 且当前变更极小

**跳过也必须留痕**：无论触发还是跳过，每个任务的 N6 结论都写一条 `qa` 事件进运行日志（`触发:原因` / `跳过:评分{N}` / `合并至feature级`）——cm:ai 必记事件本就含 qa，此处重申是因为实跑失守：json-keeper 7 个任务 0 条 qa 事件，N6 被整场静默绕过，连"连续 5 个 task 未触发"的必触发条款命中了都无人知晓。**不可见的跳过等于没有 N6**

## 触发格式

```text
🧪 触发 QA — 原因: {理由}
   累积变更: {N} 个 task | 风险评估: {总分}
```

QA 通过 → 继续。发现问题时，**不得在 N6 直接修改**已由 N4 绑定并在 N5 提交的
代码或测试；先保存失败报告，再按有效配置的 `policies.auto_fix` 走唯一分支：

- auto_fix: `never` → 写 `BLOCKED` 并报告，不修改。
- auto_fix: `explicit` → 展示缺陷摘要，取得本次修复授权后调用 `$cm-fix`。
- auto_fix: `auto` → 直接调用 `$cm-fix`，不额外停车。

`$cm-fix` 必须生成自己的结构化 handoff、通过独立审查门禁并按 delivery 策略落盘；
QA 新增或修正测试也属于该 fix diff，不能在旁路写入。修复闭环后重新 QA，总计最多
3 轮；仍失败则 `BLOCKED`。这样原 task 的审批 SHA 不被事后覆盖，QA 修复拥有独立
审查、提交与日志证据。

实际触发 QA 时按 `../../../runtime/logging.md` 写 `test_run/start` 与
`test_run/complete`，只记录模式、用例/通过/失败/阻塞数量、结论和报告路径；
测试输出、截图和浏览器日志仍留在 `.reviews/`。

feature 存在 `test-cases.json` 时按 `runtime/test-contract.md` 消费：正式项目命令
仍照常执行；browser cases 由 `cm-qa-engineer` 逐条模拟并保存截图/日志；
N4 中的 `INSUFFICIENT_EVIDENCE` 必须在本节点补运行时证据或保持 `BLOCKED`。
任一 blocking case 为 `FAIL`/`BLOCKED` 时不得宣称 QA 通过。

交付形态为微信小程序时读取
`../../cm-miniprogram-engineer/references/release-checklist.md`，按 feature 实际能力补
开发者工具模拟器与真机专项。Web/H5 截图不计小程序证据；授权、设备差异或平台 API
缺真机证据时保持 `BLOCKED`/待人工，不能用静态核验或模拟器推断通过。

**QA 新补的测试须变异自证**(机械,非审查轮):种 1-2 处行为变异必须变红,不红的测试修到红再入库——QA 测试是未来所有回归的安全网,安慰剂 QA 测试 = 永久性假安心(实跑先例:dogfood 中 QA agent 自发做过「5 个变异全被抓」,本条把自觉变成规则)。

触发 QA 后，回填 METRICS.md 中对应任务行的 QA 列：`通过(覆盖率{x}%)` / `{N}轮通过(覆盖率{x}%)` / `失败上报`——覆盖率随列落盘，不留在会话里。

## 形态确认卡点（仅 0.bootstrap 完成时，一次性）

`0.bootstrap` 完成触发的 QA 中，**必须**执行形态确认：启动 dev server / 模拟器 / 开发者工具，截取首屏发给用户——

```text
📸 形态确认：这是项目当前的形态（{Web 页面 / App 模拟器 / 小程序}），与你要的交付形态一致吗？
```

**截图来源按 CLAUDE.md 交付形态写死**（防"替代形态截图糊弄卡点"）：

- Web → 浏览器访问 dev server
- **App → iOS/Android 模拟器或 Expo Go 真机截图；react-native-web / `expo start --web` 的浏览器截图不作数**——浏览器里渲染出"长得像 App 的网页"恰恰是本卡点要拦的形态错配
- 小程序 → 微信开发者工具模拟器

模拟器环境不可用 → 如实上报，请用户在本机/真机启动确认；**不得用替代形态的截图过卡点**。

**用户确认后才进入业务 feature**。成本是一张截图 + 3 秒确认；收益是架构错配的发现时点从"任务过半"提前到"零行业务代码"（历史事故：要 App 画成了 HTML，跑到一半才发现）。

## 业务验收走查（feature 完成时）

触发原因为「当前 feature 所有 task 完成」且 QA 通过后，调用 `cm-product-manager` skill 做**业务验收走查**（用户视角流程闭环 + AC 逐条对照，非技术测试）：

- 小偏差 → 记录进走查报告，继续
- 涉及需求本意的偏差 → 暂停问人，不自行认定"也可以"

金融/营销类 feature 的走查协同 `cm-finance-expert` 追加金融视角（数字口径抽查 + 营销红线扫描）；**涉合规的偏差一律上报，不适用"小偏差放行"**。
