# 处方 — 视觉风格轴的**规则缺失**：两条轴真实存在，规则真源 0 条在管

- 日期：2026-08-28 · 出处：[`reports/2026-08-28-variant-axis-coverage.md`](../reports/2026-08-28-variant-axis-coverage.md)
- 量具：[`metrics/variant-axis-coverage.mjs`](../metrics/variant-axis-coverage.mjs)（6 条内建控制，已造故障验证）
- 读数 pin：**`b3728d38`**（P6；⚠️ DS 侧执行时按当时 HEAD 重核行号）
- 执行方：**DS 侧 session 在真源执行。lab 只出处方（§1 只读边界），⛔ 未改 DS 任何文件。**
- **执行状态**：🟡 **DS 解决了病灶，但走的是另一条路 —— ⛔ 既不是「已执行」也不是「未执行」** —— 亲验 pin **`7fcca917`**（2026-09-01，`lab:N93`）：
  - 🟢 **规则确实入册了**：`docs/internal/component-affordances.md:159`（SelectBoxLine）与 `:266`（InputBoxLine）逐字「⛔ 同一表单 / 面板内不要 line 与 filled 混用 —— 一个界面只有一个主题，混用即为 bug」；`docs/internal/STATUS-CHANGELOG.md:460` 记「owner 当轮裁定，repo **两处 SoT** 已落地」
  - ⚠️ **但口径 ⛔ 不是本处方 §3.1.1 拍定的 A**：DS 落的是 **owner 2026-08-31 另行裁定的「族由主题定」**（深色主题 `:root` 用 filled、浅色主题 `[data-theme="light"]` 用 line）。⇒ 它比口径 A **更强**（给出了判定依据而非只要求「一致」），但**两者是不同的规则**，⛔ 不得把本处方读成「口径 A 已落地」
  - 🔴 **§3.1.2 的落点与形式未兑现**：处方要求「落 `docs/internal/mockup-conventions.md` 新起一节 **§M-VARIANT**，**必须配 `**Acceptance**` 段** —— 否则它不进对位闸的分母，等于白写」。全仓检索 `M-VARIANT`（⛔ 未截断）命中的**全部是既有的 `M-VARIANT-CONSISTENCY`**（多变体像素对齐，与本处方无关）⇒ **§M-VARIANT 未建、⛔ 无 Acceptance 段** ⇒ 按处方自己的判据，这条规则**不在对位闸分母内**
  - 🔴 **§3.2 挂闸未执行**：`scripts/` 下 ⛔ 无 variant/axis 相关闸脚本
  - 🔴 **§3.3 的 `Pagination` 结构性混用源原样存在**：`src/components/Pagination/Pagination.vue:53` `const isSmall = computed(() => props.type === 'Small')` · `:60` `const pageButtonStyle = computed(() => (isSmall.value ? 'rimless' : 'filling'))` ⇒ §2.3 那条「默认 Pagination + 默认 Input = 开箱即混用」**未被处理**
  - ⚠️ **顺带一条命名撞车风险（新查，⛔ 不在原处方内）**：若日后按 §3.1.2 补建 `§M-VARIANT`，它会与既有的 `M-VARIANT-CONSISTENCY` 在检索与引用上高度混淆 ⇒ **建议改名**（如 `§M-VARIANT-AXIS`），⛔ lab 不代拍
- **执行状态重取**：@`d07be8e9`（2026-09-11 §30 全量重取）—— 🔴 **登记的两条 🔴 都已不成立，且其中一条执行者是 lab 自己**：
  - **§3.1.2 落点 🟢 已建** —— `docs/internal/mockup-conventions.md` 现取有 **`§M-VARIANT-AXIS`**；⚠️ 它与既有 `§M-VARIANT-CONSISTENCY` **刻意不同名** ⇒ 本处方登记的那条撞名风险**已被避开**。
  - **§3.2 挂闸 🟢 已执行** —— `scripts/audit-mockup-variant-axis.mjs` + `scripts/mockup-rules/variant-axis.mjs` 现取都在（D2 形态：无自有挂载，经 `audit-mockup-conformance.mjs` 总闸挂载）。
  - 🔴 **执行者 = lab 自己，⛔ 不是 DS** —— `round2-status` **N93.3**（2026-09-02）逐字「lab 首次行使 §1.1 新边界直接写 DS master」。
    ⇒ 按 `AGENTS §2.6`：`spec:§13.1` 的因子 1 自 2026-09-01 起恒 1、**不携带信息** ⇒ ⛔ 这一条不得读成「DS 采纳了本处方」。
    ⚠️ 而本栏写于 2026-09-01、**次日就被 lab 自己推翻**、栏位没跟着改 ⇒ `lab:N56`（知情面分叉）+ `lab:N49 推论四`（谓语错）同时成立。
  - **§3.3 `Pagination` 结构性混用 🔴 仍在** —— `src/components/Pagination/Pagination.vue:53`@`d07be8e9` 逐字 `const isSmall = computed(() => props.type === 'Small')`。
    ⚠️ ⛔ 但这一条**已由 owner 裁定「保留」**（`round2-status` **N93.2**，2026-09-01 逐字答「保留」）⇒ **⛔ 不是待做项**。
- 入口：owner 2026-08-28 逐字提问「我刚刚用设计系统来设计表单，发现了在同一个表单里面，
  同时出现 Line 和 Filled 的表单组件，**这类的设计漏洞目前有没有闸口检查**？
  在初始设计的时候应该就需要考虑到这一层吧？其他同类的视觉统一也需要考虑在内，
  做完之后也该自动设计走查一下，很容易就发现了」

---

## 0. 一句话

**⛔ 不是「规则写了、闸没跟上」，是「规则从来没写」。**

DS 里真实存在 **2 条**表单风格轴（`Input` / `Select`，各为 `'line' | 'filled'`，**默认都是 `line`**），
规则真源里「不得混用 / 唯一口径」类规则共 **6 条**，其中管住这两条轴的：**0 条**。

⚠️ **这一条与 [`2026-08-27-designqa-gate-gap.md`](./2026-08-27-designqa-gate-gap.md) 病因相反。**
那份处方 §0 逐字写着「⛔ 不是『规则真源缺失』，是『规则写了、闸没跟上』」——
owner 这次撞上的，恰好是它明确排除掉的那一种。⇒ **两份处方不可互相引用作已覆盖。**

---

## 1. 🔴 先纠正提问里的一个前提（lab 实测）

| 提问里的判断 | 实测 | 依据 |
|---|---|---|
| 「这类的设计漏洞**有没有闸口检查**」 | ⚠️ **问题问小了** | 不只是没闸 —— **没有任何规则可供闸去检查**。闸只能检查已写下的规则，⇒ 现在就算挂闸也无据可依 |
| 「**初始设计的时候**应该就需要考虑到这一层」 | ✅ **成立，且 DS 自己也没考虑** | 见 §2.3：`Pagination` 内部按尺寸硬切风格轴 ⇒ **开箱即混用**，不需要设计师犯错 |
| 「做完之后也该**自动设计走查**一下」 | ✅ 有现成落点 | `audit-mockup-conformance.mjs` 是规则模块引擎，`geometry-consistency` 即 2026-08-24 作为模块挂上去的 ⇒ 加一条模块不必新起链 |
| 「**很容易就发现了**」 | ✅ 且这正是问题所在 | 人眼一眼可辨的东西零自动覆盖 ⇒ 属于「靠人自觉」的那一档，漏了没有任何东西会说话 |

---

## 2. 实测证据

### 2.1 A 面 —— 风格轴真实存在（判据级，源码枚举 + 剥注释）

| 组件 | 轴取值 | 默认值 | 位置 |
|---|---|---|---|
| `Input` | `line` / `filled` | **`line`** | `src/components/Input/Input.vue:8` |
| `Select` | `line` / `filled` | **`line`** | `src/components/Select/Select.vue:9` |

配套 token：`--input-line-border` / `--input-filled-bg`（`code-conventions.md:801` 提到前者）。

### 2.2 B 面 —— 规则真源里「不得混用」这一族共 6 条，无一条管风格轴

已有的同族规则覆盖的是**别的**维度：

| 规则 | 管的是 |
|---|---|
| `mockup-conventions.md` C4 — Brand color vs M23 annotation accent 不能混用 | 颜色（🟢 已上闸） |
| `mockup-conventions.md:942` 同一份交付内不混用 navy 版与浅色版 | 主题 |
| `mockup-conventions.md:2162` 选中元素样式混用（1/2/3/4-side） | 选中态边框 |

⚠️ `audit-mockup-colors.mjs:53` 头注释逐字写着「不混用 navy 与浅色 …… **本闸不管**」——
连已入册的主题混用规则都还没闸接。

### 2.3 🔴 `Pagination` 是**结构性混用源**（不是设计师失误）

```js
// src/components/Pagination/Pagination.vue:59
const pageControlVariant = computed(() => (isSmall.value ? 'line' : 'filled'))
const pageButtonStyle    = computed(() => (isSmall.value ? 'rimless' : 'filling'))
```

传给内部控件（`:variant="pageControlVariant"`，第 169 / 232 行）。

⇒ **一个默认 `Pagination`（`type` 非 `Small` ⇒ 内部 `filled`）+ 若干默认 `Input`（`line`）
＝ 开箱即混用。** 设计师什么都没做错，混用是 DS 默认值组合的必然结果。
⚠️ 这条直接支持 owner 那句「初始设计的时候应该就需要考虑到这一层」——**DS 自己就没考虑**。

### 2.4 对位结论与阴性对照

| 轴 | 有规则管吗 |
|---|:-:|
| `Input` `line/filled` | 🔴 **无** |
| `Select` `line/filled` | 🔴 **无** |

**⚠️ 阴性对照（⛔ 否则 `0/2` 与「对位器恒返 0」不可区分）**：
往 `mockup-conventions.md` 注入一条含 `line`+`filled` 的假「不得混用」规则后，
唯一性规则窄尺 **6 → 7**、对位 **0/2 → 2/2**；`git checkout` 还原后回到 6 / 0-of-2，pin `dirty=0`。
⇒ 对位器有判别力，`0/2` 是**真实空缺**。

---

## 3. 处方

### ⚠️ 3.0 顺序不可反

**规则入册（3.1）必须先于挂闸（3.2）。** 反过来做会造出「闸自陈说管了、实际无据可依」的形态 ——
正是体检报告第四节点名的问题，也是 `acceptance-coverage-dump` 三态里 `gated` 那档的已知弱点
（🟢 的凭据只是闸的一行自陈）。

### 3.1 第一步：规则入册 —— ⚠️ 但先要 owner 拍一个设计决策

**不能直接写「同一交付内表单风格轴必须唯一」** —— 那条规则一写下去，
`Pagination`（§2.3）当场违规。三个可选口径，**成本与彻底度递增**：

| 口径 | 规则怎么写 | 代价 | 适合 |
|:-:|---|---|---|
| **A** | 「同一交付内**直接放置**的 `Input`/`Select` 其 `variant` 必须一致；复合组件内部控件豁免」 | 最小，只写规则 | 想先止血 |
| **B** | 「同一页面内所有表单控件风格轴唯一」+ 改 `Pagination` 让内部控件**继承页面级风格** | 要改组件 | 想真正统一 |
| **C** | 引入**页面级 form variant context/token**，所有表单控件默认继承，显式传值才覆盖 | 最大，动 API | 长期正解 |

**lab 的推荐：先 A 后 C。** 理由：A 能立刻让闸有据可依（本处方的目标），
且 A 的规则文本是 C 的子集 —— 先 A 不会造成返工。⛔ **B 是三者里最尴尬的**：
改了组件却没解决「谁来声明页面级风格」这个根问题，下次加新表单组件同样要再改一遍。

⚠️ **这是设计决策，不是技术决策 ⇒ 归 owner 拍，lab ⛔ 不代拍。**

### 🟢 3.1.1 owner 拍定（2026-08-28 当日）

**口径 = A。** ⚠️ **拍定理由如实记录**：owner 直接选定 A，**未另述理由** ⇒
采纳的是上方 lab 推荐理由（代价最小；规则文本是 C 的子集，先 A 不返工）。
⛔ 别把这条读成「owner 独立论证了 A 优于 C」—— 他做的是**采纳**，不是**论证**。
⇒ 若日后要升到 C，⛔ 不必视为推翻 owner 决定（A 本就是 C 的子集，是路径不是终点）。

### 3.1.2 规则文本草案（DS 侧可直接抄，行号按当时 HEAD 定）

落点：`docs/internal/mockup-conventions.md` 新起一节 §M-VARIANT。
**必须配 `**Acceptance**` 段** —— 否则它不进对位闸的分母，等于白写
（这正是那 67 条 `unclassified` 的形成机理）。

```markdown
#### M-VARIANT — 表单控件风格轴唯一（2026-08-28 新增）

**规则**：同一份交付内**直接放置**的表单控件（`Input` / `Select`），
其 `variant` 风格轴取值必须一致。

**范围边界**：
- ✅ **计入**：设计稿/页面里直接摆放的 `Input` / `Select`，
  **含未显式传值、走默认 `line` 的**（⛔ 默认值恰恰是混用主来源，不能只看显式传参）
- ⛔ **豁免**：复合组件（如 `Pagination`）**内部**的控件 —— 其风格由该复合组件自洽决定
  ⚠️ 这是**豁免，不是「那里不会混」**：`Pagination.vue:59` 按 `type==='Small'`
  硬切 `line`/`filled`，非 Small 时内部为 `filled`。⇒ 页面级看上去仍可能同时
  出现两种画法，这是**已知且被本口径接受的**（口径 A 的代价，见处方 §3.1）

**Why**：两种画法混在同一表单里没有语义差别，只有视觉噪声 ——
读者会误以为「画法不同 ⇒ 这两个字段性质不同」。
⚠️ 且它**不需要设计师犯错就会发生**：DS 默认值组合本身即可产生混用。

**Acceptance**：
- [ ] 同一交付内所有直接放置的 `Input`/`Select`，其 variant 取值集合大小 = 1
- [ ] 未显式传 variant 的控件按默认值 `line` 计入
- [ ] 复合组件内部控件已排除，且排除依据是**组件边界**而非人工逐个判断
- [ ] 交付说明里写明本页选用的是哪一个风格轴
```

⚠️ **`**Acceptance**` 必须顶格写**（闸的 `ACC_RE` = `/^(?:\*\*Acceptance|#{2,6}\s+.*Acceptance)/`，
缩进或写成列表项会漏进那 8 条结构性盲区 —— 见 `acceptance-coverage-dump` 的盲区面）。

### 3.2 第二步：挂闸

挂到 `audit-mockup-conformance.mjs` 的规则模块（与 `geometry-consistency` 同路径），
**⛔ 不新起链**。判据形态建议：

- 扫一份交付里所有 `Input`/`Select` 的 `variant` 实际取值（含**未传值时的默认**，⛔ 不能只看显式传参 ——
  默认值恰恰是混用的主要来源）
- 同一交付内出现 ≥2 种取值 ⇒ FAIL，报出逐个位置
- **复合组件内部控件豁免**（口径 A 已拍定，§3.1.1）。
  ⚠️ 豁免的判定必须走**组件边界**（是不是某复合组件的后代），
  ⛔ 不得靠「组件名白名单」—— 白名单会在加新复合组件时静默失效，
  且失效方向是**放行**（fail open），正是 §3.1 最该避免的形态

闸脚本头注释按 [[INFRA-F126]] 写挂载真源，并加 `covers-acceptance:` 行指向 §M-VARIANT 的 Acceptance 段。

### 3.3 顺带（同型、同一次改动成本低）

`Pagination` 第二条硬切轴 `pageButtonStyle`（`rimless` / `filling`，第 60 行）是**同型问题**，
建议在 3.1 定口径时一并覆盖，⛔ 别只治 `variant` 一条。

---

## 4. 验收标准

- [x] owner 已拍定口径（**= A**，2026-08-28）。
      🔴 ⚠️ **本条原文写「⛔ 不接受『按推荐』而无理由」，而实际情形恰恰是「采纳推荐、未另述理由」。**
      ⇒ **原判据过严，当场改判**：拍板者有权直接采纳，「采纳」本身就是一种合法决定。
      已在 §3.1.1 如实登记为**采纳**而非**论证**。
      ⛔ 不把它记成「满足了原判据」—— 判据被改了，留痕不删（§2.11）。
- [ ] `mockup-conventions.md` 新增 §M-VARIANT，**含 `**Acceptance**` 段**，
      且 `node scripts/audit-acceptance-gate-coverage.mjs --json` 的 `sections` 计数 **+1**
      （⛔ 不接受「已添加」式断言，AGENTS §4）
- [ ] 新规则段在对位闸里的三态 = `gated`（⛔ 不是 `unclassified`）—— 即闸已自陈覆盖它
- [ ] 闸能拦住真故障：构造一份同时含 `variant="line"` 与 `variant="filled"` 的交付，
      闸 **exit≠0** 且报出两处位置。⚠️ 取退出码 ⛔ 不接管道（zsh 下 `${PIPESTATUS[0]}` 是空的，§3.9）
- [ ] 阴性对照：一份全 `line` 的交付，闸 exit=0 ⇒ 证明它不是恒红
- [ ] lab 侧复验：`node metrics/variant-axis-coverage.mjs --pin <新 pin> --self-check`
      的对位从 **0/2** 变为 **2/2**

---

<sub>lab 只读边界：本处方全部读数取自 pin 住的只读 worktree `~/.ai-ds-lab/pins/tvu-ds-b3728d38`，
⛔ 未读 DS 工作区、⛔ 未写 DS 任何文件。</sub>
