方法论与洞察

LiveLink v1.4.2 + v1.5.0 迭代复盘:修一个「不在自己代码里」的 bug,与三次自我纠错

记录日期:2026-08-02 来源:LiveLink(B 站直播弹幕 + 礼物助手,Electron + Vue3)两天两个版本的迭代 状态:过程已完成、结论部分待长时验证(重连修复只在模拟断网下验证过)


事实记录(不可修改区)


一、这次真正学到的:根因不在自己代码里

用户只给了一句现象。排查后确认三条根因全部在第三方库 tiny-bilibili-ws@1.0.2, 而且都不是「用错了 API」,是那个库自身的设计缺陷:

#缺陷后果
1握手 token 存进程级 Map,永不失效、无清除入口socket.reconnect 是闭包,复用旧 host/port 与 options.keytoken 过期后重连必失败;连「停止→重新开始」都救不回来,只有重启进程
2close() 取消不掉已排上的 5s 重连计时器(那是 close 处理函数里的局部变量);断线超 5s 后 live===falseclose() 直接空转两条路都留下停不掉的僵尸连接
3心跳「收到回包才排下一次」,丢一个回包即永久停摆连接静默死亡但不报 close,UI 长期假显示「已连接」

三条都写脚本实测复现过,不是读代码推断的 —— 例如打印出两次 startListen 拿到完全相同的 token, 以及「点了停止之后底层仍自动重连、socket destroyed=false」。这一步是后面所有判断的地基。

解法不是「正确调用」,是「关掉它自己接管」:自实现 WBI 签名直取新 token、 keepalive: false、自己驱动心跳与拆连接。详见 → 本文「方法论 1」。


二、三次自我纠错(本篇最该记住的部分)

纠错一:在过时检出上开发了整轮,差点回退 13 个提交 ⚠️ 最严重

本地 main 落后 origin 13 个提交(线上已是 v1.4.1,我在 v1.2.0 的旧检出上改), 且已把版本改成 1.2.1、写好发布说明。若直接发,会覆盖掉宠物系统、AI 回复、 overlay 主题、赛马押注、装修预览等 13 个提交的全部功能。

没出事的原因:发布前查了 git remote -vgit rev-list --left-right --count但这个检查做在「准备发布」而不是「开始写代码」,属于侥幸。

→ 已提炼为独立律:开工前先对基线律_v1

纠错二:把工具缺陷误判成产品 bug,并写进了已发布的 release notes

截图时 PowerShell 进程 DPI-unaware,125% 缩放下按虚拟坐标截图导致右侧被裁。 我据此判定「布局在窄窗口被撑破」,写了 CSS 修复,并把这条写进 commit message 和已发布的 GitHub release notes

后来用 CDP 量出 scrollWidth === innerWidth === 1086,又做对照实验 (900 / 820 / 760px 下加不加那段 CSS 结果完全一致)—— 该 bug 从不存在

处置:编辑线上 release notes 删掉虚假声明;commit message 已推送无法干净改写,在此如实记录; 那两行 CSS 作为无害防御性代码保留,但标注「未修复任何已观察到的问题」。

这正是 Windows下编码与DPI的所见非真相 记过的同一类陷阱的第三次发生 (本次两个新形态已补进该文档),也是 Claude完成报告核查心法 的新盲区形态: 不只是「宣称已实现 vs 实测可用」,还有「宣称已修复 vs 该 bug 根本不存在」

纠错三:整屏截图抓到无关前台窗口

CopyFromScreen 整屏捕获时,前台被其他应用(含私人聊天窗口)占据, 截图内容与项目无关且涉及隐私,发现后立即删除。 改用 CDP Emulation.setDeviceMetricsOverride + captureBeyondViewport 离屏渲染—— 只截目标页面自身,不受遮挡、也不会捞到别的窗口。

纠错四:同一天又栽在同一个坑 —— 落后的检出让我把 skill 报成「不存在」

写完「纠错一」和 开工前先对基线律_v1 不到一小时,在知识库仓库上又栽了一次

作者说「更新报告有专门的 skill,从知识库中找」。我穷尽式搜了 07_skill存档/(20 个)、 SKILL_INDEX.md、部署手册、~/.claude/skills/、两处项目级 skills 目录, 又全库 grep 关键词 —— 零命中,于是**向作者报告「库里没有这个 skill」**并列候选让他选。

真相:本地知识库落后 origin 14 个提交report-longimage 就在其中一个提交里。 git fetch 后一秒命中。

代价:白做了一版不合规的长图(自搭深色版式 + 自写渲染脚本), 最终按 skill 的 PB Arena「简洁低疲劳 V2」版式重做(1120@2x → 2240 宽、暖白/炭黑/珊瑚红、 两趟 headless Chrome 截图、裁头中尾验收)。

这次比第一次更阴:第一次是「我改的会覆盖别人的」,后果看得见; 这次是「我搜不到的,我报告成不存在」—— 穷尽搜索 + 零命中读起来像强证据, 实际只是索引旧。推论已回填进该律:在 git 仓库里做全库搜索并下否定结论前, git fetchgrep 同等必要。


三、方法论沉淀

方法论 1:依赖库「没有关闭入口的贴心默认」=必须接管,不要对抗 ⭐⭐

核心:第三方库的自动重连 / 缓存 / 心跳这类默认行为,一旦实现有缺陷且没有对外的关闭或清除入口, 上层无论怎么调都修不好。此时唯一出路是关掉它、自己接管,而不是想办法「用对」它。

来源close() 取消不了已排上的重连计时器、token 缓存无清除入口。 试图「正确调用」耗掉的时间远超「关掉 keepalive 自己写」。

验证状态:二次验证 ⭐⭐(本次 + 变通方案不等于故障点_v1 的同构结构: 都在讲「别顺着别人的表层行为反推 / 迁就,要直接定位并接管真正的控制点」)

操作规则

  1. 排查第三方库问题时,先读它的 dist 源码确认有没有对外的控制入口(关闭开关 / 缓存清除 / 状态重置)。
  2. 没有入口 → 直接评估「关掉该功能自己实现」的成本,通常比对抗便宜。
  3. 自己接管后必须显式关掉库的对应功能,否则两套机制打架。

反例 / 边界:库的功能属于核心能力(协议解析、加解密)时不适用,那部分仍应复用。

方法论 2:凭证类缓存缺失效路径 → 从偶发失败退化成永久失败 ⚠️

核心:任何带时效的凭证(token / session / 签名),只要被缓存且缺少失效路径, 就会从「偶发失败」退化成「永久失败」,且表现为重启才能恢复

来源:本次 token 存进程级 Map,导致「停止→重新开始」也救不回来。

验证状态:初次发现 ⚠️(本项目内单次)

操作规则

  1. 失效判断不要用时间(猜不准),要用权威信号(握手被拒时显式作废)。
  2. 但也不能每次都回源 —— 本次实测发现未登录状态下高频请求会被 B 站风控(-352)。 最终采用「短 TTL 复用 + 握手失败显式作废」双轨。
  3. 诊断指纹见 → 重启才好是缓存了时效凭证的指纹_v1

反例 / 边界:无时效的静态资源(图标、字体表)缓存越久越好,不适用。

方法论 3:换肤改「色阶定义」,不要逐页改 class ⭐⭐

核心:项目里成百上千处颜色写死时,品牌化的杠杆点在配置层的色阶映射,而非逐页改。

来源:656 处 slate-* / 47 处 sky-*。在 tailwind.config.js 里把这两条色阶整体重映射, 10 个页面 + overlay 一次换肤,零重构风险。

验证状态:二次验证 ⭐⭐(本次 + 美工改稿全站落地_跨稿重复才是规范_v1: 都在讲「视觉改动要找到那个能一次覆盖全站的层」)

操作规则

  1. 换肤前先统计各色系用量(grep -ohE 计数),找出承载语义的主色阶。
  2. 重映射主色阶,保留语义色(成功 / 警告 / 错误)不动,否则用户认不出状态。
  3. 组件级质感另开一层可复用类,不要散在页面里。

方法论 4:「跑起来看」抓到的缺陷,静态审查一个都抓不到 ⭐⭐⭐

核心:typecheck 全绿 + build 成功 ≠ 用户看到的东西是对的。

来源:真机截图逐页验收抓到 3 个静态检查完全无感的缺陷 —— 版本号写死(发 1.4.1 时侧栏显示 1.4.0)、观众端进房横幅硬编码不跟主题、连接状态重复显示两遍。 三个都是「代码没错,但呈现出来是错的」。

验证状态:三次验证 ⭐⭐⭐ (本次 + Claude完成报告核查心法 + 交付前实测证伪律_v1 + 桌面应用打磨发布闭环复盘_工位池塘v0.5.0_v1 的「布局量 DOM 不量截图像素」)

操作规则

  1. UI 类改动必须真的启动应用逐页看过才能说完成。
  2. 看的时候带着「这里显示的值是从哪来的」去看,而不是只看好不好看 —— 版本号那个 bug 就是这么发现的。
  3. 截图工具本身要先被证伪(见方法论 5)。

方法论 5:观测工具本身要先被证伪 ⭐⭐⭐

核心:用工具观测到的「异常」,第一嫌疑人是工具,不是被观测对象。

来源:本次两例 —— PowerShell DPI-unaware 导致截图右侧被裁(误判为布局溢出); CDP Page.getLayoutMetrics().contentSize 按宿主 DPI 报物理像素 (125% 缩放下多报 25%),当成 CSS 像素套进截图参数 → 长图底部空一大截。

验证状态:三次验证 ⭐⭐⭐(本次两例 + Windows下编码与DPI的所见非真相

操作规则

  1. 观测到异常,先用第二种独立手段复现(本次是 CDP 量 scrollWidth)。
  2. 能拿程序内数值就不要靠肉眼看图(document.documentElement.scrollWidth 胜过截图)。
  3. Windows 上凡涉及像素坐标,先确认进程 DPI 感知状态
  4. 没有第二种手段确认过的现象,不能写进对外文案。 ← 本次栽在这条

方法论 6:面向粉丝群的更新报告,画布宽度决定可读性 ⚠️

核心:群里多数人用手机看。长图画布用 1080 会让正文在手机上缩到 ~5px。 应按「手机满宽阅读 ≈ 15px 正文」反推:750 画布 + 26px 正文,2 倍渲染输出 1500 宽。

来源:本次首版按 1080 做,作者提醒「多数粉丝用手机查看,不要搞得太宽」。

验证状态:初次发现 ⚠️(单次,且尚未在真机手机上验证

操作规则

  1. 群发长图:750 画布 / 26px 正文 / 2 倍渲染,发之前先在手机上看一眼。
  2. 受众不全是产品用户时,开头必须交代「这是什么」。
  3. 体例沿用 pb-arena《项目进展说明_给团队与测试用户》: 大白话、按人群说话、用现象而非实现描述。 生成模板已存于项目仓库 docs/更新报告模板/(HTML → Electron 无头截图), 与 长期更新展示站_两层结构与无头卡片量产_v1 的无头卡片量产同一套路子。

四、没达成 / 打折扣的(诚实表)

状态
三条根因定位与修复✅ 已实测复现 + 22 项真机断言
「路由器重启几分钟能自己扛过去」⚠️ 设计意图,非实测结论(只在模拟断网下验证)
token 真实过期时能否自愈⚠️ 未验证(没等到真实过期场景)
视觉系统落地✅ 真机逐页截图验收
观众端 overlay 四套主题⚠️ 只确认「未被误伤」,没做美术优化
更新报告长图可读性⚠️ 只在渲染端看过,未在真机手机验证

五、下次改进

如果重来,最想改的一步:在动第一行代码之前先 git fetch 对基线。 本次在过时检出上开发了整轮,靠发布前的习惯性检查兜住 —— 这个检查必须前移到「开始」。

行动清单

  1. 开工前:git fetch origin && git rev-list --left-right --count origin/main...main
  2. 排查依赖问题:先读 dist 源码找控制入口,没有就直接考虑接管
  3. 写验证脚本:覆盖事故时序(用户实际操作顺序),不要只测 happy path
  4. UI 改完:真机启动逐页截图;截图工具先自证可信
  5. 写对外文案前:逐条问「这条我实测过吗」,没实测过的删掉或标为设计意图
  6. 群发长图:750 画布,发前手机上看一眼

关联文档

类型/协作工具链