# Feature Design Record — Power Control on RPS Link (New) (RPSV3-134)

- **JIRA**：[RPSV3-134](https://tvunetworks.atlassian.net/browse/RPSV3-134)（Task · Medium · assignee Lotus Chen · reporter Nancy Zeng）
- **Slack 来源**：[thread](https://tvunetworks.slack.com/archives/C080LLVJ0S2/p1787952625306489)（2026-08-29 Kalpesh Dave 提出 → Trevor Yao 确认可做并点名 Nancy）
- **Scope**：RPS Link (New) 本地 Web GUI（Figma `TVU-RPS-20220302` / `pUE6UakxCpU6Dw9vUCtBBe`）
- **Designer**：Nancy Zeng　**Status**：🟢 **已评审**（Kalpesh「looks OK」→ Trevor 复核提出 `reconnect` 歧义并给出定稿，thread 内已 ok_hand；设计稿已按定稿更新，见 §11）　**日期**：2026-08-31（视觉返工 + 文案定稿 2026-09-01）
- ⚠️ **§10 视觉一致性返工 与 §11 文案更新均未回 Jira / Slack 告知**（owner 决定不补）

## 1. 问题与根因

RPS Link (New) 的本地 Web UI 没有任何电源控制入口，重启或关机必须到设备物理位置操作。机房与转播车部署下运维成本高。上一代 Config T 本地 UI 早已提供 Power 分区 ⇒ 新版 UI 属**能力回退**，不是新功能诉求。

## 2. Scope & Non-goals

- **做**：Home 页新增 Power 行（Reboot + Power Off）+ 两个破坏性确认态 + 两个执行中态 + **一个重启失败终态**，共 **6** 张 1920×900 全景效果图，按**两条分支**排布（见 §14）。
- **不做**：Mobile 版式（同 page 有 4 个 375×667 帧，owner 拍板本轮不含）· Ethernet / Modem Settings 页不动 · 按 PID 选择重启目标 · 按钮禁用前置条件（owner 未提需求）· **Power Off 的同等失败态**（2026-09-01 owner 拍板不做，理由见 §13.3——这是显式决定，不是遗漏）。

> ⚠️ 本节 2026-09-01 已随 §14 更新。原文写「共 5 张」且把「重启失败反馈」列在不做里，两处均已 stale。

## 3. Baseline 参考（M21 sibling 视觉契约）

- **基线帧**：`6260:4239`（`RPS Link/Home`，page `New RPS Link (New) 20260312` / Section `6260:4099`，属 **Release to QA** 分类）
- **实测规格**（M32.6，全部取自 live probe 而非参考图推断）：

| 项 | 实测值 |
|---|---|
| 帧 | 1920×900，VERTICAL gap 24，fill 绑 `Color Type/Background/Layer_2`（#1f1f1f） |
| Content 卡 | 1200×480，VERTICAL gap 20，pad 32/40，radius 4，fill 绑 `Color Type/Background/Layer_1`（#141414），**primaryAxisSizingMode=AUTO** |
| 内容块 | gap 16，Form Item 高 32、间距 48，含库 `Slice Line` 分隔线 |
| 分区标题范式 | textStyle `Roboto/24 large title` + fill 绑 **`Color Type/Text/Text_1`**（实测 sibling `6260:4289`） |
| 卡内剩余垂直空间 | 189px（内容下方）⇒ 加 Power 分区需 ~194px，靠 AUTO 高度自增解决 |

- ⚠️ **该文件订阅了 `TVU UX Design System`**（`teamLibrary.getAvailableLibraryVariableCollectionsAsync` 实测，Q29 唯一合法判据），并另挂 `Nancy's Design Assets` 个人库。不是 legacy 无库文件 ⇒ 新建元素必须绑 token。

## 4. 设计 — 状态与指示模型

Power 置于 Content 卡内容行容器 `Frame 2007` 末尾，以库 `Slice Line` 与上方 CPU 行分隔 —— **它是信息网格的第六行，不是一个自带标题与底色的分区**（⚠️ 2026-09-01 返工后的形态，见 §10）。

行结构完全沿用本页 Form Item 范式：行 1120×32、HORIZONTAL、gap 8、counterAxisAlignItems CENTER；左列 `option name` 200×32 内含 `Power:` 14px Roboto Regular 绑 `UX/Grey/grey-6 #9E9E9E`；右列 pad 0/12/0/12、gap 16，⇒ 按钮起点与其它行的取值列同在 label 起点 +220 处。行本身**无 fill**。

| # | 状态 | 表达 |
|---|---|---|
| 1 | Default | 两个按钮可用；卡高维持 **480**，与基线帧一致（返工前为 537） |
| 2 | Reboot confirm | 全屏 scrim + `Notification dark/pop confirm/danger`，描述 3 行，按钮 `Cancel` / 红色 `Reboot` |
| 3 | Power Off confirm | 同一变体，描述 3 行（多一句现场开机警示），按钮 `Cancel` / 红色 `Power Off` |
| 4 | Rebooting | 全屏 scrim + `Notification dark/alert/info`，标题 `Rebooting`，**告知传输将在重启后自动恢复** |
| 5 | Powering off | 同变体，标题 `Powering Off`，告知需现场手动开机 |
| 6 | **Reboot failed** | 全屏 scrim + `Notification dark/alert/**error**`，标题 `Reboot failed`，告知传输仍中断并**提示稍后重试**（2026-09-01 新增，见 §13 / §14） |

**弹窗高度随文案 wrap 变化**，居中坐标（`y = (900 − h) / 2`）必须按**实测**高度重算，不能沿用按默认高度算出的 y —— I7 类问题，首交付踩到一次、2026-09-01 换文案后再次触发。当前实测：

| 弹窗 | node | h | y |
|---|---|---|---|
| Reboot confirm | `6562:2005` | 187（原 166） | 357（原 367） |
| Power Off confirm | `6562:2025` | 187 | 357 |
| Rebooting | `6562:2039` | 174（原 153） | 363（原 374） |
| Powering off | `6562:2072` | 174 | 363 |
| **Reboot failed** | `6656:803` | 174 | 363 |

> ⑥ 的正文比 ④ 长 22 字符，但实测仍是 2 行 wrap ⇒ 高度不变（174），居中 y 沿用 363。**这是实测结论不是假设** —— 写完文案后回读 `instance.height` 才确认，没有沿用「改文案必变高」的预判。

## 5. UX 交付

- **产品 UI 文案（设备语言单语 EN，M33）**：`Power` / `Reboot` / `Power Off`（沿用 Config T 现成措辞，非自造 `Shutdown`）；destructive 按钮用具体动词而非泛化 `Confirm`。
- **注释层（双语，M23）**：**6** 张状态标签卡（what + why + invariants；2026-09-01 补第 ⑥ 张）+ PRD 卡 + UX Delivery 卡 + User Journey Map 卡 + 参考物去向卡。字号层级按 M23.14(B)：卡标题 18/16、meta cyan 12、段名 Medium 15 cyan、正文 13/11 + ZH opacity 0.45；行距 per-bullet（EN lh = 字号×1.4，对间 itemSpacing 8，段间 24）。
- **主题探测（M23.18/M23.18.1）**：基线帧 fill #1f1f1f → luma 0.122 < 0.25 → **dark** ⇒ 注释卡用 navy `#2B2D42` + cyan `#33A4FD`；page 画布底色设 `0.418`。
- **组件来源**：

| 元素 | 来源 |
|---|---|
| **两个按钮** | **DS 库 `Button/dark M`** SET `6520:4115`（remote，setKey `12b38c8b68cd67dd3978e0d68712bc646f1e97c1`）→ 变体 `icon=left, style=ghost, color=gray 1, status=default, radius=square, fixed width=no`（variantKey `57a27949fb0e9c057a94d8d6f374f34e9f67e1d9`，基准 94×32）。⚠️ 返工前是自画 FRAME，见 §10 |
| Reboot / Power Off 图标 | **Config T ( Local UI ) 库**：`Icon/Reboot` `1e34b0e80bfeb85702f148e5e31c8b2664a775e8` / `Icon/Power OFF` `03daf29f9fd987ac16474a70c07976932d300d07`；**swap 进 Button 的 icon slot**（16×16，非返工前的 28×28），fill 绑 `UX/Grey/grey-5 #CCCCCC` 与按钮 label 同色 |
| 确认弹窗 | 库 `Notification` SET `23535120dbdc93bf90af9a118b5091e58b590dc6` → `theme=dark, form=pop confirm, type=danger`（480×145 基准） |
| 执行中提示 | 同 SET → `theme=dark, form=alert, type=info`（480×153 基准） |
| **失败提示**（2026-09-01） | 同 SET → `theme=dark, form=alert, **type=error**`（变体 `6562:1698`）；图标须显式 `swapComponent` 到 `icon/Message/Error 2`（key `a7f4f0343bd0ba1dba346e566bfc0d81ddc2d0af`）—— **切 `type` 不会自动换图标**，克隆来的实例带着把图标钉在 `Info 2` 的 instance-swap override |
| scrim effect | 库 effect style `Mask blur8  #000-60%` `38f743fa80fa9e183abe9363dd1a85608acbae95`（BACKGROUND_BLUR:8），fill 另设黑 20% |
| 分隔线 | 文件内既有 `Slice Line` instance（clone） |
| Jira 需求组件 | 本文件 `Jira requirement`，clone 自 `6379:108`；**实为 remote 库组件**（mainComponent `Priority=High`，key `8fed268d…`）—— ⚠️ 订正 FB-10040 记录里"file-local"的说法 |

### 本文件专属取值（供下轮免重查）

- **DS 库 token 映射（实测解 alias 到底值）**：`Background/Layer_1` = #141414 · `Layer_2` = #1f1f1f · **`Layer_3` = #262626** · `Layer_4` = #353535 · `Text/Text_1` = #f8f8f8 · `Text/Tips` = #9e9e9e · `Line/Light Divider` = #434343 · `Text/Heading & Button` = **#ffffff**（⚠️ 与页面标题实际用的 #f8f8f8 不同，标题应绑 `Text_1` 而非 `Heading & Button`）
- **DS 库无电源类图标**：644 icon 全量枚举 + 渲染核实排除 `Setting/standby`（月亮+Z=睡眠）、`others/electric 1`（闪电）、`electric 2`（电池）；`load/refresh` 形态确为循环箭头可代 reboot，但本轮按 owner 决定统一用 Config T 图标。

## 6. 决策记录

| # | 决策 | 依据 / 拍板人 |
|---|---|---|
| 1 | 页内 Power 分区，不是新增顶部 tab | owner 拍板（Kalpesh 原话说 "Reboot tab"，但参考效果图是页内分区，owner 选后者） |
| 2 | 落在 **Home** 页而非 Ethernet / Modem | owner 拍板；设备级操作与设备级信息同页，且 Home 卡下方本就有空白 |
| 3 | 作用于**整机（所有 PID）**，无 PID 选择器 | owner 拍板；Home 页显示 4 个 PID，若不锁定会引入选择器 + 边界规则 |
| 4 | 本轮仅 Web，不做 Mobile | owner 拍板 |
| 5 | 图标从 Config T 库 import | owner 拍板；DS 库实证无电源图标，跨库依赖已知并接受 |
| 6 | 关机文案写明需现场开机，重启文案不带此句 | owner 提供领域事实：**关机需现场开机，重启不需要** |
| 7 | 不做折叠箭头（参考图有） | 我的判断：Home 无任何可折叠分区，为 2 个按钮引入孤例违 M49 一致性 + 简约 |
| 8 | ~~标题 Roboto SemiBold 24，非参考图 Helvetica Bold 22~~ **⛔ 2026-09-01 推翻** | 所谓"本地范式"取自 `effectivelyVisible=false` 的节点（`6260:4261` 父级隐藏 / `6260:4289` 整卡隐藏）；本页渲染中**零个可见 24px 标题** ⇒ 改为不设标题。见 §10 |
| 9 | 按钮 label 14px，非参考图 12px | 参考图是 Helvetica 体系，本页正文体系为 Roboto 14。（返工后由 `Button/dark M` 组件自带 14px，结论不变） |
| 10 | 参考图的虚线框 + 灰字**不采纳** | M37 probe 发现那是**禁用态**（图层名 `Button/Disable`，dash 6/6 + #969696），默认态必须表现为可用 |
| 11 | 执行中提示保留组件自带 `OK` 按钮 | footer 不是 SLOT，替换需 `detachInstance()` 会破坏 M32 库连接；保留是较小偏离 |
| 12 | 两个弹窗共用 `pop confirm/danger`，仅文案不同 | M49 同 semanticRole 同视觉词汇 |
| 13 | **Power 做成信息网格的第六行**，不是带标题与底色的分区 | owner 拍板（2026-09-01）；基线卡内零标题、零 fill 子容器 ⇒ 分区形态必然成为孤例 |
| 14 | **按钮改用 DS 库 `Button/dark M`**，废弃自画 FRAME | consumer-product-conventions Rule 6（DS canonical 复用是开建硬约束）；该组件的 instance 本就在同一帧的 Ethernet 卡内 |
| 15 | **Default 态两个按钮同为 `color=gray 1`**，Power Off 不给红 | owner 拍板；破坏性操作在触发前保持中性（B7），红色留在确认弹窗内 —— 弹窗已是 `danger` |
| 16 | **保留 Slice Line 分隔线** | owner 拍板；基线页 PID 下方本就有一条同款 Slice Line，非孤例 |
| 17 | 图标 fill 绑 `UX/Grey/grey-5 #CCCCCC` 与 label 同色 | swap 进来的 Config T 图标自带 `#ffffff`，比按钮 label 亮 ⇒ 同一按钮内不统一 |

## 7. 自检（机器输出，非目测）

`pnpm audit:mockup-conformance --file pUE6UakxCpU6Dw9vUCtBBe --node <id> --non-blocking`

### 7.1 首交付（2026-08-31）

**主交付 Section `6554:1028`**：`integrity findings=0 exit=0` · `bilingual-spacing findings=0 exit=0（unverified=89，per-bullet 机制按规则不机器扫）` · `overlap findings=0 exit=0` · `geometry-consistency findings=0 exit=0（26 controls/buttons）` · `typography-icon findings=2`（**修复前 91 → 修复后 2**，剩余 2 条不在本轮节点内）

**REFERENCE Section `6573:715`**：integrity / colors / typography-icon / library-origin / overlap 全 `findings=0 exit=0`；binding-fidelity 10 条全为注释卡 `fontSize=12`。

> ⚠️ **这一组绿是假信号 —— 它没能挡住 §10 的三条缺陷**。`library-origin` 只查 instance 的来源库，手搓的 FRAME 不是 instance ⇒ 从规则视野里消失；没有任何规则查"契约节点是否 effectivelyVisible"；机检全是逐节点的，不问"新元素与同页既有元素协不协调"。**⛔ 别把 §7 的绿读成"设计对"** —— 那是 F1 走查的职责，见 §10。

### 7.2 视觉一致性返工后（2026-09-01）

⚠️ **本轮 `/variables/local` 返回 403（non-Enterprise）** ⇒ `colors` 的 C2/C3 与 `binding-fidelity` 的 B-SEM **可能 under-report**，且**两轮数字不可直接相比**（首交付那轮的解析条件不同）。

**主交付 Section `6554:1028`**：`integrity 0 exit=0` · `overlap 0 exit=0` · `bilingual-spacing 0 exit=0（unverified 89）` · `geometry-consistency 0 exit=0（36 controls/buttons，较首交付 +10 = 新增的 10 个 Button instance）` · `typography-icon 2 exit=1`（同首交付，均不在本轮节点内） · `colors 291 exit=1` · `library-origin 311 exit=1` · `binding-fidelity 744 exit=1` · `connector 0 exit=1（unverified 1，本交付无连线）` · **`library-binding exit=2`（无离线缓存，分母 0，⛔ 不计入通过，见 PRODUCT_INTRODUCTION §3.5）**

**REFERENCE Section `6573:715`**：全部 `findings=0 exit=0`，除 `binding-fidelity 10`（注释卡 `fontSize=12`，同首交付）。
⚠️ 改写去向卡时文本变长 ⇒ 卡底 1029 溢出 Section 1016，`integrity` I3 曾一度 `exit=1`；已把 Section 高 1016 → **1089**（卡底 + 60 对称留白）后复跑 pass。

**对准新建节点单跑（`--node 6585:2`，这才是本轮能负责的分母）**：

| 规则 | 结果 | 定性 |
|---|---|---|
| `typography-icon` | **0 violations** | ✅ |
| `library-origin` | 2 条 | 仅 Config T 两图标 —— owner 批准的跨库依赖（决策 5）。**两个 Button 通过** ⇒ 首交付时它们根本不在 5 个 scanned instance 里 |
| `colors` C1 | 2 条 | `Icon/Power OFF` 内部布尔运算**操作数** Vector（`fill=#000000`），不参与渲染（截图已证图标正常）；改动即对库组件产生 override |
| `binding-fidelity` B-SCALE | 4 条 | 两个 Button 的 `paddingTop/Bottom=10` —— **`Button/dark M` 组件自带值**，与既有残留「`Slice Line` itemSpacing=10 / dialog radius=6」同类定性 |

> clone PID 行 label 列时带进来 5 份隐藏的 `*` 与 `icon/Message/Help 1`（共 20 条 C1），已逐帧删除 ⇒ `colors` 311 → 291。

### 本轮修掉的真缺陷（逐条）

| 缺陷 | 成因 | 修法 |
|---|---|---|
| 注释层 89 个 TEXT 报 `fonts=Inter` | `createText()` 的**节点基础 style 是 Inter**；只用 `setRangeFontName` 逐段覆盖不改基础样式，闸读基础样式 | 先设节点级 `fontName = Roboto`，再重设 ZH range；`getStyledTextSegments` 复核残留 = 0 |
| 117 个换行符字体为 Inter | `\n` 不在 EN / ZH 任一 range 内 | 同上一并覆盖 |
| `Power actions` padding 20 off-scale | 我照参考图取 20，不在 `{4,8,12,16,24,32,40,56}` 上 | 改 24（5 个帧各一份，band id 均不同） |
| 状态标签 padding 20 off-scale | 同上 | 改 16 |
| 4 个 scrim effect 未套 style | 直接写 `BACKGROUND_BLUR:8` 裸值 | 绑库 effect style `Mask blur8`（radius 8 与 M41 一致） |
| 弹窗不再垂直居中 | 文案 wrap 后卡高 145→166/187/174，y 仍按 145 算 | 按实测高度重算 y（367/357/374/363） |
| `Power` 标题居中 | Content 卡 `counterAxisAlignItems=CENTER`，HUG 宽度的标题被居中 | 标题改 `layoutSizingHorizontal=FILL` + 文本左对齐 |
| 圈号 `①-⑤` 在 Roboto 段 | M-TXT-ICON-AUDIT 缺 glyph 清单项 | 绑 Noto Sans SC（设节点级字体后**需重新绑一次**，这是该操作的副作用） |

### 已知残留（定性为不可修 / deferred，非遗漏）

| 类别 | 数量 | 定性 |
|---|---|---|
| `B-TYPO` 注释卡裸 fontSize（11/12/13/15/18） | 111 + 10 | **M23.14(B) 的规定值**；M-FONT grid 只约束产品层 ⇒ §M49.1 owner-deferred |
| `B-SCALE` `Slice Line` itemSpacing=10 · dialog radius=6 | 11 | 库组件 instance **自带值**，修改即产生 override / 破坏组件绑定 |
| `library-origin` Config T 两图标 + 旧库 Slice Line + Jira 组件 | 4 | 前者为 owner 批准的跨库引入；后两者为 clone 自基线的既有债 |
| `colors` C1 icon fill 未绑变量 | 301 | 全部落在 `I6554:1030;…`（Top bar 库组件内部图标），clone ×6 放大；非本轮引入 |
| `connector` | unverified=1 | 本交付无连线，N/A |
| **`library-binding`** | **exit=2 ERROR** | 离线缓存 `figma-data/mockup/pUE6UakxCpU6Dw9vUCtBBe.json` 不存在 ⇒ **这条规则本轮未跑起来**，不计入通过 |

⚠️ `--node` 只约束 8/10 条规则；`library-binding` 与 `connector` 是全文件口径，不能表述为"已缩到本节点"。

## 8. 交付链接

> ⚠️ **全部坐标 2026-09-01 交付层重排后刷新**（§15；产品帧坐标承 §14 未动）。Section 原点仍是 `(0,0)`，故 section-relative == 画布绝对，两者可互换读。

- **Page**：`< RPSV3-134 > Power Control on RPS Link 20260831`（`6553:108`，插于 `To be confirmed` 分隔符正下方，画布底色 0.418）
- **交付 Section**：`6554:1028`（**8900×3095** @ (0,0)；沿革 8960×4740 ← 11000×3223）
  - ⚠️ **本行此前记 `8900×3881`，是错的**（2026-09-01 实测订正）。三处文档一度记两个值，`PRODUCT_INTRODUCTION` §5 的 8900×3095 才是对的。⇒ **owner 手动改稿后要回来刷这一行**，别让 spec 与实物分叉。
- **状态帧**（1920×900，两行分支布局）：

| # | 帧 | node | 位置 | 所属支 |
|---|---|---|---|---|
| ① | Default | [6554:1029](https://www.figma.com/design/pUE6UakxCpU6Dw9vUCtBBe?node-id=6554-1029) | (800, 1293) | 分叉点（垂直居中于两行之间）|
| ② | Reboot confirm | [6561:521](https://www.figma.com/design/pUE6UakxCpU6Dw9vUCtBBe?node-id=6561-521) | (2840, 620) | 行 1 · 重启支 |
| ④ | Rebooting | [6561:659](https://www.figma.com/design/pUE6UakxCpU6Dw9vUCtBBe?node-id=6561-659) | (4880, 620) | 行 1 · 重启支 |
| ⑥ | **Reboot failed** | **[6656:735](https://www.figma.com/design/pUE6UakxCpU6Dw9vUCtBBe?node-id=6656-735)** | (6920, 620) | 行 1 · 重启支（**新建**）|
| ③ | Power Off confirm | [6561:590](https://www.figma.com/design/pUE6UakxCpU6Dw9vUCtBBe?node-id=6561-590) | (2840, 1966) | 行 2 · 关机支 |
| ⑤ | Powering off | [6561:728](https://www.figma.com/design/pUE6UakxCpU6Dw9vUCtBBe?node-id=6561-728) | (4880, 1966) | 行 2 · 关机支 |

- **状态标签**（1920 宽，恒在其产品帧上方 226px 处）：① `6565:706` (800,1067) · ② `6565:711` (2840,394) · ③ `6565:716` (2840,1740) · ④ `6565:721` (4880,394) · ⑤ `6565:726` (4880,1740) · ⑥ **`6656:811`** (6920,394)
  - ⚠️ **圈号编号刻意未重排**：②④⑥ 顺着重启支、③⑤ 顺着关机支，各自沿分支递增。改成"按行从左到右"要重写 5 个三段双语标题（§12.4 踩过的那个坑），且会让所有既有引用（Jira 评论 / Slack / 本文件）失效。
- **BEFORE 对照帧**（Section 外，M23.12）：`6564:608`（未动）
- **文档列（左，x=60，PRD 与 UX 卡同宽 680 —— M23.12「文档列」）**：
  - **PRD 卡**：`6566:715`（**(60,40)**，680×**1422**；⚠️ 此前本行记 1401，实测 1422，已订正；内含需求↔验收对照表 `6646:735`，含 row 8 = `6670:863`）
  - **UX Delivery 卡**：`6568:715`（**(60,1502)**，原 (800,3120)；680×**1495**，原 1463；Interaction 段内含状态图 `6638:735` = 632×**322**，含终态框 `6665:863` + 条件 label `6666:863`）
- **Journey 卡**：**`6618:2`** —— **位置与尺寸由 owner 手动定稿（2026-09-01）：`(800, 2301)`，`1894×734`**（原 (1520,3120) 1400×818，网格版）
  - 左缘 **800 = 产品帧列 0 的左缘**；右缘 2694；底 3035。stage 列宽 **333**（原 234）
  - ⚠️ **取值沿革（⛔ 别把中间版本当现状）**：`1400`（§12）→ 我改的 `8780` 全宽（owner 否）→ 我改的 `2660`（owner 又手动改）→ **owner 定稿 `1894 @ (800,2301)`**。判据与教训见 §15.2
  - **情绪层（I7 ③ 依附几何，共 6 节点）**：`emotion-canvas` `6623:6`（**1698×100**，原 1204×70，`layoutMode=NONE` ⇒ 子层绝对定位、**不随卡宽走**）· 折线 `6623:7`（1365 宽）· 圆点 `6623:8`–`6623:12`
  - ⛔ **任何人手动改这张卡的宽度后，都必须按 §15.3 重算这 6 个节点** —— owner 手动缩宽那一次实测把折线撑出 canvas **280px**、5 个圆点偏移到 **77 / 230 / 383 / 536 / 689**
  - ⚠️ 原 `6570:715` **已删除**，⛔ 别再引用
- **流程连线**（Section 直接子，5 条，2026-09-01 全部重算路径）：

| 连线 | vector | start dot | 箭头 | 条件 label |
|---|---|---|---|---|
| ①→② 点击 Reboot | `6643:735` | `6643:736` | `6644:735` | `6643:737` |
| ①→③ 点击 Power Off | `6643:738` | `6643:739` | `6644:736` | `6643:740` |
| ②→④ 确认重启 | `6643:741` | `6643:742` | `6644:737` | `6643:743` |
| ④→⑥ **达到统一超时时限** | **`6661:863`** | **`6661:864`** | **`6661:865`** | **`6661:866`** |
| ③→⑤ 确认关机 | `6643:744` | `6643:745` | `6644:738` | `6643:746` |

  reconnect map 写在 Section `6554:1028` 的 `sharedPluginData['ux_annotation']['reconnect_map']`，实测 **version 3**（⚠️ 本行此前记 version 2，2026-09-01 实测订正）。每条 arrow 的字段全集（实测，非印象）：

  `id` · `label` · `labelZh` · `trigger`(`user`\|`system`，④→⑥ 是唯一的 `system`) · `from{triggerElemId,desc}` · `to{targetElemId,desc}` · `fromCanvas{x,y}` · `tipCanvas{x,y}` · **`parts{vector,dot,arrowhead,label}`**

  ⚠️ **`parts` 是关键**：它已经逐条列出了散落的 4 个子节点 id，**但 `audit-mockup-connector.mjs` 从来不读这个字段** —— 闸只认 `id` 指向的节点是不是 GROUP。这是 §17 那个实验的直接结论。
- **Jira 组件**：`6565:837`（Priority=Medium，issue key 带真超链接）
- **Power 行**（2026-09-01 返工新建，每帧一份，均在该帧 `Frame 2007` 末尾）：Default `6585:2` · Reboot confirm `6589:134` · Power Off confirm `6589:184` · Rebooting `6589:36987` · Powering off `6589:36995`
- **REFERENCE Section**：`6573:715`（**2100×1089**，2026-09-01 由 1016 加高以容纳改写后的去向卡）；含 Config T 参考图 `6573:716` + 去向对照卡 `6573:717`
- **Kickoff packet**：`tvu-design-system/docs/internal/_design-kickoffs/rpsv3-134.md`（`design:kickoff --check` exit 0）

## 9. 路由与评审

| 角色 | 人 | 评审结论（2026-09-01） |
|---|---|---|
| 需求发起（PM） | Kalpesh Dave（Slack `USJDRJDJS`） | 「It looks OK to me. Trevor can confirm it further.」 |
| UI ENG / 确认可做 | Trevor Yao（`U6DK2FACD`） | **提出 `reconnect` 歧义并给出文案定稿**，见 §11 |
| 领域事实提供 | **Dave Hu**（Slack `USC5M69QS` · Jira `5e61b34ecf54800ce3a05065`） | 确认重启后**直播传输自动恢复**（非仅 UI 重连）—— ⚠️ 此前 roster 未记录此人，他是 RPS 传输行为的事实源 |
| Jira assignee / 产品 | Lotus Chen（`USST27WP8`） | — |

- **Jira / Slack 回复**：✅ **2026-09-01 已发**（owner 当日确认后）。Jira 评论 `268958`（ADF 短锚文本 `Figma` + 三个真 accountId mention：Kalpesh `5e99ec4b3a8b910c0868471b` / Trevor `5e59a401a17f930c9b9627c2` / Lotus `5e58a8d02a59dc0c8fe66118`）· Slack [thread 回复](https://tvunetworks.slack.com/archives/C080LLVJ0S2/p1788225260663049?thread_ts=1787952625.306489&cid=C080LLVJ0S2)（比 Jira 多一句 scope note：Kalpesh 原话是 "Reboot tab"，交付是页内分区 + 双动作 + 本轮不含 Mobile）。
- **重启失败反馈**：~~已建单 RPSV3-139~~ —— ⛔ **该单已被 owner 判定不应存在，2026-09-01 由 owner 在 Jira UI 手动删除**，见 §14.10 第 1 条。⚠️ **这张单是我自行决定建的，没有先问 owner**；建单时未传 assignee，落到 Lotus Chen 是 RPSV3 项目默认分派的结果。原打算留给 PM 的三问（超时阈值 / 失败态给什么 / Power Off 要不要同等处理）**改为只在设计侧记录**，口径见 §13，设计已落地为第 6 态（§14.2）。

---

## 10. 视觉一致性返工（2026-09-01）

**触发**：owner 看效果图后指出「参考图是让你参考，没说照搬」+「整页 UI 风格不一致，太突兀」。补跑 F1 design-walkthrough（**首交付时这一步整个跳过了**，从 mockup 直接进外发）。

### 10.1 三条根因

| # | 根因 | 证据 |
|---|---|---|
| 1 | **视觉契约取自一个不可见节点** | design-record §3 原写「分区标题范式实测 sibling `6260:4289`」。复核：`6260:4289` `effectivelyVisible=false`（整个 Content 卡 `visible=false`）、`6260:4261` 亦 false（父 `Frame 2005` 关闭）⇒ **基线 Home 页渲染中零个 24px 标题**。我 probe 到节点属性就当契约，没查 visible、没和渲染图对照 |
| 2 | **库里有 canonical Button，却手搓了 FRAME** | `Button/dark M` SET `6520:4115` 变体轴齐全（style: filling/**ghost**/rimless · color: gray 1/green/orange/**red** · icon: **left**/right/loading/no）。现成 instance `6554:1088` 就在同一帧的 Ethernet 卡里（clone 基线时一起带来的）。违反 consumer-product-conventions Rule 6 |
| 3 | **全页唯一色块** | `Power actions` 的 `Layer_3` 是 Content 卡内**唯一有 fill 的子容器**（其余全 `null`），读成嵌入式面板，与卡的扁平语言冲突。直接照搬自 Config T 参考图 |

**为什么机检没挡住**：见 §7.1 的警告框。`library-origin` 对手搓 FRAME 是盲的（不是 instance）；没有规则查 `effectivelyVisible`；机检逐节点，不问整体协调。

> ✅ **2026-09-01 已回流 DS**：本节三条根因就是 owner 所说的「**视觉语言一致性**」的实证 —— 他的原话定义是「同一个页面的相同模块的文字大小、背景颜色等这类元素需要一致性，就比如一开始你直接把 Config-T 的开机按钮和背景颜色直接拿过来用，而没有根据当前页面的视觉元素进行调整」。
> 落点 = **`mockup-conventions.md §M37.3`**（参考物只定「做什么」，视觉取值一律取宿主页同类模块实测 + 逐属性找先例的可执行判据）+ **`design-walkthrough §1` 的「宿主页取值一致性」属性表**（走查侧）。根因 1 的 `effectivelyVisible` 那半此前已回流为 `design-process.md §M21.3`。详见 §16。
> ⚠️ **⛔ 别把这条与 `C8` 混为一谈**：`C8` 判「同一个信号是不是一号多义」（红色的两种语义），**M37.3 判「取值一不一致」**（字号 / 底色）。两条触发不同，我一度把前者当成 owner 要的那条，已订正。

**一个更难看的细节**：§6 决策表原有 4 条"不照搬参考图"（决策 7/8/9/10），说明当时知道参考图不能全信。但那 4 条全是**单个元素**的取舍 —— 逐条跟参考图比对，恰恰导致一次都没退后一步问「这一整块放进这一页协不协调」。局部严谨掩盖了整体没比对基线。

### 10.2 改了什么（owner 拍板后执行）

1. 删 24px `Power` 标题 → 改 14px `Power:` label（绑 `UX/Grey/grey-6 #9E9E9E`），200px label 列
2. `Power actions` 的 `Layer_3` 底色块整体删除
3. 两个自画 FRAME（69×69 / 87×69，VERTICAL，图标上文字下）→ `Button/dark M` instance（`icon=left, style=ghost, color=gray 1`，97×32 / 115×32）
4. `Slice Line` 从 Content 直接子层（gap 20）挪进 `Frame 2007`（gap 16），与其它行同级
5. 行 gap 40→16、pad 24→0（由沿用 Form Item 范式自然满足）

附带修掉两处返工中新发现的问题：
- swap 进来的 Config T 图标自带 `#ffffff`，比按钮 label（`grey-5 #CCCCCC`）亮 ⇒ 绑同一变量
- clone PID 行 label 列带进 5 份隐藏的 `*` 与 `icon/Message/Help 1`（20 条 C1）⇒ 逐帧删除

⚠️ 过程中踩到一次 `figma.createAutoLayout()` **默认带白色 fill**，Power 行渲染成白条 —— 靠截图发现，probe 数值看不出来。

### 10.3 交付层同步（§M-DISCIPLINE.SYNC）

改 mockup 不改说明卡 = 交付层与实物不符。共改写 8 处双语文本：

| 卡 | 节点 | 原内容 → 新内容 |
|---|---|---|
| UX Delivery `6568:715` | `6568:724` | 「分区：分隔线 + 标题 + 按钮条」→「Power 行：分隔线 + 沿用 Form Item 范式的标签+按钮行」 |
| 同上 | `6569:729` | invariant 2「分区标题 Roboto SemiBold 24」→「行沿用 Form Item 范式，200px label 列」 |
| 同上 | `6569:730` | invariant 3「按钮条底色绑 Layer_3」→「按钮为库 `Button/dark M` ghost/gray 1；行无 fill」 |
| 状态标签 `6565:706` | `6565:708` | 「Power section」→「Power row」 |
| 去向卡 `6573:717` | `6573:720` | ADOPTED「标题 + 横向按钮条」→ **REJECTED**（本页零可见标题） |
| 同上 | `6573:721` | ADOPTED「28px 图标上下叠文字」→ **REJECTED**（库已有 `icon=left` 变体） |
| 同上 | `6573:724` | ADAPTED「按钮条底色 → Layer_3」→ **REJECTED**（卡内其它容器一律无填充） |
| 同上 | `6573:725` | 「不采纳 Helvetica 22，因本页范式是 Roboto 24」→ 理由被推翻，改为「本期完全不设标题；那套 24px 范式挂在从不渲染的节点上」 |

⚠️ PRD 卡与 Journey 卡**未改** —— 它们描述需求与体验，不含视觉实现细节，「Power section」在需求语境下仍成立（且 PRD 按规矩本就不该承载设计细节）。

改写手法：先设**节点级** `fontName` 为 EN 字体、再重设 ZH range —— 即首交付时修 89 个 Inter 残留的同一修法；逐条复核 `getStyledTextSegments` 均为 2 run（Roboto 13 + Noto Sans SC 11），零残留。

### 10.4 待回流（做法进 DS 真源，取值进本仓）

| 落点 | 内容 |
|---|---|
| **DS 真源**（`mockup-conventions.md`） | ① M21 probe 取 sibling 契约时**必须验 `effectivelyVisible`**（逐级父链），节点属性存在 ≠ 契约生效 ② 交付前必跑 F1 走查：**机检绿 ≠ 设计对**，并写明 `library-origin` 对手搓 FRAME 结构性失明 |
| **本仓** `PRODUCT_INTRODUCTION.md` | RPS Link (New) Home 页的可见视觉契约（行范式取值 / 零可见标题 / `Button/dark M` setKey 与变体轴） |

---

## 11. 文案定稿 — reconnect 歧义（2026-09-01，Trevor Yao）

**评审结果**：PM Kalpesh 回「It looks OK to me」并转 Trevor 复核；**Trevor 提出 `reconnect` 有歧义** —— "The device will reconnect automatically" 到底指**直播传输自动恢复**，还是**机器重新连上控制页**？

**领域事实**（Nancy 在 thread 内向 **Dave Hu** 确认）：是**前者**，传输会自动恢复 —— Trevor 补充「That's how the packs behave」。

⇒ 原文案不只是措辞不清，是**把一个错误的心智模型写进了 UI**。

### 11.1 Trevor 的定稿（thread 内已 ok_hand）

> Reboot the device? Please note that all live transmissions will be temporarily disrupted. Transmissions will be automatically restored after reboot.

### 11.2 实际落地的四条产品文案

| 状态 | 文本节点（在弹窗 instance 内） | 文案 | 来源 |
|---|---|---|---|
| Reboot confirm | `I6562:2005;5908:7086;6563:624` | Trevor 定稿原文，见 §11.1 | **Trevor 定稿** |
| Rebooting | `I6562:2039;5840:7090;6563:634` | `The device is restarting. Transmissions will be automatically restored once it is back online.` | **延伸**：同一 `reconnect` 歧义也在这条，两处口径必须一致 |
| Power Off confirm | `I6562:2025;5908:7086;6563:627` | `Power off the device? Please note that all live transmissions will be disrupted. The device can only be powered on again manually at its physical location.` | **延伸**：术语对齐 `live transmissions` / `disrupted`；⛔ 保留「不会恢复」语义，**不套用** `temporarily` 与 `restored` |
| Powering off | `I6562:2072;5840:7090;6563:641` | 维持原文（`The device is shutting down. Power it on manually…`） | **未改**：本就无 reconnect 歧义，语义准确 |

⚠️ 后三条是设计侧按一致性做的延伸，**Trevor 只明确定了第一条**。

### 11.3 注释层同步（8 处）

旧文案背后的错误心智模型（「设备/UI 会重连」）已渗进注释层，一并改：

| 卡 | 节点 | 改动 |
|---|---|---|
| 状态标签 ② | `6565:714` | `cuts every active transmission` → `disrupts every live transmission` |
| 状态标签 ④ | `6565:724` | 「Web UI 断开连接、会自行恢复」→「传输中断、会自动恢复」 |
| 状态标签 ④ | `6565:725` | invariant：`the unit reconnects` → `transmissions are restored` |
| PRD 需求 4 | `6567:720` | `all active transmissions will be interrupted` → `all live transmissions will be disrupted` |
| PRD 需求 6 | `6567:722` | `Reboot must state that the device reconnects automatically` → `…that transmissions are automatically restored` |
| PRD 验收 5 | `6567:730` | `the device reconnects on its own` → `comes back online, transmissions resume automatically` |
| UX Delivery | `6569:725` | `the UI reconnects by itself` → `the device comes back online and transmissions resume by themselves` |
| Journey Stage 4 | `6571:728` | `the notice states automatic reconnect` → `…that transmissions are restored automatically` |

**机检复核（改完）**：`integrity` pass（I3 无溢出）· `overlap` pass（0）· `bilingual-spacing` pass（分组 + ZH 层级两档均 pass）。8 条改写后 `getStyledTextSegments` 均为 2 run（Roboto 13 + Noto Sans SC 11），零 Inter 残留。

⚠️ **未外发**：本轮文案改动与 §10 的视觉返工都**没有回 Jira / Slack 告知**（owner 决定不补）。Trevor 的定稿在 thread 里已达成一致，但「设计稿已按定稿更新」这件事没有回执。

---

## 12. 交付层可读性改造（2026-09-01 · 试点）

**触发**：owner 反馈「大部分同事看到大段文字头疼，一看就是 AI 写的；人写的 UX 流程，判断条件是写在流程线上的」。

**诊断 —— 病灶不是字多，是把结构写成了散文**：交互流程本来是一台状态机（4 态 + 带条件的转移），却写成 4 条并列句子；Journey Map 本来靠横向可比（哪个 stage 痛点最密），却做成 5 个纵向文字块，横向对比能力归零。⇒ 读者在替设计者做图形化的工作。

⚠️ **owner 否掉了我最初的方案**：我原本提「Figma 卡瘦身 + 放一行指向 design-record 的链接」，owner 指出**其他人不一定有权限访问**。核实成立 —— 本仓在 gitea `ux-team/rps`，private + 内网地址，Kalpesh / Trevor / Lotus 走 Jira 与 Slack，几乎肯定打不开。**修正后的定位**：两层受众根本不同（Figma 给评审者、design-record 给设计侧与下一个 session），评审者需要的一切必须全在 Figma 页面里，**不存在跳转需求**。⇒ 解法是把同样的信息换成更高密度的表达，全部留在页面上。

### 12.1 三张卡各做了什么

| 卡 | 改法 | 前 → 后 |
|---|---|---|
| **User Journey Map**（新 `6618:2`，原 `6570:715` 已删） | 5 stage × 5 lens **真网格** + 情绪折线；每格从整句压成短语 | 680×1503 → 1400×733；总字符 3190 → 2010（**−37%**）；**平均每块 91 → 49 字符（−46%）**；最长块 185 → 126 |
| **UX Delivery — Interaction 段** | 4 条散文 → **状态图**（`6638:735`），条件写在箭头上，Cancel 用虚线表达「无副作用」 | 4 个 TEXT → 5 状态框 + 4 转移 + 6 条件标签 |
| **PRD — Requirements / Acceptance** | 两段 13 条里 **10 条是同一件事说两遍**（要做什么 + 怎么验）⇒ 合并成**需求↔验收对照表**（`6646:735`） | 13 条 → 7 行；卡高 1371 → 1286 |

**⛔ PRD 其余段刻意不动**（Source / Background / Current State / Priority）：它们是叙述性的，本来就该是句子，且各只有 2 句。PRD 是需求文档、会被当合同看，验收条目要逐条勾验 —— **精确 > 简洁**，不做 Journey 式重构。

### 12.2 状态帧之间的流程连线（M23.6）

4 条 connector，起点**锚在触发按钮**（不是卡边 —— 这是 M23.6(A) 点名的高频违例）：

| 连线 | 起点（触发元素） | 终点（target） |
|---|---|---|
| Reboot → confirm | Default 帧 `Reboot` 按钮 `6585:12` | Reboot confirm 弹窗 `6562:2005` |
| Power Off → confirm | Default 帧 `Power Off` 按钮 `6585:18` | Power Off confirm 弹窗 `6562:2025` |
| confirm Reboot → rebooting | 弹窗内红色 `Reboot` 按钮 | Rebooting 提示 `6562:2039` |
| confirm Power Off → powering off | 弹窗内红色 `Power Off` 按钮 | Powering off 提示 `6562:2072` |

走帧下方 1520–1650 的空带、分四层避免交叉；label 全部落在 y>1520（避开 overlap 闸唯一比较的 TEXT 类型）。已在 Section 上写入 `sharedPluginData['ux_annotation']['reconnect_map']`（4 条 arrow 含 `fromCanvas` / `tipCanvas`）。

⚠️ **箭头三角与 M23.6(C) 的张力**：规则要求画箭头 ▶，又禁止斜线段；箭头三角的两条斜边（dx=5 dy=8）必然被判违例。解法是 **VECTOR 只画正交折线、箭头单独用 POLYGON**（audit 只扫 VECTOR）。这条值得回流 DS —— 规则目前没给箭头头豁免。

### 12.3 自检

`integrity` / `overlap` / `bilingual-spacing` / `geometry-consistency` / **`connector`** 全 `exit=0`。

⚠️ **`connector` 的绿是「可验证项为空」的绿**：审计自印 `降级跳过（不可验证）: anchoring×4, orthogonal×4` —— REST 拿不到 centerline `vectorPaths`，结构上验不了。**真正的证据是我用 `use_figma` 实跑的两项**：正交性 `orthogonalityViolations: []`、两端锚定 `allPass: true`（4 条 × 两端）。⛔ 别把这条的 exit=0 读成「机检验过了」。

`binding-fidelity` 744 → 771（**+27**）：新建注释节点的 B-TYPO 裸 fontSize，与既有残留同类（M23.14(B) 规定值 · owner-deferred）。**B-SCALE 已清零** —— 首建时引入 41 处 off-scale（padding 7/10、itemSpacing 1/2/20），已按 `10→8 / 7→8 / 2→4 / 1→0 / 20→24` 全部吸附；状态图的箭头随之**按实测 box 几何重算**（box 高 44→46），未沿用硬编码坐标。

`typography-icon` 仍为 2 —— 均在 `Jira requirement` 库组件内部（自带 Inter），与首交付同一批，非本轮引入。

### 12.4 过程中修掉的两处自伤

- **v2 Journey 卡整张建在了 `To be confirmed` 分隔页**（`3877:255`）上：`createAutoLayout()` 默认挂 `figma.currentPage`，而它每次调用重置为第一页。连跑 6 轮 use_figma、多次截图都没发现，直到 owner 说「我没看到有改动」。⇒ 已记入 [`PRODUCT_INTRODUCTION` §3.7](../PRODUCT_INTRODUCTION.md)：**截图能证明节点建成了，证不了它在哪**。
- **改 `① Default` 状态标签时把 Roboto 段吃成了 Noto Sans SC**：圈号 `①` 是 CJK 字符、单独绑 Noto Sans SC，我用「首段/末段」两段模型覆盖，丢掉了中间的 Roboto 段。⇒ 含圈号 / 特殊字形的双语文本是**三段**结构，改写前须照未改过的同族节点（如 `6565:712`）取参照。

### 12.5 ~~待定~~ → ✅ **已回流（2026-09-01，owner 授权）**

格式已从 pilot 转正：**`mockup-conventions.md §M23.19`**（三种形态的通用判据）+ **`design-process.md § User Journey Map`**（网格默认形态 + 情绪层三条硬约束 + 四档标记词）。落点全表见 §16.6。

⚠️ **Figma 卡上那两处 pilot 标注要跟着改**：Journey 卡 meta 行的 `grid format (pilot)` 现在**名不副实**（已是正式格式），应删或改为规则号引用。而情绪曲线的 `designer read, not measured` **必须保留** —— 它已被 §M23.19 回流成硬约束（一旦画情绪层就必须标数据来源），从「本卡的自觉标注」升格为「所有 Journey 卡的必填项」。⏳ **这两处 Figma 文字未改，登记为未决项**（见 §14.10 第 10 条）。

### 12.6 owner 看过效果后开出的两条 —— ✅ **2026-09-01 已做完，见 §14**

| # | 事项 | 现状 / 判据 |
|---|---|---|
| **A** | **流程线不直观，应按流程重排 mockup 位置** | owner 2026-09-01 提出。根因：5 个帧按 `Default / Reboot confirm / Power Off confirm / Rebooting / Powering off` 横排，而真实流程是 **1→2→4** 与 **1→3→5** 两条分支 ⇒ 连线必须跨帧绕行，读者看不出分支。**建议改法**：重排成两行（行1 重启路径 `Default → Reboot confirm → Rebooting`，行2 关机路径 `Power Off confirm → Powering off`），或 Default 居左、右侧上下分两支。⚠️ 重排会改变所有帧的绝对坐标 ⇒ **4 条 connector 的路径与 reconnect map 必须同步重算**（坐标在 §8），状态标签帧位置亦然 |
| **B** | Journey Stage 5 的 GAP 是否补齐 | ~~不补~~ → **改为做**，见 §13。2026-09-01 13:14 **owner 在对话中直接给了口径**（⚠️ 不是 PM 在 Jira 上回的 —— 该单当日 13:07 实查仍 status `To Do` / 0 评论） |

---

## 13. 重启失败态 — owner 口径（2026-09-01 13:14）

**来源辨析（重要）**：以下口径由 **owner（Nancy）在对话中直接给出**。⚠️ **不是 PM 在 Jira 上的回复** —— [RPSV3-139](https://tvunetworks.atlassian.net/browse/RPSV3-139) 当日 13:07 实查仍 `To Do` / 0 条评论。⇒ 该口径目前**只活在设计侧记录里，Jira 单上没有**；mockup 画完后建议回填一条到该单（受 slack-jira-write-confirm 约束，草稿先给 owner 过目）。

### 13.1 三问的答复状态

| # | 问题 | 答复 | 状态 |
|---|---|---|---|
| 1 | 超时阈值多久 | **沿用产品现有的统一超时机制**，不为本功能单独定义阈值 | ✅ 已答 |
| 2 | 失败态给什么 | **提示用户稍后重试** | ✅ 已答 |
| 3 | Power Off 要不要同等处理 | **不做** —— owner 2026-09-01 采纳 §13.3 的设计侧建议 | ✅ 已答 |

### 13.2 对设计的直接含义

- **⛔ mockup 里不要写具体秒数**。「沿用统一机制」意味着阈值是产品级既有约定，不是本交付定义的值 ⇒ 失败态只表达「**超时之后**」这个状态，不出现 `30s` / `2 min` 之类的字面量。若后续有人要在稿子上标阈值，那是另一个决策，得先找到统一机制的真源。
- **「提示用户稍后重试」是提示文案，不是重试按钮**。⇒ 不新增 `Retry` 控件；沿用既有 `Notification` 组件、footer 保留组件自带的 `OK`（与 §4 决策 11 一致）。⚠️ 若下一轮判断需要 `Retry` 入口，那是超出本次口径的新决策，须回头找 owner，⛔ 别自行加。
- **变体应从 `info` 换成错误语义**：现有两个进行中提示用 `theme=dark, form=alert, type=info`。失败是异常终态，不该与「正在进行」同色。需到 `Notification` SET (`23535120dbdc93bf90af9a118b5091e58b590dc6`) 枚举 `type` 轴的可用值后再定（⛔ 别凭印象假设有 `error`，按 §M32 先枚举）。
  - ✅ **2026-09-01 已 live 全枚举**：`type` 轴 = `warning / error / info / default / danger / success`；`theme=dark, form=alert` 下实际存在 5 个（`success` `info` `warning` `error` `default`，`danger` **只给 pop confirm**）。取 **`error`**（`6562:1698`）而非 `warning` —— warning 的语义是「当心，可继续」，而这是已经失败的终态。
- **文案术语**：必须沿用 §11 定的口径 —— `live transmissions`、`disrupted`；⛔ 别用 `reconnect`。失败态不能承诺恢复。

### 13.3 第 3 问 —— ✅ owner 已拍板：**Power Off 不做同等失败态**

设计侧建议（原文保留）：Power Off **不需要**同等的失败态 —— 关机后设备不再可达是**预期结果**，不是异常，「超时」概念不适用（§4 决策 6 的领域事实：关机需现场开机）。

**2026-09-01 owner 采纳该建议。** 这是一个**显式决定，不是遗漏**，已按要求写进两处交付层，让评审者在页面上就能看到：

| 落点 | 措辞 |
|---|---|
| Journey 卡 Stage 5 · Opportunities（`6621:27`）| `OUT — Power Off gets no failure state: unreachable after shutdown is the expected result, not a fault` / `不做 —— 关机不设失败态：关机后不可达是预期结果，不是故障` |
| PRD 对照表 row 8（`6670:863`）| 需求侧写明「关机不设对应失败态」，验收侧写明「**仅**重启分支有失败帧」——把「不做」变成可逐条勾验的判据 |

⚠️ `OUT` / `不做` 是本轮**新引入的第 4 个 Journey 标记**（原有 `HIT` / `GAP` / `NEXT`）。GAP 本轮已全部关闭，故当前实际在用的是 `HIT` / `NEXT` / `OUT`。

### 13.4 落地范围 —— ✅ 全部完成（2026-09-01，见 §14）

| # | 事项 | 状态 |
|---|---|---|
| 1 | 新增第 6 个状态帧「Reboot failed」，接在重排后重启支末端 | ✅ `6656:735` @ (6920,620) |
| 2 | 新增 connector `Rebooting --[超时]--> Reboot failed` + reconnect map | ✅ `6661:863`；map 升 version 2，加 `trigger` 字段 |
| 3 | 新增第 6 张状态标签（⑥） | ✅ `6656:811` |
| 4 | 回填 Journey Stage 5 的琥珀 GAP → 已覆盖，并撤掉琥珀色 | ✅ 全卡 `#FFB14A` 实测残留 **0** |
| 5 | 同步 UX Delivery 状态图，增一条通往失败态的转移 | ✅ 2 路 fork → 3 路 fork（`6665:863` + 条件 `6666:863`）|
| 6 | PRD 对照表新增一行需求↔验收 | ✅ row 8 = `6670:863` |
| 7 | UX Delivery 卡标题 5 states → 6 states（Journey 的「5 stages」不动） | ✅ 标题已改；Journey meta 行 `5 stages` **确认未动** |
| 8 | §2 Scope 两处 stale 同步 | ✅ 见 §2 |

---

## 14. 分支重排 + 重启失败态（2026-09-01 · §12.6 A 与 §13 合并执行）

**为什么合并做**：失败态新帧的位置取决于重排后的布局。先加帧再重排会让 5 条 connector 的路径与 reconnect map 各算两次，第一次全是废工。

### 14.1 布局：从横排 5 帧改成两行分支

```
                  ┌→ ② Reboot confirm → ④ Rebooting → ⑥ Reboot failed
    ① Default ────┤
                  └→ ③ Power Off confirm → ⑤ Powering off
```

- **列距 2040**（帧 1920 + 间隙 120），4 列：`800 / 2840 / 4880 / 6920`
- **行 1**（重启支）帧顶 `620`；**行 2**（关机支）帧顶 `1966`
- **① Default 垂直居中于两行之间**：帧顶 `1293` —— 取值来自 `(620 + 2866) / 2 = 1743` 再减半帧高，使 Power 行的两个按钮（实测 y 1717–1749）正落在分叉点高度上
- 状态标签恒在其产品帧上方 226px（沿用原有节奏，未新造间距）
- 注释卡整体下移 `1600 → 3120`；Section `11000×3223 → 8960×4740`（**变窄了 2040**，因为 5 列压成了 4 列）

**连线路由口径**（5 条统一）：起点圆点锚在触发元素底缘中点 → 竖直出帧 → 在空带内横走 → 竖直升入目标弹窗的**底缘中点**。这是首交付就定下的手法，本轮只是换了坐标。空带取 `1520–1740`（行 1 下方）与 `2866–3000`（行 2 下方）；两条分支线在 col0/col1 之间的 120px 间隙列（x `2760` / `2800`）里做垂直换行。

### 14.2 重启失败态

| 项 | 落地 | 依据 |
|---|---|---|
| 组件 | `Notification` SET → `theme=dark, form=alert, **type=error**`（`6562:1698`）| §13.2 补记的 live 全枚举 |
| 图标 | `icon/Message/Error 2`（红 `#EA4233`，**绑变量**）| ⚠️ 变体切了图标不会自己跟着换 —— 克隆自 ④ 的实例带着一条把图标钉死在 `Info 2` 的 instance-swap override，须显式 `swapComponent` 到 canonical |
| 标题 | `Reboot failed` | — |
| 正文 | `The device did not come back online after the reboot. Live transmissions remain disrupted. Please try again later.` | §11 术语（`live transmissions` / `disrupted`）；⛔ 无 `reconnect`、⛔ 无秒数、⛔ 无恢复承诺 |
| 按钮 | 沿用组件自带 `OK`，**未加 Retry** | §13.2 + §4 决策 11 |

**⚠️ 一条留给 owner 的观察 —— ✅ 2026-09-01 owner 已拍板并自行改稿，见下**：`type=error` 变体把 `OK` 按钮也渲染成**红色**（实测 `#EA4233`，与 `布尔` 图标同一个绑定值）。于是同一套交付物里红色承载了两种语义 —— ②③ 里红＝破坏性动作，⑥ 里红＝关闭一个错误提示。这是 DS 组件自身对 `type=error` 的定义（两处填充都绑着 token，不是 override），**改它属于 DS 层决策**，本轮按 M32/M52.1 不覆盖。

**owner 裁定（2026-09-01）**：⛔ **不走 DS 提案**；就地把 ⑥ 的确认按钮切成**绿色**即可（owner 自行改的稿）。

落地实测（我方复核，非目测）：
- 提示实例 `6656:803` 仍是 `theme=dark, form=alert, **type=error**` —— 错误语义没动
- 内嵌 `Button/dark M` `I6656:803;5840:7145` 属性已是 **`color=green`**，渲染 paint 实测 **`#2FB54E`、`bound: true`**，与 ④ 那颗按钮**同一个绑定值**
- 逐 paint 与 ④ 对齐后，⑥ 与 ④ 的唯一差异回到**一处**：错误图标 `布尔` `#3892F3 → #EA4233`（红 error 图标保留）
- ⇒ 红色在本交付集里重新变成**单义**（只表破坏性动作），②③ 的红 `Reboot` / `Power Off` 不受影响

⚠️ **这是产品侧就地取变体，不是覆盖绑定值**，故 `binding-fidelity` / `colors` 计数无变化（§15.4 机器比对）。代价是 ⑥ 偏离了 DS 对 `type=error` 的 canonical 渲染 —— **属 owner 显式授权的偏离**，⛔ 别在下一轮走查里当违例"修"回红色。

**顺带修掉的克隆残留**：`6656:803` 的图层名一直是从 ④ 克隆来的 `Notice — Rebooting`，已改为 **`Notice — Reboot failed`**。（M42.2 克隆结构审计该抓而没抓到的一条 —— 它只比了结构与文本，没比图层名。）

### 14.3 顺带修掉的既有缺陷（非本轮引入，但在本 Section 的对象全集内）

- **3 处 `→` 落在 Roboto 段**（`6646:736` PRD 表标题 1 处、`6619:10` Journey Actions 2 处）—— Roboto 无此字形，渲染为**空白**。已改绑 `Noto Sans SC`。
  - 这三处是**全 Section 扫描**扫出来的，不是我改到哪查到哪：判据 = 「任何落在 Roboto run 里、且属于 `←-⇿ / ①-⓿ / ■-◿ / ☀-➿` 四个区段的字符」。
- **Journey Stage 4 Opportunities**：新增 `HIT — a stalled reboot now resolves into an explicit failure`。Stage 4 的痛点原写「无进度反馈 —— 慢与死难分」，超时失败态出来之后这条已被**部分闭合**（"慢 vs 死"现在有界了），不同步就是交付层与实物不符。`NEXT — 倒计时或进度指示` 保留，那仍然是真的开口。

**互斥对照（§M-DISCIPLINE.SYNC ③，本轮扩了 feature 的 scope ⇒ 强制跑）** —— 判据 = 「新增一个失败态之后，交付层里哪些陈述不再成立」。
扫「自动恢复 / restored / 回到默认态 / 无需现场操作」共命中 **8 条**，逐条判定后 **2 条被推翻并已改**：

| 节点 | 原文（问题） | 改后 |
|---|---|---|
| `6565:725` ④ 状态标签的 **invariant** | 「不变量：重启后传输自动恢复，无需现场操作」—— 有了 ⑥ 之后这不再是**不变量** | 「重启**成功时**传输自动恢复…；始终未恢复的重启会走到失败态，不停在本帧」 |
| `6646:786` PRD 验收 6 | 「…之后页面回到默认态」—— 只在成功路径成立 | 「…且重启**成功后**页面回到默认态」 |

其余 6 条判定为仍然成立（Rebooting 的产品文案本就写了 `once it is back online`；状态图的 `Back online` 现在是三个并列终态之一；等等）。
其中 1 条**刻意未改**，理由见 §14.10 第 8 条。

### 14.4 自检（机器输出 + 逐条归属核验）

`Conformance report:` [`docs/handoffs/.conformance/rpsv3-134-6554-1028-20260901-final.json`](../handoffs/.conformance/rpsv3-134-6554-1028-20260901-final.json)
（`pnpm audit:mockup-conformance --file pUE6UakxCpU6Dw9vUCtBBe --node 6554:1028 --non-blocking --report <上述路径>`；
`.lines.json` 边车按 tvu-design-system 同款口径 gitignore，诊断文本留本地）

| 规则 | exit | findings | 判读 |
|---|---|---|---|
| `integrity` | 0 | 0 | ✅ |
| `bilingual-spacing` | 0 | 0 | ✅ |
| `overlap` | 0 | 0 | ✅ |
| `geometry-consistency` | 0 | 0 | ✅ 43 units（24 控件 + 19 按钮）|
| `typography-icon` | 1 | 2 | 两条**全部**落在 `6565:837` = `Jira requirement` 库组件内部自带 Inter，与首交付同一批 |
| `colors` | 1 | 351 | 逐帧均等：6 帧的 Top bar 实例**各 41 条**（`6554:1030` / `6561:522` / `6561:591` / `6561:660` / `6561:729` / `6656:736`）|
| `library-origin` | 1 | 373 | 同上，**各 16 条** |
| `binding-fidelity` | 1 | 788 | 同上，**各 42 条**；新建注释文本各 1 条 = B-TYPO 裸 fontSize（M23.14(B) 规定值，§M49.1 owner-deferred）|
| `connector` | 0 | 0 | ⚠️ **半明**：升 map schema 后 5 项锚定**真跑并通过**，5 项正交仍 REST 不可验（`unverified.count` 10 → **5**）。见 §14.5 / §14.6 |
| `library-binding` | **2** | — | ⛔ **本轮未跑起来**（离线缓存不存在，[`PRODUCT_INTRODUCTION` §3.5](../PRODUCT_INTRODUCTION.md) 已登记）。**分母是 0，既不是 pass 也不是 fail** |

**关键归属结论**：`colors` / `library-origin` / `binding-fidelity` 的绝对值比上一轮高，成因是**多了一整帧**，不是多了新的违例类别。

**实际验到什么程度（⛔ 别读成比这更强）**：
- **验死了**：三条规则在 6 个 Top bar 实例上的计数**逐个相同**（colors 41 / library-origin 16 / binding-fidelity 42），⑥ 的那份与既有 5 份一模一样 —— 这是最大的一个桶。
- **验死了**：⑥ 的提示实例与 ④ **逐 paint 对齐**（各 9 个 SOLID，差异只有两处变体驱动的 token 值，见下段）。
- **只验到量级**：三条规则的总增量 ≈ 一帧的份额（如 binding-fidelity 771 → 907，+136），且落在本轮新建节点上的 findings 逐个查过、host 全部是 ⑥ 帧克隆自 ④ 的既有内部结构，或新建注释文本的 B-TYPO 裸 fontSize（M23.14(B) 规定值）。**没有**对 ④/⑥ 两棵完整子树做逐节点一一配对。⚠️ 另外，上一轮 §7.1 记的 `library-origin 4` 是**成因数**（Config T 双图标 + 旧库 Slice Line + Jira 组件）不是节点数，⛔ 别拿它和这里的 373 直接比。

⑥ 的提示实例比 ④ 各多 1 条（colors 2→3、binding 4→5）。**逐个 paint 对齐核过**：两者各 9 个 SOLID paint，唯二差异是 `布尔` 图标填充 `#3892F3 → #EA4233` 与 `Button/dark M` 填充 `#2FB54E → #EA4233`，**两者都 `bound: true`**，是变体驱动的 token 值；唯一未绑的那条（图标 stroke `#EC5050@0.24`）在 ④ 上一模一样地存在。⇒ 多出的计数来自换图标后节点路径变了，不是新缺陷。

### 14.5 连线的真证据（自跑探针）

本轮开工时 `connector` 闸是**全盲**的（10/10 降级跳过，判过 0 个单元），所以下面这组自跑探针是当时唯一的证据；
收尾时把闸修到了半明（§14.6），锚定那一半已由机检独立复核过，两边结论一致。

| 探针 | 结果 |
|---|---|
| **正交性**（M23.6 C，scope = 5 条 flow connector）| `judged: 5, pass: true, bad: []` |
| **正交性阴性对照** | 临时插入一条 `M 0 0 L 100 100` 斜线 → **被抓到**（`caughtDiagonalProbe: true`），⇒ 该探针有判别力，不是恒绿 |
| **两端锚定**（M23.6 A，±8px）| `allPass: true`，5 条 × 两端 = 10 项全过 |
| **overlap 阴性对照** | 已知相交的两个盒必须被报出 → true |

⚠️ **两条曾被我的探针误报、逐条核归属后判定为非违例**，记在这里避免下一轮重复劳动：

| 节点 | 归属 | 为什么不是违例 |
|---|---|---|
| `6639:737` `a3 fork` | UX Delivery 状态图 | 箭头三角与 spine 画在**同一条 path** 里。按 `M` 切分子路径后 6 条子路径全部正交，斜的只有箭头腿 —— M23.6(C) 明确豁免箭头 tip。**是探针没切子路径，不是图错了** |
| `6623:7` | Journey 卡 `emotion-canvas` | 情绪折线是**数据曲线**，不是 M23 流程连线，不在 M23.6(C) 的对象集内 |

**教训**：`checkConnectorOrthogonality` 直接 walk 整个 Section 会把产品帧内的图标 `Union`、Jira 组件矢量、情绪曲线全扫进来（首跑 21 条命中，逐条核完只剩 0）。scope 应显式给 5 条 flow connector 的 id 列表，或至少按子路径切分。

### 14.6 把 `connector` 闸从「全盲」修到「半明」—— 顺带抓出一条真违例

§12.3 只说了 `connector` 的 exit=0 是假绿。本轮去读了闸的源码（`scripts/audit-mockup-connector.mjs`），
发现它**降级的原因是可修的**，而修完之后它立刻抓出了一条**存在了两轮、没人看见的 §C4 违例**。

**降级的两个原因，性质完全不同：**

| 原因 | 可修吗 | 处置 |
|---|---|---|
| `anchoring: pre-schema map / missing fromCanvas/triggerElemId` | ✅ **可修** —— 闸要的是 `from: { triggerElemId }` / `to: { targetElemId }` **对象**形态，而我们的 map 把 `from` 写成了扁平字符串 | reconnect map 升 **version 3**，`from`/`to` 改为对象。改完 **5 条锚定检查真跑起来并全部通过** |
| `orthogonal: no vectorPaths (REST API does not expose path for this node)` | ❌ **不可修** —— Figma REST 结构上不返回 connector VECTOR 的 centerline | 仍走 in-file `use_figma` 探针（§14.5） |

⇒ **降级数从 10/10 降到 5/10**，`connector` 的绿从「判过 0 个单元」变成「判过 5 项锚定 + 5 项仍不可验」。

**抓出来的真违例**：闸的 §C4 判据要求**起点圆点是白填充 + 蓝边**（cyan `#33A4FD` 是流程线唯一色，
白色圆点填充是**唯一**的非蓝处）。我们的 5 个圆点一直是**实心 cyan、零描边**。

这条为什么两轮都没被发现 —— 闸的 `colorableNodes()` 只在 arrow 的 `id` **指向一个 GROUP** 时才把
`groupChildren` 水合出来，才能找到那个 `ELLIPSE` 去查形态。我们的连线是**散落的兄弟节点**
（vector / dot / arrowhead / label 各自挂在 Section 上），于是闸只拿到 `selfNode`（那个 VECTOR），
**圆点形态检查从头到尾没有执行过**。⇒ 它不是「查过了没问题」，是「压根没查」。

已修：5 个圆点全部改为 `fill #FFFFFF` + `stroke #33A4FD` @1.5。

**⛔ 未做（连带项，显式决定不做）**：把每条连线的四个部件包成 GROUP、让 `groupChildren` 水合起来。
理由是它会把 Section 的直接子节点从「散落线条」变成「跨越大片区域的 GROUP bbox」，
而 `overlap` 闸扫的正是 Section 的直接 block 子节点 ⇒ 极可能制造一批假阳性重叠。
这属于**连线的结构范式**问题，该在 DS 侧连同 M23.6 一起定，不该由某一个消费产品单方面改。已登记为待决。

### 14.7 亲验渲染

Section 全景 ×1（两行分支成立、①→②/③ 分叉可读）· ⑥ 提示 ×1（红 error 图标、无 Retry、保留 OK）· 状态图 ×2（3 路 fork；第一版左框 68px 定高把第 3 行文字压住，实测文字需 68px 而框只有 68 ⇒ 按**实测最大需求**改为 72）· Journey 卡 ×1（琥珀已撤、`OUT` 行在位、`→` 渲染正常）。

### 14.8 本轮改到的另一件事 —— Journey 卡里的 `P2`

owner 问「`P2` 是什么？为什么在体验地图里写这种代词」。

- **`P2` 有定义**：就在同一张卡的 persona 行 `6618:9` —— `P2 On-site Engineer`。它是**第 2 号 persona**，不是工单优先级。
  ⚠️ 我第一次回答说「`P2` 是上一 session 自造的、无事实源」，那是**错的** —— 当时只 grep 了 repo，没扫 Figma 卡本身就下了结论。
- **但 owner 的批评成立**：`Unit never returns → escalates to P2` 逼读者回表头解码一个只在本卡内成立的缩写。已改为 `Unit never returns → calls in the on-site engineer` / `设备未回 → 呼叫现场工程师`（`6619:14`）。
- **判据**（可复用）：交付卡正文里不写只在本卡内定义的缩写；persona 编号留在 persona 行做索引，格子里写角色本身。

### 14.9 M48 起手清单收口

起手列的 29 条命中规则，交付前逐条对照结果：**26 条覆盖，3 条带 rationale 未覆盖**。

| 未覆盖项 | rationale |
|---|---|
| **M23.7「invariants 段必显式列规则编号」** | ⑥ 的 invariant 段**没写** `M-XX` 编号 —— 因为既有 5 张状态标签**一条都没写**。只给第 6 张加会让它成为孤例（M49 一致性），规则编号的归属地是 UX 交付卡。⇒ 刻意与同族对齐，登记为偏离而非遗漏。 |
| **`library-binding`** | 该文件恒 `exit=2`（离线缓存不存在），**分母为 0** ⇒ 既非 pass 亦非 fail。补缓存是 DS owner 未裁的决定（INFRA-F62），⛔ 本轮不擅自 `sync:mockup`。 |
| **M23.6 圆点形态由机检验证** | 需把连线包成 GROUP，风险见 §14.6 末尾 ⇒ 显式不做，改由本轮的 probe + 渲染图人验。 |

⚠️ 起手清单里**没有**、但执行中才冒出来的三条，已补记在正文：切 variant 不换 instance-swap 图标（§14.2）· 全 Section 空白字形扫描（§14.3）· `connector` 闸的 schema 降级可修（§14.6）。**起手清单不是全集，它只覆盖得到「我预先想到的」那部分。**

### 14.10 仍然未决（⛔ 别当已完成）

1. **RPSV3-139 —— ✅ 关闭：owner 判定「不应该被创建」，2026-09-01 已在 Jira UI 手动删除。**
   - 该单是**我自行决定建的**（`creator`/`reporter` 实测均为 Nancy Zeng，2026-08-31 18:14），0 评论、0 issuelink。
   - ⚠️ **Atlassian MCP 无 delete issue 工具 ⇒ 我删不掉**，只能人工在 Jira UI 删（详见 [`PRODUCT_INTRODUCTION` §5](../PRODUCT_INTRODUCTION.md)）。
   - **删除的证据层级 —— ⛔ 别升格**：owner 于 **2026-09-01 17:36 口头报告已删**；**我没有做程序化核验**。不是遗漏，是纪律使然 —— 处置决定本身就是「AI 一概不碰这张单」，为验证而 `getJiraIssue` 就破了它。⇒ 本条按 owner 陈述关闭。
   - 原「回填评论」的议题**随之作废** —— 不再需要把 §13 口径写进这张单。
   - **根因教训**：「设计里发现一个 PM 待决问题」≠「该开一张 Jira 单」。建单是对外动作（分派给人、进别人 backlog），本例未先问 owner 就做了。⇒ **开单前先问**，⛔ 别当成交付收尾的常规动作。
   - 附带订正：当日 owner 答「JIRA 已经回复过」，实测 RPSV3-139 `comments.total = 0`；有评论的是 **RPSV3-134**（`268958`）。**转述与实测冲突时以 `getJiraIssue` 为准。**
2. **§10 视觉返工 + §11 文案定稿 + §14 分支重排与失败态 + §15 交付层重排，四轮改动都没有回 Jira / Slack 告知** —— ✅ **2026-09-01 owner 明确「暂不发」**（与前两轮同处置）。本条**按 owner 决定关闭**，不再作为待办；若后续 RPSV3-139 出结论要发回执，届时一并告知。
3. **`type=error` 的红色 `OK`** —— ✅ **2026-09-01 owner 拍板：不走 DS 提案，就地切绿色确认按钮**（owner 自行改稿，我方逐 paint 复核）。落地实测与偏离性质见 §14.2。**本条关闭。**
4. **`OUT` 标记** —— ✅ **2026-09-01 owner 授权回流，已合入 DS**。四档词表 `HIT` / `GAP` / `NEXT` / `OUT` 连同「为什么必须有 OUT」的判据进 `design-process.md § User Journey Map`，见 §16.6。**本条关闭。**
5. **交付卡的网格 / 状态图 / 对照表格式** —— ✅ **2026-09-01 owner 授权回流，pilot 转正为 `M23.19`**，见 §16.6。**本条关闭。**（⇒ §12.5「待定」状态同步作废。）
6. **`tvu-design-system/docs/internal/_design-kickoffs/rpsv3-134.md` 整份 stale** —— ✅ **2026-09-02 owner 拍板：留作历史 + 加 stale 抬头，正文一字不改。已落地**（DS `ea1e6dc3`，双 remote 已推）。见 §18.2。**本条关闭。**
7. **Jira 评论 268958 的 mention 渲染跨四个 session 从未验证**，只能人眼看，⛔ 别写成已验。
8. **Trevor 定稿的 Reboot confirm 文案「Transmissions will be automatically restored after reboot.」** —— ✅ **2026-09-01 owner 拍板：维持现有文案，不加限定语，不回 Slack 找 Trevor。**
   背景保留备查：互斥对照（§M-DISCIPLINE.SYNC ③）扫到过它 —— 严格讲重启现在**可能失败**，这句无条件承诺已不完全成立；但它是 Trevor 在 Slack thread 里逐字定稿并 ok_hand 的（§11.1）。owner 选择了「确认弹窗只讲正常路径」这一支。
   **注释层的口径此前已闭合**：④ 状态标签的 invariant 与 PRD 验收 6 都已加「重启成功时」限定（§14.3）。⇒ **产品文案与注释层的这处不对称是显式决定，不是遗漏**，⛔ 别在下一轮走查里当违例改掉。**本条关闭。**
9. **连线未包 GROUP**，导致 `connector` 闸的圆点形态检查仍不执行 —— ✅ **2026-09-01 实验已做，推测已变实测；结论是「别包 GROUP，改闸」。全部证据见 §17。** 下面保留当时的推测原文供对照。
   2026-09-01 owner 问「包的意义是什么？视觉上有什么不一样？」，已答：**视觉零差异**（GROUP 在 Figma 里不改变渲染，只在节点树上多一层容器）；唯一作用是让闸的 `colorableNodes()` 能水合 `groupChildren`、从而真跑 §C4 圆点形态检查；代价是 GROUP 的 bbox 会横跨大片区域，而 `overlap` 闸扫的正是 Section 直接 block 子节点 ⇒ **推测**会制造假阳性。
   owner 说「或者你改一个我看看」⇒ 已规划成一次**真实验**（拿 ①→② 那条包成 GROUP，跑闸看 ① 圆点检查是否真跑起来、② overlap 是否真报假阳性，把推测变实测）。**本轮因 owner 转向交付层重排（§15）而挂起，实验未做。**
10. **Figma Journey 卡上的两处 pilot 标注未随回流更新** —— ✅ **2026-09-02 owner 拍板：改成规则号引用，已落地。** meta 行 `6618:4` 现为 `grid format (M23.19)`；`designer read, not measured` **原样保留**（所有 Journey 卡的必填项）。见 §18.1。**本条关闭。**

---

## 15. 交付层重排 —— 让注释层跟上 §14 的产品帧布局（2026-09-01）

**触发**：owner —— 「更新完 UX 交付、用户体验地图、流程连线之后，Mockup 位置改变了，但是 UX 交付和用户体验地图的位置没有调整，目前整个看起来比较乱，还不适合回流，等调整好了我看看效果」。

**根因一句话**：§14 把 5 个产品帧从**横排 5 列**压成**两行 4 列**（Section 11000×3223 → 8960×4740），但注释层只做了「整体下移 1600 → 3120」这一个动作 —— **帧的布局变了，交付层的布局没跟着变**。这是 §M-DISCIPLINE.SYNC 的一个盲区：那条规则管的是「口径 / 文案的同步」，**没管「几何布局的同步」**。

### 15.1 改之前的实测几何（不是印象）

| 项 | 实测 |
|---|---|
| mockup 网格 | x `800–8840`、y `394–2866`（4 列 × 2 行） |
| 三张文档卡 | 全部挤在 x `60–2920` |
| **底部空带** | x `2920–8960` × y `3120–4740` ≈ **6040 × 1620 全空** |
| Section 高 4740 / 内容底 | 4615 ⇒ 底部还多 125 空 padding |
| 卡距最近 mockup 边 | **254px** ⛔ 破 M23.12 的「≤ 200px」 |
| 文档卡宽度 | PRD 680 / UX 680 / **Journey 1400** ⛔ 破 M23.12「文档卡统一宽度」 |

### 15.2 改成什么 + 每一步的规则依据

**定稿形态**（owner 手动摆过 Journey 卡之后的最终布局）：

```
┌─ PRD 卡 (60,40)  ─┐   Jira widget           ② → ④ → ⑥     ← 行 1 重启支
│  文档列 680 宽     │        ① Default
│                   │   ┌─ Journey Map ─┐     ③ → ⑤         ← 行 2 关机支
└─ UX 卡 (60,1502) ─┘   │ (800,2301)    │
                        │  1894×734     │
                        └───────────────┘
```

| 动作 | 落地 | 依据 |
|---|---|---|
| **PRD + UX 卡竖直堆成左侧文档列**（x=60，同宽 680）| UX 卡 (800,3120) → **(60,1502)** | M23.12「PRD / UX Delivery / Journey 属同一**文档列**语义，宽度取同一值 = PRD 宽」。此前三卡横躺在底部，「文档列」名存实亡 |
| 文档列紧邻 mockup | 卡右缘 740 ↔ ① 帧左缘 800 = **60px** | M23.12 Placement「≤ 200px、一屏内同见」。原 254 违例已消 |
| **Journey 卡位置 / 尺寸** | **owner 手动定稿 `(800,2301)` `1894×734`** —— 我自选的两版都被否，沿革见下 | owner 拍板 |
| **Section 收到实测内容 bbox** | 8960×4740 → **8900×3095** | 内容 bbox 实测右缘 8840（⑥ 帧）/ 底 3035（Journey 卡）+ 60 padding |

#### Journey 卡宽度 —— 我判断过头了两次，记录沿革与教训

| # | 谁 | 值 | 结果 |
|---|---|---|---|
| 1 | 我 | **8780**（跨内容带全宽）| ⛔ owner 否：「不应该填充满整个 Section」 |
| 2 | 我 | **2660**（右缘对齐 ① 帧右缘 2720）| owner 未采纳，直接手动改 |
| 3 | **owner** | **1894 @ (800,2301)** | ✅ 定稿（「放在这个位置和大小就 OK」）|

**我错在哪**：M23.12 里「注释整个流程的 summary 卡 = **跨内容带统一宽度**」确实存在，我据此把卡拉满 8780 —— **规则条文支持，不等于这是好设计**。实际代价是 stage 列被抻到 1710 宽、单元内文字只占约 15%，卡"贴合内容"这个更根本的原则反而被违反了。⇒ **判据（可复用）：拿规则条文为一个尺寸辩护前，先问「这个尺寸下内容还贴合吗」——条文授权的是上限，不是目标值。**

**第二个教训**：第 2 次我又自选了 2660，owner 直接手动改掉。**同一个尺寸判断连错两次后就不该继续自选**，该把候选值和取舍摆出来让 owner 挑，或者直接请他手动摆一次我照抄。

**owner 定稿版的几何关系**（我事后实测补的依据，非他明说）：左缘 800 = 产品帧列 0 左缘；宽 1894 ≈ 产品帧宽 1920；卡落在 ① Default 帧正下方（① 底 2193 → 卡顶 2301，间距 108）。

⚠️ **owner 手动缩宽当场把情绪层打断了**（他不知情，这是 §15.3 那个坑的必然结果）：折线仍是缩宽前的 1978，撑出 canvas **280px**；5 个圆点偏离列中心 **77 / 230 / 383 / 536 / 689**。已按 §15.3 重算复原（delta 全 0）。**⇒ 这条依附关系不能只靠"我记得改宽要重算"，它对手动改稿的人是完全隐形的**，已在 §8 的 Journey 条目里加了显式告警。

### 15.3 I7 ③ 依附几何 —— 本轮真正的坑

`emotion-canvas` `6623:6` 的 `layoutMode` 是 **`NONE`**（纯 frame）⇒ 其子层**绝对定位、不随卡宽走**。依附清单**共 6 个节点**，⛔ 不止那条折线：

| 节点 | 是什么 |
|---|---|
| `6623:7` | 情绪折线 VECTOR（`layoutSizingHorizontal=FIXED`）|
| `6623:8` – `6623:12` | 5 个 stage 的情绪 `pt` 圆点 ELLIPSE |

⚠️ **只看折线会漏掉 5 个圆点** —— 我起手的探针正是先只查了 VECTOR，是把扫描面扩到「整棵子树里所有 VECTOR/LINE/ELLIPSE/POLYGON/RECTANGLE + 任何 `layoutPositioning=ABSOLUTE`」才抓全的。**判据（可复用）：容器改宽前，按「不随 auto-layout 走的节点类型」全扫子树，别按「我记得那里有什么」列清单。**

**重算方法（⛔ 不用公式预判，用实测）**：读 5 个 stage 列头 `6618:13/15/17/19/21` 的 `absoluteBoundingBox`，算出各列中心相对 canvas 的偏移，再据此写折线 path 与圆点坐标。**每次卡宽变化都重跑这一段**（本轮跑了 3 次：8780 / 2660 / owner 的 1894）。
- 定稿版实测列中心（canvas 1698 宽）：`167 / 508 / 849 / 1190 / 1531`
- **对齐复验：5 个圆点 vs 列中心 delta 全为 `0`**（机器输出）；折线 1365 宽，落在 canvas 内不溢出

**纵向幅度也必须跟着宽度走，否则曲线读不出来**：情绪值 `[27,13,13,0,40]` 是**相对数据**，渲染幅度要按 canvas 宽等比缩放才能保持原稿的坡度观感。基线 = canvas `1204` 宽承载 `40px` 幅度 ⇒ **`K = canvasWidth / 1204`**。
- 8780 版：K=7.13 太大 —— 我当时误用了固定的 K=2.5，折线在 6874px 上只有 100px 起伏，仍偏平
- 定稿版：canvas 1698 ⇒ **K=1.41**，幅度 56.4，canvas 高 `70 → 100`

> 本条同时命中 I7 的「**同一批次里多次改动同一容器，每次改动后都量一次**」—— 卡高每变一次（681 → 755 → 760 → 734）Section 都重收了一遍，⛔ 没有只在批次末量一次。

**一条被排除的假阳性（留档避免下轮重查）**：owner 定稿位置后，`6643:738`（①→③ Power Off 连线）的**包围盒**与 Journey 卡相交。按路径**逐线段**复验 ⇒ **零线段穿过卡**，L 形连线绕在卡外。⇒ **非违例**。同 §14.5 的两条：**bbox 求交对 L 形 / 折线形 connector 恒虚胖，判连线压盖必须下钻到线段**。（`audit-mockup-overlap` 本身不把 VECTOR 纳入 block 级求交，故它 exit 0，与本次线段实测结论一致。）

### 15.4 自检（机器输出 + 与上一轮的程序化比对）

`Conformance report:` [`docs/handoffs/.conformance/rpsv3-134-6554-1028-20260901-layout.json`](../handoffs/.conformance/rpsv3-134-6554-1028-20260901-layout.json)
（**跑了 3 次、报告覆盖最后一次**：我的 8780 版 / 我的 2660 版 / **owner 定稿 1894 版**。三次逐规则 exit 与节点集合**完全一致**，下表是定稿版的。）

| 规则 | exit | 判读 |
|---|---|---|
| `integrity`（I1–I4）| 0 | ✅ |
| **`overlap`** | **0** | ✅ 本轮最关键 —— 移卡 + Section resize 后零重叠。⚠️ 该闸不下钻 VECTOR，连线压盖另由 §15.3 末的线段实测覆盖 |
| `geometry-consistency` | 0 | ✅ |
| `bilingual-spacing` | 0 | ✅ |
| `connector` | 0 | 仍「半明」（锚定真跑 / 正交 REST 不可验），未因本轮变化 |
| `colors` / `typography-icon` / `library-origin` / `binding-fidelity` | 1 | 存量，见下方比对 |
| `library-binding` | **2** | ⛔ 恒 could-not-run，**分母 0**，既非 pass 亦非 fail（[`PRODUCT_INTRODUCTION` §3.5](../PRODUCT_INTRODUCTION.md)）|

**存量四条不是本轮引入 —— 用节点级集合差证明，不是比总数**：

```
nodes with findings:  OLD 1433  /  NEW 1433
NEW 有而 OLD 没有（本轮引入）:  ∅
OLD 有而 NEW 没有（本轮消解）:  ∅
per-rule (node×rule):  binding-fidelity 788 / colors 351 / library-origin 373 / typography-icon 2  —— 两份逐条相同
```

⇒ **移动 / 缩放节点不产生新的绑定或色彩违例**，符合预期（本轮没新建任何带颜色的节点）。

**前置 gate 的机器证据**：
- **I6（move 前 probe）**：UX 卡目标区 `(60,1502,680×1495)` 与所有兄弟求交 = **空**，确认后才放
- **I2（Section resize 后）**：page 顶层三节点两两求交 = **0 对**；BEFORE `6564:608` 与 REFERENCE `6573:715` 均在负 x，未受影响
- **归属回读**：两张卡移动后 `node.parent` 均回读为 `6554:1028`（§3.7 第 2 坑的教训 —— 截图证明不了节点挂在谁下面）
- **连线未受影响**：5 条 flow 的 20 个部件与三张卡求交 = **0** ⇒ I7 ③ 对「卡移动」这一项的答复是**实测无依附**（连线锚在产品帧上，产品帧本轮没动），不是推断

### 15.5 亲验渲染

Section 全景 ×4（重排前 / 8780 全宽版 / 2660 版 / **owner 定稿 1894 版**）· Journey 卡单张 ×1（情绪曲线起伏恢复、圆点落在列中心、5 个 stage 列文字未截断）。

⚠️ **连线压盖那一条是先看渲染图起疑、再用线段实测证伪的** —— 机器 bbox 说「相交」，渲染图看着像绕在外面，两者矛盾时**以线段实测为准**（§15.3 末）。⛔ 别只凭渲染图说"看着没压到"，也别只凭 bbox 说"压到了"。

### 15.6 本轮暴露的一条规则空白 —— ✅ 2026-09-01 已回流

**§M-DISCIPLINE.SYNC 只管「口径 / 文案的同步」，不管「几何布局的同步」。**

§14 改完产品帧布局后，SYNC 的三轮扫查（旧措辞正则 / 新事实逐条 / 互斥对照）**全都跑了且全绿** —— 因为它们扫的是**文字**。而错的是**几何**：注释卡还停在按 5 列横排设计的位置上。闸绿、文档对、图是乱的。

**建议的条文形态**（做法进 DS，取值留本仓）：
> 产品帧的**布局形态**（列数 / 行数 / 跨度 / Section bounds）发生变化后，交付层的 **placement 与 sizing 必须按 M23.12 重新推导一次**，并实测「卡距最近 mockup 边」与「文档列同宽」两个判据 —— ⛔ 整体平移不算同步。

✅ **已落地**：`mockup-conventions.md §M-DISCIPLINE.SYNC` **新增第 7 条**（不是把它塞进 §2a 的三轮 —— 那三轮是「文字扫查」的三种形态，几何对照与它们不同族，并列为独立条目更清楚）。含触发清单（列数/行数/跨度/拓扑变化·帧增删·Section bounds 变化）、五项复核表、以及两条红线（整体平移不算同步 · 机器闸抓不到）。jump 表 SYNC 触发行同步更新。分支 `rules/rpsv3-134-backflow`（commit `be96b9e7` + `80c7f0f6`，已 push，**未合 master**）。

### 15.7 条件标签脱离连线 —— owner 目视抓到、所有闸全绿（2026-09-01）

**触发**：owner —— 「Power Off 的流程线上的文字说明现在离流程线很远，为什么没有检测出来呢？」

#### 事实（按 M-DISCIPLINE.SCOPE 全量测 5 条，不只测被指出的那条）

**标签放置范式**（从其余 4 条反推，此前从未写下来）：**标签贴在「通向目标态的那段横线」上方 19–25px，且距该段起点右移 60–72px。**

| 连线 | 标签贴在哪 | 修前 |
|---|---|---|
| ②→④ · ④→⑥ · ③→⑤ | 通向目标的横线上方 **25px** / 右移 60–72 | ✅ 合范式 |
| ①→② | 通向 ② 的第二段横线上方 **19px** / 右移 60 | ✅ 合范式 |
| **①→③** | **没贴任何横线** —— 贴在长竖段（x=2800, y 1840→2950）**左侧** | ⛔ **违例** |

#### 为什么「30px」这个数字是假的 —— 框宽 vs 可见字形

`6643:740` 是 **`LEFT` 对齐的定宽框**：框宽 220，**真实字形宽只有 75**，框内死空间 **145**。

| 量法 | 结果 |
|---|---|
| 节点框右缘 → 线 | **30px**（看着正常）|
| **可见字形末端 (x=2625) → 线 (x=2800)** | **175px**（owner 看到的那个"很远"）|

⇒ **差 5.8 倍。⛔ 判「标签是否贴着线」必须按可见字形量，不能按 TEXT 节点的框量。** 五个标签框内死空间 91–158 不等，框量法在任何一个上都可能同样失真。

⚠️ **为什么框量法对其余 4 条恰好没出事**：它们都贴在**横线上方**，此时起支配作用的是**纵向**间隙，框的**横向**死空间不产生视觉间隙。**只有唯一那条贴竖线的，横向死空间才直接变成可见间隙。** ⇒ 这不是"框量法基本够用、偶有例外"，是**它在整整一类几何关系上系统性失效**，而那一类在本交付里恰好只有一个样本。

#### 为什么没有任何闸抓到 —— 逐个查它们各自断言了什么

| 闸 / 规则 | 它实际断言的 | 能否抓到本例 |
|---|---|---|
| `connector` 闸 | 两端锚定（端点 ±8px 落在触发/目标元素）+ 正交性 | ❌ **标签不在它的对象集里** —— reconnect map schema 只有 `from` / `to` / `triggerElemId` / `targetElemId`，**没有 label 字段** |
| `overlap` 闸 | Section 直接 block 子节点两两**求交** | ❌ 结构性反向：它抓"压住/太近"，"离太远"对它是恒绿 |
| M23.6 | Connector **Endpoint** Anchoring + Frame Content Avoidance | ❌ 管端点与避让帧内容，**无标签附着条款** |
| §14.5 我的自跑探针 | 正交性 5 条 + 两端锚定 10 项 | ❌ 两项**都只关于那条 vector**，一次都没碰标签 |
| §15.3 线段实测 | 连线是否穿过卡 | ❌ 对象是卡与线，不是标签与线 |

**结论：这条性质从来没有被任何判据表达过。** 闸全绿，绿的是别的事 —— `M-GATE-FALSIFIABILITY` 的教科书形态（「判过 0 个单元的『没发现问题』= 没验」，本例更彻底：**判据根本不存在**）。

**它与 §15.6 是同一族**：SYNC 管文案不管几何 → 卡没跟着帧走；连线闸管端点不管标签 → 标签没跟着线走。**两次都是「布局变了，但只有一部分几何被重新推导」。**

#### 已修 + 复验

`6643:740`：**(2550,2220) → (2860,2896)**，即通向 ③ 的横线（y=2950, 起点 x=2800）上方 **25px**、右移 **60px**，与其余 4 条同范式。

- I6 前置 probe：排除**本连线自身部件**后零碰撞（⚠️ 首跑因为没排除自身，被自己那条连线的虚胖 bbox 挡住了 —— 守卫生效，但判据要写对）
- 线段实测：标签矩形**不与自身任何线段相交**（贴着但不压住）
- 离 ③ 帧底 30px、离 Journey 卡右缘 166px

**全 5 条复验（可见字形口径，阈值 60px）**：19 / 25 / 25 / 25 / 25 ⇒ **全 PASS**。
**阴性对照**：把 ②→④ 的标签平移 `(+400,+400)` 后实测 **346px ⇒ 判 FAIL**，随即归位 ⇒ **该判据有判别力，不是恒绿**（M-GATE-FALSIFIABILITY 硬要求）。
修后 5 条的 `distByBox == distByGlyph`，因为它们现在**全部贴在横线上方** —— 这条等式本身就是"范式已归一"的旁证。

#### ✅ 2026-09-01 已回流（分支 `rules/rpsv3-134-backflow`（commit `be96b9e7` + `80c7f0f6`，已 push，**未合 master**））

建议给 **M23.6** 增补第三款（现有两款 = 端点锚定 + 帧内容避让）：

> **(C) 条件标签必须附着于它所标注的那一段**：标签的**可见字形**外框距其连线的目标段 ≤ 60px，且**统一贴在该段的同一侧**（本项目范式：通向目标态的那段横线**上方 19–25px**、距段起点右移 60–72px）。
> ⛔ **判据必须按可见字形量，禁按 TEXT 节点框量** —— 定宽 + `LEFT` 对齐的标签框内死空间可达 145px，框量法在"标签贴竖直段"这一类几何上系统性失真（实证 RPSV3-134 `6643:740`：框量 30 / 字形量 175）。
> 阴性对照：任取一个合规标签平移 400px，检查必须转红。

落点：`mockup-conventions.md §M23.6`（做法），本仓 §15.7 留实证与取值。**本轮未动 tvu-design-system 仓。**

---

## 16. 回流 DS 设计系统（2026-09-01，owner 授权）

**owner 原话**：「把这次出现的问题和解决方案回流到 DS 设计系统中，能提前限制的就提前限制，提前限制不了的就事后检查的时候看，包括但不限于 UI 视觉语言的一致性等问题。」

**✅ 已合入 DS `master`**：`2a26955b → bfce9d5d`，三个 commit（`be96b9e7` 回流主体 · `80c7f0f6` VARIANT 扩子层 · `bfce9d5d` M37.3），gitea + GitHub 双 remote 均已推。**临时分支与 worktree 已清理**（删前程序化确认 tip 确在 `origin/master` 内）。

🔴 **一处我判错的前提，如实记下**：过程中我用 git worktree 隔离作业，理由是「DS 仓有并行 session 正在推 master」——**依据是 HEAD 从交接记的 `b5fcacdd` 变成了 `2a26955b`**。owner 当日澄清 **当时并没有并行 session**，那个位移来自**他自己更早的 session**。
⇒ **「HEAD 动过」只能证明「有人推过」，不能证明「此刻有人在并发操作」。** 判并发要看的是别的信号（是否有活跃的 dirty 工作树 / 是否有人正在同一分支上 commit），⛔ 别拿 HEAD 位移当并发证据。
⇒ 隔离本身**没有造成损失**（worktree 是纯增量、事后已清理干净），但它让我多走了「分支 → push HEAD:master → 删分支 → 清 worktree」一整圈，且据此在文档里留下了错误陈述。**成本不高但结论是错的，所以记下来。**

⚠️ 顺带一条仍然成立的做法：合并时**没有走 DS 主工作树**，而是从隔离环境直接 `git push origin HEAD:master`。这条在真有并发时是对的；本次虽无并发，做法本身无害。合并后主工作树曾落后 3 个 commit，**已在 owner 澄清后 `pull --ff-only` 拉齐**。

### 16.1 落点全表

| # | 问题 | 落点 | 档 |
|---|---|---|---|
| 0 | **同页同类模块的视觉取值不一致** —— 把 Config T 参考图的按钮 + 底色块整块搬进 RPS 页面，没按本页既有同类模块的实测值调整（owner 提出后才改）| **`M37.3`（新子号）** | 提前限制 |
| 1 | 一个视觉信号在同一交付集内承载两种**语义**（红＝破坏性动作 / 红＝关闭错误提示）| **`C8`（新号）** | 提前限制 |
| 2 | 条件标签脱离它标注的那段线 | **`M23.6 (D)`（新子款）** | 提前限制 |
| 3 | 量法错：按 TEXT 节点框量而非可见字形；按 bbox 判折线而非线段 | **`M-DISCIPLINE.MEASURE`（新子规则）** | 提前限制 |
| 4 | 布局形态变了，交付层只做了整体平移 | **`M-DISCIPLINE.SYNC` 第 7 条（新）** | 提前限制 |
| 5 | 援引条文把卡拉满宽（条文授权 ≠ 好设计）| **`M23.12` 新增补段** | 提前限制 |
| 6 | 依附几何清单凭印象列；`layoutMode: NONE` 容器；对手动改稿者不可见 | **`I7 ③` 扩写** | 提前限制 |
| 7 | 切 variant 不覆盖子层 instance-swap override（承 §14.2，此前记为未回流）| **`M-DISCIPLINE.VARIANT` scope 由「文本」扩到「子层全体」** | 提前限制 |
| 8 | clone 残留图层名 | **`M42.2` 病灶 3 + checklist 5 + Acceptance** | 提前限制 |
| 9 | 自行决定建 Jira 单（对外动作、且多数 MCP 删不掉）| **`design-process.md` §交付收尾 第 7 条（新）** | 提前限制 |
| 10 | 上述里机器闸**结构上**抓不到的那些（几何 / 信号语义）| **`design-walkthrough` §5.10 新固定层轴 E1–E9** + Axis Ledger 登记 + Completeness Gate 加 E3/E8 | 事后检查 |
| 11 | **全页唯一值**（新增模块的某个视觉取值在本页找不到同类先例）| **`design-walkthrough` §1 新增「宿主页取值一致性」属性表** + Axis Ledger 登记 + Completeness Gate 一行 | 事后检查 |

**「提前限制 vs 事后检查」的分界**（owner 那句话的落地判据）：**判据能在动手前用一句话说清、且违反时能指出具体节点** ⇒ 写成规则（提前限制）；**判据依赖「看整套交付物之后的判断」** ⇒ 进走查轴（事后检查）。C8 两边都进：规则给判据，E8 给走查时的执行动作（产出信号 × 语义对照表）。

### 16.2 为什么必须新开走查轴（§5.10）而不是只加规则

本轮的两个缺陷**都是 owner 目视发现的，而当轮机检全绿** —— 且不是漏跑，是**断言对象不同**：

| 闸 | 它断言什么 | 为什么抓不到 |
|---|---|---|
| `overlap` | 直接子节点两两**相交** | 「离太远 / 空一大片」对它恒绿 |
| `connector` | 端点锚定 + 正交 | **对象集里没有标签**（⚠️ 2026-09-01 已部分修复：标签经 `parts.label` 进了**颜色**对象集，**附着几何仍不判** —— 见 §17.8） |
| `colors` / `binding-fidelity` | **每一处**填色是否合法 | 不判「同一信号在整套里是否单义」|
| `M-DISCIPLINE.SYNC` ①②③ | **文字** | 文案全对、图是乱的，三轮全绿 |

⇒ §5.10 在 Axis Execution Ledger 里明确写了：**「conformance 机检全绿」不是本轴 SKIPPED 的合法理由**。

### 16.3 机检与刻意未做

- DS 侧四闸全绿：`rule-inventory` / `rule-number-collision` / `rule-load-map` / `stale-anchors` 均 **exit 0**（改前改后各跑一次）。
- `audit:docs-overflow` **could-not-run**（`playwright: command not found`，环境缺依赖）—— ⛔ 既不是 pass 也不是 fail，与本次改动无关。
- **`docs/STATUS.md` 未加条目 —— 理由，不是遗漏**：① 它的 `Last updated` 已经是 2026-09-01，无需改；② 该文件顶部段是**按「落地线轮次」写的替换式叙述**，归属正在跑落地线的那个 session（当前是「第八十四轮」），我这次是规则回流、**不属任何落地线轮次**，写进去会覆盖它的轮次总结；③ 本次改动**没有关闭任何 Active / backlog entry**，没有"按段移动完成项"可做。⇒ 如果 DS owner 认为规则回流也该在 STATUS 留痕，那是一条独立动作。
- ~~**仍未回流（不属本次 scope）**：§14.10 第 4 条 `OUT` 标记 + 第 5 条交付卡网格/状态图/对照表格式~~ —— ✅ **2026-09-01 owner 授权，已回流，见 §16.6。**

### 16.4 owner 更正「视觉语言一致性」+ 扩到产品级（2026-09-01 下半场）

**我上一轮理解错了。** owner 说的不是 `C8`（一个信号两种语义），而是：

> 「同一个页面的**相同模块**的文字大小、背景颜色等这类元素需要一致性，就比如一开始你直接把 Config-T 的开机按钮和背景颜色直接拿过来用，而没有根据当前页面的视觉元素进行调整，等我提出来后你才调整成现在的样式。」

随后又扩了作用域：

> 「可扩展为**同一个产品下页面之间的视觉结构的一致性**，在**设计前期**就需要考虑，**验收和设计走查**的时候也应该检测到。」

**已落地（DS master `bfce9d5d` → `a150340c`）**：

| 落点 | 管什么 |
|---|---|
| `mockup-conventions.md §M37.3` | 参考物只定「做什么」，**视觉取值一律取宿主同类模块实测**；逐属性找先例，找不到 = 全页唯一值 |
| **`design-process.md §M21.4`** | **作用域是「产品」不是某个 sibling 页**；两级先例搜索（本页 → 本产品同类页）、**止于产品**；三个时点：**前期建契约 · 验收查 · 走查兜** |
| `tvu-design-mockup` 起手硬闸 ⑥ | 把「产品级视觉结构契约」提到**开建前**（并顺手订正了「四件套」这个已过时的说法 —— 实际已是六件）|
| `design-walkthrough §1` 属性表 | 加**先例来源**列（本页 / 本产品某页 / 无）+ 顺带核「UX 卡 Acceptance 里那条在不在」|

**⛔ 三条别混**（已在 DS 的 jump 表里互相划清）：`C4` 判**跨图层**的色彩归属 · **`M37.3` / `M21.4` 判「取值一不一致」** · `C8` 判「同一个信号是不是一号多义」。

#### 16.5 code 侧镜像补齐（DS master `a150340c` → `4b7e9724`）

owner 指出：**只改了 mockup 侧的设计流程，没改 code 侧，两边应该同步。** 属实 —— 而且 DS 仓本来就有这条明文惯例：R 规则是 M 规则的 code-side mirror，指针原文写着「**改此条必同步对面**」。⇒ 只落一侧，本身就是这批规则要治的那类 drift。

| M 侧 | code 侧镜像 | 该镜像补的是什么洞 |
|---|---|---|
| `C8` | **`R2.2`** | R2 / R2.1 保证「每一处颜色都合法」，**不保证「同一信号只表达一件事」**；lint 全绿仍可能违例 |
| `M21.4` | **`R6.1`** | R6 的决策树只说「复用 app 已有范式」，**没说这个范式是谁的** |
| `M37.3` | **`R18.1`** | R18 的 probe 表本来就有 `Style / spacing tokens` 一行，**但它只要求"提取"、没禁止"照抄"** |

- **code 侧特有的高危形态**（已写进 R18.1）：**copy 一段 CSS / 一个 class / 整个组件文件过来改** —— 病灶与 Figma 侧照搬参考图相同，但 diff 里只是「多了几行样式」，**review 更难看见**。
- **M 侧三条都钉了反向指针**（`↔ Code 端镜像` + 「改此条必同步对面」），否则下次改 M 侧的人不知道对面有镜像。
- `tvu-design-code` skill 补 **step 6.5**（前期视觉结构契约硬 gate），与 `tvu-design-mockup` 起手闸第 ⑥ 件对齐。
- `design-walkthrough` 补 §1 属性表的 **Path B 读法**（代码交付物用 `文件:行` + computed style，没有 node id）。

⚠️ **顺带修掉一处同型的既有不同步**：`tvu-design-code` 的「回流入口」还写着二分法「全部追加到 code-conventions.md」，而 `tvu-design-mockup` 侧早已改成**三档落点**（memory / DS 真源 / 项目 repo）。**同一条纪律只改了一边** —— 正是 owner 这次指摘的形态。已补齐，并在回流步骤末尾加了一条：「若该规则有 mockup 端镜像，两侧必须同批改并互留指针，**只改一边 = 未完成**」。

#### 对 RPS 自己的直接含义（⏳ 未做，下一轮起手要面对）

M21.4 要求**开建前**手里有该产品的视觉结构契约。**RPS 目前只有半份**：

- ✅ [`PRODUCT_INTRODUCTION` §3.6](../PRODUCT_INTRODUCTION.md) 是 **RPS Link (New) Home 页**的可见视觉契约（内容行范式 / label 列 / 值列起点 / 卡内填充规则 / 按钮变体），且已注明每条取自哪个 node id —— 这正是 M21.4 说的那种产物。
- ⛔ **但它只覆盖一页**。M21.4 要的是**本产品同类页面全集**的共同结构。RPS 主文件有 **29 个 page**（§3.1 实测），Home 之外的页从未 probe 过。
- ⇒ **下一个 RPS 需求起手时**：要么把 §3.6 扩成跨页契约（probe 同类页面全集），要么显式声明「本轮只涉及 Home 页，契约范围限本页」并写明理由。⛔ 别再默认「有 §3.6 就等于有契约」。

#### 16.6 交付卡格式 pilot 转正 + `OUT` 标记（2026-09-01 下半场，owner 授权）

**触发**：owner —— 「记回流 / 第 5 条交付卡格式 pilot，可以回流」。即 §14.10 第 4、5 两条（此前 §16.3 明确记为「不属本次 scope」的那两条）。

**✅ 已合入 DS `master`**：`72a3f196 → a5448609`（单 commit，gitea + GitHub 双 remote 均已推）。**走 DS 主工作树，未开 worktree** —— 按 §16 那条更正后的判据：HEAD 从交接记的 `4b7e9724` 位移到 `72a3f196` 只证明「有人推过」，不证明「此刻并发」；实测工作树干净、无活跃 dirty，故不隔离。

| # | 回流内容 | 落点 | 档 |
|---|---|---|---|
| 12 | 有结构的内容被写成并列句子（状态机 / 可比矩阵 / 一一对应两列） | **`mockup-conventions.md §M23.19`（新号）** | 提前限制 |
| 13 | Journey Map 摊成纵向文字块 ⇒ 横向可比性归零 | **`design-process.md § User Journey Map` 输出格式段**（网格升为默认形态） | 提前限制 |
| 14 | 情绪层的三笔隐性账（数据来源未标 / 被连线闸当 connector 扫 / 卡宽变了不重算） | 同上，写成三条硬约束 | 提前限制 |
| 15 | 被拍板「不做」的事项只能挂成 🔴，读成交付缺陷 | 同上，`✅/🟡/🔴` → **`HIT` / `GAP` / `NEXT` / `OUT`** 四档 | 提前限制 |

**几处刻意的边界，⛔ 别当遗漏**：

- **`M23.19` 显式排除叙述性段落**（Source / Background / Current State / Priority）。承 §12.1 那条「PRD 其余段刻意不动」—— PRD 会被当合同看，**精确 > 简洁**。规则里把「把叙述段硬塞进表格」写成了**反向违例**，防下一个人把本条读成「一切都该表格化」。
- **`OUT` 必须连拍板依据一起写**（`OUT — <结论>: <依据>`）。裸 `OUT` 会退化成一个更委婉的 `GAP`，等于没解决原问题。
- **`M23.19` 没有 code 端镜像 —— 这是实测不是省略**：`grep "交付卡\|M23" code-conventions.md` 命中 **0**，M23 卡族的对象是 Figma 注释层，代码交付物不产这种卡。已把「grep 结果 + 将来若出现等价物按镜像纪律两侧同批建」写进条文本体（承 §16.5 那条「只改一边 = 未完成」的纪律 —— **这次是查过之后确认无对面，不是没查**）。
- **`design-process` 的文档端 markdown 模板同步改了标记词**，Figma 端强制文字词、文档端两种皆可。

**机检**：DS 四闸 `rule-inventory` / `rule-number-collision` / `rule-load-map` / `stale-anchors` 改后各跑一次，**全 exit 0**；两个新写的跨文件锚点已过 `stale-anchors` 的 slug 校验（非目测）。

**⏳ 遗留**：Figma Journey 卡上的 `grid format (pilot)` 字样未改（格式已转正，标注名不副实）—— 见 §14.10 第 10 条。

---

## 17. 连线包 GROUP —— 真实验（2026-09-01，owner「或者你改一个我看看」）

**问题来源**：§14.10 第 9 条。当时只有推测：包 GROUP **可能**让 `connector` 闸的圆点形态检查跑起来，但**可能**让 `overlap` 闸出假阳性。本节把两条推测都做成了可证伪的实验。

### 17.1 设计实验前先读闸的源码 —— 这一步改掉了实验方案

⛔ **如果直接「在 Figma 里框一个 GROUP 然后跑闸」，实验会得出错误的阴性结论。** 读 [`scripts/audit-mockup-connector.mjs`](https://github.com/NancyZeng0210/TVU-Design-System/blob/master/scripts/audit-mockup-connector.mjs) 才发现：

```js
if (groups[a._id]) a.groupChildren = groups[a._id].children || []
else if (node) a.selfNode = node
```

`a._id` 取自 **reconnect_map 里那条 arrow 的 `id` 字段**，不是「Figma 里那三个节点的父容器」。⇒ **包 GROUP 本身对闸零影响**，必须同时把 map 里的 `id` 从 VECTOR 改指 GROUP。两个动作缺一，实验就会假阴。

### 17.2 基线（改动前，机器输出）

| 闸 | 输出 | exit |
|---|---|---|
| `connector` | `connectors: 5 条，节点已解析 5 条` · `降级跳过（不可验证）: orthogonal×5` · ✅ | 0 |
| `overlap` | `sectionInternalOverlap = 0` · ✅ | 0 |

### 17.3 改动

`figma.group([6643:735 VECTOR, 6643:736 ELLIPSE, 6644:735 POLYGON], Section)` → GROUP **`6694:863`**（bbox 1464,1164 · 2341×636），并把 map 里 `Reboot → confirm` 那条的 `id` 从 `6643:735` 改指 `6694:863`。Section 直接子 36 → 34。

### 17.4 结果 A —— `overlap` 假阳性：**坐实，且比预测的多**

推测是「会制造假阳性」，实测 **5 条**，`exit=1`：

| # | 与谁相交 | 相交量 |
|---|---|---|
| 1 | `6554:1029` ① Default 产品帧 | 1256×507 |
| 2 | `6561:521` ② Reboot confirm 产品帧 | 965×356 |
| 3 | `6565:706` ① 状态标签 | 1256×80 |
| 4 | `6565:716` ③ 状态标签 | 965×60 |
| 5 | **`6643:737` —— 它自己的条件标签** | 220×29 |

**根因**：`audit-mockup-overlap.mjs` 的 `BLOCK_TYPES = {FRAME, GROUP, INSTANCE, COMPONENT, TEXT}`。VECTOR / ELLIPSE / POLYGON **本来就不在比较集里**，所以裸连线对 overlap 天然免疫；一包 GROUP，连线**首次成为被比较对象**，而它的 bbox 是一个 L 形折线的包围盒（§M-DISCIPLINE.MEASURE 说的那种「恒虚胖」），横跨两个产品帧。
⇒ 第 5 条尤其说明问题：**GROUP 和它自己的条件标签被判为互相压叠**。这不是设计缺陷，是把不该进比较集的东西塞了进去。

### 17.5 结果 B —— 圆点形态检查：**确实跑起来了，但闸的绿证明不了这一点**

包完 GROUP 后重跑 `connector`，输出**与基线一字不差**（`✅ 可验证项全部合规`，`--json` 里 `color: []`、无 color 的 skipped 条目）。

⚠️ **这里差点收出一个错误结论。** 该输出在两个世界里完全一样：

- **世界 A**：`groupChildren` 水合成功 → 找到 ELLIPSE → 圆点检查跑了 → 圆点本来就合规 → 无输出
- **世界 B**：走 `selfNode` 分支 → `ell` 为 `undefined`，而 `palette.length === 1`（非 0）⇒ `if (ell)` 与 `else if (palette.length === 0)` **两个分支都不进** → 检查静默不执行，**连一条 `skipped` 都不记**

⇒ **只能靠阴性对照分开**。把圆点 `6643:736` 的 fill 临时改成红色后重跑：

```
color violations: [{"label":"Reboot → confirm","why":"dot fill not white (hollow/wrong)"}]
exit=1
```

**且只有被包起来的那一条报，另外 4 条静默** —— 双向证据：包了的真跑，没包的结构上就不跑。（圆点原值 fill `#FFFFFF` / stroke `rgb(0.2,0.643,0.992)`，已逐 paint 复原。）

🔴 **本实验最有价值的副产物是一个闸缺陷**：世界 B 里圆点检查**不跑也不进 skip 列表**。`connector` 闸自印的「降级跳过」只有 `orthogonal×5`，读者会以为颜色项全验过了 —— 而 5 条里实际上 **0 条**做过圆点形态检查。这正是 §16.2 那张表的同型病灶，且比那些更隐蔽：**它连「我没验」都没说**。

### 17.6 结果 C —— 「视觉零差异」这个此前给 owner 的答复**不完全准确**

2026-09-01 上半场我答过「GROUP 视觉零差异，只在节点树上多一层容器」。**GROUP 本身确实不渲染，但 `figma.group()` 会把成员塌缩到单一 z 位置**：三个节点原索引是 **14 / 15 / 26**（中间隔着 10 个节点），成组后 GROUP 落在索引 **33**。实测与该 bbox 相交的兄弟共 8 个，其中 `6643:737`（条件标签，原 16）、`6643:738`/`6643:739`（关机支连线，原 17/18）原本在我方 vector/dot **之上**，成组后全部变到**之下**。

⇒ 严格的说法是：**渲染顺序确实变了；是否有像素级差异取决于实际像素重叠，我没有做像素级比对**。⛔ 别把「GROUP 不渲染」直接等同于「视觉零差异」——前者是真的，后者需要额外论证。

### 17.7 复原（已做，非声明）

`ungroup` + `insertChild` 显式把三个节点放回**原索引 14 / 15 / 26**，map 的 `id` 与 `note` 逐字复原。**验证不是目测**：

| 项 | 基线 | 复原后 |
|---|---|---|
| Section 直接子数 | 36 | **36** |
| vector / dot / arrowhead 索引 | 14 / 15 / 26 | **14 / 15 / 26** |
| `arrowIds` | 6643:735, 6643:738, 6643:741, 6661:863, 6643:744 | **完全一致** |
| `connector` 闸 | exit 0，跳过 `orthogonal×5` | **完全一致** |
| `overlap` 闸 | exit 0，`sectionInternalOverlap = 0` | **完全一致** |

**⛔ 为什么不把 GROUP 留着给 owner 看**：视觉上没有东西可看（结果 C 说的 z 序变化肉眼不可辨），而留着的代价是 `overlap` 闸对这个 Section **永久红**、后续每一轮走查都要先排除 5 条幻影。实验目的是把推测变实测，这已达成。**要重现，照 §17.3 两行即可。**

### 17.8 结论 —— **别包 GROUP，改闸**（✅ 已执行，DS master `ef80ffb0`）

包 GROUP 能换来圆点检查，但代价是 5 条 overlap 假阳性 + z 序变动，**净负**。而实验中发现 map 里**早就有现成的答案**：

```json
"parts": { "vector": "6643:735", "dot": "6643:736", "arrowhead": "6644:735", "label": "6643:737" }
```

⇒ **`reconnect_map` v3 每条 arrow 都已逐条列出散落子节点的 id，闸从来没读过这个字段。** 让 `audit-mockup-connector.mjs` 在没有 GROUP 时从 `parts` 水合 `groupChildren`，即可拿到圆点检查，**且 Figma 侧一个节点都不用动** —— 零 overlap 影响、零 z 序影响、零视觉风险。

✅ **两条 DS 侧改动已落地**（owner 2026-09-01 授权「按建议执行」，合入 DS master `ef80ffb0`，gitea + GitHub 双 remote 已推）：

| # | 改了什么 | 结果 |
|---|---|---|
| **G2**（先做 —— 诚实） | `classifyConnectorViolations()` 补第三支：`ell` 缺失但 palette 非空时记 `check: 'dot-shape'` skip | 「不跑也不说」的静默洞关闭。⛔ 刻意**不**升成 violation：散落 schema 是既有事实，false-flag 违反该闸头注释的降级纪律 |
| **G1**（后做 —— 能力） | `buildConnectorInput()` 解析改三级：`GROUP` → **`parts{}` 水合** → `selfNode` | 圆点形态检查真跑起来，**Figma 侧一个节点未动** |

**验证是阴性对照，不是闸的绿**（这正是 §17.5 的教训）：把 5 个起点圆点填色临时改红 ⇒ **5/5 全部命中 `dot fill not white`、exit=1**；逐 paint 复原后 `color: []` 且 `dot-shape` skip 数为 **0**、`unverified=5` 全是 `orthogonal` ⇒ 5 条**都真跑了**。改动前同一对照只有被临时包成 GROUP 的那 1 条会红。

**顺带闭合的两件**：

- **条件标签进了 §C4 的颜色对象集** —— `parts.label` 一并水合。§C4 Acceptance 第 2 条本来就点名了「条件标签」，此前只是闸没落地。⛔ **但 §M23.6 (D) 的附着几何仍无机器兜底、仍须人跑**：闸能判标签是不是绿的，判不了它离那段线多远。
- **订正 DS 里一句写错的理由**：§M23.6 (D) 原写「reconnect map schema 没有 label 字段」—— 错的，字段一直在（`parts.label`），是闸没读。已就地订正，(D) 的结论一字未变。

⚠️ **同批清掉了本工作线自己欠的 acceptance-coverage 债**（此前我误判为「别人的既有债」，对照点选错所致）：在回流开始前的 `2a26955b` 实测 `audit:acceptance-gate-coverage` 是 **exit 0 · unclassified 59 = BASELINE**，⇒ 那 6 段新 `unclassified` 与 1 条 S4 **全部**由 RPSV3-134 回流引入。已逐段登记 `C8` / `M23.6 (D)` / `M21.4` / `R2.2` / `R6.1` / `R18.1` 为 not-machine-checkable（每条各写理由 + 各标 borderline，⛔ 没做打包豁免、⛔ 没动 BASELINE）。它们落这一档是自洽的 —— 那批回流的主题本来就是「机器闸结构上抓不到的那类缺陷」。

🔴 **S4 那条暴露出一个结构性风险，值得单独记**：`covers-acceptance` 自陈用的 `Acceptance~N` 是**序号不是锚点**。M21.4 在 `design-process.md` 靠前处插入一个 Acceptance 段，把其后所有序号顶了一位，`audit-handoff-deliverable-sections.mjs` 的自陈就此静默指向了另一段。**这次是 Σ 计数（3 ≠ 2）把它抓出来的；若漂移后恰好撞上一个条数相同的段，S4 恒绿而自陈已指错。** 已修正目标并把这条风险写进那个脚本的头注释。

---

## 18. owner 决策的落地（2026-09-02）

**起点**：§14.10 剩下的三条开项 + DS `INFRA-F142` 的候选解待拍。四件里 owner 当日拍了三件，第四件（Jira mention 渲染）性质上只能人眼看 ⇒ AI 不碰。计划文件见 [`2026-09-02-rpsv3-134-followup-plan.md`](./2026-09-02-rpsv3-134-followup-plan.md)。

起手基线：RPS `b44bae4`（干净）· DS `0827ccfc`（干净，**behind origin/master 1**，动手前已 pull 到 `861d76c6`）。

### 18.1 Journey 卡 meta 行 —— `pilot` → 规则号（§14.10 #10）

owner 决策：**改成规则号引用**（不是删掉），因为格式已从 pilot 转正为 `M23.19`，留个指针下一个人才知道去哪查判据。

| 项 | 值 |
|---|---|
| 节点 | `6618:4`（整条 meta 行是**一个** TEXT，单字体段 Roboto/Medium 12） |
| 改动 | `grid format (pilot)` → `grid format (M23.19)`，其余一字未动 |
| ⛔ 保留 | `emotion curve = designer read, not measured` —— 所有 Journey 卡必填项 |

**几何实测（改前 / 改后）**：宽 `1846 / 1846` · 高 `14 / 14`（`textAutoResize=HEIGHT`，**没换行**）· 右缘 slack `24 / 24` · 卡框 `1894×734` 未动 · `node.parent` 回读仍是 `6618:2`（§3.7 第 2 坑：截图证不了节点挂在谁下面）。

**亲验渲染**：`6618:4` 单节点截图，肉眼读到 `… × 5 lenses · grid format (M23.19) · emotion curve = designer read, not measured`。
> 顺带一条量法佐证（§5「文本按可见字形不按 TEXT 节点框」）：该 TEXT 节点框 **1846** 宽，渲染出来的可见字形只有 **690** 宽。

#### 🔴 本轮最值钱的一段：8 条新 `binding-fidelity` **不是我引入的**

改完跑 `audit:mockup-conformance --node 6554:1028`，与 §15.4 那份基线报告（`.conformance/rpsv3-134-6554-1028-20260901-layout.json`）比，**节点级集合差多出 8 个**（`binding-fidelity` 788 → 796）。逐层拆变量：

| 对照 | 结果 | 说明 |
|---|---|---|
| **闸变量**：把闸退回基线那版（worktree @ `2a26955b`）打**当前**文件 | **逐节点完全一致** | ⇒ 不是闸改的（虽然期间并行线新上了 `variant-axis`，我这轮 11 条 vs 基线 10 条） |
| **文件变量**：同一闸打基线文件 vs 当前文件 | **+8** | ⇒ 差异在 Figma 文件侧 |
| **我这一笔**：把文字**改回** `(pilot)` 再打一次 | **8 条一条不少，总数同为 796** | ⇒ **与我无关** |

那 8 条是：Section `6554:1028` **自身**的 `fills=(n/a)（fill-unbound）` + `strokes=(n/a)（stroke-unbound）`，以及帧 ① Top bar 库实例内层 7 个 Vector（`I6554:1030;1148:131;1136:*`，`Union` / `路径`）。probe 实测 Section 现在**有** fill `#444444` + 白色 10% stroke，两者 `boundVariables` 皆空 —— **是 2026-09-01 08:06Z 之后被加上的**，加的人不是本 session。

⇒ **⛔ 别把它读成本轮回归**，也别顺手改掉（Section 底色可能是 owner 有意加的）。**留给 owner 拍**：是把这两个 paint 绑 token，还是清掉。

> 方法上的一条：`6618:4` 自己在**两态里都**带 `binding-fidelity` —— 那是既有的 B-TYPO（注释卡 `fontSize=12`，§7.2 已定性的 M23.14(B) 规定值残留），不是新增。**「改动后它报红」和「改动导致它报红」是两件事**，分辨方法只有把改动撤回去再打一次。

### 18.2 DS kickoff packet —— 留作历史 + stale 抬头（§14.10 #6）

owner 决策：**留，不修**。理由：kickoff packet 本来就是**时点产物**，改写等于抹掉「当时以为什么」这条记录，还会变成 design-record 的重复副本、将来二次分叉。

落地 = DS `ea1e6dc3`（单文件 +26 行，双 remote 已推）。抬头逐条列了 **8 处已被推翻**的结论（隐藏节点取契约 / 自画 icon-button / `#252525` 底色块 / 两条 `reconnect` 系文案 / 「error 态 deferred」/ 组件自带 `OK` 页脚 / 五帧单排布局），每条给现行口径的落点；并**显式保住两条仍成立的**（`library-binding` could-not-run · 两个 Config T 图标 key），避免整份被读成「全错」。

⚠️ 抬头里写进一条判据：**`design:kickoff --check` 至今 exit 0，那只证明表格填满了，证不了内容是对的** ⇒ ⛔ 别把它的绿读成「本文件仍准确」。（实测：加抬头前后均 exit 0；`audit-stale-anchors` 同样 exit 0。）

### 18.3 DS INFRA-F142 —— 序号寻址换锚点（候选解 ②）

owner 从三个候选里选了 **②「换成稳定锚点」**。落地 DS `7d8fc715`（12 文件），另有一笔 `edcd4bf0` 是被它逼出来的既有债（见下）。

**改法**：`#### Acceptance` 这种**标题形态**的段，锚点按 heading **层级**上溯到拥有它的规则标题。`#Acceptance~11` → `#M37`、`#Acceptance~7` → `#M21.4`、`code-conventions#Acceptance~11` → `#R18`。`**Acceptance**` 粗体形态口径不变（本来就锚在规则标题上）。**128 段中 72 段换 id，18 条自陈 + 13 条 allowlist id 同批迁**，另修了 4 处 reason / 头注释里的旧序号交叉引用（「只改一边 = 未完成」）。

⛔ **走过一条错路，记下来别再走**：先试「owner = 最近的**非 Acceptance** 标题」，结果 DS 规则真源用 `#### Why` / `#### 反例` / `#### 实证` 当规则**内部小节** ⇒ owner 取到兄弟小节，id 变成 `#Why~6` / `#反例~7` —— **依然是序号、还锚到无意义的通用词，比原样更差**。必须按层级上溯才跨得过这些兄弟。

**验证是阴性对照，不是闸的绿**：

| 判据 | 结果 |
|---|---|
| 迁移后按 `file:line` 逐段比对 `--list` | **128/128 段状态与归属闸完全一致** ⇒ 每条自陈仍落在原段上 |
| ⛔ 为什么不比三态计数 | 顶位后计数**一个都不变** —— 那正是 S4 抓不到的那一档 |
| 注入一个 Acceptance 段（阴性对照） | **旧闸顶位 13/21 段 · 新闸 0 段** |
| 新增回归测试 3 条 | 全绿；**其中 2 条在旧解析器下实测转红**。第 3 条（改名 → S3 红）两版都绿，**是兜底不是判别器**，如实登记 |
| `pnpm audit:acceptance-gate-coverage` REAL exit | **0**，`unclassified 59 = BASELINE` 未动（⛔ 看的是真退出码，不是 `\| tail` 后的 `$?`）|
| 全量 vitest | 3065 passed / 14 skipped |

**残留（⛔ 别读成「序号寻址已全灭」）**：① 同一条规则**内部**多个 Acceptance 段仍用 owner 内序号（`#M45~2` / `#M47.2~2` / `#M-INTEGRITY~3`）② `code-conventions.md#Acceptance` 本身就是独立二级章节（`## Acceptance criteria（build 自审）`）、无更浅祖先，保留原 slug ③ 规则标题改名仍使 id 失效 —— 但那是**响的**（S3 当场红），与序号漂移的**哑**失效不同档。候选解 ① / ③ **未做**，理由记在 backlog。

#### 顺带被逼出来的一笔既有债（DS `edcd4bf0`）

提交时 pre-commit 拦住：`audit-gate-mount-declaration` 报并行线新上的 `scripts/audit-mockup-variant-axis.mjs`（`2541423f`）缺挂载自声明。**按对照点纪律先归因**：在**我改动前的 HEAD** 上跑同一条闸，输出一字不差、同样 exit 1 ⇒ **既有债，不是我引入的**。

它只在「有闸脚本进暂存区」时才触发，所以 §18.2 那笔纯文档提交过得去、这笔过不去 —— **等于它挡住了任何人往下提交闸脚本**。该文件头部本就把挂载关系讲清楚了（判据真源 → 规则模块 → conformance 引擎），缺的只是 D2 闭集要求的那句「无自有挂载」。三处 grep（`package.json` / `.husky/pre-commit` / `.gitea/workflows/`）零命中 ⇒ 这句是实测不是凑词。补后 D2 计数 10 → 11、exit 0。

⚠️ **如实说：这动了并行线的文件。** 单独一笔提交、只加 6 行头注释、零行为改动，commit message 里写明「由 INFRA-F142 这条线补，不是加这条闸的那条线」。

> ⚠️ **`pnpm audit:acceptance-gate-coverage` 这次是被 pre-commit 真拦下来的** —— 但这不推翻本 session 硬约束「别把 pre-commit 当安全网」：它拦得住只是因为**这批改动恰好把闸脚本放进了暂存区**。判据仍是自己跑、自己看真退出码。

### 18.4 仍然未决

- ~~**§14.10 #7 Jira 评论 `268958` 的 mention 渲染**~~ —— ✅ **2026-09-02 关闭**。
  - **可复核的那一半（AI 做）**：`getJiraIssue` 实测该评论 ADF，三个 @ 全是 `type: "mention"` 节点、带真 accountId（`5e99ec4b…` Kalpesh / `5e59a401…` Trevor / `62280880…` **Bonnie Zhou**）。
  - **只能人眼的那一半（owner 做）**：owner 2026-09-02 打开评论确认渲染正常。⇒ **证据层级如实标：AI 验结构 + owner 目视验渲染，两半合起来才闭。** ⛔ 别把它读成「AI 验证通过」。
  - ⚠️ 顺带证伪了一条项目文档：`PRODUCT_INTRODUCTION.md` §2 写「cc `Trevor Yao` / `Lotus Chen`」，**实测 cc 的是 Bonnie Zhou，Lotus Chen 根本没被 @**。owner 确认 **Bonnie Zhou 是 RPS 相关开发者** ⇒ 那条评论的 cc 本身**完全合规**（「cc 相关模块开发」），错的只是本仓的记载。已订正。
- ~~**Section `6554:1028` 的裸 paint**~~ —— ✅ **2026-09-02 已解决，见 §18.5**。
- **59 段 `unclassified`** —— ⛔ 仍不是待办（F142 在 `## Triggered`，碰到才看）。

### 18.5 Section `6554:1028` 的裸 paint —— 绑定收口（2026-09-02，owner 拍板）

**owner 决策**：「要解决问题，不要光记录；不要无中生有新增色值；色值等同就行。」并问「不明白为什么会引起 Section 为红」。

#### 18.5.1 先回答「为什么红」—— 读闸的源码，不凭印象

红的是 `binding-fidelity` 的 **B-COVERAGE**（未绑即报）探针，`scripts/audit-mockup-binding-fidelity.mjs:219-226`：

```js
const solidFill = (node.fills||[]).some(f => f.visible !== false && f.type === 'SOLID')
if (solidFill && !bv.fills && !hasStyle && !['ELLIPSE'].includes(node.type)) → fill-unbound
const solidStroke = (node.strokes||[]).some(s => s.visible !== false && s.type === 'SOLID')
if (solidStroke && !bv.strokes) → stroke-unbound
```

⇒ **它不看节点类型**（只排除 `ELLIPSE`），SECTION 仅因「有可见 SOLID fill + `boundVariables.fills` 为空」入选。

🔴 **「色值等同就行」在这条闸下不成立** —— 闸的作者把这点写死在注释里（同文件 `:201-202`）：

> 未绑即报：…没绑任何变量/style 就报，**不管裸值是否恰好 = token**（区别 B-SCALE 只报 off-scale）

把 `#444444` 手改成 `#434343` 而不绑变量，**闸照红**，且白动一次颜色。红转绿只有「真的绑上」一条路。

⛔ **闸的逃生口不能用**：`COVERAGE_IGNORE_RE = /\[\[(raw-ok|mock|not-component)\]\]/i` 命中节点名即**跳过整棵子树**。`6554:1028` 是整个交付 Section 的根 ⇒ 挂上去等于把 2651 条 B-COVERAGE 全部致盲。

#### 18.5.2 两级先例搜索（约束「止于产品」）—— 推翻了 §18.1 的一个推论

9 个 Section 实测（跨 4 页、2025-10 → 2026-08）：

| 页 | Section | fill | stroke | 绑 token |
|---|---|---|---|---|
| `6553:108` | `6554:1028` 交付 Section | `#444444` | 白 10% | ❌ |
| `6553:108` | `6573:715` owner 放的 Config T 参考图 | `#444444` | 白 10% | ❌ |
| `6259:4097` | `6260:4099` 基线 | `#444444` | 白 10% | ❌ |
| `6259:4097` | `6260:5929` · `6461:908` | `#303030` | 白 10% | ❌ |
| `5813:34235` | `5819:2723` · `5886:5052` · `5839:2909` | `#444444` | 白 10% | ❌ |
| `5813:34235` | `5929:6289` | `#484848` | 白 10% | ❌ |
| `6345:1794` | `6348:2` | `#2f2f2f` | **黑** 10% | ❌ |

⇒ ① `#444444` + 白 10% 是**本文件 Section 的惯用形态**（5/9，横跨近一年）；② **9/9 全不绑 token**。
🔴 **§18.1 的推论「Section 底色可能是 owner 有意加的」不成立** —— 它是本文件建 Section 的默认形态，不是谁挑的颜色。「非本 session 引入」那半仍成立。

#### 18.5.3 取值选择

TVU DS 的 grey 梯 14 档自带值名，**无 `#444444`**：`grey-8 #595959` · **`grey-9 #434343`** · `grey-10 #353535` · `grey-11 #262626` · `grey-12 #1F1F1F` · `grey-13 #141414`。

- **fill → `UX/Grey/grey-9 #434343`**，每通道差 1/255（0x44→0x43），肉眼不可辨。**是 DS 已有值，不是新增色值**（合 owner 约束）。
- **stroke → `UX/Grey/grey-1 #FFFFFF`**，paint 级 opacity 保持 0.1 ⇒ **视觉零差异**。
- ⛔ 未选 `Color Type/Line/Light Divider`（同为 `#434343`，但语义是「线」，给 fill 用是语义误用）。
- **先验过不会触发同规则的 B-SEM**：B-SEM 只对 `TEXT` 与 icon 语境下的 `VECTOR`/`BOOLEAN_OPERATION` 生效（`:150-153`），SECTION 两者都不是。

#### 18.5.4 🔴 自己引入又自己抓到的一个回归 —— `setBoundVariableForPaint` 会丢 paint 级 opacity

`figma.variables.setBoundVariableForPaint(paint, 'color', v)` 返回的**新 paint 不止换了 color，还把 `opacity` 重置为 1**。

| | 改前 | 绑定后（错） | 修正后 |
|---|---|---|---|
| stroke | `#ffffff` op **0.1** | `#ffffff` op **1** | `#ffffff` op **0.1** |

⇒ 一度变成 8900×3095 整圈 **100% 纯白描边**。修法：`strokes[0] = { ...strokes[0], opacity: 0.1 }`（spread 保住 `boundVariables`，回读 `bvId` 前后同一个）。

**抓到它靠的是改动前后各 snapshot 一次 paint 全字段**（hex / opacity / blendMode / boundVariables），不是截图 —— 0.157 缩放下 1px stroke 的 opacity 截图**根本判别不了**。

🔴 **但真正的教训不是「发现了一个新坑」，而是「DS 早有这条规则，我没读到」**：

> `figma-technical-reference.md` **Q2 — 绑 Color Variable 与设 opacity 必须分两步（原 M18）**
> 「`setBoundVariableForPaint(paint, 'color', v)` API quirk：返回的新 paint **opacity 默认 1**，丢失输入 paint 的 opacity 字段。**单步合并必失败**。」——连两步法代码都给好了。

⇒ 我一度把它当成「待回流 DS 的新发现」（原 §18.6-A），**若真写进去就是造重复副本**，正是回流纪律明令的反模式。**已撤回。**

**过程根因**：本轮 M48 起手清单只覆盖了 `mockup-conventions.md` 的 M-rule 与本仓 `PRODUCT_INTRODUCTION` §3.7，**没把 `figma-technical-reference.md` 的 Q 系列纳入**。⇒ **下轮凡涉及 `use_figma` 的 paint / variable / instance 写操作，M48 清单必须显式过一遍 Q 系列**（Q2 opacity 两步法 · Q7 嵌套 instance paint.opacity 不持久 · Q28 fallback base color）。这条是**做法**，但它是本仓 M48 清单的组织方式 ⇒ 记在本仓。

#### 18.5.5 阴性对照（判据能判别）

| 判据 | pre | post |
|---|---|---|
| `6554:1028` 在 `nodeFindings` | `["binding-fidelity"]` | **null（整条消失）** |
| B-COVERAGE 违例总数 | 2651 | **2649（−2，正是我修的两条）** |
| fill-unbound | 未绑 1008 / **总 1940** | 未绑 1007 / **总 1940** |
| stroke-unbound | 未绑 110 / **总 142** | 未绑 109 / **总 142** |
| spacing-unbound | 1533 / 1958 | 1533 / 1958（未动） |
| 其余 10 条规则 exit / 行数 / 节点数 | — | **一字未变**（`library-binding` 恒 exit=2，§3.5） |

报告原始文件：`rpsv3-134-6554-1028-20260902-{pre,post}.json`（+ 各自的 `.lines.json` 边车，共 384 K）。

⚠️ **两条关于这些报告的实测更正**：
1. 闸默认写到 **DS 仓库根的 `.conformance/`，那里既没被跟踪也没被 `.gitignore` 忽略** —— `.gitignore:86` 只覆盖 `docs/handoffs/.conformance/*.lines.json`。⇒ ⛔ 别以为跑闸不会脏工作树。
2. DS 主工作树有并发 session（约束「禁 `git add -A`」）⇒ 留 4 个未跟踪文件正是那条约束担心的场景。**本轮已把它们移出 DS 工作树**（移到本 session scratchpad），DS 工作树回到干净。**报告本身是 session-local、不入库**（与 §18.1 引用的 `…20260901-layout.json` 同样已不在，属既有惯例）；⇒ **上表的数字就是留存下来的证据本体**，要复现就重跑闸。

##### 🔴 `findingNodeIds` 是样本窗口的产物，不是真违例集合

`binding-fidelity` 的 `findingNodeIds` 从 **798 → 799**，且冒出两个「新」节点 `I6554:1030;1148:131;1136:3258 路径` / `…3259 形状`。**不是新债**：

- **分母 1940 / 142 / 1958 一个没变** ⇒ 文档节点 population 完全相同，没有节点被新增。
- B-COVERAGE 在人读层折叠成「摘要 + top 10 样本」。`6554:1028` 的**两条**违例原本占着样本窗口头两格；修掉后窗口前移两格，把窗口外的 3258 / 3259 顶了进来。`798 − 1 + 2 = 799`，严丝合缝。
- 该族在 pre 是 7 个（`1136:3246/3247/3251/3252/3253/3254/3257`），post 是 9 个 —— 增的两个正是被顶进窗口的。

⇒ ⛔ **跨轮比 `findingNodeIds` 集合会得出「多了 2 个」的错误结论**。折叠摘要型探针的真判据是**计数与分母**，不是 node-id 集合。

**根因已读源码坐实（不是推断）** —— `audit-mockup-conformance.mjs:241-242`：

```js
const { lines, nodeIds: hitIds } = extractFindings(r.stdout)
entry.findingNodeIds = hitIds
```

`findingNodeIds` 是**从子闸 stdout 里正则刮出来的**；而 B-COVERAGE 在**规则模块内部**就已折叠成「摘要 + top 10 样本」再打印 ⇒ stdout 里只有 10 个 id，report 再忠实也只能拿到这 10 个。

🔴 **该文件 `:129` / `:173` 明写「`findingNodeIds` 始终落全集」——这个被当成契约写在注释里的不变量，对折叠摘要型探针不成立。** 那句话只对 `findingLines` 的 `FINDING_LINES_CAP` 截断成立，而**折叠发生在打印之前**，在引擎看不见的上游。根因是**引擎去刮 stdout，而规则模块本来就返回了结构化 findings**（`ruleResult({ findings })`），数据现成却没被用。

⚠️ **回溯含义**：§18.1 的「节点级集合差多出 8 个」用的是同一方法 ⇒ **那个数字本身可能含窗口位移成分**（它自己列举时是 Section 2 条 + Top bar 7 个 Vector = 9 项，与「8」对不上，正是这类噪声的形态）。**但 §18.1 的结论仍然成立** —— 它最后是靠「把改动撤回去再打一次」坐实的，那是比集合差强得多的证据。⇒ 教训不是「§18.1 错了」，是**「集合差只能当线索，撤回重测才是判据」**。

#### 18.5.6 亲验渲染

`get_screenshot 6554:1028 @1400`（原始 8900×3095）：深灰底、左侧文档列 + Journey 卡 + 两行分支 6 帧 + 连线全在，无破相。
⚠️ **边界如实说**：0.157 缩放下截图**证不了** 1px stroke 的 opacity；那一条的判据是 probe 回读 `op=0.1`。截图只证「没出现 100% 白框那种一眼可见的破相」。

### 18.6 回流 DS —— ✅ **已落地（2026-09-02，DS `7e46598b`，双 remote 同 SHA）**

> owner 2026-09-02：「这个需要你帮我判断，而不是我判断」⇒ **B1 / B2 / C 的取舍由 AI 判，判完即执行**。判断依据与结论见下表，**做了什么 / 刻意没做什么都写全**。

两条都过了 §M-DISCIPLINE.SOURCE 三问，判为 **DS 档（做法/判据，换个产品仍成立）**：

| # | 内容 | 落点 | owner 裁定 |
|---|---|---|---|
| ~~**A**~~ | ~~`setBoundVariableForPaint` 会重置 paint 级 `opacity`~~ | — | ⛔ **撤回，不写** —— owner 已拍板做，但**落笔前实测发现 DS 早有此规则**：`figma-technical-reference.md` **Q2（原 M18）**，表述一字不差且附两步法。写进去 = 重复副本。⇒ 本条不是「规则缺失」而是「没读已有规则」，教训改记在 §18.5.4 |
| **B1** | 改掉 `audit-mockup-conformance.mjs:129 / :173` 那两句「`findingNodeIds` 始终落全集」的错误断言（**含两处同义的 stdout 提示**）+ 写明折叠机制、后果、实证数字与替代判据 | 同文件注释 | ✅ **做了** —— 纯注释/提示文本，零判定改动。理由：**一句假的不变量，正好坐在做归因的人会去读的位置上**，比没写更坏 |
| **B2** | 引擎改吃规则模块的**结构化 findings**（`ruleResult({ findings })`）而不是刮 stdout，让 `findingNodeIds` 真的成为全集 | `audit-mockup-conformance.mjs` `buildReport` | ⛔ **没做，立项 `INFRA-F143`（Triggered）** —— 那是闸引擎重构，且当日 DS 主工作树有活跃并行 session（同小时两次提交）。B1 已把口径写进注释与 stdout，残余风险低 |

**C —— 一条 convention 与一条闸互相矛盾**

`mockup-conventions.md` §C1 下的例外（2026-07-17 owner 回流）规定：**带透明度的 tint 背景填充（token 值 + opacity）不绑 Variable，用同值 raw 色**，并明写「**M-COLOR audit 对这类 raw fill 应豁免**」。

🔴 **查实后，实情比「两条规则矛盾」更精确 —— 那句豁免豁免错了闸**：

| 闸 | 会不会报这类 tint raw fill | 实测依据 |
|---|---|---|
| `audit-mockup-colors.mjs`（M-COLOR） | **不会** | C1 只查「icon instance 内的 vector fill」（`:201`），**从不查背景/容器填充** ⇒ 「豁免」**空转** |
| `audit-mockup-binding-fidelity.mjs` B-COVERAGE | **会报 `fill-unbound`** | `:219-222` 未绑即报、不看节点类型 |

⇒ 豁免落到了**不需要它的那一侧**，真正开火的那条闸从未被提及。**这是硬约束 7「只改一边 = 未完成」的一个存量实例。**

⚠️ **我上一轮说的「没有出路」是错的** —— 有出路：`COVERAGE_IGNORE_RE = /\[\[(raw-ok|mock|not-component)\]\]/i`（`binding-fidelity.mjs:199`）。但它**此前只存在于 `docs/_archive/` 的旧计划与一份 spec**，活的规则真源零提及 ⇒ 照 convention 干活的人不可能知道。

| | 做了什么 | 裁定 |
|---|---|---|
| **C-doc** | 在该例外下补「机检口径订正」小节：两条闸的实测扫描面对照表 · 点明真正开火的是 B-COVERAGE · 把 `[[raw-ok]]` 写进活的真源，附两条硬约束（**它跳过整棵子树 ⇒ 只许挂叶子、⛔ 别挂容器/Section/卡片根**；名字里留 `= <token> @<opacity>` 痕迹） | ✅ **做了** —— 这是**文档缺口不是设计争议**，照现有文档干活必然撞墙，可以直接修 |
| **C-code** | 给 B-COVERAGE 加窄豁免（背景角色 + `opacity < 1` + RGB 等于某 token 现值 ⇒ 不报） | ⛔ **没做，立项 `INFRA-F144`（Triggered）** —— **规则语义变更**，边界（什么算「背景角色」/ 比哪个 mode 的解析值 / token 升级后 raw 漂了报不报）得 owner 划。⚠️ 且**别做成宽豁免**：「值等于 token 就放行」会削掉该探针的整个立意（不管值对不对，只问绑没绑），窄在 `opacity < 1` 上才不冲突 |

⚠️ **本轮的 stroke 绑定是否踩了那条例外？严格讲不算**：例外 scope 写明「仅限**背景/底色**的半透明 tint」，本轮改的是 SECTION 的 **stroke**；且其 Why（instance 层读不出 opacity）在**顶层 SECTION** 上不成立 —— 回读实测 `op=0.1` 保住了。

#### 18.6.1 为什么两条都进 `## Triggered` 而不是 `## Active`

DS backlog 的 `## Triggered` 段自述是「**碰到时读的说明书，不是待办**」，且 owner 2026-08-28 明确质疑过「不是说清理未做的任务吗，怎么越做越多」。⇒ F143 / F144 都不是「排队等人做的工程」（F144 甚至是未 scoping 的设计题，与该段迁入 `INFRA-F63` 的理由同型）⇒ **⛔ 不拿它们撑待办数。**

#### 18.6.2 机检（改了规则真源 + 闸脚本，两样都要验）

| 判据 | 结果 |
|---|---|
| `pnpm audit:acceptance-gate-coverage` **REAL exit**（⛔ 非 `\| tail` 后的 `$?`） | **0**，`checkedUnits=128`（与 §18.3 一致） |
| **阴性对照**：我插了个 `#####` 标题，会不会顶位？`--list` 改动前/后对比 | **去掉行号后逐字节相同**；128 段、`unclassified` 与「有闸」计数一致 ⇒ **零顶位** |
| 判据能判别吗 | 能 —— §18.3 注入一个 Acceptance 段时旧闸顶位 13/21 段，正是这个比法抓到的 |
| 全量 vitest | **3070 passed / 14 skipped**（§18.3 时 3065，多的 5 条是并行线 Button blue） |
| pre-commit | 通过（输出里的 `❌ FAIL` 是阴性对照测试自印的期望失败） |
| 并发防护（约束 8） | 提交前三方 SHA 核对 `963df5fe` 一致；`git add` 全显式路径；commit 后 `--stat` 复核**正好我那 3 个文件**；push 后 local/origin/github 三方同为 `7e46598b` |

> ⚠️ **DS 基线 SHA 会漂，别拿本文件的记载当现值**：本轮起手时 DS 是 `7d8fc715`，收尾复核时已是 **`0c75580b`**（并行 session 的 `feat(button): color="blue"` 落地，与本轮零文件交集）。⇒ 下轮起手**自己跑 `git log -1`**，⛔ 别沿用文档里的 SHA。

---

## 19. 电源图标迁回 DS 库 —— 跨库依赖清零（2026-09-02 下半场）

> **起因**：owner 一句通报 ——「Reboot 的图标已经在 DS 里面更新了，你可以引用，我已经手动修改了一张」。
> ⚠️ **这句话里有两处必须靠实测澄清、不能靠推理的歧义**：「在 DS 里面」指 code 侧还是 Figma 库侧？「手动修改了一张」是哪一张、改成了什么？

### 19.1 实测把两条 DS 文档判为假

同日早些时候 DS 刚落 `805b116b`（queue #13 电源图标，落点是 **code 侧 icon registry 手工层**），其 `figma-component-catalog.md:714` 逐字写着「⚠️ **但 Figma 侧仍然没有这两个图标**」。若照此推理，就会得出「owner 说的是 code 侧、Figma 里没法引用」的错误结论。

**实测（`search_design_system` + `importComponentByKeyAsync` + SVG 导出）**：

| 组件 | 库 | key | `updatedAt` |
|---|---|---|---|
| `icon/Setting/Reboot` | **TVU UX Design System** | `6493b7d0…` | **2026-09-02T04:00:50Z** |
| `icon/Setting/Switch` | **TVU UX Design System** | `9a6fdd67…` | **2025-07-28T03:30:15Z** |

⇒ **两条 DS 记载当场被推翻**：
1. 「Figma 侧仍然没有这两个图标」—— 假。它写于 15:21，而图标 12:00 就已在库里；**写的时候没去核 Figma 活源**，是照着 code 落地时的认知写的。
2. 「DS 库 644 icon 全量枚举实证：没有电源开关符号」—— 假。`icon/Setting/Switch` 是标准 ⏻，**早于 RPSV3-134 首交付一年多**。而**整个跨库 import Config T 的决定就建立在这条假结论上**。

🔑 **本轮最贵的一条教训**：`figma-component-catalog` 里原有的告诫是「关键词搜索会漏，判定库里有没有某语义必须**枚举发布目录全集**」。这次证明**枚举全集也漏了** —— 因为枚举产出的是名字列表，而 `Switch` 这个名字不会让人联想到电源。⇒ **枚举解决的是「取数范围」，解决不了「名字与语义无关」**；两者是两个独立的失败面，补了前者不等于补了后者。

### 19.2 owner 改的是**两张**不是一张

全 Section 探查（`findAll` + `getMainComponentAsync`）后才看清：① 帧 `Power — Default` 的**两个**按钮都已换过 —— Reboot → `icon/Setting/Reboot`、Power Off → `icon/Setting/Switch`。其余 5 帧共 10 个图标仍指向 Config T。

⇒ **⛔ 别把 owner 口述的数量当范围**。他说「一张」，实际是一帧里的两个；真实待办面只能靠扫描得出。

### 19.3 一个 owner 未必看得见的后果 —— 已报告、owner 知情后维持原决定

SVG path 逐条比对（**⛔ 截图在 16px 下判不了**）：

| 组件 | 形状 |
|---|---|
| `icon/Setting/Reboot`（DS） | 竖线 + 断口圆环 **+ 右上 r≈0.6 小圆点** |
| `icon/Setting/Switch`（DS） | 竖线 + 断口圆环（标准 ⏻） |
| `Icon/Reboot`（Config T，原用） | 圆环 + 缺口 + **三角箭头**（循环箭头） |

⇒ 照 ① 帧统一后，**同一行的 Reboot 与 Power Off 只差一个小圆点**，而原设计里两者泾渭分明。这与 §14.2「同一信号在整套交付里须单义」是**同型问题**（那次是红色双义，owner 裁定改绿）。

**已把 SVG 证据 + 同型判例 + 一个可区分的备选（`icon/load/refresh`，DS 库内，循环箭头，已导 SVG 核过形状）一并摆出后提问，owner 选择「照 ① 帧用 `Setting/Reboot`」** ⇒ **这是已知代价的设计决定，不是疏漏**。⛔ 下轮别当 bug 去「修」。

### 19.4 做了什么

| # | 动作 | 范围 |
|---|---|---|
| 1 | `swapComponent` → `icon/Setting/Switch` | 5 个 Power Off 图标（②③④⑤⑥ 帧） |
| 2 | `swapComponent` → `icon/Setting/Reboot` | 5 个 Reboot 图标（②③④⑤⑥ 帧） |
| 3 | 交付层文案同步（§M-DISCIPLINE.SYNC） | UX Delivery 卡 `6568:726` + REFERENCE 卡 `6573:722` —— 原文「图标从 Config T 库引入，因为 DS 库内没有电源类图标」**两个分句现在都假** |

**⛔ 刻意没改的 5 处**：`6566:724` / `6566:725` / `6568:720` / `6621:6` / `6573:719` 也提到 Config T，但讲的是「上一代 Config T 本地 UI 已有 Power 分区」「参考图出处」这类**需求背景与历史事实，仍然为真**。⇒ 逐条判过再改，别按关键词批量替换。

### 19.5 🔴 自己引入又自己抓到的缺陷 —— 改 `characters` 会清掉 fill 分段 override

改完文案，Section 闸 `bilingual-spacing` 报 **findings=1**，精确点名 `6568:726`：§M23.14 (B) 要求注释卡里每个 ZH run 的 **fill opacity = 0.45**，而我改完是 **1**。

**机制**：设 `n.characters` 会把整个节点的分段 override 重置。我重建了 `fontName` / `fontSize` 分段，**却没重建 fill 分段**。

⚠️ **比这个 bug 更该记的是：我自己的验证设计有漏洞。** 改动前我确实做了 snapshot，但只记了 `segs[0].fills`（**英文段**）—— 而丢的是 ZH 段。⇒ **snapshot 取样取在了不会变的那一段上，于是 before/after 对比恒等，看不出任何异常**。这与 §18.5.4「绑完要回读整个 paint 的全字段」是同一条教训在文本域的翻版：**对照要覆盖所有会被这个操作影响的分段，不能只取第一段**。

**这也是 §14.10 反思 (3)「修一个属性会把另一个刚修好的属性打回去」的第三个实例**（前两次：设节点级 `fontName` 清掉 Noto Sans SC 分段）。

**修法**：⛔ 不手搓 `0.45`，而是从**同卡片里未被动过的合规兄弟**（`6568:724` / `6573:724`）取完整 paint 对象 `JSON.parse(JSON.stringify(...))` 套用 —— 两级先例搜索**止于本卡**。

⚠️ **闸只报了 1 处，实际中了 2 处**：`6573:722` 在 REFERENCE Section `6573:715`，**不在 `--node 6554:1028` 的扫描面内**。程序化回读证实它 opacity 同样是 1。⇒ **「闸只报一条」≠「只坏了一处」，先问这条闸的扫描面覆不覆盖你改过的全部节点。**

### 19.6 验证 —— 判据能不能判别

| 判据 | 结果 | 能判别吗 |
|---|---|---|
| Section `6554:1028` 全量 285 个 INSTANCE 扫 Config T 两 key | **残留 0** | ✅ 改之前必为 10 |
| Q7 持久性：**另起一次 `use_figma`** 回读 10 个图标 | 10/10 仍是 DS 组件、fill 仍绑 grey-4 | ✅ Q7 明言嵌套 instance override 可能下次 probe 就没了 |
| `bilingual-spacing`（两个 Section 分别跑） | **findings=0 / exit=0** | ✅ **天然阴性对照**：同一判据在「没修」时报 1 并点名，修完归 0 |
| 按钮级四规则闸，与 **owner 手动定稿的 ① 帧按钮**逐项对照 | `6585:12` vs `6656:779` = library-origin 0·colors 1·binding 10·typo 0，**完全一致**；`6585:18` vs `6656:780` 同理（colors 2） | ✅ 参照系是 owner 亲手做的那张 ⇒ 一致即证明零额外引入 |
| 亲验渲染 | ⑥ 帧全景 + Changes 卡：无破相、无溢出、ZH 灰度与兄弟段一致 | — |

⚠️ **整文件闸（`--file` 不带 `--node`）exit=1、8 条 findings，⛔ 不可用于归因** —— 那是 29 页历史页的存量（`library-origin` 11852 条 foreign 主要来自 Top bar 等旧库组件）。且闸头注释明写 **`--node` 只约束 8/10 条规则**，`library-origin` / `library-binding` 是 file scope ⇒ **⛔ 不得表述成「范围已缩到这个节点」**。本轮真正的归因判据是上表最后一行的**按钮级对照**，不是任何集合差。

⚠️ `library-binding` 照旧 **exit=2 / 分母 0**（§3.5，硬约束 8）—— 既不是 pass 也不是 fail，⛔ 没去 `pnpm sync:mockup` 补缓存。

### 19.7 回流 DS —— ✅ 已落地（DS `b36a5769`）

⛔ **提回流前先 grep DS 真源确认它不存在**（§18.5.4 的教训：上一轮把 DS 早有的 Q2 当成新发现）。本轮三条 grep 过，均为「订正已有的假记载」而非新增重复副本。

| # | 内容 | 落点 |
|---|---|---|
| **A** | `figma-component-catalog.md` 两处**现为假**的记载：「Figma 侧仍然没有这两个图标」·「644 全量枚举 ⇒ 库无电源符号」 | ✅ 整节重写 |
| **B** | 「**枚举全集也会漏**：枚举给的是名字列表，候选靠看名字挑」—— 与既有的「关键词搜索会漏」是**两个独立失败面** | ✅ 同节 |
| **C** | 两个库图标自带 `Ellipse 502 fill=#000000` 未绑 ⇒ 消费方每用一次吃一条 `colors` C1 | ✅ 同节登记（AI 改不了，硬规则 #1 禁写 Figma 库） |

### 19.8 owner 追加两条裁定 ⇒ code 侧同批改（DS `098a2d2f`）

owner 就 §19.3 那个跨链不一致给了裁定：**①「以 Figma 为主，且是新加的 Icon，Code 那边也需要改变」②「只用一份，不要重复，因为 DS 图标库只有一个关机按钮」**。

🔴 **落地时查出的事实比裁定本身更值钱：同一个 ⏻ 一度存在四份。**

| 份 | 位置 | 备注 |
|---|---|---|
| 1 | Figma 库 `icon/Setting/Switch` | 真源，2025-07-28 |
| 2 | code **派生层** `setting/switch` | 正规同步，带 nodeId，与库逐字相同 |
| 3 | code **手工层** `setting/toggle` | manifest 带同一个 `figmaNodeId 1114:24139`，path 逐字相同 |
| 4 | code 手工层 `setting/power-off` | `805b116b` 当日新造，从 Config T 读，viewBox 28 |

⇒ **同一个 name-search 盲区连续骗过了三条链**：Figma 叫 `Switch`、派生层叫 `switch`、手工层叫 `toggle` —— 没有一个名字里带 "power"，而找的人搜的是 power / shutdown。

**已执行**：删掉第 4 份（常量 + barrel re-export + manifest 条目，manifest 14 → 13）；`setting/power-off` / `setting-power-off` 降为第 3 份的 **alias**，其 tags 补 power/off/shutdown。⚠️ **落点是 `setting/toggle` 而不是并行线原推荐的 `setting/switch`** —— 因为派生层被 `icon-artifacts.mjs` 整目录 `rmSync` 重写，**别名不能手加在那里**（这个约束是并行线自己写下的，却与他们的推荐相冲突）。`setting/reboot` 的 path 换成库的 `icon/Setting/Reboot`。

**⏳ 未收口、已登记（⛔ 别以为全清了）**：手工层第 3 份与派生层第 2 份**仍是两份**（4 → 2）；收口要先定「同一个 Figma 节点在两层同时存在时谁是权威」的**通用**策略。以及 `icon/Setting/Reboot` 尚未进派生层，这是 `setting/reboot` 至今无 `figmaNodeId` 的原因 —— ⛔ 别手写一个顶上。

> 🔴 **本段原写「等下次 `sync:figma-library` 就能补上 figmaNodeId」—— 同日被实测证伪，⛔ 别照做**（DS `52fd94d1`，立项 **INFRA-F145**）。
>
> 并行线真去跑了一次 `pnpm sync:figma-library --with-extract`：**全绿 EXIT=0，而图标依然没进来**。根因是管线自指 —— `cleanup-unpublished` 判「这个图标还发布着吗」查的是 `figma-data/published/icons/manifest.json`，**那正是本管线自己上一轮的产物**；于是库里**新加**的东西在任何一步看见它之前就被 Step 1 删掉了，永远进不了 manifest，下一轮 cleanup 依旧不认识它 —— 闭环。那一次运行删了 **16** 个，报告措辞是断言性的「**已被取消发布或废弃**」，而回 Figma 活源逐个探针，抽的 **14/14 全是活着的已发布 COMPONENT**。
>
> ⇒ 对本项目的两个后果：① **`setting/reboot` 的 `figmaNodeId` 被 INFRA-F145 阻塞**，不是「跑一下就好」；② 更要紧的是 **`Figma → code` 这条唯一的自动通道，对「新增组件」这一类改动是关闭的** —— 以后 DS 库新加任何东西，都别假设它会自己流到 code 侧。（库里 `icon/Setting/Reboot` 的 node id = `6430:126`，**并行线实测，本轮未亲验**。）
>
> 🔑 **方法论教训，比这个 bug 本身更该记**：我写下那句时用的是「等下次 sync 就能补上」——**那是一条预测，不是事实**，而它被排版成了和周围实测结论一样的语气。本 session 反复栽在同一件事上（「库里没有电源图标」「Figma 侧仍然没有」都是没去核的推断）。⇒ **凡是「等 X 之后就会 Y」的句子，要么当场去测，要么显式标成「未验证的预测」**，⛔ 别让它长得像实测结论。

#### 19.8.1 本轮验证里三条值得复用的做法

1. **判重用 path 指纹，不用名字** —— 对 643 个 icon 常量算「长度 + charcode 校验和」，几秒钟就暴露了这次的重复（外加 3 组既有的、语义上合理的重复）。整场事故的根因就是靠名字判断语义。
2. **跨进程双侧自证** —— 在 Figma plugin 上下文算 `d` 的 长度+校验和，再对**写进文件后的** `d` 重算，两侧比对（reboot len 1364 / ck 271780）。这挡的是「我从工具输出手抄到代码里」这一步的错误，而那一步没有任何闸覆盖。
3. **故障注入证明断言不是空过** —— ① 摘掉 alias ⇒ 3 条红（含反重复那条）② viewBox 改回 28 ⇒ 1 条红。⛔ 新写的断言在没见它红过之前，不算验过。

#### 19.8.2 两个新踩的坑

- 🔴 **`verifyHint` 是有格式契约的字段，不是自由文本**：`audit-translation-completeness` 要求它**以 `grep`/`rg` 开头且实跑要有匹配**（`→` 之后才当说明）。我按自然语言写，把该闸的 `activeFindings` 从 0 弄成 1。⇒ **改 `divergences-decisions.json` 后要跑这个闸，别只跑 vitest。**
- ⚠️ **视觉门对 icon 改动是失效的**：pre-commit 的视觉批准闸按**扩展名**匹配 `(html|css|svg|vue|jsx|tsx)`，而 icon 的 SVG 全住在 `src/icons/raw.ts` 里 ⇒ 改图标形状是实打实的视觉改动却不触发它。已在 DS commit 里登记（与 INFRA-F86 ① 同型）。本轮**没有**自设 `VISUAL_COMMIT_APPROVED`（AGENTS 明令 AI 不得自设），依据是 owner 已指定目标形状且那形状是他本人加进库的。

#### 19.8.3 并行线撞车 —— 这次是 rebase 解决的

DS 有活跃并行 session，本轮 push 时被拒（`e49545e8` 已先落地）。**他们独立查到了同一件事，且比我深一层**（发现了派生层那份）。处理：rebase + 手工解冲突（divergences 条目 + 测试块两处），**保留他们更完整的 `figmaSide` 叙述与 FNV-1a 实证，只覆盖「已执行」的那部分字段**；并同步他们那轮写于 owner 裁定**之前**的 `figma-component-catalog.md`（否则它会教下游「⛔ 不要用 `setting/power-off`」，而那个名字现在已经是正确的别名）。

⇒ 教训：**并行线的「⏳ 待 owner 拍」文字会在 owner 拍完后立刻变成错误指引**。谁拿到裁定，谁负责把那些文字一起改掉，⛔ 别只改自己动的文件。
