ATRIUMsearch → argument graph
Article · 2026-07-05 · 6 moments

sqlite-utils 4.0rc2, mostly written by Claude Fable (for about $149.25)

I wrote about the sqlite-utils 4.0rc1 release a couple of weeks ago. Since we only have Claude Fable on our Max subscriptions for a few more days, I decided to see if it could help me get to a 4.0 stable release that I felt truly comfortable about, since I try to keep to SemVer and like my incompatible major versions to be as rare as possible. I started with this prompt, in Claude Code for web on my iPhone: Final review before shipping a stable 4.0 release - very important to spot any last min ✦ AI generated

01
Definition

Every write method in sqlite-utils runs inside and commits its own transaction automatically, so changes are saved to disk as soon as the method call finishes, without needing an explicit commit().

The 4.0rc2 documentation clarifies that insert(), upsert(), update(), delete() and other write methods each commit their own transaction immediately, and that explicit transaction control (db.atomic() or db.begin()) is only needed for grouping multiple writes or manual management.

transcript

Claude Fable: Every method in this library that writes to the database - insert(), upsert(), update(), delete(), delete_where(), transform(), create_table(), create_index(), enable_fts() and the rest - runs inside its own transaction and commits it before returning. Your changes are saved to disk as soon as the method call finishes.

rebuts · 2

02
Claim

Having one AI model review another model's code work is a genuinely valuable practice rather than mere superstition.

Simon Willison describes overcoming his initial skepticism about cross-model code review, saying he now routinely has Anthropic's best model review OpenAI's output and vice versa because it reliably turns up useful findings.

transcript

Simon Willison: I used to think that the idea of having one model review the work of another was somewhat absurd - it felt weirdly superstitious. The problem is it really does work - I've started habitually having Anthropic's best model review OpenAI's work and vice versa, because I've had that turn up interesting results often enough to be valuable.

supports · 1

03
Anecdote

sqlite-utils's automatic transaction handling was fundamentally incompatible with connections created using Python 3.12's new autocommit=True/False options, which broke nearly the entire test suite once discovered.

Simon Willison admits he hadn't considered Python 3.12's new autocommit connection modes; testing with Fable revealed that db.atomic() and per-method auto-commits broke almost the whole test suite on such connections, requiring a fix so the library would explicitly reject them.

transcript

Simon Willison: I admit I hadn't thought about how sqlite-utils would react to the more recent autocommit setting, added in Python 3.12. It turns out "behave differently on those connections" equated to almost the entire test suite failing, so I worked with the model to ensure that this difference would not break how the library works.

04
Fact

sqlite-utils's db.query() commits a rejected write statement to the database before raising the ValueError that says the statement was disallowed, so the write takes effect despite the error.

GPT-5.5's review of the release candidate found that db.query() calls db.execute() (which auto-commits) before it checks whether the statement returns rows, so db.query("update ...") actually commits the update even though it then raises a ValueError.

transcript

GPT-5.5: db.query() now rejects non-row statements only after calling db.execute(), and ... auto-commits those writes first. So db.query("update ...") raises ValueError but the update is already committed. That is a surprising side effect for a method documented as "can only be used with SQL that returns rows."

explains mechanism · 1

05
Mechanism

Table.delete_where() never wraps its DELETE in a transaction, so it fails to commit and leaves the database connection permanently poisoned, causing silent data loss on subsequent operations.

Claude Fable's pre-release review found that Table.delete_where() executes its DELETE without an atomic() wrapper, leaving the connection stuck 'in_transaction', which silently rolls back that delete plus any later writes—even to unrelated tables—when the connection closes.

transcript

Claude Fable: Table.delete_where() (sqlite_utils/db.py:2948) runs its DELETE via a bare self.db.execute() with no atomic() wrapper — compare Table.delete() at db.py:2944, which wraps correctly. The connection is left in_transaction=True, so every subsequent atomic() call takes the savepoint branch (db.py:430-440) and never commits either.

06
Data

The full multi-agent sqlite-utils 4.0rc2 development session cost an estimated $149.25 in unsubsidized API costs, which is why Simon Willison upgraded to the $200/month Claude Max plan ahead of the July 7th API-pricing cutover.

Running AgentsView inside the same Claude Code session, Simon Willison calculated the main session plus several review subagents totaled $149.25 at full API rates, prompting his upgrade to Claude Max $200/month before the 'Fablepocalypse' pricing change on July 7th.

transcript

Simon Willison: For an estimated (unsubsidized) cost of $149.25 I upgraded to the Claude Max $200/month plan (I was previously on $100/month) to increase my Fable allowance for the remaining time until the July 7th Fablepocalypse, when even Claude Max subscribers will have to pay full API cost for the model.

Highlight slides
Related episodes