---
id: mode-switch-failure-state
title: WiFi/Hotspot 模块 Mode 切换失败态 — 两端补交付（V4-1865 Config-T + V4-2318 LCD）
doc_type: plan
status: in-progress
last_updated: 2026-09-11
sources:
  - { type: slack, ref: "V4 dev thread 2026-09-09（winniyang 报现象 → danner 问 UI 流程）", url: "https://tvunetworks.slack.com/archives/C021E1BNTPF/p1788947469888169?thread_ts=1788937060.213829&cid=C021E1BNTPF" }
  - { type: jira, ref: "V4-1865（Config-T 3-mode）", url: "https://tvunetworks.atlassian.net/browse/V4-1865" }
  - { type: jira, ref: "V4-2318（LCD 3-mode）", url: "https://tvunetworks.atlassian.net/browse/V4-2318" }
---

# 计划 — Mode 切换失败态（两端补交付）

**判档**：**US-3**（Existing product 增/改）/ **W2**
三问：① 无新入口、无新可点对象 → 否；② **新增「切换失败」状态 + 新回显判据** → 命中；③ 切换流程与页面均已存在、只加分支 → 否 ⇒ Journey 卡免除。
⇒ W2 必产：design-record 更新 · Jira 组件（既有，复用）· Jira/Slack 回复 · Change Log/逻辑更新块 · UX 交付卡同步。**免** Journey 卡新建。

---

## 1. 起因（现场事实，非推测）

Slack thread 2026-09-09：TM1000V4 84503 + dial patch 187new wifi，Hotspot 名称信息不显示。

链路（winniyang + youngzhang 逐字）：
1. 模块被切到 WiFi 角色去连外部网络（`danner-test-6`/`NETGEAR62`），驱动把 `hotspot` 网卡摘下改名 `wifi20`。
2. 模式切回 Hotspot（`H2W=0`、开关显示 `enabled`），但**网卡层没真正重建 AP 接口、没重新挂回 `br10`**。
3. young 逐字：「由于 wifi 被禁用了，所以逻辑里 wifi 线程对于这个信息不感知，故实际没有从 wifi 切换到 hotspot」。
4. ⇒ `hotspotgetinfo` 查不到 `ssid`/`password`（只返回 `slotid`/`is_disabled:1`/`H2W`），页面 Name 框拿不到值。

**owner 确认（2026-09-11）**：thread 里说的「关闭了 WiFi」= Config-T **WiFi 段最上面那个 `WiFi:` 开关**
（实测 `6602:2819` Content 帧首行 `6602:2832` = `WebUI/Form/Item/Radio`），**不是** Hotspot 段 Mode→Disable。

## 2. 决策

| # | 决策 | 依据 |
|---|---|---|
| **D1** | **WiFi 服务关闭状态下，模块必须仍能切回 Hotspot** | Mode 是模块角色 SSOT（V4-1865 §4）；`WiFi:` 只管服务开关，不决定模块归属。不支持即死锁：热点是本地扫码进 Config-T 的唯一入口 |
| **D2** | **⛔ 不做「WiFi 未开就置灰 Hotspot 选项 + 提示先开 WiFi」** | 把实现约束推给用户；且与 V4-2318 L3「本地失联须有兜底」精神相反 |
| **D3** | **Mode 回显取实际生效状态，不取状态机意图值**。判据：`hotspotgetinfo` 返回得出 `ssid` + `password` 才算 Hotspot 生效 | 本次现场即此条被破（`H2W=0` + `enabled` 但 AP 未重建） |
| **D4** | **切换失败 ⇒ Mode 回退到原模式 + 错误提示**，⛔ 不停在目标模式让字段空着 | 显示必须等于实际（D3 的直接推论）。停在目标模式 = 复现本次 bug 的表现 |
| **D5** | **不新增 retry 按钮** | 回退后 Mode 选择器本身就是重试入口；加按钮 = 多一个控件表达同一件事 |
| **D6** | **不在关 `WiFi:` 时提示「该模块仍占 WiFi 角色」** | owner 2026-09-11 拍定：Hotspot 模式那里就看得到，无需重复提醒 |
| **D7** | 形态走 **§M23.17 逻辑更新块**（既有交付卡下方同级 navy 卡 + 橙边框/header），**不删既有卡**、不动既有 monolithic 文本节点 | 本轮属「交付后开发发现既有逻辑需补充」，且对旧注释是**纯补充**（旧卡完全没提失败态）非推翻 |

## 3. M48 起手 Rule-Coverage Self-Check

| 规则 | 命中理由 | 落地计划 | ✅ |
|---|---|---|---|
| **M0** Phase 0 mapping | 多帧 + ≥3 element | §4 mapping table，probe 后回填 | ✅ |
| **M45.1 / M46** 新建 vs 迭代分诊 | 迭代既有交付 ⇒ **就地改**，⛔ 不新建 page | 帧落既有 Section `6601:2` / `8672:11` 内 | ✅ |
| **M42.3** 禁旁路克隆搭样板 | 新增失败态帧须 clone 既有 mode 帧 | clone 最近邻同族帧、就地改，不另起样板集 | ✅ |
| **M23.17** dev-discovered 逻辑调整 | 本轮正是此场景 | 两端各一块橙色逻辑更新块；header `▲ 2026-09-11 · 8.4 逻辑更新 — Mode switch failure`，带 Slack 来源链接；橙色仅边框/header/标题短语，正文浅灰 | ✅ |
| **M53** 提示文案触发族 | 新增失败提示 | 判为 **user-initiated**（用户刚点 Hotspot）；报「那一下没生效 + 现态」，用控件标签原文 `Hotspot`/`WiFi` | ✅ |
| **M23.18 / M23.18.1** 卡主题探测 | 要建/改交付卡 | probe 所属 Section 产品帧主题 → 定 navy / light；page 画布底色不动（沿用既有） | ✅ |
| **M23.0 / .10 / .11 / .14 / .19** UX 卡 | 要同步 UX 交付卡 | 更新块用 canonical 段结构；终态优先、禁沿革句；双语行距三档 | ✅ |
| **M23.6** 连线 | 失败分支要连线 | **⊘ 本轮不画连线** —— 跨行必横穿 UX 卡行与 linked-screens 行，命中 frame-content-avoidance 禁项（见 §5）；流程归属改由行标题 + UX 卡 + 文案表承载 | ⊘ |
| **M23.7** state-label | 新状态帧要标注 | what + why + invariants | ✅ |
| **M-COLOR §C4 / §C6 / §C7 / §C8** | 失败态用红 + 橙注释 accent | §C8 出「信号 × 语义」对照表（红已用于哪些语义）；§C4 品牌色与注释 accent 不混；文字双绑 | ✅ |
| **M32 / M52.1** library-first + token | 提示用库组件 | LCD 用库 `Message`（V4-1646 先例）；Config-T 用库反馈组件，probe 后定 | ✅ |
| **M-FONT** | 新文字节点 | fontSize 落 textStyle grid | ✅ |
| **§字体规范〈产品层字体〉** | LCD 产品帧内新文字 | LCD=`Verdana` 本环境不可写 ⇒ 走 §M47.2 整段覆盖，**产出待改节点清单交 owner** | ✅ |
| **M-INTEGRITY I5 / I6 / I7** | 新增帧 + 改 Section bounds | 落点先 probe 防叠加；加帧后复测 Section 尺寸 + 兄弟碰撞；**I2 收尾固定跑** | ✅ |
| **Q15** SECTION 子节点坐标 | 往 Section 内 create/move | 赋坐标先减 section 原点，落点 probe `absoluteBoundingBox` 验 | ✅ |
| **M41**（LCD 例外 L2） | 确认弹窗族 | **⊘ 无可判对象** —— 本轮未建 / 未改任何确认弹窗，既有原生确认范式未动 | ⊘ |
| **M-DISCIPLINE.SYNC** | 每批编辑后 | 写后机检 `--report` 落盘 + 交付层同步 + 三轮扫查 | ✅ |
| **M-DISCIPLINE.MEASURE** | 几何判据 | 文本按可见字形、连线按逐线段 | ✅ |
| **M-LIFECYCLE** | create/update 任务 | BEFORE probe · AFTER 机检 · 走查兜底 | ✅ |

**主题探测结果**：两端产品帧均 **dark**（LCD F3 fill `#252525`→luma 0.145；Config-T F5 fill luma 0.204，均 < 0.25）⇒ 交付卡与逻辑更新块走 **navy 版**，不用 M23.18 浅色版。page 画布底色未动（沿用两端既有）。

## 4. M0 Phase 0 — Element-to-Component Mapping

| Element | component_set（key）| Variants | Status | 来源 |
|---|---|---|---|---|
| 失败提示条（两端共用）| 库 `Message` `e9574b39e93ac3ffe94834f05d704798ce997274` | `status=error, size=L` | ✅ 库直用 | catalog 有条目（Primary use = operation result）。⚠️ `feedback-not-selfdrawn` 闸对本轮是 **unverified 不是 PASS**（射程不匹配 `Message / error / L` 这种命名，见 §6）|
| Mode pills（LCD）| file-local `Item/normal` · `Item/active` | — | ✅ clone 继承，未新建 | clone F3 `8493:131` |
| Mode radio（Config-T）| `WebUI/Form/Item/Radio` | — | ✅ clone 继承，未新建 | clone F5 `6626:851` |
| 导航条 / 底栏 / 按钮 | clone 继承 | — | ✅ 未改 | 同上 |
| 行标题 · caption | clone 既有 TEXT（`8606:57` / `6628:1127`）| — | ✅ 保范式改文案 | §M42.3 就地改 |
| 交付卡 | clone 既有 UX 卡（`8509:317` / `6650:1116`）| — | ✅ 保范式改文案 | §M46 迭代既有 |
| 逻辑更新块容器 | **自建** vertical auto-layout（**注释层**，非产品 UI）| — | 🟡 按 §M23.17 规格自建 | 注释层不入 catalog；navy fill + 橙 2px stroke |

⚠️ **`Content#4797:0` 是 SLOT 属性，`setProperties` 改不了**（命中 Q22）⇒ 文案改内部 TEXT 的 `characters`。已登记为本轮实证。

## 5. 施工结果

| # | 项 | 产物 |
|---|---|---|
| 1 | LCD 失败态帧 | `10464:357`（`F5 · Switch failed — mode reverted`）abs (202,2948) 480×320；内含 `10464:1328` `Message / error / L` —— 落位终值 **`x=20.5, y=8`**（§12 改定，⛔ 施工当轮的 `y=118` 已作废）|
| 2 | LCD 行标题 | `10464:378` |
| 3 | LCD UX 交付卡 | `10466:393` 520×245 |
| 4 | LCD 逻辑更新块 | `10467:431` 520×409（header `10467:432` / 要点 `10467:433`）|
| 5 | LCD 文案表补节 | `8664:233` 扩高 524→613；新增 `10468:393` 分隔线 / `10468:394` 小节标题 / `10468:395` 正文 |
| 6 | LCD Section | `8672:11` 扩高 2764→3700 |
| 7 | Config-T 失败态帧 | `8753:1140`（`F6 · Hotspot section · Switch failed`）abs (-120,2218) 1280×941；内含 `8753:1552` Message —— 落位终值 **`x=420.5, y=24`**（§12 改定，⛔ 施工当轮的 `y=235` 已作废）|
| 8 | Config-T caption | `8753:1460` |
| 9 | Config-T UX 交付卡 | `8757:1244` 1280×747（Why / Changes / Interaction / Acceptance）|
| 10 | Config-T 逻辑更新块 | `8758:1244` 1280×274 |
| 11 | Config-T Section | `6703:1116` 扩高 4468→5864 |
| 12 | **文案常驻页孪生卡**（§8.1「两边同源」硬要求）| [`10476:40`](https://www.figma.com/design/0054ib0nLmt27bC3QlGDl7/?node-id=10476-40) `Card · Scenario — Module mode switch failure` 680×924，插在 `Doc column` 第 4 位（Hidden SSID 卡之后 / Registry 卡之前）；Section `10167:2` 扩高 2591→3539 |

> ⚠️ 常驻页注释层字体是 **Roboto + Noto Sans SC**（与两个工作页的 Inter 不同）—— 新卡按 clone 源逐段继承样式，未硬编字体。

**未做（有依据）**：
- **⛔ 不画跨行连线**（§M23.6）—— 失败分支若从 Switching 帧拉线到新行，必横穿 UX 卡行与 linked-screens 行，命中 M23.6 的 frame-content-avoidance 禁项。改由行标题 + UX 卡 + 文案表说明流程归属。
- **⛔ 不改既有 PRD 卡 / 既有 UX 卡**（§M23.17 明文：纯补充走「下方新建更新块」，不动既有 monolithic 文本节点，避免回滚风险）。
- **⛔ 不新建 Journey 卡**（W2 档免除，用户流程未变）。

## 6. 机检（`audit:mockup-conformance --node`，机器输出原文）

**LCD `--file 0054ib0nLmt27bC3QlGDl7 --node 8672:11`**：
```
  ✅ PASS  integrity              scope=node  checkedUnits=1
  ❌ FINDINGS  bilingual-spacing      scope=node  checkedUnits=16
  ✅ PASS  overlap                scope=node  checkedUnits=30
  ✅ PASS  geometry-consistency   scope=node  checkedUnits=11
  ✅ PASS  source-link-integrity  scope=node  checkedUnits=2
```

**Config-T `--file rJJjWWs51n2iFOlCIC7aYG --node 6703:1116`**：
```
  ✅ PASS  integrity              scope=node  checkedUnits=1
  ❌ FINDINGS  bilingual-spacing      scope=node  checkedUnits=49
  ❌ FINDINGS  overlap                scope=node  checkedUnits=25
  ✅ PASS  geometry-consistency   scope=node  checkedUnits=11
  ✅ PASS  source-link-integrity  scope=node  checkedUnits=2
```

**逐条归属（机器复验：本轮节点 id 在这两段的命中数 = 0 / 0）**：

| 闸 | 结论 |
|---|---|
| `integrity` · `geometry-consistency` · `overlap`(LCD) | ✅ PASS |
| `overlap`(Config-T) 唯一 finding | **既有节点**：`F3 · Mode=Disable` `6626:287` ⨯ 连线标签组 `6654:1119` 重叠 7×13 —— 本轮未触碰，登记不修 |
| `bilingual-spacing` 剩余 | 全为既有卡存量；**本轮节点 0 命中** |
| `source-link-integrity` | 本轮先红后绿：header 的 `V4-1865` / `V4-2318` 原是纯文本 ⇒ 已补 `setRangeHyperlink` + cyan `#33A4FD` + `UNDERLINE`（§M23.8.1 第 2 款），重跑转 **PASS** |
| `colors` · `typography-icon` · `library-origin` · `binding-fidelity` · `default-white-bg` · `library-binding` | 文件存量（§M49.1 owner-deferred）。本轮命中项经对照**既有同类节点全部同样命中** ⇒ 延续存量，无新引入类别 |

**常驻页 `--node 10167:2`**（第 12 项交付）：
```
  ✅ PASS  integrity              scope=node  checkedUnits=1
  ❌ FINDINGS  colors                 scope=node  checkedUnits=117
  ✅ PASS  typography-icon        scope=node  checkedUnits=66
  ✅ PASS  library-origin         scope=node  checkedUnits=6
  ❌ FINDINGS  binding-fidelity       scope=node  checkedUnits=117
  ✅ PASS  bilingual-spacing      scope=node  checkedUnits=21
  ✅ PASS  overlap                scope=node  checkedUnits=0
  ✅ PASS  source-link-integrity  scope=node  checkedUnits=6
  ✅ PASS  feedback-not-selfdrawn scope=node  checkedUnits=2
```
剩余 `colors`(C1 icon-fill 4) / `binding-fidelity`(B-TYPO 64 · B-SCALE 2 · B-COVERAGE 144) / `default-white-bg`(闸自身 CONTROL 失效 ⇒ ⛔ 读数不可解读) = **整页存量**：机器对照 **新卡 `10476:` 命中 15 次 vs 既有 Hidden-SSID 卡 `10394:` 命中 15 次**，完全对称，且页标题 `10167:5` 本身也在 B-TYPO 名单内。

**本轮修掉的四条真 finding**（都是本轮引入，非存量）：
1. `source-link-integrity` M23.8.1-A — 两端逻辑更新块 header 的 ticket ID 纯文本无 hyperlink ⇒ 补 link + cyan + UNDERLINE
2. `bilingual-spacing` zh-opacity — LCD UX 卡标题 ZH 段 `0.7` 应为 `0.45`（clone 自既有卡 `8509:318`，**既有那条在基线内、本轮这条算新引入**）⇒ 已改 0.45
3. `integrity` I3 — 常驻页加卡后 `Doc column` 底部 3499 溢出 Section 2591 ⇒ Section 扩到 3539
4. `source-link-integrity` — 常驻页出处行**中文那半**的 ticket ID 仍是纯文本（首轮只给英文那半加了 link，`indexOf` 只找第一处）⇒ 改为循环全部出现位置

**三条施工期自纠（截图 / 机器读数发现，非机检项）**：
- Message 首版落 `y=54`（设置页通则值）**整条盖住 Mode pills** ⇒ 当轮改 `y=118`。🔴 **该结论已被 §12 推翻**：owner 判「应该在顶部」，终值 `y=8`（盖导航标题、保 pills）。⛔ 别再引用 `y=118`
- Config-T Message 首版落页面顶部 ⇒ **读成 LAN Setting 段的错误** ⇒ 移到 Hotspot 段标题同行
- 逻辑更新块 TEXT 因 `resize()` 重置了 `textAutoResize` ⇒ 文字被裁（Config-T 块高只有 112，四条要点只显一条）⇒ 赋 `characters` 后重设 `textAutoResize='HEIGHT'`，高度 112→274
- 常驻页新卡 ZH 行残留字面 `**`（§M23.11 画布无 markdown 渲染器）⇒ 已去，全卡残留扫查 = 0
- 常驻页新卡 15 个 TEXT 的**图层名**仍是 clone 源的旧文案（含错的 `V4-1646` 单号）⇒ 已同步为实际文案，`staleTicketInNames` 复验 = 0

### 6a. `library-binding` 单独处理（全文件口径闸，两端都补跑过）

首轮两端这条闸都**没真跑**：Config-T 是 `ERROR (could not run)`（离线缓存从未生成），LCD 是 `SKIPPED — 离线缓存过期`。按 §M-GATE-FALSIFIABILITY，**跑不起来 / 被跳过 ≠ 通过**。已各跑一次 `pnpm sync:mockup <fileKey>` 后重跑：

| 文件 | 结果 |
|---|---|
| Config-T | 659 findings（M0=620 · M1=6 · M30=33），全文件口径 |
| LCD | 1792 findings（M0=1615 · M1=110 · M30=67），全文件口径 |

**⛔ 这条闸的逐节点归属判不了，别用它的输出下"本轮干净"的结论**（本轮实测踩过三次，逐条记下）：

1. **人读层只印 10 条**，其余「另 1782 条只进结构化 findings，人读层折叠」⇒ grep 人读层得到的「本轮节点 0 命中」是**折叠造成的假阴性**（对照：连 clone 源 `8493:131` 也 0 命中）。
2. `--node` 对它**无效** —— 它自己印「扫到 103350 个节点（离线缓存整文件快照，**与 --node 无关**）」。
3. `--report` 的 `nodeFindings` 只有 **24 条**（总数 1792），且**两次用不同 `--node` 跑出的 24 条完全相同** ⇒ 它是那份折叠输出的反推产物，不是全集。`10476:40`（本轮常驻页新卡）出现在里面曾让我误判成「本轮新引入」，**实测推翻**：直接 probe 该卡 **INSTANCE 数 = 0**（纯 TEXT + FRAME），这条判 INSTANCE 引用的闸对它**根本无可判对象**。

**⇒ 改用直接取证（probe `getMainComponentAsync().remote`），结论确凿**：

| 对象 | INSTANCE 总数 | file-local | 结论 |
|---|---|---|---|
| LCD 本轮 F5 `10464:357` | 10 | **2**（`LCD Button` Type=Home · Type=Primary）| 与 clone 源**逐字相同** |
| LCD clone 源 F3 `8493:131` | 7 | **2**（同上两个）| — |
| Config-T 本轮 F6 `8753:1140` | 181 | **1**（`WiFi list module`）| 与 clone 源**逐字相同** |
| Config-T clone 源 F5 `6626:851` | 178 | **1**（同上）| — |
| 本轮新增库 Message（两端 `10464:1328` / `8753:1552`）| 各 2 子 instance | **0** | 全部 `remote=true` ✓ |

⇒ 本轮**没有新引入任何 file-local instance**；命中项全是 clone 继承的存量。唯一新增的 instance 是库 `Message`，干净。

**⚠️ 登记为 unverified，⛔ 不读成通过**：
- `library-binding` 的**逐节点归属**：见 §6a，工具链给不出全集明细 ⇒ 对本轮按未覆盖处置（结论靠 probe 取证，不靠该闸）。
- `feedback-not-selfdrawn` 两端报「本轮扫描面里没有任何名为 `Notification` / `Message` 的节点」—— 但本轮**确实**建了 `Message / error / L`。闸按精确名匹配，`Message / <status> / L` 这种既有命名范式（V4-1646 三条同样命名）落在它射程外 ⇒ 该闸对本轮 **没验**。按 §M-GATE-FALSIFIABILITY 记为未覆盖。
- `colors` C2/C3 与 `binding-fidelity` B-SEM：依赖 `/variables/local`，本轮拿到 0 个变量名 ⇒ 可能 under-report。

## 7. 存量偏离（如实登记，不静默）

| 项 | 事实 | 处置 |
|---|---|---|
| 注释层字体 = **Inter** 而非 DS 要求的 `Roboto` + `Noto Sans SC` | 两端既有交付卡（LCD `8509:318` / Config-T `6650:1117`…）全部是 Inter，源自 V4-2318 建页时的 §M47 fallback。本轮 clone 继承 | 按 §M21.4「取宿主页同类模块实测值」保 Inter 保页内一致；改 Roboto 会让新卡与同行既有卡视觉分裂。`annotation-font-roboto-noto` 闸命中项**既有同类同样命中** ⇒ 存量债，待 owner 决定是否整页收敛 |
| LCD 产品帧文字也是 Inter（非 `Verdana`）| 同上，V4-2318 建页存量 | 同上，不在本轮 scope |
| Config-T 既有 `overlap` 7×13 | `6626:287` ⨯ `6654:1119` | 登记，待 owner 决定 |

## 8. owner 已决（2026-09-11 收尾轮）

| # | 决议 | 落地 |
|---|---|---|
| **Q1** | ✅ **不说原因，但必须提醒稍后再试** —— 尾句 `Retry later.` / `请稍后重试。` | 4 处全改完，见 §10 |
| **Q2** | ✅ **只回 V4-2466**，V4-2318 / V4-1865 **完全不动**（不评论、不转 assignee、不建关联）| 见 §9 |

### 8a. Q1 的一条实测代价（⛔ 后续别按语感改回去）

owner 原话是「稍后再试」，最自然的英文 `Try again later.` **装不下**：最长参数组合实测 **480×66 折两行**，命中 §4.1 第 5 条禁项（宽度条件满足、高度条件不满足）。终值取 `Retry later.`（458×45 单行）。逐候选实测表见 [`lcd-message-strings.md` §5.2](../specs/lcd-message-strings.md)。

⚠️ **这条改动是回到合规、不是新增例外**：§4.1 第 3 条原文就要求失败提示「只说失败 **+ 下一步**」，旧文案缺的正是下一步。

## 9. Jira 收尾：三单实况（2026-09-11 `getJiraIssue` 直读，⛔ 非 JQL）

| 单 | 状态 | assignee | 判定 |
|---|---|---|---|
| **V4-2466** | **To Do** | Danner Dai | winni 2026-09-09 已开的现象单，0 评论 0 关联。**唯一还在跑的单 ⇒ 本轮唯一回复落点** |
| V4-2318（LCD）| **Resolved** | **Edward Wu** | 已关闭；assignee **本来就对** |
| V4-1865（Config-T）| **Done** | **Bonnie Zhou** | 已关闭；assignee **本来就对** |

**三条推翻了收尾轮起手时的前提，逐条记下**：

1. **「转 assignee」这一步不存在** —— 两单 assignee 早已是 Edward / Bonnie（交付当轮就转过了）。⛔ 别再把它当待办；`design-process.md:208` 的路由规则没错，错的是假设它还没执行。
2. **两个功能单都已关闭** —— 往 Resolved / Done 单贴 `UX Design updated.` 不产生开发动作。**收尾前必须直读目标单状态**，别默认「交付过的单还开着」。
3. **Q2 的前半自己解决了** —— 现象单不用开，winni 当天就开了 V4-2466，描述逐字含 §1 的链路与 youngzhang 的临时恢复法。**「要不要开单」类问题，先搜再问**。

## 10. 收尾轮改动（Q1 落地，对象全集 = 4 处 + 1 文档）

改前按 `findAllWithCriteria` + 正则扫三个 surface 取全集（⛔ 非凭记忆列帧）：

| # | 文件 / page | 节点 | 类型 | 结果 |
|---|---|---|---|---|
| 1 | LCD `8476:2` | `I10464:1328;4798:1714;10464:1344` | 产品层 Message 内部 TEXT | 439×45 单行 |
| 2 | LCD `8476:2` | `10468:395` | 文案表「Failure feedback copy」双语正文 | 样式段数 4→4 ✓ |
| 3 | LCD `10166:2` | `10476:52` | 常驻页孪生卡 strings 行（含实测数字）| 样式段数 2→2 ✓；数字 369 → 439/458 |
| 4 | Config-T `6601:2` | `I8753:1552;4798:1714;8753:1578` | 产品层 Message 内部 TEXT | 439×45 单行 |
| 5 | 文档 | `lcd-message-strings.md` §5.1 / §5.2 | 文案真源 | 规则 + 文案表 + 量法 |

**双语节点（2/3）用定点 `insertCharacters(..., 'BEFORE')` 而非整段覆盖赋 `characters`** —— 规避 R8〈稀疏属性逐段重设盖不住〉：整段覆盖会把 EN/ZH 的分段样式（含 ZH opacity 0.45）压平成首段样式。复核判据 = **改前后 `getStyledTextSegments` 段数不变**。

**孪生卡溢出复核**：文本框 37→55（折三行）⇒ 卡高 924→942、`Doc column` 底 3517 vs Section `10167:2` 高 3539 ⇒ **余量 22px，无 I3 溢出**。

### 10a. 机检（2026-09-11 收尾轮，三面各一跑，先 `pnpm sync:mockup` 刷缓存）

`rulesErrored: 0` ×3。**逐节点归属（机器输出，非目测）**：

| 我改的节点 | report 命中 |
|---|---|
| `I10464:1328;4798:1714;10464:1344`（LCD 产品层）| **0** |
| `I8753:1552;4798:1714;8753:1578`（Config-T 产品层）| **0** |
| `10468:395`（LCD 文案表）| 仅 `binding-fidelity` |
| `10476:52`（常驻页孪生卡）| 仅 `binding-fidelity` |

**为什么判为存量而不是新引入** —— 用对称性取证，⛔ 不靠「这条闸是 deferred 的」一句话带过：

```
孪生卡 10476:*（改了 :52）        15 个 TEXT 全部且仅命中 binding-fidelity
对照卡 10394:*（本轮零触碰）      15 个 TEXT 全部且仅命中 binding-fidelity   ← 逐字同形
文案表 10468:394（没碰）/ :395（改了）  两个同样命中 binding-fidelity
```

⇒ 改过的节点与没碰过的兄弟节点命中集合**逐字相同**，**本轮零新增**。`10476:40` 的 `library-binding` 是 §6a 已查实的假归属（该卡 INSTANCE 数 = 0）。

⚠️ **两个把「没跑起来」看成「通过」的坑，本轮都踩到了（记下量法）**：
1. worktree 里 `pnpm sync:mockup` 报 `FIGMA_FILE_KEY is not configured` —— **worktree 缺的不只是 `node_modules`，`.env` 同样要软链**（gitignore 掉的运行时配置一律没有）。
2. 那一跑里三个 audit 一行输出都没有，但整条命令 **`exited with code 0`**（退出码来自循环最后一条命令）。⇒ **判据看闸自己的 summary 行，⛔ 不看 shell 退出码**。成因是 zsh 不对无引号变量分词，`set -- $n` 让 `--node` 收到空值。

## 11. Jira：🔴 **不回复**（owner 2026-09-11 最终决定）

owner 逐字「**不用回复 JIRA**」⇒ **本需求不发任何 Jira 评论**，三个单（V4-2466 / V4-2318 / V4-1865）一条都不发、assignee 也不动。

⛔ 这是**终态不是暂缓** —— 别在下一轮把它当成「等落位定稿后再发」的待办捡回来。真要发须 owner 重新开口，且仍走 [[slack-jira-write-confirm]]。草稿只作存档，说明当时打算怎么发。

<details><summary>存档草稿</summary>

正文 `UX Design updated — failure-state UI for this case:` + 两个短锚文本（`Figma — LCD` → `8672:11` / `Figma — Config-T` → `6703:1116`）+ @Trevor Yao review + cc @Danner Dai（本单 assignee）/ @Edward Wu / @Bonnie Zhou，走 ADF link mark + accountId 真 mention（四个 id 均由本轮 `getJiraIssue` 直读取得）。

⚠️ 对 CLAUDE.md「正文固定就一句 `UX Design updated.`」有一处有意偏离：加了 `— failure-state UI for this case`，理由是 V4-2466 是后端根因单，光写 `UX Design updated.` 会被读成"根因已解决"。
</details>

## 12. 落位返工（owner 2026-09-11 看 mockup 后）

owner 逐字：「**错误的提示应该在顶部，当前的位置不对**」⇒ 两端提示条全部移到顶部。

| 端 | 旧值 | 新值 | 被完整覆盖的字形 | 保持可见 |
|---|---|---|---|---|
| LCD `10464:1328` | `y=118` | **`x=20.5, y=8`**（§2.5 A 档）| 导航标题 `Hotspot` | `Mode` + 三 pills + 正文 + 按钮 + `Home` |
| Config-T `8753:1552` | `x=456, y=235` | **`x=420.5, y=24`**（居中于页面壳，中心 640=640）| 顶栏 `Live` / `File` / `Router` | `General` / `Advanced` / Tab 栏 / Hotspot 分区 |

**两端都无半截字**（逐字形机器复核，不是目测）。

### 12a. 🔴 顺带抓出一个我本轮引入的真错：提示条右溢出画框 15px

`x=56, w=439 ⇒ right=495 > 480`，帧 `clipsContent=true` ⇒ ✕ 按钮被切掉。

**根因**：我加 ` Retry later.` 后宽度 369→439，但 `x` 还是按**旧宽度**算出的居中值 56，没跟着重算。现已改 `x=(480−439)/2=20.5`，右缘 459.5 ✓。

⚠️ **判据教训（比这个 bug 本身值钱）**：§4.1 的「宽 ≤480 且单行」只管**文案本身装不装得下**，**管不住这个 hug 盒子摆进帧里之后右缘在哪**。两件事。⇒ **改任何 HUG 宽度的文案后，必须重算落位并复核 `x + width ≤ 帧宽`**，⛔ 只量宽度会漏。我这轮就是量了宽度、报了「439×45 单行 ✓」，然后漏了它已经戳出画框——**而且截图当时就在手边，是 owner 先看出来的**。

### 12b. 两条旧否决意见的射程被放大了（登记，避免下次照抄）

| 旧结论 | 实际成立范围 |
|---|---|
| 「LCD 不能放顶部，会盖住 Mode pills」 | 只对 **`y=54`** 成立（提示 54..99 压 pills 46..98）。`y=8` 提示 8..53，pills 从 63 起，**根本不冲突** |
| 「Config-T 不能放页面顶部，会读成 LAN Setting 的错误」 | 只对 **`y≈155`（紧贴第一个分区上方）** 成立。`y=24` 浮在顶栏之上、不沾任何分区，歧义消失 |

⇒ 两次都是**把「某个具体落点不行」记成了「整个顶部区域不行」**，于是把提示一路推到了不该去的地方。**否决一个值时，要写清否决的是这个值还是整个方向。**
