可视化作品有一个通病:图做得很好看,机制说错了,而且没人看得出来。 因为观众没法验证——他说 96MiB,你怎么知道不是 256MiB?
这一页就是来拆掉这个问题的。它把本次创作的全部核验过程摊开:核了什么、改了什么、 故意删掉了什么、又自己发现了哪些错。每一条都注明官方出处,你可以逐条去核。
region-split-size、各调度限流参数等默认值你会在这个作品里看到两个数字,它们不是同一件事:
· 72 条核验明细 —— 本次核验一共核了多少条,见本页第 6 节。
· 69 条可点击取证卡 —— 已经核过、又做进画面可以点开查官方原文的条目数(含后续几轮新增演示台补做的卡片,见站点首页「技术口径」)。
本次核验里没做成卡片的是两处:7 条结论为"官方未列明",作品刻意不写,只作为删除依据存档; 其余是同一参数的重复出现与纯背景性条目。之后每新增一个演示台,新核的条目都会补进取证卡。
coprocessor.region-split-size 默认 256MiB(估算值);v8.4.0 之前为 96MiB这是本页最重要的一个立场。大多数作品遇到"官方没公开"时,会含糊带过、或者补一个看起来专业的值。 本作品的做法是把它们逐条列出来。
每一行都可以点开,看到的是同一句话:这里官方没说,所以本作品不写。 看过这 7 条的人,才有理由相信画面里其余的数字。
raft-base-tick-interval / raft-election-timeout-ticks),
未给出端到端时长。作品只写 tick 机制,画面中不出现任何端到端时长 ——
efConstruction、Async Commit 默认 等字样即拦截)。
这不是靠记性守住的,是靠脚本守住的。
'latency' / 'ranking' / 'heat' / 'breakdown',而图表映射表的键写成了视角 id。取值得到 undefined,调用即崩。v:'100' 与 u:'%' 分开声明),任何字面量搜索都抓不到,只有读懂渲染逻辑才会发现。纯靠人工检查,这一处必然会漏。分 A / B / C / D 四组,前三组分别对应 01 / 02 / 03 号演示台,D 组为站点口径。状态含义: ✅ 已核验 可直接使用 · ➕ 增强 新获官方依据 · ⛔ 未列明 作品不写。
| # | 核验项 | 官方口径 | 出处 | 状态 |
|---|---|---|---|---|
| A1 | Region 分裂阈值 | coprocessor.region-split-size 默认 256MiB(估算值);v8.4.0 之前为 96MiB | S1 | ✅ 已核验 |
| A2 | 允许超出分裂阈值多少 | raftstore.region-split-check-diff 默认 Region 大小的 1/16 | S1 | ✅ 已核验 |
| A3 | Raft tick 间隔 | raftstore.raft-base-tick-interval 默认 1s | S1 | ✅ 已核验 |
| A4 | Raft 选举超时 | raft-election-timeout-ticks 默认 10;按 tick 换算约 10s | S1 | ✅ 已核验 |
| A5 | Raft 心跳间隔 | raft-heartbeat-ticks 默认 2 → 每 2s 一次心跳 | S1 | ✅ 已核验 |
| A6 | Leader 租约上限 | raft-store-max-leader-lease 默认 9s | S1 | ✅ 已核验 |
| A7 | Region 副本数默认配置 | PD replication.max-replicas 默认 3(1 leader + 2 follower) | S2 | ✅ 已核验 |
| A8 | 多副本能否落同一节点 | 原文:"在一般情况下,PD 只会保证多个副本不落在一个节点上,以避免单个节点失效导致多个副本丢失" | S3 | ➕ 增强 |
| A9 | 副本调度三种基本操作 | 增加一个副本 / 删除一个副本 / 将 Leader 在不同副本间 transfer | S3 | ✅ 已核验 |
| A10 | Leader 故障到新 Leader 就绪的端到端时长 | 官方未给出,只给出 tick 机制 | S3 | ⛔ 未列明 |
| # | 核验项 | 官方口径 | 出处 | 状态 |
|---|---|---|---|---|
| B1 | 分裂/合并最小间隔 | schedule.split-merge-interval 默认 1h | S2 | ✅ 已核验 |
| B2 | Region 调度并发上限 | region-schedule-limit 默认 2048 | S2 | ✅ 已核验 |
| B3 | Leader 调度并发上限 | leader-schedule-limit 默认 4 | S2 | ✅ 已核验 |
| B4 | 热点调度并发上限 | hot-region-schedule-limit 默认 4 | S2 | ✅ 已核验 |
| B5 | 副本调度并发上限 | replica-schedule-limit 默认 64 | S2 | ✅ 已核验 |
| B6 | 合并调度并发上限 | merge-schedule-limit 默认 8(设为 0 关闭 Merge) | S2 | ✅ 已核验 |
| B7 | 单 store 快照并发上限 | max-snapshot-count 默认 64 | S2 | ✅ 已核验 |
| B8 | 单 store pending peer 上限 | max-pending-peer-count 默认 64 | S2 | ✅ 已核验 |
| B9 | 判定热点所需持续时长 | hot-region-cache-hits-threshold 默认 3 分钟 | S2 | ✅ 已核验 |
| B10 | 判定 store 彻底失联时长 | max-store-down-time 默认 30m;超时后在其他节点补充副本 | S2 | ✅ 已核验 |
| B11 | Region 健康巡检频率 | patrol-region-interval 默认 10ms | S2 | ✅ 已核验 |
| B12 | 均衡缓冲区 | tolerant-size-ratio 默认 0(自动调整) | S2 | ✅ 已核验 |
| B13 | 单 store 限流模式 | store-limit-version 默认 v1 | S2 | ✅ 已核验 |
| B14 | 热点 Region 可调度体积上限 | max-movable-hot-peer-size 默认 512 MiB(v6.1.0 起引入) | S2 | ✅ 已核验 |
| B15 | 是否使用 Joint Consensus | enable-joint-consensus 默认 true(v5.0 起引入) | S2 | ✅ 已核验 |
| B16 | Placement Rules 默认状态 | enable-placement-rules 默认 true | S2 | ✅ 已核验 |
| B17 | 跨表 merge | enable-cross-table-merge 默认 true | S2 | ✅ 已核验 |
| B18 | Store 状态机 | Up / Disconnect / Offline / Down / Tombstone 五态;心跳丢失超 20s → Disconnect,超 30m → Down;手动下线 → Offline → 计数归零后 Tombstone | S3 | ➕ 增强 |
| B19 | 下线时若无满足条件的目标 store | 原文:"该 Store 将一直处于 Offline 状态" —— 即本作品的"缩容下限保护" | S3 | ➕ 增强 |
| B20 | store 负载打分公式 | 官方未公开 | S3 | ⛔ 未列明 |
| B21 | 各调度器逐项默认启用状态 | 官方未逐项列明 | S3 | ⛔ 未列明 |
| B22 | PD 心跳携带的信息 | Store 心跳:磁盘容量/可用、Region 数、读写速度、Snapshot 收发、是否过载、labels | S3 | ✅ 已核验 |
| B23 | 调度建议是否强制执行 | 原文:操作"只是给 Region Leader 的建议,并不保证一定能得到执行" | S3 | ✅ 已核验 |
| # | 核验项 | 官方口径 | 出处 | 状态 |
|---|---|---|---|---|
| C1 | 默认事务模式 | 自 v3.0.8 起默认悲观事务模式;仅新建集群,从 3.0.7 及之前升级的不改变 | S4 | ✅ 已核验 |
| C2 | 两阶段提交的阶段名称 | 第一阶段 prewrite;第二阶段 commit | S4 | ✅ 已核验 |
| C3 | 2PC 完整流程(官方 7 步) | 取 start_ts → 读 → 写(存私有内存)→ commit → prewrite 加锁 → 取 commit_ts → 向 Primary Key 所在 TiKV 提交并清锁 → 返回 → 异步清理 | S4 | ✅ 已核验 |
| C4 | 冲突检测在哪一层、哪个阶段 | 原文:"冲突检测是在 TiKV 中进行,主要发生在 prewrite 阶段" | S4 | ✅ 已核验 |
| C5 | 冲突检测并发控制参数 | scheduler-concurrency 默认 2048000 | S4 | ✅ 已核验 |
| C6 | 事务模型的优缺点 | 优点:原理简单、跨节点事务、锁管理去中心化;缺点:网络交互增多、需要中心化时间戳服务、大事务易内存暴涨 | S4 | ✅ 已核验 |
| C7 | 事务自动重试 | 从 v8.0.0 起不再支持乐观事务自动重试;冲突需在应用层捕获重试 | S4 | ✅ 已核验 |
| C8 | 单事务大小上限 | 默认 100 MB(txn-total-size-limit,最大支持 1 TB);内存放大可达 2–3 倍以上 | S5 | ✅ 已核验 |
| C9 | 单行大小上限 | 6 MB | S5 | ✅ 已核验 |
| C10 | 事务键值对数量限制 | v4.0 以前不超过 30 万条,v4.0 起取消 | S5 | ✅ 已核验 |
| C11 | 惰性检查(乐观事务) | 默认不在 DML 时检查主键/唯一约束,而在 COMMIT 时检查;可用 tidb_constraint_check_in_place 关闭该优化 | S4 | ✅ 已核验 |
| C12 | 隔离级别 | 悲观事务在 Repeatable Read 下使用当前读 | S4 | ✅ 已核验 |
| C13 | Async Commit / 1PC | 存在该两项优化,从 v5.0 起引入;因果一致性事务仅在启用时生效 | S5 | ✅ 已核验 |
| C14 | Async Commit / 1PC 的默认值 | 官方未列明 | S5 | ⛔ 未列明 |
| C15 | Percolator 列族级数据布局 | 官方事务文档正文未展开(仅见社区博客) | — | ⛔ 未列明 |
| # | 核验项 | 官方口径 | 出处 | 状态 |
|---|---|---|---|---|
| D1 | 向量搜索最低版本 | Self-Managed 需 v8.4.0+(推荐 v8.5.0+);当前公测阶段 | S8 | ✅ 已核验 |
| D2 | 向量数据类型 | VECTOR(变长)与 VECTOR(D)(定长);为 TiDB 特有 | S10 | ✅ 已核验 |
| D3 | 支持的向量索引算法 | 目前仅支持 HNSW | S9 | ✅ 已核验 |
| D4 | 支持的距离函数 | 目前仅支持 VEC_COSINE_DISTANCE() 与 VEC_L2_DISTANCE() | S9 | ✅ 已核验 |
| D5 | 建索引语法 | VECTOR INDEX idx ((VEC_COSINE_DISTANCE(embedding)));或 CREATE VECTOR INDEX ... USING HNSW | S9 | ✅ 已核验 |
| D6 | 向量索引的依赖组件 | 必须提前部署 TiFlash 节点;否则需 ALTER TABLE ... SET TIFLASH REPLICA 1; | S9 | ✅ 已核验 |
| D7 | 召回率 | ANN 搜索"召回率可保持在 90% 以上";HNSW 特定场景准确率可达 98% | S9 | ✅ 已核验 |
| D8 | 向量索引的限制 | 不能作主键/唯一索引;只能建在单个向量列;不支持多个同距离函数索引;只能建在定长向量列;不支持静态加密的 TiFlash 节点 | S9 | ✅ 已核验 |
| D9 | 预过滤(WHERE)能否用向量索引 | 不能。WHERE 在 KNN 之前执行;应先在子查询做 KNN 再过滤,且"结果可能少于 LIMIT 条数" | S9 | ✅ 已核验 |
| D10 | 索引命中的判断方式 | EXPLAIN 的 operator info 出现 annIndex: 即为命中 | S9 | ✅ 已核验 |
| D11 | 索引构建进度与耗时 | 查 INFORMATION_SCHEMA.TIFLASH_INDEXES,ROWS_STABLE_NOT_INDEXED 为 0 即完成;500 MiB / 768 维参考耗时可达 20 分钟 | S9 | ✅ 已核验 |
| D12 | HNSW 可调参数(M / efConstruction 等) | 官方未列明 | S9 | ⛔ 未列明 |
| D13 | TiFlash 的复制与一致性机制 | 以 Raft Learner 角色异步复制;通过 Raft 校对索引配合 MVCC 获得 Snapshot Isolation | S6 | ✅ 已核验 |
| D14 | TiFlash 的部署与写入限制 | 无法直接接受写入;部署后默认不会同步任何数据,需按表构建副本 | S6 | ✅ 已核验 |
| D15 | TiFlash 硬件要求 | Linux AMD64 需 CPU 支持 AVX2;ARM64 需 ARMv8 架构 | S6 | ✅ 已核验 |
| D16 | MPP 执行模式 | 官方提供"使用 MPP 模式"文档,为 TiFlash 并行计算执行模式之一 | S6 | ✅ 已核验 |
| D17 | TiFlash 副本默认数量 | 官方未列明(口径为"默认不同步数据") | S6 | ⛔ 未列明 |
| D18 | 在线 DDL 机制 | 采用在线异步变更,不阻塞其他会话 DML;官方明确"不支持离线 DDL" | S7 | ✅ 已核验 |
| D19 | ADD INDEX 状态流转 | absent → delete only → write only → write reorg → public;public 前索引不可用 | S7 | ✅ 已核验 |
| D20 | 物理 DDL 的范围 | 物理 DDL = Reorg DDL,只包含 ADD INDEX 与有损列类型变更 | S7 | ✅ 已核验 |
| D21 | DDL 的执行者 | DDL Owner 单例(etcd 选举产生,run-ddl 控制是否参选) | S7 | ✅ 已核验 |
| D22 | DDL 并发能力 | v6.2.0 起可并行执行 DDL;v8.2.0 起支持并行执行不同表的逻辑 DDL | S7 | ✅ 已核验 |
| D23 | 物理 DDL 速度调节变量 | tidb_ddl_reorg_worker_cnt / tidb_ddl_reorg_batch_size;官方推荐 20/2048(无负载)或 4/256(有负载) | S7 | ✅ 已核验 |
| D24 | DDL 观测命令 | ADMIN SHOW DDL [JOBS]、ADMIN CANCEL/PAUSE/RESUME DDL JOBS、information_schema.DDL_JOBS | S7 | ✅ 已核验 |
这个作品的前端代码、文案初稿、视觉元素由 AI 工具生成。而 AI 生成技术内容最大的风险, 不是写得不好,是写得很好但事实错了 —— 它会把 96MiB 说得和 256MiB 一样可信。
所以本作品的质控是三层,缺一层都不成立:
| 防线 | 做什么 | 落在哪 |
|---|---|---|
| 第一层 · 铁律 | 未核验的数字一律不写;官方没公开的,一个字都不写 | 本页第 1、4 节 |
| 第二层 · 举证 | 69 条做成可点击取证卡,把"我说我核过"变成"你随时可查" | 全站 等锚点 |
| 第三层 · 守卫 | 自动化断言:脚本每次重跑,覆盖状态机、数据自洽、悬空引用、内容禁区 | 见下表 |
一句话:AI 负责产出速度,人负责判定标准,脚本负责守住底线。 这三层里只有第一层依赖自觉,另外两层是机械的、可重复的、不靠记性的。
| 检查 | 断言数 | 拦住什么 |
|---|---|---|
| 02 号状态机回归 | 25 | 分裂后副本数恒等、同 Region 副本不得落同一 store、调度收敛、下线不丢副本、缩容下限保护 |
| 03 号状态机回归 | 56 | 2PC 七步次序、冲突发生阶段、回滚与清锁范围、主键选择 |
| 驾驶舱渲染级验证 | 34 | 四个视角真实执行渲染、跨来源数据自洽(矩阵行和 = 节点 Region 数)、下钻链接可达 |
| 核验实录自洽 | 30 | 本页声明的数字必须等于表格实际行数(防手写数字漂移)、来源编号无悬空、引用的取证卡键必须存在、本页声明的守卫用例数、浏览器渲染断言数、实测笔记证据链断言数、病理证据链断言数 = 四个脚本的实际执行数 |
| 实测笔记证据链 | 91 | 实测笔记页上的每个实测值,必须能在 dev/probe/ / dev/cloud-evidence/ 的原始采集输出里逐字找到——读者手里没有那些日志,所以这条最容易被糊过去,必须机器比对;含一处反向断言:该页不得出现任何性能数字(本次未做压测,写了就是编) |
| 病理证据链 | 62 | 病理页上的 46 条关键读数,必须能在 dev/probe/ / dev/cloud-evidence/ 的原始采集输出里逐字找到;并守住这一页特有的等级纪律:实测 / 实验设置 / 推导 / 官方未列明四档必须分开标注,推导段必须带显式边界,官方未给出的端到端时长不得编造;含一处反向断言:病理页不得出现任何性能数字(本次未做压测,写了就是编);含 未覆盖清单(P-C):B1–B4(Offline → Tombstone / 跨 Region 多行事务 / FOR UPDATE 锁范围 / TSO 批分配)已在云集群全部兑现、必须带 done 标记与日期落款(B4 兑现的是机制:批合并与时间戳对账),官方口径锚点必须落在清单区块内 |
| 取证卡一致性 | — | 锚点必须存在于字典、无悬空引用、字典无闲置条目 |
| 全站一致性 | — | JS 语法、重复 ID、缺失 DOM 引用、内部链接可达、内容禁区守卫、数字漂移(断言数 / 文件数 / 形式数 / 演示台数 / 实测条数 / 核验条数必须等于当场算出的真值;且认七类引用 —— 前五类为"写法"盲区的互补:紧邻的"N 项断言"、分开写的"数字 + 单位"、表格行里的裸数字、本页副产物计数、与断言数无关的独立计数;第六类为"文件"盲区:跨文件的"N 条实测"(真值 = 实测笔记页实测卡数,含视频帧分写与配套文档清单);第七类为「引用」盲区:跨文件的「N 项 / N 条 / N 键核验」(真值 = 口径字典的键数,含表格窄规则与视频帧分写)。每漏一类,就漏一片区域) |
| 守卫自检 | 22 | 守卫本身也必须被测:确认广告法合规守卫既抓得住文案中的禁用词与绝对化数值,又不会把 CSS / SVG 的布局百分比值与官方奖项名误判为违规 —— 防「假绿」;并校验反面样例声明机制:允许页面引用被拦下的违规表述本身,但引用处数必须与源码内声明一致,少报与夹带都报错 |
| 真实浏览器渲染 | 162 | 用 headless 浏览器真实打开 cockpit / index / 本页 / posters / loop / video / 实测笔记 / 自助核验 / 现场直连 / 口径速查 / 在线核验台 / Raft 选举模拟器 / HTAP 信息图 / 真机 vs 模拟对账页 / 向量搜索演示台(04 号)/ 在线 DDL 演示台(07 号)/ 容灾实况演示台(08 号)/ 故障病理解剖室(10 号) 十八个页面,确认不白屏、关键内容渲染。每多一种形式,就多一个可能白屏的入口,必须过同一道关(自助核验页此前漏检,2026-09-19 补上;口径速查 / 在线核验台于 2026-09-20 补上;Raft 选举模拟器 / HTAP 信息图于 2026-09-20 第三轮补上;否决权页的操作模拟器 / 真机回放两块于 2026-09-20 第四轮补上;真机 vs 模拟对账页于 2026-09-20 第五轮补上;向量搜索演示台(04 号)于 2026-09-20 第六轮补上;在线 DDL 演示台(07 号)于 2026-09-21 第七轮补上;容灾实况演示台(08 号)于 2026-09-21 第八轮补上;向量搜索演示台(04 号)于 2026-09-21 第十轮升级为真算法仪器、断言 6 → 9 条;2026-09-25 补上驾驶舱真机快照与 04 号反例台两块(P-B / P-D),并新增故障病理解剖室(10 号)—— 断言 148 → 160;同日病理页(10 号)补「未覆盖清单」(P-C)—— 断言 160 → 162) |
累计 482 项断言(25 + 56 + 34 + 30 + 91 + 62 + 22 + 162;取证卡与全站一致性为扫描式检查,条数随页面数变化,故不计入)。
这些数字不是自评,是脚本每次都会重跑的结果 —— 任何一处失败,页面就不会被交付。
其中「核验实录自洽」这 30 项,专门用来校验本页自己写的数字 ——
因为这一页主张"每个数字都可查",那第一个该被查的就是它自己:它甚至会用脚本算出自己的断言条数,
并反向核对另外四个脚本(守卫自检 / 浏览器渲染 / 实测笔记证据链 / 病理证据链)的真实执行数,对不上就报错。
本页管「数字有没有官方出处」,实测笔记页管「机制在真机上是不是这样」。 两件事的可信等级不同,所以分开两页、两套守卫,而不是混在一张表里互相借光 —— 去看实测笔记 →
信息图视频不是录屏,而是由帧号驱动逐帧渲染出来的(video/frames.html + dev/make-video.py)。
没有 CSS animation、没有随机数、不读系统时间 —— 因此它可以被逐帧检查:
540 帧全部渲染通过,每帧输出非空且不含 NaN / undefined。
为什么单列而不并进上面的总数 —— 因为"帧能渲染出来"和"逻辑断言成立"不是同一类东西。 把两类数字加成一个更大的数,总数会更好看,但读者反而更难核。 所以这里分开写:482 项逻辑断言 + 540 帧渲染校验,各自独立可验证。
画面上的断言数与单位是分写在两段字符串里的
(T(…,'482') 与 T(…,'项自动化断言')),中间隔着坐标、字号、颜色等参数,
于是数字漂移守卫那条"N 项断言"正则根本看不见它 ——
视频里的 215 就这样一路躲过守卫,直到这次全站同步才被人眼发现。
现在守卫补到五种写法都认:① 紧邻的"482 项断言";② 分开写的"数字串 + 单位"
(从单位往前取最近的一个纯数字串);③ "脚本名 + 项数"表格行里的裸数字
(自助核验页把浏览器渲染断言数写成裸的 65,同样没被看见);
④ 本页"副产物"的计数 —— N 个自查发现的问题 / N 处自我纠错 / N 条主动删除;
⑤ 与断言数无关的独立计数 —— N 种形式 / N 台演示台。
第 ④ 类最难看,因为它不是"总数没同步",而是本页自己跟自己打脸: 第五节副标题写「3 个自己发现的问题」,而它下面明明摆着 4 张 SELF 卡 —— 脚本一直在数卡片(= 4),却从没去看那句"3";这个"3"还被抄进了首页、否决权页与视频帧。 现在真值改由"当场数 SELF 卡"得出,五类写法一起比对。
第 ⑤ 类是被"形式扩容"撞出来的:形式从四种扩到五种(新增实测笔记)之后, 首页 chip、录屏脚本与另外 3 处正文里的形式数与演示台数都还停在旧值上 —— 共 5 处,全部靠人眼逐个找出来。真值同样当场算:形式数 =「首页角标数 + 1」,演示台数 = 首页演示台卡片数。 它跟断言数毫无关系,所以前四种正则天生管不到它 —— 这就是"多一类数字,就多一片盲区"。
补这条时又被咬了同一个老问题:守卫分不清"使用错误值"与"记录错误值" —— 我在上面这几句里引用旧值来说明问题,它照样报错,一口气报出 5 处"并不存在的漂移"。 处理方式与第五节那句副标题完全一致:陈述漂移时一律不写具体数字 —— 这也是下文通篇只说"旧值"的原因。
这五种写法不是设计出来的,是五次真实漏报逼出来的 —— 每一条都对应一次"改了总数、守卫没响"。教训与前面几条一样: 一条正则漏掉一种写法,守卫就少守一片区域,而且失败方式是静默的,它不报错,只是不响。
第二轮实测把实测笔记又扩了一条之后,页面自己的声明改了, 但视频帧收尾场景与几份配套文档里的条数没跟上 —— 前面五类守的是"页面里怎么写",这一次的问题是护栏立在哪里: 实测条数没有一条规则守过,几份配套文档也从来不在扫描范围内,两个盲区叠在一起。
补法仍是"真值当场算":真值 = 实测笔记页里实测卡的数量; 凡在页面、视频帧(含分写)或指定配套文档里写「N 条实测」的,N 必须等于它。 写出规则的第一次运行,它当场抓出了两处旧值(视频帧与交接文档)。
症状依旧一模一样:改了总数,守卫不响 —— 它不报错,只是不响。
04 号演示台落地时,口径字典扩进一组新口径,于是全站的「N 项口径 / N 条取证卡」 类引用都要跟着换 —— 但「核验条数」这个数字此前没被任何一类守卫覆盖: 它不是断言数,也不是文件数、形式数、演示台数或实测条数,前面六类全都看不见它; 这轮同步靠人工逐文件排查,视频帧里还是分写形式(数字与「项可点开的取证卡」隔着参数)。
补法仍是「真值当场算」:真值 = spec.js 字典的键数;
凡写「N 项 / N 条 / N 键 + 核验 / 口径 / 取证卡 / 字典」的,N 必须等于它 ——
页面、视频帧(含分写与表格窄规则)与配套文档清单一并纳扫。
与第六次共享同一个教训:没有被守卫覆盖的维度,就是下一次漏报的藏身处 —— 而它的症状从来不是报错,是不响。
广告法守卫有 22 项自检用例(10 项验证"该拦的拦得住"、 12 项验证"不该拦的没被拦"),而数字漂移守卫没有对应的自检用例 —— 它每一次的有效性,主要靠"补上规则后立刻抓出旧值"这件事本身证明,不是靠单元测试。
唯一一次主动自测,是补完第 ⑤ 类之后:故意把首页改回旧的形式数,确认守卫报错,再改回 —— 也就仅此一次。 更值得记的是:第 ⑤ 类补完第一次跑,报出来的其实是它自己算错了真值 —— 它把首页 CSS 注释里那句"(第五种形式)"也当成了一个角标,真值算成 6,于是误报了三处并不存在的漂移。 误报比漏报更伤 —— 它会让人开始不信任守卫。
换句话说:对它,我只能说"它每次补上规则后都抓到过旧值",不能说"它被证明足够灵敏"。 这条差别记在这里,免得读者把两者当成同一等级的保障。
本页声明的浏览器渲染断言数,是由脚本输出文件反查的; 而那份输出文件如果停留在旧版脚本(当时 index 页只有 10 条检查), 真值 66 就会被读成 65,并让整张表的合计跟着错。
所以规矩是:改了脚本,必须重跑一遍再核对页面。 数字的权威来源永远是"脚本现在跑出来的结果",不是磁盘上那份可能过期的输出。