# 质量三板斧（落地到 SDD 流程）

> 解决「验收清单不完备、交互无法 1:1 还原 PRD」的三层兜底机制。  
> 全部以 **gate exit code** 为准，不依赖口头确认。

## 总览

```mermaid
flowchart LR
  subgraph A["1. PRD 原子化 + ID 化"]
    PRD["source/PRD.md"] --> Atoms[".prd-review-atoms.json"]
    Atoms --> AC05["05 PRD 出处列 PRD-XXX"]
    AC05 --> G1["gate docs-closed\nprd-ac-coverage ≥95%"]
    G1 --> L12["12-未覆盖清单"]
  end

  subgraph B["2. 关键交互自动化断言"]
    AC05b["05 验收标准字面量"] --> TDD["e2e/unit [AC-XX]"]
    TDD --> G2["gate prd-coverage\nac-assertion-binding"]
  end

  subgraph C["3. 争议闭环"]
    D03["03 矛盾点"] --> Reg["13-争议闭环登记册"]
    D05["05 Intentional"] --> Reg
    D08["08 待人工确认"] --> Reg
    L12 --> Reg
    Reg --> G3["gate dispute-closed\n(含于 ac-signed)"]
  end
```

---

## 1. PRD 原子化 + ID 化（防遗漏可计算）

### 做什么

把 `source/PRD.md` 拆成最细粒度条目，每条有唯一 ID（`PRD-001`…），写入 `.prd-review-atoms.json`。

### 何时做

| 阶段 | 动作 | 命令 |
|------|------|------|
| B（写 05 前） | 生成/刷新原子 manifest | `npx sdd-flow-kit prd-review --run-id <runId>` |
| B（可选增强） | PRD 细节层（字段/状态机/交互） | `npx sdd-flow-kit prd-enrich --run-id <runId>` |
| B（写 05） | 每条 AC 带五层标签 + AC 审查 | `ac-review` → `gate docs-closed` |

### 通过标准

- `.prd-review-atoms.json` 存在且 `count > 0`
- 前端原子被 05 引用覆盖率 **≥ 95%**（`PRD_AC_COVERAGE_THRESHOLD`）
- 未覆盖项自动写入 `12-前端PRD未覆盖AC清单.md`，须补 AC 或登记豁免

### 写 05 的 AI 规则

1. 打开 `.prd-review-atoms.json` 与 `source/PRD-details/`、`.page-state-model.json`
2. 每条前端原子至少被一条 AC 的「PRD 出处」引用
3. 「验收标准」列前缀须含 `[业务]`/`[UI]`/`[交互]`/`[数据]`/`[技术]` 之一
4. 生成 05 后执行 `ac-review`，审查 Agent 补遗漏 AC
5. 禁止仅写章节号 `PRD §1.2.3` 而不带 `PRD-XXX`

### docs-closed 新增门禁

| 检查项 | 说明 |
|--------|------|
| `ac-layer-coverage` | 五层标签齐备 |
| `ui-fidelity-dimensions` | P0 UI AC 保真五维 ≥3/5 |
| `interaction-state-matrix` | loading/success/error + 页面状态 |
| `data-ac-quality` | [数据] AC 含接口+映射+枚举/null |
| `ac-source-trace` | `.ac-source-trace.json`：P0 须有 prd[] + verify[] |
| `ac-interaction-gap` | PRD 交互能力遗漏/过弱；**P0 与 P1 均 FAIL**，须拟补清零 |
| `intentional-table-quality` | Intentional「确认来源」须指向 03 Qx/用户原话 |
| `prd-filter-inventory` | PRD 筛选项须在 04/05 **逐项**出现；禁「从A到B全部」 |
| `prd-list-column-inventory` | PRD 列表列名 + tip 须落入 04/05 |
| `prd-field-mapping-completeness` | §4.3.1 映射行数/列覆盖；禁「与 VO 对齐」搪塞 |
| `prd-form-field-inventory` | 可编辑/双击/表单字段须落入 §5.1 与 05 |

报告产物：`.ac-interaction-gap-report.json`、`14-AC交互遗漏清单.md`；  
清单门禁另产：`.prd-inventory-gap-report.json`、`15-PRD清单遗漏.md`。  
失败时可用 `gate --expect docs-closed --auto-remediate` 自动拟补后再检。

逃生：`OPSX_SKIP_AC_INTERACTION_GAP=1`；`OPSX_SKIP_PRD_INVENTORY_GATE=1`（均须说明原因）。

### 来源追踪与指标

```bash
npx sdd-flow-kit ac-metrics --run-id <runId>
# 写入 .ac-metrics.json：AC Coverage / Fidelity / Source Trace
# docs-closed 时自动同步 .ac-source-trace.json（从 05 推断，保留人工补全字段）
```

---

## 2. 关键交互自动化断言（防「看起来差不多」）

### 做什么

P0 / 含 `e2e` 的 AC，必须在自动化测试中绑定**验收标准里的可观测字面量**（toast 全文、placeholder、壳层描述等）。

### 何时做

| 阶段 | 动作 | 命令 |
|------|------|------|
| D（实现前） | 按 AC 自动/手动生成 E2E 骨架；缺文件则 `impl-allowed` FAIL | `phase advance --to impl` 或 `scaffold-e2e --change <name>` |
| D（TDD） | test 标题含 `[AC-XX]`，expect 含 05 字面量 | `/opsx-apply` + `tdd-script` |
| E（交付前） | 断言绑定门禁 | `gate --expect prd-coverage` |

### 通过标准（`ac-assertion-binding`）

- P0 禁止纯 `manual`
- test/it 块引用 `[AC-XX]` 且含真实 `expect`
- `expect` 须出现 05「验收标准」中提取的字面量（引号内文案、placeholder 等）
- 含 `e2e` 的 AC 须在 `e2e/` 目录有绑定块

### 禁止伪断言（gate 失败）

- `expect('x').toContain('x')` 断言字符串常量
- `expect(true).toBe(true)` / `expect(1).toBe(1)` 恒真空壳
- `toContainText('')` 空串
- `click({ trial: true })`
- `body.click({ position: { x: 8, y: 8 } })` 作为 `[交互]` AC 的唯一交互
- `prd-atoms-coverage` 仅登记 PRD-ID 数组

### 05 写法建议（便于机器绑定）

交互类 AC 的「验收标准」或「预期结果」须含**可抽取字面量**（书名号、引号、或冒号后顿号分隔的步骤名）：

```
验收标准：[交互] 自定义列：搜索字段、分组全选半选、恢复默认、清空
预期结果：成功：Drawer 展示「搜索字段」「恢复默认」；列名显示「下单总价」，不得出现函数源码
```

对应测试：

```typescript
test("[AC-11] 列配置 Drawer", async ({ page }) => {
  await page.goto("/#/finance/project-report");
  await page.locator('[title="列配置"]').click();
  const drawer = page.locator(".n-drawer");
  await expect(drawer).toContainText("搜索字段");
  await expect(drawer).toContainText("恢复默认");
  await expect(drawer).toContainText("下单总价");
  await expect(drawer).not.toContainText("NTooltip");
});
```

---

## 3. 争议闭环记录（防偏差被口头消化）

### 做什么

凡与 PRD 有意不一致、复查未决、覆盖率缺口、gate 阻塞项，统一汇总到 `13-争议闭环登记册.md`，须显式裁决后才能签收。

### 来源与闭环要求

| 来源 | 文件 | 闭环要求 |
|------|------|----------|
| 矛盾点 | `03-待确认问题清单.md` | P0 闭合 + 用户原话 |
| 有意偏差 | `05` Intentional 偏差表 | 「确认来源」列必填 |
| 复查未决 | `08-PRD一致性复查报告.md` | 不得留「待人工确认」 |
| 覆盖缺口 | `12-前端PRD未覆盖AC清单.md` | 补 AC 或豁免 |
| 结构化项 | `.pending-confirmations.json` | `confirm-item` / `allow-gate-pass` |

### 何时做

| 阶段 | 动作 | 命令 |
|------|------|------|
| E（prd-coverage 通过后） | 自动同步登记册 | （`prd-coverage` ok 时自动） |
| E（签收前） | 手动同步 + 裁决 | `npx sdd-flow-kit sync-disputes --run-id <runId>` |
| E（签收） | 争议闭环门禁 | `gate --expect dispute-closed`（**已含于** `ac-signed`） |

### 常用裁决命令

```bash
# 确认单项
npx sdd-flow-kit confirm-item --run-id <runId> --id CONF-xxx --status confirmed --response "用户原话..."

# 临时放行（须可审计原因，签收前须改回或留档）
npx sdd-flow-kit allow-gate-pass --run-id <runId> --ids CONF-xxx

# 查看结构化待确认项
npx sdd-flow-kit list-confirmations --run-id <runId>
```

### 紧急跳过

```bash
OPSX_SKIP_DISPUTE_CLOSURE=1 npx sdd-flow-kit gate --expect ac-signed ...
```

### deliver / chain 关键门禁硬失败

`deliver` 与 `chain` 在 **`ac-signed` / `prd-coverage` 未通过时硬失败**（`ok: false`，不写 `.delivery-pass.json`，不伪造 `.prd-coverage-pass.json`）。  
`chain` 在 `gate prd-coverage`、`gate ac-signed`、`deliver` 任一步失败时**立即停止**后续步骤（含 `shipped`）。

紧急软放行（须说明原因）：

```bash
OPSX_ALLOW_DELIVER_GATE_SOFT_PASS=1 npx sdd-flow-kit deliver --project-root . --run-id <runId> --change <name>
```

### ac-signed 证据路径须落盘存在

`gate --expect ac-signed` 中的 `ac-signed-evidence-paths` 除正则形态外，还校验「对应证据」中解析出的相对路径（`e2e/*.spec.ts`、`playwright-pic/...`、`evidence/` 等）在**业务包根**下真实存在。  
ADI 类「证据写了 `import.spec.ts` 但磁盘无此文件」会 **FAIL**。

逃生：

```bash
OPSX_SKIP_AC_EVIDENCE_EXISTS=1   # 仅跳过落盘存在性（须说明原因）
OPSX_SKIP_AC_EVIDENCE_GATE=1     # 跳过整段证据门禁
```

### ac-e2e-depth：禁止伪 e2e / 须真浏览器断言

`gate --expect prd-coverage` 中的 `e2e-assertion-depth` + `ac-e2e-depth-AC-xx`：

- 每个测试类型含 `e2e` 的 AC 须有 `test("[AC-XX]"…)` 且为**浏览器**断言
- **禁止** `readFileSync` 读源码 `expect(content)` 冒充 E2E
- **禁止** 仅 `getByPlaceholder` + `toBeVisible` 冒充覆盖
- `[交互]` AC 还须含 `click`/`dblclick`/`fill` 等真实交互
- change 已 archive 时仍解析 `openspec/changes/archive/*-<change>`，**不再跳过**

逃生：`OPSX_SKIP_E2E_ASSERTION_DEPTH=1`（须说明原因）。

### impl-allowed：change 级 E2E 骨架（第 5 闸）

`phase advance --to impl` 会自动按 05 的 P0/`e2e` AC 调用 `scaffold-e2e` 生成 `e2e/<module>/<change>.spec.ts`。

`gate --expect impl-allowed` 新增 `change-e2e-scaffold`：

- 存在 P0 或测试类型含 `e2e` 的 AC，且磁盘上无 `e2e/**/<change>.spec.ts`（及 scope 配置中的真实路径）→ **FAIL**
- 无此类 AC → 跳过（PASS）
- 骨架仅保证文件落地；断言深度仍由 `prd-coverage` / pipeline 后续闸卡住

```bash
# 手动补骨架
npx sdd-flow-kit scaffold-e2e --project-root . --change <name> --run-id <runId>
# 紧急跳过（须说明原因）
OPSX_SKIP_E2E_SCAFFOLD=1
```

### pipeline 强制 SDD_RUN_DIR + assess-ac-assertion-binding

`opsx` / `poquan` delivery-pipeline 在 **tasks**、**short**、**archive 前** 强制：

1. 解析 PRD run（优先 `SDD_RUN_DIR`，否则 `scripts/resolve-prd-run-dir.mjs`）
2. 跑 `scripts/assess-ac-assertion-binding.mjs --run-dir <run>`
3. **任一步失败 → pipeline exit ≠ 0，不执行 archive**

无 05 / 未设 `SDD_RUN_DIR` 且无法自动解析 → **die**（不再 warn 跳过）。

```bash
SDD_RUN_DIR=openspec/PRD/<runId> pnpm run delivery-pipeline -- <change>
# 紧急跳过（须说明原因）
OPSX_SKIP_AC_ASSERTION_BINDING=1 pnpm run delivery-pipeline -- <change>
```

---

## 阶段对照（一张表走完）

| SDD 阶段 | 板斧 1 | 板斧 2 | 板斧 3 |
|----------|--------|--------|--------|
| A 提问 | — | — | 03 矛盾点池 |
| B 文档 | prd-review → 05 引 PRD-ID → docs-closed | 05 写可观测验收标准 | 03 闭合结论 |
| C propose | ac-ready 校验 P0 映射 | tasks 含 `[AC-XX]` | — |
| D apply | — | scaffold-e2e（impl 自动生成）+ TDD 字面量断言 | 05 Intentional 登记 |
| E 交付 | prd-coverage 语义 diff | ac-assertion-binding | sync-disputes → dispute-closed |

---

## 相关文件

| 文件 | 说明 |
|------|------|
| `src/core/prdParse.ts` | PRD 原子解析 |
| `src/core/prdCoverage.ts` | 覆盖率 + 语义 diff |
| `src/core/acAssertionBinding.ts` | AC 断言绑定 |
| `scripts/resolve-prd-run-dir.mjs` | pipeline 解析 SDD_RUN_DIR / PRD run |
| `scripts/assess-ac-assertion-binding.mjs` | pipeline 强制 AC 断言绑定 |
| `src/core/disputeClosureGate.ts` | 争议闭环 |
| `src/templates/artifacts/13-争议闭环登记册.template.md` | 登记册模板 |
