# V4-2333 走查轮二十五 —— AI 协作过程复盘（UX / PM 内部分享）

> 日期：2026-08-05 · Owner：Nancy · 范围：一个 session 内完成的完整走查轮（落地轮）
> 实现层真源在 `docs/specs/2026-07-28-v4-2333-test-signal-parameters-design.md`（D31–D34）+ `docs/handoffs/2026-07-28-…-handoff.md` §1k-Z；**本文只讲协作过程，不复述设计内容**。
> 本轮特点：**零返工**。上一轮（轮二十四）刻意把两块高风险结构改造推到本轮，本轮开工前先把选型问清楚，因此没有出现"画完再改"。

---

## 1. TL;DR

| 指标 | 值 |
|---|---|
| 用户轮次 | **2**（1 次派单 + 1 次收尾）+ 1 次我方发起的选型询问（2 问） |
| 交付 | LCD 新帧/连线/annot 3 个节点 + L3 十档列表 + 4 处回显；Config-T 五帧结构改造 + CT3 十项 dropdown + 五帧五 annot 改名；两端交付层 22 个文本节点；DS repo 2 个 commit |
| `use_figma` 调用 | **26**（含 9 次只读 probe）· `get_screenshot` **6** |
| 核心 process gap | **1 个新坑**（`clone()` 的 parent 不可假设）+ **1 个跨轮暴露的旧病灶**（未验证的附带断言潜伏多轮） |
| 关键 insight | **"先算再改"能整块消灭几何改造** —— L3 十档看起来必然要扩面板，实测默认档恰好落在首屏第 5 位 ⇒ 面板与连体绿框 delta 0，一行几何都没动 |
| 返工次数 | **0**（两处脚本级失败均被工具原子性拦下，未污染文件） |

---

## 2. Session 概览

**起点**：用户给了一份结构完整的派单（5 项 + 纪律清单），本身已经是上一轮 handoff §1k-Y ⑥ 的逐条展开 —— 落位、素材、连线范式、待选型项、风险点全部预先实测好。

**终点**：决策全集 D1–D34 全部落地，唯一挂账项是 owner 侧的本地改名（第 3 轮未做，且已因 D29 降级为非验收级）。

**process artifacts**：design-spec 回填 D31–D34 + 修订 D4/D6/D25/§3.1/§3.2/§4/§7/§8 · handoff 新增 §1k-Z（9 小节，含机检原文）· DS repo `b0f4ea8f`（bug 修 + 6 条双向回归测试 + 硬约束 5 第⑤条）+ `31bfb0e7`（词库两条 toast 文案）· memory + MEMORY.md 索引 · 工作日志。

---

## 3. 主要迭代

| # | 阶段 | 触发 | 改动量 |
|---|---|---|---|
| 1 | 起手协议 | 派单里明写"起手 load tvu-design-mockup + figma-use" | 读真源 + scoped rule + M48 清单，零 Figma 写入 |
| 2 | 选型 gate | **我方主动发起**（派单要求"先给 owner 选型，别直接画"） | 2 问（toast 有无 / CT 前缀写法），各 3 候选 + ASCII 示意 |
| 3 | D32 落地 | owner 选"给 toast" | 3 个新节点 + 1 处 annot 措辞改写 |
| 4 | D31 LCD | 派单 | L3 十行重排 + 4 处回显（**面板零扩容**） |
| 5 | D31 Config-T | 派单 | 五帧删一行 + 五帧值改写 + CT3 SLOT 3→10 + 14 节点 reflow |
| 6 | D33 改名 | owner 选 `CT*` | 10 个节点名 + 6 处正文引用 |
| 7 | 交付层同步 | §M-DISCIPLINE.SYNC 状态转移自动触发 | 两端 22 个文本节点 |
| 8 | D34 DS repo | 派单（owner 轮二十四已口头同意） | 3 个文件 + 6 条测试 |
| 9 | 收尾 | 用户说"收尾" | spec/handoff/memory/日志/复盘 |

**没有出现"画完被打回"的迭代** —— 这是本轮与轮十五/十六（4 轮迭代才收敛）最大的差别，直接原因见 §5 第 1 条。

---

## 4. Process gap（按返工成本排序）

### G1 · `clone()` 的 parent 不可假设（新坑，成本：1 次修正调用）

**实证**：clone `AFTER 2` 帧与 `s3` 连线后只设了 `x/y`，返回值里 `parent` 字段显示两者都被挂到了 **page**（`9343:2`）而不是原来的 AFTER 子 Section —— 于是新帧落在 page 坐标 (40,1840)，完全在 Section 之外。

**为什么会漏**：Plugin API 文档语义上 clone 应留在同一 parent，且此前多轮 clone 都"看起来对"（因为那些 clone 的源节点本身就挂在 page 层）。**这个假设在 Section 内部第一次被证伪。**

**抓到它的机制**：脚本 return 里带了 `parent: nf.parent.id` —— 不是靠看画布。**规则 15「必须 return 所有创建/改动节点 ID」的价值在这里兑现了一次**：如果只 return 了 id 没 return parent，这个错误要等到下一次几何复测才暴露。

**固定修法**：`sec.appendChild(node)` **之后**再设 section-relative 坐标（§Q15），顺序不可颠倒。已写进 handoff §1k-Z ① 与 memory。

### G2 · "顺便宣告另一个组件没问题" —— 未验证的附带断言潜伏多轮（成本：跨 4 轮的错误判断）

**实证**：`mockup-conventions.md:1090` 在给 I2 缺口定根因时写了「缺口是替代脚本覆盖面窄，**不是规则漏写、也不是真源脚本有 bug**」。前半句验过、后半句**没验过**，是顺手加上的。本轮实测坐实真源 `probeI2()` 确实另有一个独立 bug —— 而且方向相反：替代脚本**漏报**真实重叠，真源脚本**虚报**不存在的重叠。两个缺陷同时存在了多轮。

**代价**：I2 的 4 处 FAIL 被连续多轮写成"脚本 bug 假阳性"（这个判断本身后来是对的），但**在此之前**它被那句话挡住过 —— 有那句"不是真源脚本有 bug"在，就不会有人去读脚本源码。

**回流**：`:1090` 已就地加更正段 + 教训句：**给一个缺陷定根因时，别顺便宣告另一个组件没问题，只写自己验过的那一半。**

### G3 · 容器增高后的复位要在**每次**文本编辑后做，不能只在批次末做一次（成本：1 次重复复位）

**实证**：Config-T UX 卡的 Journey 间距刚复位到 60，紧接着一次给追加内容加日期标注的小编辑又让卡长高 14 → 间距变 46，得再复位一次。

**教训**：§I7 的"实测比对"不是批次级动作，是**每次改变容器内容后**的动作。文本编辑再小也会改高度。

### G4 · 追加块的样式模板可能不存在 —— 脚本要如实报 missing，不能硬造（成本：0，但差点造出一档新样式）

**实证**：LCD 的 `Changes` 卡有 `Roboto/Medium 13 cyan` 的批次标题档，Config-T 的没有（它是平铺 bullet 结构）。按 M47.2「新块样式取自同节点内最后一个同类 seg」实现的取模板函数**如实返回 `missing: TEMPLATE:h`**，于是我改用带日期的行内标注，而不是给那张卡凭空造一档标题样式。

**这是设计对了的地方**：如果取模板函数在找不到时 fallback 到硬编码档位，就会静默给 Config-T 卡加一档它本来没有的层级 —— 而且看起来还挺合理，很难在走查里被抓到。

### G5 · 存量偏差会藏在"我没动过的地方"（成本：0，顺带修掉）

**实证**：Config-T UX 卡与 Journey 卡开工前就**重叠 −34px**（我方本轮之前没动过该卡）。是因为本轮要再增高该卡、必须先量基线才发现的。已显式登记为「存量、非本轮引入」并顺带修掉。

**教训**：§I7 的"与变更前基线比对"顺带具有**存量体检**功能 —— baseline 本身就可能是错的，量出来的第一个数不要默认当成"正常值"。

---

## 5. 本轮验证有效的做法（同等重要，直接复用）

> 按 CLAUDE.md「收尾复盘拆分记录：教训 vs 验证过的好方法」，这一节与 §4 同权重。

### ✅ V1 · 高风险改造前先问选型，是本轮零返工的直接原因（ROI 最高）

派单明写"先给 owner 选型，别直接画"。两个问题各给 3 个候选 + 判据 + ASCII/示意，owner 各选一个，之后**一次画对**。

对照：轮十五/十六的 Text Color 方案改了 3 版（v1 被否 → 保留存档 → 又被要求删除）；轮十一推翻轮十的底栏两形态。**这些返工的共同点都是"先画后问"。**

**判定线（可复用）**：改动会产生**不可从上下文客观推断**的形态选择（要不要加一个元素 / 命名用哪种写法）→ 先问。反之，能从可探测的事实推断的（面板容量、几何余量、库归属）→ 自己实测定，不要多问一轮。

### ✅ V2 · "先算首屏容量再决定要不要扩容" —— 整块消灭了预期中的几何改造

L3 从 7 档变 10 档，直觉上必然要扩面板（而这正是派单里标为高风险的那一项：触 D13 + 轮五"被裁切等于没交付"）。**先算**：新清单里默认档 `1080i59.94` 恰好排第 5 位，而面板 `240×211` 的首屏容量正好是 6 行（`5×35 + 36`）⇒ 默认档天然在首屏 ⇒ **面板与连体绿框 delta 0**，物理上限复测确认 6 行仍是上限（7 行 246 > 228）。

**一行几何都没动，风险最高的那一项变成了零风险。** 教训：把"看起来必须做的大改动"先量一遍，经常发现约束刚好自洽。

### ✅ V3 · 让测试自证有意义 —— 复刻修前逻辑跑一遍

修完 `probeI2()` 后，另写脚本复刻**修前**的收集逻辑，在同一份干净 3-Section 构造上跑出 `[{1:1,1:1},{2:2,2:2},{3:3,3:3}] pass=false` ⇒ 证明新测试的"干净 page 必 0"那条**在修前必然失败**，不是永远绿的空测试。

**这比"6 tests pass"有信息量得多** —— 后者不排除"测试压根没测到东西"。

### ✅ V4 · 双向探针（残留 0 + 反向有命中）

每次残留扫查都配一条反向探针：扫 `1080i5994` 得 0 的同时，扫 `1080i59.94` 得 5（LCD）/ 12（Config-T）⇒ 证明过滤条件没把目标排除掉。**"扫出 0"和"扫不到"在输出上长得一样**，只有反向探针能分开。

### ✅ V5 · 改字复用而不是删+建

L3 的 `2160p30` 那行要删、`1080p59.94` 要加 —— 直接把那一行**改字复用**，比 `remove()` + clone 少一次结构变更，也不产生新节点 id。同理 Config-T 保留 `row · Resolution` 就地改造成 `row · Format`（合 M42.3 就地改优先）。

---

## 6. 已回流 / 待回流的规则

| 规则 | 真源文件 | 简述 | 状态 |
|---|---|---|---|
| 硬约束 5 第 ⑤ 条 | `mockup-conventions.md` §M-DISCIPLINE.SCOPE | 覆盖面对齐之后还要验**判据本身** —— 双向探针 + 已知基线比对；"它是真源"不构成判据正确的证据 | ✅ 已回流（`b0f4ea8f`） |
| `:1090` 更正 | `mockup-conventions.md` §I2 实证段 | 删掉未验证的附带断言"不是真源脚本有 bug"，加教训句 | ✅ 已回流 |
| `probeI2()` bug 修 + 双向回归测试 | `scripts/audit-mockup-integrity.mjs` + `tests/AuditMockupIntegrityProbeI2.test.ts` | 自配对假 FAIL ⇒ I2 恒 FAIL ⇒ 总闸恒 blocking | ✅ 已修并实跑坐实 |
| 词库两条 toast 文案 | `skills/shared-vocab-rules/vocabulary.md` | `Video input lost — test signal resumed.` / `Video input detected — now live.` | ✅ 已回流（`31bfb0e7`） |
| `clone()` 的 parent 不可假设 | `figma-technical-reference.md` **Q15 形态 C** | 坐标算对了但落在另一个坐标系里；`x/y` 语义完全由 parent 决定 ⇒ 先 `appendChild` 再设坐标。判据升三条通吃 + Acceptance 加两条（`return` 带 `parent` · `appendChild` 在设坐标前） | ✅ **已回流**（owner 收尾拍板「如有需求则修改」） |
| §I7 复位是每次内容变更后的动作、不是批次末动作 | `mockup-conventions.md` **§I7 Acceptance** | 条文字面已覆盖，补的是执行强度：改容器内容的调用与量间距的调用一一对应，文本编辑再小也算改内容 | ✅ **已回流**（同上） |

**两条都并入既有条文、未续新编号** —— 合「优先并入既有 umbrella」。判据：两者都有确定性替代手法（先 appendChild / 一一对应地量）、失败模式静默、且都有现成归属条文，不需要新 M-rule。

**DS STATUS.md 已更新**（owner 收尾拍板「你现在修改吧，并行 Session 先暂停了」）：原先跳过的两条理由里，「同文件并发写风险」已由 owner 暂停并行线消除；「框架失真」按 CHANGELOG §5 既定口径处理 —— 长叙述进 `STATUS-CHANGELOG.md` 顶部新建 **session AA** 段（7 小节），STATUS 顶部只加摘要 + 指针，并把 `Last updated` 从「session Z（单任务）」改为「2026-08-05（两个 session）」。闸复核：`audit:doc-shape` S1 顶部摘要区 `1782 → 2533 B`（上限 3000、无豁免）PASS · `status-consistency` / `stale-anchors`（440 锚点零孤儿）/ `rule-load-map` 全 PASS。

> **这条本身是个可复用的判断**：跳过一个必做步骤时，把**理由拆成独立的几条**写下来（此处 = 框架失真 + 并发写风险），比写一句"有风险所以不做"有用得多 —— owner 只消除了其中一条（暂停并行线），另一条就能立刻看出还需要用别的办法解决（放 CHANGELOG），不必重新讨论一轮。

---

## 7. Model 选型推荐（基于本轮实证）

| 环节 | 推荐 | 依据 |
|---|---|---|
| 派单解析 + 起手协议 + M48 清单 | **Opus** | 本轮要同时持有 D1–D34 的决策链 + 十几条 M-rule + 两端交付层结构；跨条目冲突（D4/D6/D25 被 D31 修订）靠的是长链一致性判断 |
| 几何/容量计算与决策（V2 那类） | **Opus** | "先算再改"的价值全在**算对**，算错会导致反向结论（扩容 vs 不扩容） |
| 批量文本替换执行 | Sonnet 可胜任 | 字符级样式映射一旦写成 helper 就是机械执行，判据（段数相等）是硬的 |
| 只读 probe / 残留扫查 | Sonnet 可胜任 | 输出格式固定、判据明确 |
| 跨轮病灶识别（G2 那类） | **Opus** | 要发现"一句没验证的话挡住了多轮排查"，需要同时读到条文、handoff 历史与脚本源码 |

**本轮全程 Opus**。真正需要 Opus 的是 §5 V2 与 §4 G2 这两处；其余约六成调用（probe / 批量替换 / 扫查）换 Sonnet 不会掉质量 —— 但**拆分本身有交接成本**，单轮内不值得，跨轮固定套路（如"跑机检并贴 handoff"）才值得外包。

---

## 8. Action items

**给 UX / 提需求方**
1. **高风险改动请预期"先收到一个选型问题"** —— 本轮 2 个问题换来 0 次返工，比"先画一版看看"便宜一个数量级。问题会带候选 + 判据 + 示意图，回一个字母即可。
2. **派单里预先实测好的落位/素材信息价值极高** —— 本轮派单直接给了坐标、连线范式节点、可 clone 的源帧，省掉整轮勘察。这是上一轮 handoff §1k-Y ⑤b 的产物，值得继续保持。

**给 AI 协作流程**
3. **`clone()` 之后必须 return `parent`** —— 不只 return id。本轮就是靠它抓到 G1。
4. **每次改容器内容后立刻量一次依附/兄弟间距**，别攒到批次末。
5. **给缺陷定根因时只写验过的那一半**，不要顺便宣告别的组件没问题（G2）。
6. **残留扫查一律配反向探针**，否则"0 命中"与"扫不到"无法区分。

**给 owner**
7. ~~C2 `9742:392` `PM_X7M → PM_X7L`~~ → ✅ **收尾时已改完并复核闭合**（五重证据见 handoff §1k-Z ⑥）。
8. ~~两条候选规则请裁定~~ → ✅ **已拍板「如有需求则修改」并回流**（§6 表末两行）。
9. ~~DS STATUS 是否露出~~ → ✅ **已拍板「现在改」并更新**（§6 末段）。
10. **唯一剩下的阻塞项 = mockup 定稿**。本轮两块结构改造形态变化较大，建议先走查 LCD `9402:59` 与 Config-T `8075:1000` 两个 Section 再定稿；定稿后才发 Jira（精简格式 + 发前贴全文确认）。

> **收尾追记**：owner 在收尾时一次给全三条决策，本轮因此**没有留下任何"待 owner 定"的悬置项**——上面 7/8/9 三条从"提请裁定"到"已落地"发生在同一次对话里。这是本轮效率高于前几轮的另一个原因：**把待决项集中到收尾一次问清，而不是分散在过程中逐个问**。

---

## 附录 · use_figma 改动统计

| 文件 | 只读 probe | 写操作 | 截图 |
|---|---|---|---|
| LCD `0054ib0nLmt27bC3QlGDl7` | 5 | 8（含 1 次原子失败、未污染文件） | 4 |
| Config-T `rJJjWWs51n2iFOlCIC7aYG` | 4 | 9 | 2 |
| 合计 | **9** | **17** | **6** |

失败调用 1 次（`node._segsAfter` 赋值到 TEXT 节点不存在的属性）—— `use_figma` 原子性生效，脚本整体未执行、文件零污染，改掉那两行后重跑成功。**这是"失败即回滚"这条工具性质第 N 次省下清理成本**。
