Files
HomeAgent/internal
JianFeeeee 969206eb85 test(lua): 加适配器漂移报告 + 键名契约判据
上一提交(66200ff)纠正了一处误判:仓库的 openai.lua 缺 stream_index 透传,
而生产实例早就有 —— 仓库版本落后于生产。这个漂移当时没有任何判据能发现。

## 两条判据,定位不同

### TestBundledAdaptersMatchDeployedOnes —— 诊断式,刻意**不**作为失败判据

实测结果:生产部署的 7/10 个适配器比仓库旧(anthropic 2537 vs 5144 字节),
而 openai 那份反而领先。这**是正常的** —— 生产是长期运行的部署,适配器在它首次
创建时就解包落地,之后仓库一直在演进。

若把"必须一致"写成失败判据,它会在任何老部署上恒红,而恒红的判据会被无视 ——
那等于没有判据,甚至更糟(它会掩盖真正的漂移)。所以这里只把差异摆出来。

但它顺带把**部署陷阱**摆到了明面上:

    vm.go writeBundledAdapters: if 文件已存在 { continue }

升级二进制**不会更新已部署的适配器文件**。于是"仓库改了适配器但老实例上不生效"
与"仓库根本没改"在现象上完全一样 —— 这大概就是仓库长期缺 stream_index 却
没人发现的原因之一。

### TestAdapterEmitsOnlyKnownFields —— 这条才是能自动抓 bug 的

契约 = agentAPI.ToolCall / StreamChunk 的 json tag:
`id, type, name, arguments, raw_arguments, stream_index`(+ 上游原样透传的
`index` / `function`)。

★ 为什么必须有:Go 侧按 json tag 反序列化,**键名拼错会静默取零值**。
`streamindex`(少个下划线)与"没写这行"的表现完全一样 —— 无报错、字段为零、
分桶全部并到槽 0。这正是本次 stream_index 缺失的形态。

判据只看"适配器吐出来的键名对不对",与环境无关,所以能在 CI 里恒定生效。

## 顺带确认的一件事(事后查明:是我的操作失误)

压测实例解包出的 openai.lua 是 4709 字节(无 stream_index),而二进制 embed
里是 5672 字节(有)。`rm -rf` 后重新解包**仍是旧的**。

我逐行读过 `writeBundledAdapters`,只找到 embed 一条来源,一度判为"未解释的
矛盾"。**真因是操作失误**:`rm -rf` 之后启动的那一轮,用的还是修复前编译的
/tmp/homed-stress —— 删除与重编之间隔了几轮,中间又用旧二进制起了好几次
实例,每次解包出来的自然都是旧版。

用当前 main 重新构建 + 全新数据目录验证:全新解包 5672 字节、含
stream_index×3、md5 与源文件一致;删掉再解一次仍一致;启动日志有
`[lua] installed bundled adapter: openai.lua`。⇒ **writeBundledAdapters 无缺陷**。

★ 教训:验证"二进制内嵌内容是否更新"时,必须确认跑的就是**刚编译出来的那个
二进制**。否则会得出"代码有 bug"的错误结论 —— 我确实这么怀疑了好几天。

(下面那条"部署陷阱"观察本身仍然成立:升级二进制确实不会更新已部署的适配器
文件。但它与这次的现象无关。)
2026-09-27 18:29:15 +08:00
..