# UX 场景覆盖扩容（边界态 · 响应式 · a11y 自查 · 方案比选）Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 把四个已有仓库实证、但没有条文的 UX 场景立成规则真源条文：**B** runtime 边界态（空 / 加载 / 0 结果 / 加载失败）、**A** 多断点响应式出稿纪律、**C** 设计阶段 a11y 自查、**D** 多方案比选交付纪律。每个 Task 交付一条完整条文（含 jump 路由 + 实证 + 与既有规则的分工声明），四个 Task 零依赖、可各自独立提交、owner 可逐 task 砍。

**Architecture:** 优先复用既有机制而非另立顶级规则——B = 给 `design-process.md` §State-Completeness Enumeration **加第 7 维**（不新增编号）；C = 给 `design-process.md` §M49 的 UX Principle Contract #4 **挂执行细则子表**（不新增编号、不破坏六合同形状）；A、D 确无宿主机制 → 各立一条**新顶级 M-rule，落 `mockup-conventions.md`**（编号执行时 `pnpm rule:next M` 取）。所有断点 / 尺寸数值只指向 token / WCAG 真源，条文不抄字面值。

**Tech Stack:** 纯 Markdown 规则文档改动（`docs/internal/design-process.md` + `docs/internal/mockup-conventions.md`）· 编号器 `pnpm rule:next M` · 三道既有机械闸收尾（`audit:rule-load-map` / `audit:rule-inventory` / `audit:stale-anchors`，A/D 另跑 `audit:rule-number-collision`）。零代码、零 changeset。

## 判据真源（owner 裁定记录 · 2026-08-12）

- **owner 2026-08-12 授权**：四个新 UX 场景**全部立条文**，一份计划四个 Task；**执行时 owner 可逐 task 砍**——被砍的 task 直接跳过，其余照常执行（四个 Task 零依赖，砍任意子集不产生返工）。
- **优先级排序（owner 已见并未反对）**：**B 边界态 > A 响应式 > C a11y > D 方案比选**。本计划 Task 顺序即此序；执行顺序也按此序（若某 task 被砍则顺延）。
- 每个场景的仓库内实证（立条依据，非凭直觉）：

| 场景 | 实证 |
|---|---|
| B 边界态 | [`docs/PROJECT_GOAL.md`](../../PROJECT_GOAL.md) §文档站（L106-107）明文「Status Matrix 只建模 Figma 导出的变体状态，runtime 态（empty / loading / error / 0 数据 / 超长文本）不在矩阵建模范围……按需触发补」——超长文本后来有了 M43，**空 / 加载 / 0 结果 / 加载失败至今零条文**。枚举缺维的失效模式已有先例：2026-07-13 RPS One（M12 反例）因枚举缺「状态 × 外部事件」维整类状态系统性漏画，后补第 6 维 |
| A 响应式 | [[INFRA-F113]]（[`docs/internal/backlog.md`](../../internal/backlog.md)）：docs 站 Button 页 390 档 46px 页级横向溢出，元凶 = demo 卡自身 370px 塞进 350px 正文列——连 DS 自己的站都没有出稿断点纪律。DG-1 断点 token 已 ship（owner 2026-07-31 拍板，[`dg1 spec §6c`](../specs/2026-07-30-dg1-layout-primitives-design.md)），但没有任何条文说「一个页面该出几档、每档取哪个断点、哪些元素允许换形态」 |
| C a11y | 仓库 a11y 工具链全在 code 侧事后（`docs/internal/_generated/a11y-contrast-report.md` 2891 行 + a11y-survey.json + [`designer-review 收口`](../../internal/a11y-contrast-designer-review-2026-06-01.md)：library 文字 47 配对 + non-text 20 对已全部 owner/设计师收口）。设计阶段无对应自查 |
| D 方案比选 | 实践先行无条文：[`2026-05-11 SaaS Dashboard 2-option PM review`](../../_archive/retrospection/2026-05-11-saas-dashboard-2-option-pm-review.md)；M24 自检反例也点到「Plan A 已有 Figma，画 Plan B 对比方案」（只声明了「不触发 M24」，没有声明适用什么） |

## Global Constraints

每个 Task 的要求都隐含包含本节。

- **规则编号不预写死**：本计划一律用占位符 `M〈A〉`（Task 2）/ `M〈D〉`（Task 4）。执行到该 task 时当场跑 `pnpm rule:next M` 取号（工具从 `origin/master` + 本地合并算，先执行者取的号后执行者自动 +1）。⛔ Task 2 / Task 4 **不要并行开工同时取号**——同 session 按序执行，或后者开工时重跑 `rule:next`。
- **编号系列的家园纪律（本计划撰写时实测，load-bearing）**：`rule-next.mjs` 与 `audit-rule-number-collision.mjs` 的扫描面都**只含** `mockup-conventions.md` / `code-conventions.md` / `figma-technical-reference.md`——**`design-process.md` 是编号盲区**（住在那里的 M48–M51 对两个工具都不可见；M52 的标题注记就记着一次真实撞号事故：「原 M49，因与 design-process §M49 撞号重号」）。⇒ 本计划**新的带编号条文一律落 `mockup-conventions.md`**（Task 2 / Task 4）；落 `design-process.md` 的改动（Task 1 / Task 3）**不引入任何新编号、不新增 H2**。
- **每个 Task 独立可交付可砍**：四个 Task 之间零依赖，各自完整四件套（条文候选文本 + jump 表行（如适用）+ 实证段 + 分工声明）+ 独立 commit。owner 砍掉任意 task 不影响其余。
- **优先复用既有机制**：placement 判断写在每个 Task 开头、先于文本。B = 枚举加维、C = M49 加项（这两个 task 的「新顶级规则」形态已在 placement 判断里论证放弃，不再给第二版文本）；只有 A / D 立新规则。
- **提交纪律**（本仓库常态多 session 并行、共享 git index）：先 `git diff --stat -- <路径…>` 核行数，再 `git commit -F <msg 文件> -- <显式路径…>`（`-F` 在 `--` **之前**），提交后**紧跟 `git reset -- <路径…>`**。⚠️ pre-commit 闸链可超过 2 分钟——commit 用 `run_in_background` 跑，别用 2 分钟前台超时。⚠️ 撞上 `.git/index.lock` 是对方正在提交，不是故障；等一下重来。
- **纯 docs 改动不写 changeset**。每个 Task 收尾三闸全绿：`pnpm run audit:rule-load-map` + `pnpm run audit:rule-inventory` + `pnpm run audit:stale-anchors`；Task 2 / Task 4 另跑 `pnpm run audit:rule-number-collision`。
- **数值只指向真源**：条文里所有断点 / 容器 / 尺寸数值只指向 token 真源（[`src/tokens/variables.css`](../../../src/tokens/variables.css) §Layout breakpoints / §Layout containers）或标准编号（WCAG SC），**不抄字面值**——抄了就是会漂的副本（`640/960/1100/1280` 这类数字不允许出现在新条文正文里）。
- **条文形态跟随目标文件惯例**：mockup-conventions 新 M-rule 用既有四段式（触发条件 / 必做 / Acceptance / 实证 + 本计划要求的分工声明段）；design-process 段内改动跟随所在段落的既有形态。日期戳一律写执行当日。
- **不动 [`docs/PROJECT_GOAL.md`](../../PROJECT_GOAL.md) 的 Status Matrix scope 声明**——那是 docs 站（code 侧）的建模范围声明，mockup / 设计侧立规不改变它。
- **执行前先读活源**：每个 Task 的 Step 1 都是重读目标段落的 live 版本（并行 session 可能已动过；本计划引用的行号是 2026-08-12 撰写时的快照，**以内容锚定位，不以行号定位**）。

## File Structure

| Task | 文件 | 动作 |
|---|---|---|
| 1 (B) | `docs/internal/design-process.md` §State-Completeness Enumeration + §M48 起手清单表 | 枚举加第 7 维 + item 5 去重 + M48 摘要行指针化 |
| 2 (A) | `docs/internal/mockup-conventions.md` | 新 M-rule `M〈A〉` 正文（M52 块之后、§实证档案之前）+ 顶部 jump 表加一行 |
| 3 (C) | `docs/internal/design-process.md` §M49（#4 UX Principle Contract 之下 + Acceptance + 反例表） | 挂 §4a a11y 自查子表 + Acceptance 加一行 + 反例表加一行 |
| 4 (D) | `docs/internal/mockup-conventions.md` | 新 M-rule `M〈D〉` 正文（同上位置）+ jump 表加一行 + §M24 自检反例处加一行指针 |

---

## Task 1 (场景 B): runtime 边界态 —— State-Completeness Enumeration 加「数据形状」维

**Files:**
- Modify: `docs/internal/design-process.md`（§State-Completeness Enumeration 枚举清单 + §M48 起手清单表第 3 行）

### Placement 判断（先于文本）

**推荐形态 = 给既有枚举加第 7 维，不立新顶级规则。** 依据：

1. 枚举机制已被验证过一次同型扩容——第 6 维「外部事件矩阵」就是 2026-07-13 M12 反例后补的维（M12 反例段原话：「根因：State-Completeness Enumeration 缺『状态 × 外部事件』维」）。空 / 加载 / 0 结果 / 加载失败与之同构：都是「一整类状态被系统性漏画」的维度级缺口。
2. 枚举已是 **M48 起手清单的强制一行**（design-process §M48 表第 3 行明文引用「枚举清单见本文件 §State-Completeness Enumeration」）——加维即自动进所有任务的起手清单，**零新增加载路由**：design-process 是所有任务 mandatory 读，`audit:rule-load-map` 不扫它、`rule-inventory` 只核 H2（本改动在 H4 段内，H2 不变）。
3. 新顶级规则形态被放弃的理由：会与枚举第 5 维（现文本里已含「空数据」关键词）形成双重归属，违反判据单一归属原则（M23.0）；且带编号规则按 Global Constraints 只能落 mockup-conventions，会把 path-agnostic 的状态建模规则塞进 Path A 专属文件。
4. **现有枚举实测**（2026-08-12 撰写时 live 读取 + 全真源 grep）：枚举共 6 维——1 CRUD / 2 选择·多选 / 3 核心动作前后态 / 4 后置修改态 / 5 edge·错误态（含「空数据」一词但无任何设计要求）/ 6 外部事件矩阵。**loading 作为「须被设计的状态」在三份规则真源里零出现**——现有命中全是别的语境：design-process 的 `🟡 skeleton` 是组件 catalog 置信标记；mockup-conventions M23（Default / Loading / Applied 多状态流程图）管「已识别的多状态怎么交付」、M31（loading/empty/error 走 Boolean property）管「状态在组件里怎么建模」、M32.2 前置 gate 管「状态类件先查库 variant」——**没有任何一条要求「每个数据视图必须把这些态枚举出来」**。加维方式 = 追加第 7 项、不重排 1–6（M12 正文与反例段引用「枚举第 6 项」，重排会打断既有引用）。

### 条文候选文本（完整版，执行时逐字落入；「2026-08-12」换执行当日）

**改动 1 —— 枚举清单 item 5 去重**（把「空数据」从 item 5 收走，避免双重归属；其余保持原文）：

```markdown
5. **edge / 错误态**：断流 / 冲突 / 二次确认 / 失败回滚（数据形状类的空 / 加载 / 0 结果 / 加载失败 → 第 7 维，单一归属、此处不重复列）
```

**改动 2 —— 枚举清单追加 item 7**（插在 item 6 的两行子 bullet 之后、`**Acceptance**：` 之前）：

```markdown
7. **数据形状维（runtime 常态数据形状）— 2026-08-12 新增**：每个数据承载视图（列表 / 表格 / 卡片流 / 图表 / 详情区）枚举以下四个形状态，每态有显式 mockup 落点或 🟡注释说明：
   - **首载 loading**：进入视图到数据可见之间的形态，**选型要声明、禁 dev 现场拍**。判据：内容结构可预知（表格 / 卡片列表）→ skeleton；结构不可预知或全页阻塞 → spinner；进度可量化（上传 / 导出 / 批量任务）→ progress。
   - **空态（从未有数据）**：说明文案（这里是什么）+ 前进路径（第一步动作的 CTA 或指引）；禁只留空白区域或一句裸 "No data"。
   - **0 结果（有数据、但筛选 / 搜索后为 0）**：与空态是**两个态**，文案必须区分——0 结果要保留筛选上下文并给「清除筛选 / 放宽条件」类返回路径；禁与空态复用同一句文案。
   - **加载失败**：错误说明 + 重试路径；反馈载体按谱系取库组件、禁自画（同源原则见 M52.3；谱系隔离见 domain-tvu M12 §反馈载体：设备侧 `Message` / web 侧 `Notification`）。

   **分工声明（与既有规则不重叠）**：本维管**常态数据形状**——视图在正常生命周期里必然经过的四个形状（未加载 / 空 / 0 结果 / 加载失败）。[`domain-tvu.md` M12](./domain-tvu.md) 管「进行中模式 × 外部异步事件」的实时告知（→ 第 6 维）；M13 管突发中断后的恢复态；[`mockup-conventions.md` M43](./mockup-conventions.md) 管数据到达后**单条内容超长**的容器溢出三态；M23 多状态流程图管「已识别的多状态**怎么交付**」、M31 管「状态在组件里**怎么建模**」——本维管「这些态**先被枚举出来**」，枚举出的态随后照 M23 / M31 交付与建模；M49 UX Principle Contract #4 只在走查时问「错误 / 空态能否继续操作」。docs 站 Status Matrix 明文不建模 runtime 态（PROJECT_GOAL §文档站），本维即其 mockup 侧的补位。
```

**改动 3 —— §M48 起手清单表 state-completeness 行的括号摘要指针化**。该行现状是维度枚举的**第二份镜像**且已经漂了（只摘了 1–5 维、漏第 6 维；今天加第 7 维它会漂得更远）。按「一个值只有一处定义」把摘要改成指针，行内其余单元格不动：

```markdown
| **state-completeness（首类实体全态，2026-06-15 升起手）** | 每个 first-class entity 按本文件 §State-Completeness Enumeration 的 **7 维枚举清单（唯一真源）** 逐维列出本任务须覆盖的状态 | … |
```

### 实证段（写进条文所在段的实证脉络，一句话即可，挂在 item 7 尾部或该 section 既有反例段旁）

```markdown
（缺口实证：PROJECT_GOAL §文档站 2026-06-10 起明文「runtime 态不在 Status Matrix 建模范围、按需触发补」——超长文本后来由 M43 补位，空 / 加载 / 0 结果 / 加载失败四态至 2026-08-12 立本维前零条文；维度级缺口的失效模式同 2026-07-13 RPS One 第 6 维反例。）
```

### Steps

- [ ] **Step 1**: Read live `docs/internal/design-process.md` §State-Completeness Enumeration（grep `State-Completeness Enumeration` 定位）+ §M48 表 state-completeness 行——确认枚举当前仍是 6 维、item 5 仍含「空数据」字样；有出入则以 live 为准调整改动 1/2 的锚点措辞，**枚举序号只追加不重排**
- [ ] **Step 2**: 落改动 1（item 5 去重）+ 改动 2（item 7 全文 + 分工声明 + 实证句）
- [ ] **Step 3**: 落改动 3（M48 表摘要行指针化）
- [ ] **Step 4**: `grep -n "枚举第 6 项\|枚举清单" docs/internal/*.md` 核既有跨引用（M12 正文 / 反例段、M48 注记）在改后语义完好——追加式加维应零破坏，凡命中「6 项/6 维」措辞处酌情改「第 6 项」为准确指称（不改语义）
- [ ] **Step 5**: `pnpm run audit:rule-inventory && pnpm run audit:stale-anchors && pnpm run audit:rule-load-map` 三绿（本 task 无新 H2、无新编号，闸应零负担；红了先查是否踩到别人的并行改动）
- [ ] **Step 6**: commit（msg 文件写入 scratchpad；`git commit -F <msg> -- docs/internal/design-process.md`，随后 `git reset -- docs/internal/design-process.md`）

---

## Task 2 (场景 A): 多断点响应式出稿纪律 —— 新 M-rule `M〈A〉` 落 mockup-conventions

**Files:**
- Modify: `docs/internal/mockup-conventions.md`（新 M-rule 正文，插在 M52 块之后、`## 实证档案（Empirical Archive）` 之前；顶部 §🤖 AI 读取指引 jump 表加一行）

### Placement 判断（先于文本）

**推荐形态 = 新顶级 M-rule，落 `mockup-conventions.md`。** 依据：

1. 无宿主机制可加项：M43 管容器内文本溢出、M17 管 surface canonical 几何、M48/M49 是过程闸——「一个页面出几档、每档什么形态」没有任何既有条文的管辖面能容纳，这是四个场景里最接近 M43 型（有触发条件 / 决策树 / 状态完整性要求的实体规则）的一个。
2. 落 mockup-conventions 而非 design-process：带编号规则的编号盲区约束（见 Global Constraints）+ 出稿断点纪律的主要执行面是 Figma mockup 帧（Path A）；code 侧的对应约束已由既有闸覆盖（`audit:layout-tokens` S3 管 @media 值 ∈ token 集合、`audit:docs-overflow` 管页级溢出），条文里写清这个分工即可，不需要 path-agnostic 落位。
3. M49 加项形态被放弃的理由：M49 合同是「每任务填一次的自查问答」，而本条含触发分档、豁免形态、逐帧自检等实体规范，塞进合同表会把 M49 撑变形；M48 起手清单只需要新增「命中本条时列一行」的常规联动（规则自身写明），不用改 M48 正文。

### 条文候选文本（完整版；`M〈A〉` 执行时替换为 `pnpm rule:next M` 所得号；日期换执行当日）

```markdown
### M〈A〉 — 多断点响应式出稿纪律（目标断点档声明 + 每档一帧或显式豁免 + 溢出自检）— 2026-08-12 新增

任何**完整页面 / 视图级**交付（新页、页面级改版、布局重排）出稿前，必须声明本次交付的**目标断点档集合**，并对声明的每一档给出一帧（或显式豁免声明）。禁止只出单一宽度的 happy-path 帧、把「窄了怎么塌」留给 dev 现场拍。

#### 触发条件

**强制触发**：交付物是完整页面 / 视图（dashboard / 列表页 / 详情页 / 设置页的新建或页面级改版）。
**跳过**：组件级微改（US-5 / US-5.S）、单弹窗、单卡片——宿主页面断点行为已有定义且本次不动布局。
**设备固定屏**（LCD / Touch Screen 等物理尺寸唯一的 surface）：天然单档，起手声明「固定屏单档」即满足本条。

#### 必做

1. **起手声明目标断点档**（进 M48 起手清单一行）：档位以 DS layout breakpoint token 的**值集合为区间边界**定义（真源 [`src/tokens/variables.css`](../../src/tokens/variables.css) §Layout breakpoints，`--bp-sm/md/lg/xl`；非 CSS 消费方经 `./tokens` DTCG / `./tokens/js` 出口读同一份值）。声明写 token 名（如「`--bp-lg` 及以上档 + `--bp-sm` 以下档」），**禁发明集合外的新档位边界**（DG-1 owner 2026-07-31 裁定「值只有一处定义、多处引用」，见 [`dg1 spec §6c`](../superpowers/specs/2026-07-30-dg1-layout-primitives-design.md)；将来断点真源迁 Figma Variables 时只换指针方向，本条不用改）。每档的**代表帧宽**从该档区间内取并在 UX 卡登记——代表宽是采样点不是新契约。默认最小集 = 桌面主档 + 一个窄档；只出一档必须写豁免 rationale（如「内部工具、仅桌面投放」）。
2. **每档一帧或显式豁免**：声明的每一档都有一帧 mockup；某档确不出帧 → 在 UX 卡显式写该档行为（如「沿用主档布局、允许流式收缩」），不留空白让 dev 猜。
3. **换形态元素显式声明**：跨档允许改变形态的元素（导航塌陷 / 表格转卡片列表 / 侧栏折叠 / 列隐藏优先级）逐个列「从哪一档起、变成什么」；未声明的元素默认各档同构、只允许流式收缩。
4. **每帧溢出自检**：每帧建完自检「任何子树右边界 ≤ 帧宽」——内容比帧宽即出稿缺陷（自检手法同 M43.2 zoom-in 截图扫 + M-INTEGRITY §I3），禁把溢出帧交付出去。

#### 分工声明

M43 管**容器内单条内容**（cell / chip / title）超长的截断三态；本条管**页面级布局**随视口档位的形态与溢出。M17 管同一 surface 内新页照 canonical 范式（几何取值）；本条管同一页跨断点档的形态差。断点档位取值真源 = variables.css §Layout breakpoints（DG-1 code-first，登记于 translation/divergences-decisions.json）——本条只引用集合、不复制数值。code 侧镜像约束已有机械闸（`audit:layout-tokens` S3：@media 值 ∈ token 集合；`audit:docs-overflow`：页级溢出）；本条是 mockup 侧的前置对应物，无需新闸。

#### Acceptance

- [ ] M48 起手清单含「目标断点档：〈token 名列表〉」一行（含单档豁免 rationale，如适用）
- [ ] 声明的每档有帧、或有显式行为声明；换形态元素逐个声明
- [ ] 每帧过溢出自检（子树右边界 ≤ 帧宽），截图或 probe 结果留 trace
- [ ] 无集合外发明的档位边界

#### 实证

- **INFRA-F113（2026-08-12，backlog.md）**：docs 站 Button 页窄档 46px 页级横向溢出（demo 卡自身宽度顶破正文列）——连 DS 自己的站都没有出稿断点纪律，且当时页级溢出闸只扫桌面两档、窄档从未进过扫描面。
- **DG-1（2026-07-31，dg1 spec §6c）**：断点 token ship 前，首方 CSS 10 处 @media 用了 7 个互不相同的宽度值——没有出稿纪律时，每个页面各自发明断点。
```

### jump 表行（加进 §🤖 AI 读取指引「触发后再读」表，插在 §M43 + §M43.2 行之后，语义相邻）

```markdown
| §M〈A〉 多断点响应式出稿纪律（目标断点档起手声明（档位边界 ∈ `--bp-*` 值集合）+ 每档一帧或显式豁免 + 换形态元素逐个声明 + 每帧溢出自检） | **交付物是完整页面 / 视图**（新页 / 页面级改版 / 布局重排）时——组件级微改与单弹窗不触发；设备固定屏声明单档即满足 |
```

### Steps

- [ ] **Step 1**: `pnpm rule:next M` 取号，记为 `M〈A〉` 实号（工具会印已用号与下一可用号；降级提示出现时先联网重跑）
- [ ] **Step 2**: Read live `mockup-conventions.md` 的 M52 块尾与 `## 实证档案` 之间区域（撰写时 M52 共用 Acceptance 约 L3098–3133）确认插入点；条文正文全文落入（替换占位号 + 执行当日日期）
- [ ] **Step 3**: jump 表加行（锚点：现表 `| §M43 + §M43.2 |` 行之后）
- [ ] **Step 4**: 核对条文内引用的相对路径与规则号现值（variables.css / dg1 spec / M43.2 / §I3 / M48——③ 号核查是 rule:next 落地三件事之一）
- [ ] **Step 5**: `pnpm run audit:rule-number-collision && pnpm run audit:rule-load-map && pnpm run audit:rule-inventory && pnpm run audit:stale-anchors` 全绿（load-map 会核新号已进 jump 表）
- [ ] **Step 6**: commit（`git commit -F <msg> -- docs/internal/mockup-conventions.md` + `git reset -- docs/internal/mockup-conventions.md`）

---

## Task 3 (场景 C): 设计阶段 a11y 自查 —— M49 UX Principle Contract #4 挂执行细则

**Files:**
- Modify: `docs/internal/design-process.md`（§M49：#4 UX Principle Contract 表之后插 §4a 子块；§M49 Acceptance 加一行；§M49 反例表加一行）

### Placement 判断（先于文本）

**推荐形态 = M49 加项（#4 行挂执行细则子表），不立新顶级规则、也不加第 7 张 contract。** 依据：

1. M49 UX Principle Contract #4 已有 **Accessibility / readability** 行（必答「层级、对比、可读性、长文本是否被处理？」）——设计阶段 a11y 自查就是这一行的执行细则，宿主现成。
2. **不加第 7 张 contract** 的理由：M49 的六合同名单（Baseline / Delta / Semantic / Feedback Inventory / UX / Simplicity / Content）至少在 3 处被 echo（design-process 流程图 ⓪.5 行、§各步的回流真源表、mockup-conventions 起手必读段）——加第 7 张会让全部 echo 同时 stale；挂在 #4 之下则六合同形状不变、零 echo 联动。
3. 无新编号、无新 H2（子块用 `#####` 层级）→ rule-inventory / rule-load-map / collision 三闸零触碰；M49 已在 mockup-conventions 起手必读段路由，不用加 jump 行。
4. designer-review 收口记录（library 文字 47 配对 + non-text 20 对全部 accept/伪影收口，零 Figma 改动）正是「token 配对免算」的合法性来源：配对已过 owner/设计师裁定，逐 mockup 重算是负功。

### 条文候选文本（完整版；插在 §M49 `#### 4. UX Principle Contract` 的表格与规则句之后、`#### 5. Simplicity Contract` 之前；日期换执行当日）

```markdown
##### 4a. a11y 设计自查（Accessibility 行的执行细则）— 2026-08-12 新增

Accessibility 行不允许只答一句笼统 evidence——按下表逐项标 ✅ / N/A + 一句证据。低成本路线：**用 DS 登记过的 token 配对 = 免算**（配对已经 owner/设计师收口，逐 mockup 重算是负功）。

| 自查项 | 判据 |
|---|---|
| **颜色对比** | 文字 / 图标的颜色配对全部使用 DS 登记 token 且组合已收口（library 文字 47 配对 + non-text 20 对的收口记录见 [`a11y-contrast-designer-review-2026-06-01.md`](./a11y-contrast-designer-review-2026-06-01.md)，逐节点明细在 [`_generated/a11y-contrast-report.md`](./_generated/a11y-contrast-report.md)）→ 标 `token-pair 免算`。**自造颜色**（本就须走 M-COLOR 例外流程）或**未收口的新 token 组合** → 必须附实算对比度数值，按 WCAG AA 判（正文 4.5:1 / 大字与 UI 组件 3:1），或显式归入收口清单已有的豁免类（disabled / placeholder，Group B 先例）。 |
| **焦点顺序** | web/SaaS 侧含表单 / 弹窗 / 多可交互控件的页面：在 UX 卡 Interaction 段标注键盘 Tab 序（一行即可），弹窗声明初始焦点 + 焦点闭环 + Esc 行为。设备固定屏（LCD 按键导航）按该 surface 的按键行为范式声明（M17），不套 Tab 序。 |
| **触摸目标** | 自建或被缩小的可点击件（非库组件默认尺寸）自查最小可点尺寸，判据引 **WCAG 2.5.8（AA, target size minimum）**——本条不抄 px 数，以 SC 原文为真源。库组件按默认尺寸 instance 的免测（库侧已收口）。 |
| **非色彩冗余** | 状态语义不得只靠颜色一个通道（icon / 文字 / 形状至少再给一个）——同 domain-tvu Trend 三层信号映射（icon + 数值色 + token）先例。 |

**分工声明**：本子表管**设计阶段**的 a11y 自查；code 侧事后链路（a11y-contrast-report / a11y-survey / CI 非阻塞 axe job）照旧，两层互补不替代。token 值本身的可达性裁定权在 owner/设计师（收口记录即真源），本子表不重开已收口的配对。
```

**M49 Acceptance 追加一行**（加在既有 checkbox 列表尾）：

```markdown
- [ ] Accessibility 行按 §4a 四项自查逐项标注（token-pair 免算 / 自造色或新配对附对比度数 / 焦点序 / 触摸目标）
```

**M49 反例表追加一行**：

```markdown
| 自造色 / 未收口的新 token 配对不附对比度数，拿「token 免算」当挡箭牌 | 违反 §4a——免算只覆盖已收口配对 |
```

> 注：第 4 行「非色彩冗余」超出 owner 已授权的三点（token 免算 / 焦点序 / 触摸目标），是撰写时基于 domain-tvu Trend 既有先例的顺势补项——**owner 不要可直接砍此行，零联动**。

### Steps

- [ ] **Step 1**: Read live §M49（grep `M49 — Design Quality Contract` 定位）确认 #4 表格、Acceptance、反例表锚点仍如撰写时形态（六合同 + `#### 5. Simplicity Contract` 紧随 #4）
- [ ] **Step 2**: 插入 §4a 子块（`#####` 层级，防被 rule-heading 正则与 H2 inventory 误捕）
- [ ] **Step 3**: Acceptance 加行 + 反例表加行
- [ ] **Step 4**: 核对候选文本里两个相对链接从 design-process.md（docs/internal/ 下）出发可达：`./a11y-contrast-designer-review-2026-06-01.md` 与 `./_generated/a11y-contrast-report.md`（以 `audit:stale-anchors` 输出为准）
- [ ] **Step 5**: `pnpm run audit:rule-inventory && pnpm run audit:stale-anchors && pnpm run audit:rule-load-map` 三绿
- [ ] **Step 6**: commit（`git commit -F <msg> -- docs/internal/design-process.md` + `git reset -- docs/internal/design-process.md`）

---

## Task 4 (场景 D): 多方案比选交付纪律 —— 新 M-rule `M〈D〉` 落 mockup-conventions

**Files:**
- Modify: `docs/internal/mockup-conventions.md`（新 M-rule 正文，位置同 Task 2 的插入区；jump 表加一行；§M24 自检反例处加一行指针）

### Placement 判断（先于文本）

**推荐形态 = 新顶级 M-rule，落 `mockup-conventions.md`。** 依据：

1. 无宿主机制可加项：design-process §交付收尾 3（Feature design-record）只管「拍板结论落哪」，不管「比选怎么出」；M24 只声明了比选场景**不触发**它自己；M46/M42.3 管 clone 纪律但明确不覆盖显式比选。比选的交付形态（同 fidelity、差异对照卡、Section 摆放、落选归档）主要发生在 Figma 交付集内 → Path A 文件是对的家。
2. 带编号规则的编号盲区约束（见 Global Constraints）也指向 mockup-conventions。
3. 拍板落点**复用**既有机制不新造：design-record 模板 §6 就叫「决策记录」（`templates/consumer-product/docs/specs/_feature-design-record.template.md`），条文直接指过去，不改模板。

### 条文候选文本（完整版；`M〈D〉` 执行时替换实号；日期换执行当日）

```markdown
### M〈D〉 — 多方案比选交付纪律（同 fidelity + 差异显式标注 + 拍板落 design-record + 落选归档）— 2026-08-12 新增

一个需求要交付 **≥2 个候选方案**（用户点名「出两个方案 / Plan A、Plan B」，或 AI 判断存在正交设计路线值得比选）时，**比选本身是交付物**，按下面四条出。禁止「主推方案画完整、陪跑方案摆姿态」——那不是比选，是既成事实包装。

#### 必做

1. **同 fidelity**：各方案同保真度、同 surface、同 sample data、同状态覆盖深度（M48 起手清单一套口径跑 N 遍）。A 高保真 + B 线框 = 比选被保真度差污染，评审者永远选画得全的那个。
2. **差异点显式标注**：交付集含一张**差异对照卡**（M23 规则卡样式）：逐差异点列「Plan A 怎么做 / Plan B 怎么做 / 影响（用户价值 · 实现成本 · 风险）」；两案共有部分不进对照卡。
3. **摆放**：同 page 内各方案独立具名 Section（名带 `Plan A` / `Plan B` 标识）并列摆放，共用同一份 Context Section（PRD / Jira / 差异对照卡）——Section 组织与画布左右序按 design-process ①（对照物在左、交付集在右）与 M45 惯例。
4. **拍板闭环**：owner/PM 拍板后——(a) 结论 + 落选理由记入该需求 design-record **§6 决策记录**（design-process §交付收尾 3；至少一句「为什么不选 X」）；(b) 落选 Section 重命名加 `❌ Not Selected` 前缀、**默认不删**（保留追溯；确要清场由 owner 显式指令，按 M42.3 / M46 清理纪律走）；(c) 选中案进入常规交付闭环。**拍板前两案地位平等**——AI 不得把自己推荐的方案画得更全（推荐可以给，写在差异对照卡的影响列，不体现为保真度差）。

#### 分工声明

M24 管「已有 code 还原效果图」的前置 mapping，其自检反例已明确「画 Plan B 对比方案」**不触发** M24——本条即该场景的正面管辖规则。M42.3 禁的是**迭代时旁路克隆并行集**（无对照卡、无拍板闭环的隐性重复）；本条的多方案 Section 是显式比选交付，区分标志 = 有差异对照卡 + 有拍板闭环 + 落选案有归档去向。拍板结论唯一落点 = design-record §6（M23.11 (B) 终态优先：Figma 卡不写沿革，沿革归 design-record / handoff）。

#### Acceptance

- [ ] 各方案 M48 清单同口径（同 locked spec 集 + 同 state-completeness 枚举维）
- [ ] 差异对照卡存在且逐差异点三列齐（Plan A / Plan B / 影响）
- [ ] 拍板后 design-record §6 有结论 + 落选理由；落选 Section 已标 `❌ Not Selected` 且未被擅删
- [ ] 无保真度差（一案高保真一案线框即不合格）

#### 实证

- **2026-05-11 SaaS Dashboard "Cost per Token Trend" 2-option PM review**（[`retrospection`](../_archive/retrospection/2026-05-11-saas-dashboard-2-option-pm-review.md)）：2 方案 PM 评审实践先行、无条文约束交付形态——本条即此回流。
- **M24 自检反例**：「Plan A 已有 Figma，画 Plan B 对比方案」长期只有「不适用 M24」的排除声明、没有「适用什么」的正文——比选场景反复出现而无规则家。
```

### jump 表行（加进「触发后再读」表，插在 §M46 行之后，语义相邻：都管交付集组织）

```markdown
| §M〈D〉 多方案比选交付纪律（两案同 fidelity + 差异对照卡 + 拍板落 design-record §6 + 落选 Section 标记不删） | **一个需求要出 ≥2 个候选方案**（用户说「出两个方案 / Plan A、Plan B / 比选」，或 AI 判断存在正交路线值得比选）时 |
```

### §M24 自检反例处的一行指针（在 M24 既有「误触发反例」清单中「画 Plan B 对比方案」条目处追加指路，防死胡同）：

```markdown
（该场景的正面管辖规则 → §M〈D〉 多方案比选交付纪律）
```

### Steps

- [ ] **Step 1**: `pnpm rule:next M` 取号，记为 `M〈D〉` 实号（若 Task 2 已执行，工具自动给下一号）
- [ ] **Step 2**: 条文正文落入（插入区同 Task 2：最后一个 M-rule 块之后、`## 实证档案` 之前；若 Task 2 已落 `M〈A〉`，本条排它后面）
- [ ] **Step 3**: jump 表加行（锚点：现表 `| §M46 |` 行之后）
- [ ] **Step 4**: §M24 自检反例条目处加指针一行（grep `Plan B` 定位；只加指针不改 M24 判据）
- [ ] **Step 5**: 核相对路径（retrospection 归档路径从 docs/internal 出发 = `../_archive/retrospection/…`）与所引规则号现值（M23 / M23.11 / M42.3 / M45 / M46 / M48 / design-record §6 模板段名）
- [ ] **Step 6**: `pnpm run audit:rule-number-collision && pnpm run audit:rule-load-map && pnpm run audit:rule-inventory && pnpm run audit:stale-anchors` 全绿
- [ ] **Step 7**: commit（`git commit -F <msg> -- docs/internal/mockup-conventions.md` + `git reset -- docs/internal/mockup-conventions.md`）

---

## owner 可推翻点（执行前过目即可，不阻塞开工）

1. **Task 1 改动 3（M48 摘要行指针化）**：也可以选「保留摘要 + 补第 6/7 维关键词」，但那是维护第二份镜像（该镜像已实证漂移一次：漏了第 6 维）——推荐指针化；owner 若要保留摘要形态，执行时改为补全关键词即可。
2. **Task 2 档位定义采用「token 值为区间边界」而非「帧宽 = token 值」**：断点是阈值不是帧宽（手机代表宽本就不在 `--bp-*` 集合里），故条文禁的是**发明新边界**、允许档内取代表帧宽。若 owner 想更严（帧宽也钉死枚举），改必做 1 的措辞即可。
3. **Task 3 第 4 行「非色彩冗余」**：超出 owner 授权三点的顺势补项（有 domain-tvu Trend 先例背书），可直接砍、零联动。
4. **Task 4 落选方案「默认不删、标 `❌ Not Selected`」**：也可以走「移到归档 page」路线；推荐就地标记（保留与 Context/对照卡的相邻性，且不触发 M45 新建 page 联动）。
5. **A / D 两条新规则的落位文件**：按编号盲区实测（rule-next / collision 闸都不扫 design-process.md，M52 撞号事故为证）推荐落 mockup-conventions.md；若 owner 认为 D 应 path-agnostic 落 design-process，需接受该条脱离编号工具管辖（改用具名无号 H2 + 手工更新 rule-inventory 块）。
