LiveLink v1.4.2 + v1.5.0 迭代复盘:修一个「不在自己代码里」的 bug,与三次自我纠错
记录日期:2026-08-02 来源:LiveLink(B 站直播弹幕 + 礼物助手,Electron + Vue3)两天两个版本的迭代 状态:过程已完成、结论部分待长时验证(重连修复只在模拟断网下验证过)
事实记录(不可修改区)
- 项目:LiveLink ·
{AIGC工作站外}D:\B站直播弹幕+礼物插件→ github.com/Mr-Salticidae/livelink - 迭代时间:2026-08-01 ~ 08-02
- 起始版本:v1.4.1(线上已发布)
- 交付:v1.4.2(08-01 发布,断线重连修复)、v1.5.0(08-02 发布,视觉系统「蛛丝」),均含 NSIS 安装包
- 代码改动:34 文件、+1757 / −186 行
- 提交:
fb52809→46a45a7→6449780→6b94807 - 触发需求:用户一句现象描述「意外断网后 app 断联后不间断重连但始终失败」
- 验证:对真实 B 站服务器跑自建回归脚本 22 项断言全通过;半开连接看门狗实测 75s 准点判死;两个 typecheck + build 全绿
- 数据来源:git log、gh release、CDP 实测输出、会话全过程记录
- 执行:跳蛛先生(需求 / 决策)+ Claude Code(排查 / 实现 / 验证)
一、这次真正学到的:根因不在自己代码里
用户只给了一句现象。排查后确认三条根因全部在第三方库 tiny-bilibili-ws@1.0.2 里,
而且都不是「用错了 API」,是那个库自身的设计缺陷:
| # | 缺陷 | 后果 |
|---|---|---|
| 1 | 握手 token 存进程级 Map,永不失效、无清除入口;socket.reconnect 是闭包,复用旧 host/port 与 options.key | token 过期后重连必失败;连「停止→重新开始」都救不回来,只有重启进程 |
| 2 | close() 取消不掉已排上的 5s 重连计时器(那是 close 处理函数里的局部变量);断线超 5s 后 live===false,close() 直接空转 | 两条路都留下停不掉的僵尸连接 |
| 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 -v 与 git 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 fetch 和 grep 同等必要。
三、方法论沉淀
方法论 1:依赖库「没有关闭入口的贴心默认」=必须接管,不要对抗 ⭐⭐
核心:第三方库的自动重连 / 缓存 / 心跳这类默认行为,一旦实现有缺陷且没有对外的关闭或清除入口, 上层无论怎么调都修不好。此时唯一出路是关掉它、自己接管,而不是想办法「用对」它。
来源:close() 取消不了已排上的重连计时器、token 缓存无清除入口。
试图「正确调用」耗掉的时间远超「关掉 keepalive 自己写」。
验证状态:二次验证 ⭐⭐(本次 + 变通方案不等于故障点_v1 的同构结构: 都在讲「别顺着别人的表层行为反推 / 迁就,要直接定位并接管真正的控制点」)
操作规则:
- 排查第三方库问题时,先读它的 dist 源码确认有没有对外的控制入口(关闭开关 / 缓存清除 / 状态重置)。
- 没有入口 → 直接评估「关掉该功能自己实现」的成本,通常比对抗便宜。
- 自己接管后必须显式关掉库的对应功能,否则两套机制打架。
反例 / 边界:库的功能属于核心能力(协议解析、加解密)时不适用,那部分仍应复用。
方法论 2:凭证类缓存缺失效路径 → 从偶发失败退化成永久失败 ⚠️
核心:任何带时效的凭证(token / session / 签名),只要被缓存且缺少失效路径, 就会从「偶发失败」退化成「永久失败」,且表现为重启才能恢复。
来源:本次 token 存进程级 Map,导致「停止→重新开始」也救不回来。
验证状态:初次发现 ⚠️(本项目内单次)
操作规则:
- 失效判断不要用时间(猜不准),要用权威信号(握手被拒时显式作废)。
- 但也不能每次都回源 —— 本次实测发现未登录状态下高频请求会被 B 站风控(
-352)。 最终采用「短 TTL 复用 + 握手失败显式作废」双轨。 - 诊断指纹见 → 重启才好是缓存了时效凭证的指纹_v1
反例 / 边界:无时效的静态资源(图标、字体表)缓存越久越好,不适用。
方法论 3:换肤改「色阶定义」,不要逐页改 class ⭐⭐
核心:项目里成百上千处颜色写死时,品牌化的杠杆点在配置层的色阶映射,而非逐页改。
来源:656 处 slate-* / 47 处 sky-*。在 tailwind.config.js 里把这两条色阶整体重映射,
10 个页面 + overlay 一次换肤,零重构风险。
验证状态:二次验证 ⭐⭐(本次 + 美工改稿全站落地_跨稿重复才是规范_v1: 都在讲「视觉改动要找到那个能一次覆盖全站的层」)
操作规则:
- 换肤前先统计各色系用量(
grep -ohE计数),找出承载语义的主色阶。 - 重映射主色阶,保留语义色(成功 / 警告 / 错误)不动,否则用户认不出状态。
- 组件级质感另开一层可复用类,不要散在页面里。
方法论 4:「跑起来看」抓到的缺陷,静态审查一个都抓不到 ⭐⭐⭐
核心:typecheck 全绿 + build 成功 ≠ 用户看到的东西是对的。
来源:真机截图逐页验收抓到 3 个静态检查完全无感的缺陷 —— 版本号写死(发 1.4.1 时侧栏显示 1.4.0)、观众端进房横幅硬编码不跟主题、连接状态重复显示两遍。 三个都是「代码没错,但呈现出来是错的」。
验证状态:三次验证 ⭐⭐⭐ (本次 + Claude完成报告核查心法 + 交付前实测证伪律_v1 + 桌面应用打磨发布闭环复盘_工位池塘v0.5.0_v1 的「布局量 DOM 不量截图像素」)
操作规则:
- UI 类改动必须真的启动应用逐页看过才能说完成。
- 看的时候带着「这里显示的值是从哪来的」去看,而不是只看好不好看 —— 版本号那个 bug 就是这么发现的。
- 截图工具本身要先被证伪(见方法论 5)。
方法论 5:观测工具本身要先被证伪 ⭐⭐⭐
核心:用工具观测到的「异常」,第一嫌疑人是工具,不是被观测对象。
来源:本次两例 —— PowerShell DPI-unaware 导致截图右侧被裁(误判为布局溢出);
CDP Page.getLayoutMetrics().contentSize 按宿主 DPI 报物理像素
(125% 缩放下多报 25%),当成 CSS 像素套进截图参数 → 长图底部空一大截。
验证状态:三次验证 ⭐⭐⭐(本次两例 + Windows下编码与DPI的所见非真相
操作规则:
- 观测到异常,先用第二种独立手段复现(本次是 CDP 量
scrollWidth)。 - 能拿程序内数值就不要靠肉眼看图(
document.documentElement.scrollWidth胜过截图)。 - Windows 上凡涉及像素坐标,先确认进程 DPI 感知状态。
- 没有第二种手段确认过的现象,不能写进对外文案。 ← 本次栽在这条
方法论 6:面向粉丝群的更新报告,画布宽度决定可读性 ⚠️
核心:群里多数人用手机看。长图画布用 1080 会让正文在手机上缩到 ~5px。 应按「手机满宽阅读 ≈ 15px 正文」反推:750 画布 + 26px 正文,2 倍渲染输出 1500 宽。
来源:本次首版按 1080 做,作者提醒「多数粉丝用手机查看,不要搞得太宽」。
验证状态:初次发现 ⚠️(单次,且尚未在真机手机上验证)
操作规则:
- 群发长图:750 画布 / 26px 正文 / 2 倍渲染,发之前先在手机上看一眼。
- 受众不全是产品用户时,开头必须交代「这是什么」。
- 体例沿用 pb-arena《项目进展说明_给团队与测试用户》:
大白话、按人群说话、用现象而非实现描述。
生成模板已存于项目仓库
docs/更新报告模板/(HTML → Electron 无头截图), 与 长期更新展示站_两层结构与无头卡片量产_v1 的无头卡片量产同一套路子。
四、没达成 / 打折扣的(诚实表)
| 项 | 状态 |
|---|---|
| 三条根因定位与修复 | ✅ 已实测复现 + 22 项真机断言 |
| 「路由器重启几分钟能自己扛过去」 | ⚠️ 设计意图,非实测结论(只在模拟断网下验证) |
| token 真实过期时能否自愈 | ⚠️ 未验证(没等到真实过期场景) |
| 视觉系统落地 | ✅ 真机逐页截图验收 |
| 观众端 overlay 四套主题 | ⚠️ 只确认「未被误伤」,没做美术优化 |
| 更新报告长图可读性 | ⚠️ 只在渲染端看过,未在真机手机验证 |
五、下次改进
如果重来,最想改的一步:在动第一行代码之前先 git fetch 对基线。
本次在过时检出上开发了整轮,靠发布前的习惯性检查兜住 —— 这个检查必须前移到「开始」。
行动清单:
- 开工前:
git fetch origin && git rev-list --left-right --count origin/main...main - 排查依赖问题:先读 dist 源码找控制入口,没有就直接考虑接管
- 写验证脚本:覆盖事故时序(用户实际操作顺序),不要只测 happy path
- UI 改完:真机启动逐页截图;截图工具先自证可信
- 写对外文案前:逐条问「这条我实测过吗」,没实测过的删掉或标为设计意图
- 群发长图:750 画布,发前手机上看一眼
关联文档
- 上位心法:Claude完成报告核查心法 —— 本次贡献「宣称已修复 vs 该 bug 根本不存在」这一新盲区
- 同类陷阱:Windows下编码与DPI的所见非真相 —— 本次两个新形态已补入该文档
- 工具证伪:全站文字截断体检_检测工具本身要先被证伪_v1 · 交付前实测证伪律_v1
- 本次提炼的律:开工前先对基线律_v1 · 重启才好是缓存了时效凭证的指纹_v1
- 同类项目复盘:桌面应用打磨发布闭环复盘_工位池塘v0.5.0_v1 · 2026-07-14_vpn-guard从工具到宣传片_全链路复盘_v1
- 视觉一次覆盖:美工改稿全站落地_跨稿重复才是规范_v1
- 无头截图量产:长期更新展示站_两层结构与无头卡片量产_v1
- 复盘体例:复盘事实先行原则