判据要用真值而非代理
# 判据要用真值而非代理
你反复踩的坑不是「判断写错了」,而是判断用的那个量根本不是你要问的东西。 「第几次事件」「滚了多远」「动画转到第几帧」「这次会话缓存过没有」「多久没结束」「字符串变了没有」—— 它们都只是真值的代理,在边界条件下会和真值分叉,而且不报错。 解法统一:把判据换成真值本身(内容是否变化、元素是否可见、真实进度、真实加载状态)。
十处证据:每一处都是「用代理当判据」
| 出处 | 代理(错的判据) | 真值(对的判据) | 分叉后的症状 |
|---|---|---|---|
| 首次内容事件不能无条件跳过 | 事件序号(第 1 次就跳过) | 内容是否仍等于初始内容 | 空正文第一次粘贴图片被整个丢掉 → 保存不了 |
| 显示阈值应按元素可见性而非固定像素 | 固定滚动距离(scrollTop > 18) | 标题元素是否已滚出覆盖区 | 字号/行高不同 → 标题提前或延后出现 |
| 进度条要由真实进度驱动 | 无限循环动画 / 帧间隔计时 | 真实加载状态或经过时间 | 条件永不成立 → 进度条卡在假进度上 |
| 图片加载失败要可重试并升级到HTTPS | 「这场会话加载失败过」的缓存标记 | 本次请求的实际结果 | 一次失败被会话级永久记住 → 整场不再重试 |
| 缓存命中率 | 命中率数字(0 就以为坏了) | 「是不是每次都是新会话首请求」 | 测量动作本身绕过了被测对象 → 误判实现有 bug |
| 编辑器显示不能脱离真实光标状态 | 固定的渲染规则 / 滞后的 selection | 真实光标位置 | 源码与渲染同时出现;「看起来正常」但输入落到错误位置 |
| 编辑器初始化会触发假保存 | 字符串 diff(编辑器 round-trip 后变了) | 内容语义是否真的变化 / 是否是用户输入 | 打开即保存 → updatedAt 变新 → 列表跳位 |
| 长任务不要用固定超时判定 | 固定 RUN_TIMEOUT_MS(10 分钟) | turn/completed 等结构化事件 | 长任务被误杀;超时被当成「已停止」 |
| 终端画面同步Agent状态不稳定 | 「终端画面几秒没变」 | app-server 的结构化事件 | turn 结束判断早或晚;输出夹杂 UI 文本 |
| 两条写入路径要用内容判重 | id(本地 id / 镜像 id) | 内容 + 会话 + 时间窗 | 同一条消息被插两次 |
| 共享历史不等于共享活会话(懒加载反模式) | 「用户点进会话才拉」 | 会话是否真的有更新 | 电脑端在跑,手机端不更新也不通知 |
综合判断
-
代理量在「典型输入」下与真值一致,只在边界处分叉。 这正是它们难被发现的原因: 日常使用完全正常,只在「第一次」「刚好滚动到临界」「刚好那一次失败」时出错 —— 所以第一反应永远是「偶发」,而不是「判据错了」。
-
代理量往往比真值更「好拿」,所以会被顺手用上:事件序号比内容比较便宜, 固定像素比测量元素位置便宜,循环动画比接真实进度便宜,字符串 diff 比语义比较便宜, 数秒画面静止比接结构化事件便宜。省下的那点成本,全部以「静默错误」的形式还回来。
顺带一个识别信号:代理量几乎总是「单点」的(一个 id、一个序号、一个数字、一个时间), 而真值往往需要多点或结构化信息(内容 + 会话 + 时间窗、内容前后对比、事件类型 + 状态)。 判据越依赖单点,越可能在边界处分叉。
-
可迁移的动作:写下任何一个条件判断时,追问一句 —— 「这个量和我真正要问的东西,在所有情况下都等价吗?」 只要答案不是「是」,就换成真值:内容比较、元素可见性、真实状态、显式记录。 配套:真值若不可得,就显式记录它(对照 更新时序要由本地最新状态决定)。
与既有 synthesis 的关系
- 更新时序要由本地最新状态决定 —— 讲时序上不能拿「最后发生的」冒充「最新的」; 本条更宽:讲判据本身不能拿代理量冒充真值。时序是「用环境状态当时间」的特例。
- 对照才产生信息 —— 代理量的问题正是「没造对照」:没拿真值对照,就看不出分叉。
- 分层要落到运行时才有效 —— 同源:约束/判据不落到真实来源上,就等于不存在。
为什么这是 synthesis
单看每一页,都是某个功能的一个具体 bug(粘贴丢图、标题时机、进度条、图片重试、缓存命中、 光标显示、打开即保存、长任务误杀、终端画面判结束、重复插入、懒加载不推送)。 并排之后才看得出它们共享同一个失败形状:用可得的代理量代替了要问的真值。 这个形状与框架、业务无关,可以直接带走。
相关
- 首次内容事件不能无条件跳过 / 显示阈值应按元素可见性而非固定像素 / 进度条要由真实进度驱动
- 图片加载失败要可重试并升级到HTTPS / 缓存命中率 / 编辑器显示不能脱离真实光标状态
- 编辑器初始化会触发假保存 / 长任务不要用固定超时判定 / 终端画面同步Agent状态不稳定
- 两条写入路径要用内容判重 / 共享历史不等于共享活会话
- 更新时序要由本地最新状态决定
- 对照才产生信息
- 判断现状要看真正在跑的那一份 —— 同源:文件/状态页/接口返回都是「现状」的代理
- 检索是一条分层链 —— 迭代层的停止条件看覆盖度(真值),不看命中数量(代理)