Claude 与 GPT-5.5 互审揪出 sqlite-utils 数据丢失 bug
Simon Willison 让 Claude Fable 与 GPT-5.5 互相审查 sqlite-utils 4.…
sqlite-utils 是知名的 Python SQLite 工具库,作者 Simon Willison 在准备 4.0 正式版前,借助 Anthropic 的 Claude Fable 与 OpenAI 的 GPT-5.5 两个大模型互相审查代码,发现了一个会引发数据丢失的严重 bug,以及若干 P1 级问题。
最关键的发现:delete_where() 不提交且污染连接
在 iPhone 的 Claude Code 网页版中,Simon 让 Fable 对即将发布的版本做最终审查。Fable 将 5 个问题标为「release blockers」,其中最严重的一处位于 Table.delete_where():
- 该方法直接通过
self.db.execute()执行 DELETE,缺少atomic()包装,导致连接停留在in_transaction=True。 - 之后所有
atomic()调用都会走 savepoint 分支,永远不会真正提交。 - 复现脚本显示:先
delete_where、再对其他表insert、关闭数据库后重开——所有变更(包括删除、行 50、表 u 的内容)全部丢失。
Simon 表示「非常庆幸自己没把这版发出去」,并补充说这类问题至少可以在 4.0.1 点版本里修复,不需要为此再升一个 5.0。
工作量与成本
整个 RC1 → RC2 过程的数据如下:
- 提示轮次:37
- 提交次数:34
- 涉及文件:30
- 代码变更:+1,321 / -190 行
- 总花费:约 $149.25
Simon 提到一个有趣的体验:难题反而给了 AI 代理 10–15 分钟思考的空档,期间他可以抽身去参加 Half Moon Bay 的独立日巡游,用手机时不时发下一条提示。
新的事务模型与 Python 3.12 兼容性
早期 RC 引入的事务处理是这次的主线改动,新 RC 给出了更清晰的文档说明,原文要点如下:
库内所有写入方法(
insert、upsert、update、delete、delete_where、transform、create_table、create_index、enable_fts等)都在各自的事务中执行,方法返回前已自动提交;db.execute()直接执行的写语句同样如此。用户只在两种场景下需要关心事务:想把多个写入合并成原子操作(用db.atomic()),或者自己用db.begin()显式管理事务。
在审查文档过程中,Simon 还意识到 sqlite-utils 对 Python 3.12+ 引入的 sqlite3.connect(..., autocommit=True/False) 模式没有做适配——该模式下 commit() 与 rollback() 行为不同,几乎整套测试都会失败。最终他和模型一起补上了相关处理。
GPT-5.5 反向复核再挖出两个 P1
Simon 曾对「让一个 AI 审另一个 AI」的做法半信半疑,但近几个月他已经习惯让 Anthropic 的最强模型去审 OpenAI 的工作,反之亦然。这次他让 Codex 桌面版与 GPT-5.5 xhigh 复核变更和新 changelog,得到两个值得调查的 P1 级发现:
db.query()在拒绝非 row 语句前会先提交写入,导致db.query("update ...")已经在数据库落库后才抛ValueError,与文档「只能用于返回行的 SQL」的说法不一致。- 通过
db.query()执行INSERT ... RETURNING时,事务只在生成器被完全消费后才提交;若调用方不迭代或只用一次next(),写入会在连接关闭时被回滚,与 changelog 的描述相悖。
Simon 表示这种「跨厂商互审」已经不止一次给出有价值的结果,未来会继续沿用。
