# 变异覆盖与残留红 —— rev-14 冻结字节记录 + rev-15 重锚(t1 重锚 · t2 审计 · t3 复核 · t4 质量门 · t5 初稿 · t6 外观覆盖 · t7 质量门 · t8 定稿 · r15/t6 全仓重锚与收口 · r16/t1 rev-16 重锚 · r17/t1 rev-17 重锚与收口 · r18/t1 rev-18 重锚与收口 · r19/t1 rev-19 重锚与收口 · r20/t1 rev-20 重锚与收口) > 定稿版本(t8)。本文档是「把这变异报红全修了」这一轮的收口证据:**29 个**声明变异逐条实测 > (t2 的 18 个 + t6 的 11 个外观变异)、残留非零逐条理由、被修文件的前后哈希与差异性质、 > 断言零删除对照、以及本轮**不可验证**项。 > > - 行文里的「已证」= 本工作区可重复实测(含哈希/退出码/逐行对照);「未证」= 本机无法验证 > (真机像素、颜色观感、DSH 真实主题令牌、UA 是否支持 `base-select`、统计性竞态)。 > - 零产品改动:`lib/client.js` **147062 B / sha256 730D1C2F7F58E19471D4B77EE43A221B4FD255BC06AD2AE3752A2C4443466BD5**, > `lib/index.js` **46638 B / sha256 03778391E15163487BC0F26082A73CBA15FAAF44CDC2CF93B0C185D75FB0B938** > (run-r13 第 0 节 9 文件逐条 OK;t3/t4/t7 各自重算 0 mismatch)。 > - 纪律:只修**锚串 / 替换助手 / 驱动 / 声明**;**没有删除或放宽任何断言**(§5 用「名字集合」对照, > 含改名检测)。 > > **rev-15 增量(r15/t6,细节全在 §12)**:`lib/client.js` 现为 **149196 B / sha256 32F0E31F…**(OBS-A 修复)、 > `verify/client-half.test.mjs` **88005 B / BB2C1A34…**、`verify/custom-audio.test.mjs` **20263 B / D13A8D63…** > (同长,只差 1 个字节);probe-17/18/19 的指纹字面量与 `run-r13.ps1` 已重锚到 rev-15;新增失败路径探针 > `r15t2-independent-probe.mjs`(canonical 同时跑 shipped 42/42 与 `--mutant`,红集恰好等于它声明的 10 条); > 10 条 legacy 豁免**退役**(非零现在直接 FAILURE);rev-15 变异表 **30/30**。§1–§11 里写的「现在的哈希」 > 是 rev-14 那一轮的记录,**一律以 §12 为准**。 > > **rev-16 增量(r16/t1,细节全在 §13)**:`lib/client.js` 现为 **150663 B / sha256 76B4D4E7…** > (会话头部的铃铛与 caret 分开:新增常量 `BELL_GAP_PX = 6`,`.dacBellWrap` 的 `gap` 由它拼出, > 同一常量上控制台面 `sessionIcon.bellGapPx`)、`verify/client-half.test.mjs` **88441 B / B5FC3935…**(371 → 373 条)、 > `verify/custom-audio.test.mjs` **20263 B / 5AD07459…**(同长,仍只差 1 个字节);probe-17/18/19、`r15t2`、 > `r15t6-mutation-table.mjs`、`run-r13.ps1`、`probe-8`(legacy)、`probe-2`(仅断言名)已重锚; > probe-20 **23/23**、声明变异 11 → **13**;rev-16 变异表 **32/32**;10 条 legacy 探针重锚后**重新 10/10 全绿**。 > **§12 之后以 §13 为准。** > > **rev-17 增量(r17/t1,细节全在 §14)**:`lib/client.js` 现为 **154457 B / sha256 B4A0A1B323A9BC9A99803EDBE28E770DE1097162165B16000995DD8E84A4A2F4** > (会话头部的 caret 在弹层打开时**以过渡动画下转 90°**、关闭时转回;旋转作用于 `` 字形而非按钮盒, > 新增常量 `CARET_OPEN_ROTATE_DEG = 90` / `CARET_ROTATE_MS = 160`,两者上控制台面 > `sessionIcon.caretOpenRotateDeg` / `caretRotateMs`,并带 `prefers-reduced-motion:reduce` 降级)、 > `verify/client-half.test.mjs` **95803 B / 97EEB2D0…**(373 → 388 条)、 > `verify/custom-audio.test.mjs` **20263 B / 523572EA…**(同长,仍只差 1 个字节);probe-17/18/19、`r15t2`、 > `r15t6-mutation-table.mjs`、`run-r13.ps1`(含日志前缀 `r16-` → `r17-`)、`probe-8`(legacy)、 > `probe-2`(仅断言名)已重锚;probe-20 **30/30**、声明变异 13 → **20**;rev-17 变异表 **39/39**; > 10 条 legacy 探针重锚后**再次 10/10 全绿**。**§13 之后以 §14 为准。** > > **rev-18 增量(r18/t1,细节全在 §15)**:真机反馈「**要有过渡动画能看到在转动的箭头**」—— rev-17 的 > **160ms 转动在真机上看不出来**。本轮把 `CARET_ROTATE_MS` 由 **160 改成 300**(CSS 过渡时长仍由该常量拼出), > 并新增**顶层**诊断 `__DSH_APPROVAL_CHIME__.reduceMotion()`(**实时**回答本页是否处于 > `(prefers-reduced-motion: reduce)`;不在 `sessionIcon` 里)。`lib/client.js` 现为 > **156937 B / sha256 1C8DB24CA492CE2A27B6BEC10A3366612F2F5AD7294D39E8B9D4D78C2FC8B54D**、 > `verify/client-half.test.mjs` **100983 B / AB6F7E48…**(388 → 399 条:**1 条被更强的替换 + 11 条新增**,净删除 0)、 > `verify/custom-audio.test.mjs` **20263 B / 6EF2F162…**(同长,仍只差 1 个字节);probe-17/18/19、`r15t2`、 > `r15t6-mutation-table.mjs`、`run-r13.ps1`(含日志前缀 `r17-` → `r18-`)、`probe-8`(legacy)、 > `probe-2`(仅断言名)已重锚;probe-20 **30/30 → 37/37**、声明变异 20 → **26**;rev-18 变异表 **45/45**; > 10 条 legacy 探针重锚后**再次 10/10 全绿**。**§14 之后以 §15 为准。** > **本轮不写"现在已经看得见了"**:能证的只有"时长常量 = 300ms、CSS 与诊断面一致、`reduceMotion()` 三种 > 分支各自正确";**"看不看得见"只有用户能在真机判定**(这正是本轮的起因,见 §15.8)。 > > **rev-18 修复增量(r18b/t5,细节全在 §16)**:产品里只留可陈述的事 —— `REVISION` 由 > `'rev-18 · the caret turn is slow enough to see'` 改成 `'rev-18 · the caret turn takes 300 ms'` > (可测事实),常量注释 251-257 的两句观感断言**降级为判断**(明确写成预期/待真机确认;用户原话保留 > —— 那是事实)。**行为一字未改**(角度 90、`ease`、reduced-motion 语义、`reduceMotion()` 全不动)。 > `lib/client.js` 现为 **157296 B / sha256 7FE150A0C1A89D6797ED3491F8CC74251CD7FD64DD8FA708621BA33FB9F49C45** > (r18/t1 的 156937 B / `1C8DB24C…` 已归档 `_raw/r18b-t5-archive/lib__client.js.1C8DB24C.txt`); > 重锚 **44 行 / 46 处**、**8 个文件**(工具 `verify-independent/r18b-t5-reanchor.mjs`;这三个数取自它的 > `_raw/r18b-t5-reanchor-figures.json`,**不手数** —— r18c/t7 修的就是手数把"行"当成"处"、把 8 写成 7); > 文档机检由 **81 → 102 条**(新增的其中一条正是 F2 要求的那条防复发检查: > 头部那一行的 (bytes, sha) 对,旧机检读的是 §15.1 的另一行,所以错着也全绿); > canonical 换 **`r18b-`** 前缀重跑,**`r18-*` 证据一个字节未动**。**§15 之后以 §16 为准。** > > **rev-18 修复增量 2(r18c/t7,细节全在 §17)**:第二条质量门抓到的是**同一个类的第二处** —— > `verify/client-half.test.mjs` 的断言**名字**在断言"真机看得见"(条件只断言 `=== 300`)。本轮把它改成 > 只陈述可测事实(`'the duration is the rev-18 one: 300 ms, the value this round shipped (rev-18)'`), > 观感部分留在同一段注释里并标明是**预期/判断**;`client-half` 因此 **101496 B / `B24E22E8…`**, > 冻结清单里的 bytes/sha 由重锚工具**当场从磁盘量出**(`verify-independent/r18c-t7-reanchor.mjs`)。 > 同时按构造法把**这一类**扫了一遍(`lib/**` + `verify/**` + `verify-independent/**` + `README.md` + > `CHANGELOG.md` + `docs/**`,模式与 allow-list 见 §17.2):**非例外命中 0 处**,并**新增一条机检**每次扫描 > 交付面(含明确 allow-list),所以 t8 不会再抓到第三个同族。`r18c/t7` 的 canonical 用 **`r18c-`** 前缀, > `r18-*` 与 `r18b-*` 证据一个字节未动;文档机检 **232 条**(**r18c/t7 当轮值,历史**)。**§16 之后以 §17 为准。** > > **rev-21 增量(r21/t1 + 本轮文档面,细节全在 §20)**:用户看截图指出设置页那个构建戳"只显示版本号就行了" —— > 于是 badge 由渲染整串 `rev-20 · the caret turn takes 160 ms` 改成渲染**版本 id**(`rev-21`), > 新增 `var REVISION_ID = REVISION.split(' · ')[0];`(**由戳推导**,id 与描述半句不可能各说各话)、 > 快照的 `bundleRevisionId` 与控制台面的 `revisionId`;`.revision` 仍是整串。 > **rev-21 的产品字节(历史)**:`lib/client.js` **159172 B / sha256 AE391909…**(现为 rev-22;当值见 §21.2), > **行为/样式/几何/音频路径与 host 半一律未动**; > 重锚工具 `verify-independent/r21-reanchor.mjs` 两个 phase 共 **75 行 / 99 处 / 13 个文件**, > 改前字节归档 `_raw/r21-evidence/archive/`(**19 个副本**);四套作者 harness 合计 **625** 项 > (client-half **404** / host-half **124** / waterfall **22** / custom-audio **75**),`probe-17` **95/95**; > 变异表 **45/45**;canonical 重跑 **1** 次并全绿(exit 0 / `FAILURES` 0 / 冻结 9/9 / legacy 10/10)。 > **本轮开工时先查明一件事并登记**:提交 `b0ad1eb`(术语统一 556 处 + 新版 README)在 r20 的 canonical 之后 > 改了产品字节(158549 → 158546),**已提交的树上 runner 第 0 节当时就是红的**,文档机检有 **9 条声明值**失配 —— > 本轮把这 9 条一并重锚(逐条见 §20.6),并**没有**改写 §15–§18 的冻结历史。 > **"只印版本号是不是你要的样子"不在本段内 —— 只有用户看页面能判定**(§20.9)。 > > **rev-22 增量(r22/t1 + 本轮文档面,细节全在 §21)**:用户看着会话头部那个蓝铃铛说「这个铃铛也要有动画」—— > 于是**字形**在**点击切换本会话**的那一刻摇一下:动的是 `` 字形、**不是**那个 28px 按钮(按钮就是 hover 底板), > 支点是字形的**顶边** `transform-origin:50% 0`(铃铛挂在冠上,绕中心转看起来是"打转"); > **载入不响、hover 不响**,响的是用户刚做的那次切换 —— `data-ring` 在第一次点击前是 `"false"`, > 字形带一个随点击递增的 React `key`,key 变了节点就被替换、替换就重放动画,所以首帧是静默的。 > `BELL_RING_MS = 420`、`BELL_RING_DEG = 14`(都挂在控制台面 `sessionIcon.bellRingMs` / `bellRingDeg`), > 关键帧文本由 `BELL_RING_FRAMES` 与峰值角**生成**(角度 `0,14,-11.06,7,-4.06,0` 只存在一处); > **没有加 `prefers-reduced-motion` 降级**(与 rev-20 对箭头的处置一致,样式表里它仍是 **0** 次)。 > **rev-22 的产品字节(历史)**:`lib/client.js` **166986 B / sha256 04376143…**(现为 rev-23,当值见 §22.2);`client-half` **421/421**(+17 条)、 > `probe-20` **47/47**(+10 条,**26 个变异一个没加**)、四套作者 harness 合计 **642**; > §21.5 记了本轮的一个事故与它的补救:**rev-21 的产品字节一度在盘上没有任何副本**, > 已用**逆向重建**补回并由 **sha256 相等**证明(重建脚本自己声明每一条逆操作的命中数)。 > **"这算不算动画 / 好不好看"不在本段内 —— 只有用户看真机能判定**(§21.6)。 > > **rev-19 增量(r19/t1 + r19/t2 + r19/t3,细节全在 §18)**:本轮的要求是把 caret 转动**再放慢** —— > `CARET_ROTATE_MS` 由 **300 改成 400**(rev-19),**行为其余部分零改动**(角度仍 90、曲线仍 `ease`、 > reduced-motion 仍只关过渡并保留 90° 终态、`reduceMotion()` 仍实时读且仍在 `sessionIcon` 之外)。 > **rev-19 的产品字节(历史)**:`lib/client.js` **157296 B / sha256 1C75C8B59634F2B44988056A2A343B4E46241D86C122C35B13EF1FAE2F3B6B35** > —— **字节数与 rev-18 的 157296 相同**(六处改动全是等长替换:300→400、~18→~24、rev-18→rev-19), > 但哈希前进;改前字节归档 `_raw/r19-t1-archive/lib__client.js.7FE150A0C1A89D67.txt`。 > r19/t2 把 `verify/**` 与 `verify-independent/**` 的活指纹、canonical runner 的前缀(`r18c-` → **`r19-`**) > 与冻结清单重锚了一遍:**95 行 / 100 处替换**、覆盖 **11 个活文件**(三个数取自工具自己写的 > `_raw/r19-t2-reanchor.json`,**不手数**),改前字节逐个归档在 `_raw/r19-evidence/archive/`; > 变异表在 rev-19 字节上重跑 **45/45**;`r18-*`/`r18b-*`/`r18c-*` 的证据一个字节都没被覆盖。 > r19/t3 做的是**文档面**:本文件头部与 §18、`CHANGELOG.md` 的 rev-19 节、`README.md` 与 > `docs/挂载与验收.md` 的**当值**句子(构建戳 / `.revision` 例子 / `caretRotateMs` / 过渡时长)一起搬到 rev-19, > **rev-18 及更早的各节与历史叙述逐字节保留**;文档机检重锚到 rev-19,本轮新增/改动的检查各附一次证伪实测。 > **本轮不写"现在已经看得清 / 快慢合适"**:能证的只有"常量 = 400、样式表由它拼出、戳是可测事实、 > 指纹与归档一致";**快慢与观感只有用户在真机上能判定**(§18.6)。 ## 0. 结论速览 | 事项 | 结果 | | --- | --- | | 29 个声明变异(18 + 11) | **29/29**:真改写被求值的源 + 红集**恰好等于**声明 + 被抓住时 **exit 0**(退出码由 `_raw/r13c-mutation-table.mjs` 自己跑出来,不硬编码;该脚本 29 行全绿 exit 0) | | probe-20(t6 新增) | **22/22** shipped exit 0 + **11/11** 变异「exactly as declared」;补上 t3 的 V1 缺口(§2A) | | probe-17 驱动缺陷 | 修前 5 个变异**一律 exit 1**(被抓住也报红)→ 修后 **5/5 exit 0**(run-r13 第 2d 节) | | 声明更正 | 3 处(probe-17 `slot` 3→9、`order` 1→3、`rogue` 3→6);**期望值一字未改**,新增项全是更强的「必须红」 | | 空转 / 死变异自检 | probe-11/17/18/19/20 全部有「变异真改写」sha256 自检 + 锚串唯一性度量(恰好 1 次);6 例驱动自测证明会红 | | 断言零删除 | 调用点 34→**35** / 102→**103** / 99→99 / 73→73;名字字面量 33/95/94/68 一个不少;**唯一允许的改名**是 4 处 `rev-12`→`rev-14` 版本字面量重锚(脚本强制配对校验);probe-20 为**纯新增** 22 条 | | `.scratch` 遗留探针 | 1 个**本就绿**(`reqcheck-host-413.mjs` 9 passed / 0 failed);3 个**登记为有理由的非零**(§6,逐条引用断言行);**一个字节都没改** | | canonical 运行器 | `run-r13.ps1`(t6 接入 probe-20 后 **40294 B / `BA4910D9142D7EB2793AA094D681A9E0970E912C9EC39242AE7D170A00BF0513`**,旧字节归档 `_raw/r13w-archive-run-r13-AAE3FDDA….ps1`):exit 0、**0 条 FAILURES**、回归集唯一非零 = probe-13、9 冻结文件 before/after **identical**;本轮日志归档为 `_raw/r13-final-*`(57 个 runner 日志 + console) | | 独立复核 / 质量门 | t3 verifier:4 条结论全部独立复现 + 1 条对抗性发现(V1,探针集对铃铛外观无覆盖)+ 3 条方法学限制;t4 reviewer:**verdict = pass**,三张豁免表相对 run-r12 **零 delta**;t7 reviewer:**verdict = pass**,A1–A4 现在真的被抓住 | | 证据链瑕疵 | t4 的 3 条(exit 列硬编码 / 改名查不出 / 日志覆盖)t5 已修;t7 的 2 条(红集行不可解析 / guard 副本可能被选中)t8 已修(§8.3、§8.5) | | **rev-16 增量(r16/t1)** | 铃铛↔caret 间隔 **6px**(用户真机反馈);client-half **373/373**、四套合计 **594**;probe-20 **23/23**、变异 **13**(`--mutate=all` 13/13 恰好命中且两两红集不同);变异表 **32/32**;10 条 legacy 探针重锚后 **10/10 全绿**;`lib/client.js` **150663 B / `76B4D4E7…`**;冻结清单 9 文件 before/after identical(§13) | | **rev-17 增量(r17/t1)** | caret 打开时**字形下转 90°(过渡 160ms)**、关闭转回(用户真机反馈);client-half **388/388**、四套合计 **609**;probe-20 **30/30**、变异 **20**(`--mutate=all` 20/20 恰好命中且两两红集不同);变异表 **39/39**(38 个声明变异 + r15t2 失败路径);10 条 legacy 探针重锚后 **10/10 全绿**;`lib/client.js` **154457 B / `B4A0A1B3…`**;冻结清单 9 文件 before/after identical;canonical exit 0 / 0 条 `FAILURES`(§14) | | **rev-18 增量(r18/t1)** | 真机反馈「要有过渡动画能看到在转动的箭头」→ 转动 **160ms → 300ms**,并新增**顶层**诊断 `reduceMotion()`(实时回答本页是否 `prefers-reduced-motion: reduce`,用来区分"时长太短"与"环境要少动效");client-half **399/399**(1 条被更强的替换 + 11 条新增,净删除 0)、四套合计 **620**;probe-20 **37/37**、变异 **26**(`--mutate=all` 26/26 恰好命中且两两红集不同);变异表 **45/45**(44 个声明变异 + r15t2 失败路径);10 条 legacy 探针重锚后 **10/10 全绿**;`lib/client.js` **156937 B / `1C8DB24C…`**;冻结清单 9 文件 before/after identical;canonical exit 0 / 0 条 `FAILURES`(§15)。**"现在看得见了吗"不在本表内 —— 只有用户能判定** | | **rev-18 修复增量(r18b/t5)** | 产品戳与注释只保留可测事实:`'rev-18 · the caret turn takes 300 ms'` + 两句观感断言降级为判断(**行为零改动**);`lib/client.js` **157296 B / `7FE150A0…`**;重锚 **44 行 / 46 处**、**8 个文件**(`r18b-t5-reanchor.mjs`,dry-run 与 post-state 双 exit 0,归档是写的前置条件;三个数取自工具 JSON 而非手数);四套 harness **620 项全绿(124 + 399 + 22 + 75)**;当轮机检条数 102(历史)(新增的一条覆盖原先漏检的那一行,且被证伪脚本证成会红);canonical 以 **`r18b-`** 前缀重跑(`r18-*` 证据保留);变异表 **45/45**(r18b 重跑);**"现在看得见了吗"仍不在本表内**(§16) | | **rev-18 修复增量 2(r18c/t7)** | 第二条质量门抓到**同类的第二处**:`client-half` 的断言**名字**在断言"真机看得见"(条件只 `=== 300`)。改名成只陈述可测事实 + 观感降级为注释里的**预期/判断**;`client-half` **101496 B / `B24E22E8…`**(旧 100983 / `AB6F7E48…` 归档 `_raw/r18c-t7-archive/verify__client-half.test.mjs.AB6F7E48.txt`);冻结清单由重锚工具当场从磁盘量出(`r18c-t7-reanchor.mjs`:30 行 / 30 处 / 2 文件,dry-run 与 post-state 双 exit 0);**类扫描**:模式与 allow-list 见 §17.2,**非例外命中 0 处**,并新增机检每次扫交付面;**新增机检自计数**(文档声明条数 vs 实跑条数)与 **r18b/t5 三个数字对账**(44 行 / 46 处 / 8 文件,取自工具 JSON);当轮机检条数 117(历史);canonical 以 **`r18c-`** 前缀重跑(`r18-*`、`r18b-*` 证据一个字节未动);**"现在看得见了吗"仍不在本表内**(§17) | | **rev-19 增量(r19/t1 + r19/t2 + r19/t3)** | 把 caret 转动**再放慢**:`CARET_ROTATE_MS` **300 → 400**(**行为其余部分零改动**:角度 90 / `ease` / reduced-motion 只关过渡并保留终态 / `reduceMotion()` 仍实时读且不在 `sessionIcon` 里);`lib/client.js` **157296 B / `1C75C8B5…`** —— **字节数与 rev-18 相同**(六处改动等长),改前字节归档 `_raw/r19-t1-archive/`;r19/t2 重锚 **95 行 / 100 处**、**11 个活文件**(三个数取自工具 JSON,不手数)+ canonical 前缀 `r18c-` → **`r19-`** + 冻结清单当场从磁盘量出,改前字节归档 `_raw/r19-evidence/archive/`;四套作者 harness **620 项全绿(124 + 399 + 22 + 75)**(本轮实测);变异表 **45/45**(rev-19 字节上重跑);当轮机检条数 **179 条**(历史)(重锚到 rev-19)并逐条带证伪实测;`r18-*`/`r18b-*`/`r18c-*` 的证据一个字节未动。**"现在快慢是否合适 / 转动是否真的能被察觉"不在本表内 —— 只有用户能判定**(§18) | | **rev-20 增量(r20/t1 + r20/t2 + r20/t3)** | **语义翻转**:减少动效不再有例外 —— `CARET_ROTATE_MS` **400 → 160**,并**删掉两条** `@media (prefers-reduced-motion: reduce)` 块(caret 的那条 + 设置页开关 `.18s` 过渡的同款覆盖)⇒ 产品里 `@media (prefers-reduced-motion` 出现 **0** 次、`transition:none` **0** 次,**任何环境下都是同一段 160ms**(应真机要求"开不开都要有动画",**有意的取舍**);`reduceMotion()` 保留但**降级为纯环境报告**(不改变行为);`lib/client.js` **158549 B / `4B6C8B91…`**,改前 rev-19 字节归档 `_raw/r20-t1-archive/`;r20/t2 重锚 **111 行 / 115 处**、**11 个活文件**(取自 `_raw/r20-evidence/r20-t1-reanchor.json`,不手数),改前字节归档 `_raw/r20-evidence/archive/`;四套作者 harness 合计 **621** 项(client-half **400** / host-half **124** / waterfall **22** / custom-audio **75**);变异表 **45/45**(rev-20 字节上重跑,逐条日志 `_raw/r20-evidence/r20-mut-*.txt`);r20/t3 是**文档面**(本文件 + `CHANGELOG.md` + `README.md` + `docs/挂载与验收.md` + 文档机检),改前字节归档 `_raw/r20-t3-archive/`。**"这段 160ms 是否真的能被察觉"不在本表内 —— 只有用户能判定**;本轮修复(t16)又修掉 runner 的退役变异名与绿色总结行里 rev-19 的旧数字,并**重跑 canonical 1 次(exit 0 / `FAILURES` 0 / 冻结 9/9 / legacy 10/10 / harness 621 / 变异表 45/45)**,console 自身事实由机检直接读(§19.4) | | **rev-21 增量(r21/t1 + 本轮文档面)** | 构建戳由整串改成**只印版本 id**:新增 `var REVISION_ID = REVISION.split(' · ')[0];`(**由戳推导**)、分区快照 `bundleRevisionId`、控制台面 `revisionId`(`.revision` 仍是整串);`lib/client.js` **159172 B / `AE391909…`**,`client-half` **404/404**(+4 条)、`probe-17` **95/95**(+1 条)、四套合计 **625**;重锚工具 `r21-reanchor.mjs` 两个 phase 共 **75 行 / 99 处 / 13 个文件**,改前字节归档 `_raw/r21-evidence/archive/`(19 个副本);变异表 **45/45**;canonical 重跑 **1** 次并全绿(exit 0 / `FAILURES` 0 / 冻结 9/9 / legacy 10/10)。**开工时先查明并登记**:提交 `b0ad1eb`(术语统一 556 处 + 新版 README)把产品字节从 r20 钉的那一对改成 158546,**已提交的树上 runner 第 0 节当时就是红的**,机检有 **9 条声明值**失配(逐条见 §20.6)。**"只印版本号是不是你要的样子"不在本表内 —— 只有用户能判定**(§20) | | **rev-22 增量(r22/t1 + 本轮文档面)** | 用户原话「这个铃铛也要有动画」:**字形**在**点击切换本会话**的那一刻**摇一下**(动的是 ``、不是那个 28px 按钮;支点 `transform-origin:50% 0` = 字形顶边);`data-ring` 首帧 `"false"`、点击后 `"true"`,字形带随点击递增的 React `key`(key 变 → 节点替换 → 动画重放),**所以载入不响、hover 不响**;`BELL_RING_MS = 420`、`BELL_RING_DEG = 14`(控制台面 `sessionIcon.bellRingMs` / `bellRingDeg`),`@keyframes dacBellRing` 由 `BELL_RING_FRAMES` + 峰值角**生成**(`0,14,-11.06,7,-4.06,0` 只写一处);**没有加 `prefers-reduced-motion` 降级**(产品里它仍出现 **0** 次 —— 与 rev-20 对箭头同一条取舍)。产品 **166986 B / `04376143…`**(**rev-22 记录**);`client-half` **421/421**(**+17** 条)、`custom-audio` **75/75**、四套合计 **642 = 124 + 421 + 22 + 75**;`probe-20` **47/47**(新增第 7 组共 10 条,**26 个变异一个没加、一个没改名**,`--mutate=all` 复测按各自声明命中);重锚工具 `r22-reanchor.mjs` 改 **8** 个独立层文件、**42 行 / 64 处**,改前字节归档 `_raw/r22-evidence/archive/`(16 个副本)。**本轮的事故与补救(§21.5)**:先改产品、后建工具,**rev-21 的产品字节一度在盘上没有任何副本**,已用**逆向重建**补回并由 **sha256 全等**证明(159172 B / `AE391909…`);重建第一次差 **2 字节**(多一个空行)、第一次判词是**假红**(脚本里大小写比较写错)——两次都记在案。**"这算不算动画 / 好不好看"不在本表内 —— 只有用户能判定**;沙箱没有浏览器引擎,**动画帧本身未被任何探针观察过**,`probe-13` 是本轮唯一非零探针(§21.6) | ## 1. 判定标准(三问) 每个变异都被问了同样三件事,任一不成立即缺陷: 1. **变异体哈希是否真的变了**——先量,再跑。probe-11 比 `css` 与 `injectedCss` 的 sha256;probe-17 比 `EVALUATED_SOURCE` 与 `CLIENT_SOURCE` 的 sha256;probe-18 先量锚串出现次数、再比变异体与 shipped 的 sha256;probe-19 比 `mutated` 与 `SHIPPED` 的 sha256。 (注意 probe-17 `order` 的字节数**相同**(147062 vs 147062)——只有哈希能证明它没有空转。) 2. **红集是否恰好等于声明**——多红(声明漏了)与少红(声明多了 / 断言不稳)都算缺陷;四个探针都把 这条**写进退出码**(probe-18 原就有;probe-11/17/19 本轮补齐)。 3. **被抓住时退出码是否为 0**——probe-11/18/19 的约定;probe-17 本轮对齐。 ## 2. 18 个声明变异逐条实测 退出码由 `_raw/r13c-mutation-table.mjs` **在本次运行中把每个变异当独立 node 进程跑出来**并记录 (stdout/stderr 重定向到普通文件、不用管道);红集与「恰好等于声明」由同一次运行的输出解析。 逐条原始日志:`_raw/r13c-mut--.txt`(18 个,由该脚本写出)。 | # | 探针 | 变异 | 变异体 sha256(16) | shipped sha256(16) | 字节/对象变化 | 声明红集 | 实测红集 | 锚串次数 | exit | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 1 | probe-11 | `card-cap-92px` | `21B4B74D…` | `D14B9B4D…` | 注入后的样式表(自测打印两 sha) | 3 | 3 | – | 0 | | 2 | probe-17 | `slot` | `0B34BE2E…` | `730D1C2F…` | 147071 vs 147062 B | 9 | 9 | – | 0 | | 3 | probe-17 | `order` | `D6053958…` | `730D1C2F…` | 147062 vs 147062 B(只有哈希能证明) | 3 | 3 | – | 0 | | 4 | probe-17 | `heading` | `DEA74178…` | `730D1C2F…` | 147002 vs 147062 B | 2 | 2 | – | 0 | | 5 | probe-17 | `picker` | `2318C1DE…` | `730D1C2F…` | 146973 vs 147062 B | 2 | 2 | – | 0 | | 6 | probe-17 | `rogue` | `6A355AB8…` | `730D1C2F…` | 147215 vs 147062 B | 6 | 6 | – | 0 | | 7 | probe-18 | `mute-ignored` | `4B23A5A0…` | `730D1C2F…` | 源字节(日志逐条打印 changed=true) | 14 | 14 | 1 | 0 | | 8 | probe-18 | `unmute-writes-true` | `F8B113E8…` | `730D1C2F…` | 同上 | 5 | 5 | 1 | 0 | | 9 | probe-18 | `batch-gap-zero` | `A513ADA0…` | `730D1C2F…` | 同上 | 6 | 6 | 1 | 0 | | 10 | probe-18 | `session-id-constant` | `49D3E90B…` | `730D1C2F…` | 同上 | 8 | 8 | 1 | 0 | | 11 | probe-18 | `custom-tone-no-fallback` | `F6AB3AB3…` | `730D1C2F…` | 同上 | 1 | 1 | 1 | 0 | | 12 | probe-18 | `batch-merge-first-only` | `04634743…` | `730D1C2F…` | 同上 | 6 | 6 | 1 | 0 | | 13 | probe-18 | `evict-newest-first` | `38B47549…` | `03778391…` | 同上(host 半) | 5 | 5 | 1 | 0 | | 14 | probe-18 | `convergence-reread-removed` | `7E38CD0C…` | `730D1C2F…` | 同上 | 2 | 2 | 1 | 0 | | 15 | probe-18 | `home-blank-accepted` | `7D382915…` | `03778391…` | 同上(host 半) | 1 | 1 | 1 | 0 | | 16 | probe-19 | `popover-dropped-from-shared-picker` | `64AD86B9…` | `730D1C2F…` | 源字节(日志打印 mutant vs shipped) | 9 | 9 | – | 0 | | 17 | probe-19 | `popover-list-resplit-12px` | `77FB55D9…` | `730D1C2F…` | 同上 | 7 | 7 | – | 0 | | 18 | probe-19 | `popover-cap-loses-one-row` | `4BD04260…` | `730D1C2F…` | 同上 | 3 | 3 | – | 0 | - 「锚串次数」列只有 probe-18 会在日志里逐条打印 `anchor occurrences in the shipped bytes: 1`; probe-11/19 由自带的 `replaceOnce()` 在越界时抛错(存在性+唯一性),probe-17 由 t2 新增的 `replaceOnce()` 度量(0 次 → `anchor not found`,2+ 次 → `is not unique`)。 - shipped(非变异)基线同时复核:probe-11 **32/32**、probe-17 **94/94**、probe-18 **131/0**、probe-19 **66/66**,四者 exit 0。 - 18 个变异 = probe-11 ×1 + probe-17 ×5 + probe-18 ×9 + probe-19 ×3,**没有「声明了但没跑」的变异**。 - 确定性:probe-17 五个变异各跑 3 次,红集逐字一致(`_raw/r13c-determinism-probe-17.txt`,15 次运行)。 ### 2.1 probe-17 五个变异各自的红集(3 次重跑同集) - `slot`(9):`the injected slot names` / `the entry slot name (stub ledger)` / `no settings.plugin.item registration ledger entry` / `no settings.plugin.item slot was injected` / `the "plugins tab" vocabulary is absent as well (no keyed-card leftovers)` / `the bundle no longer claims a keyed-card key` / `the host-visible register option set is exactly name/id/order/label/locale` / `the private id is the plugin namespace, not a shipped one` / `the host projection (sort by order) puts this row directly after 插件` - `order`(3):`order is 16 (right after 插件 at 15)` / `order 16 falls strictly between plugins(15) and agent-presets(20)` / `the host projection (sort by order) puts this row directly after 插件` - `heading`(2):`the page heading is an

carrying 通知提醒` / `exactly one

on the page` - `picker`(2):`the card list's ::picker(select) resolves box-sizing:content-box AND max-height:84px` / `3 rows are exactly what the popup shows: max-height 84px = 3 x (line-height 20px + 4px padding x2)` - `rogue`(6):`exactly two slots.inject happened (rev-10 adds the session-header bell)` / `the injected slot names` / `exactly two slots.register happened (the section + the bell)` / `no settings.plugin.item registration ledger entry` / `no settings.plugin.item slot was injected` / `the "plugins tab" vocabulary is absent as well (no keyed-card leftovers)` ### 2.2 probe-19 三个变异各自的红集 - `popover-dropped-from-shared-picker`(9):共享 picker 规则只剩卡片 / 两张表不再同源 / popover 的 `font-size` 掉回 12px / property map 不再只差 `max-height` 等 9 条(见 `_raw/r13c-mut-probe-19-…txt`)。 - `popover-list-resplit-12px`(7):`@supports` 块里能触及 picker 的规则数 3→4、popover 的非 max-height 声明不再来自同一条规则、`font-size:13px` 丢失等 7 条。 - `popover-cap-loses-one-row`(3):popover 上限 120px / 四行 + slack 的算式 / `diagnostics.pickerMetrics` 与 CSS 的对应关系。 ## 2A. rev-13/14 外观覆盖:probe-20 的 22 条断言与 11 个变异(t6 新增,t7 质量门 pass) ### 2A.1 为什么要有这个探针 t3 的对抗性发现 V1:`lib/client.js` rev-13/rev-14 的**会话铃铛外观**(放大 + 变蓝)在独立探针集里 **没有任何覆盖**——probe-11/17/19 直接 `readFileSync(lib/client.js)`、没有内存源钩子,probe-18 的 131 条断言里也没有一条提到 `.dacBell`/`muted`/`fill`。t3 自造的 4 个外观变异(A1–A4)交给 probe-18 实测 **红集 0 条**;当时只有 author 套件 `verify/client-half.test.mjs:721-752` 抓住它们。 t6 因此新增 `verify-independent/probe-20-r14-bell-appearance.mjs`: - **独立**:只 import node 内建,`verify/` 零依赖;自建 vm 沙箱跑真 `lib/client.js`,读 bundle 自己 append 的 `