A SQLite Fork Built by 2,000 Agent PRs Has Exactly One Known Engine Bug
DoltLite reached beta on about 2,000 AI agent pull requests. We counted its divergence files ourselves: 4,705 classified entries, and exactly one is a real bug.
The WJS Desk
Sep 1, 2026 · updated 4 hours ago · 5 min read

DoltHub shipped DoltLite 0.50.0 this week, a fork of SQLite that rips out the B-tree storage engine and puts a prolly tree in its place, so you can branch, merge and diff a SQLite file the way you would a Git repo. It took about 2,000 pull requests to get there, and the blog post is upfront that a team of AI agents did most of them, running under Steve Yegge's Gas Town orchestrator.
The Hacker News thread was thin, 13 points and two comments, but one of them was the right objection. anon291 wrote that a database effort's "main product is not exotic data structures but validation," and that they would not trust agents with their data without extensive validation.
So we went and read the validation. It is public, it is in the repo, and it is considerably more interesting than the database.
What actually shipped
DoltLite keeps SQLite's tokenizer, parser, planner and VDBE, and replaces everything below btree.h: the B-tree, the pager and the on-disk format all become a single-file, content-addressed chunk store. You get dolt_commit, dolt_merge, dolt_diff, branches, rebases, cherry-picks, plus push, pull, clone and fetch, in an embeddable library.
It is Apache 2.0, which is a real difference from upstream SQLite's public domain, and the repo is explicit that upstream files keep their public-domain blessing comment while DoltLite sources carry no header at all.
The performance cost is stated plainly, which we appreciated:
| Workload | vs SQLite |
|---|---|
| In-memory reads | 10% slower |
| In-memory writes | 60% slower |
| File-backed reads | Parity |
| File-backed batched writes | 10% slower |
| Autocommit writes | 3.1x slower, ~400 us vs ~125 us |
That autocommit number is the one to look at. If your workload is a loop of individual inserts without an explicit transaction, you are paying triple. Batch your writes and most of it disappears.
The other HN comment was about WAL
grebc asked whether they had read correctly that there is no WAL. They had. There is no write-ahead log and no rollback journal sidecar, and the WAL and journal tests are skipped.
That sounds alarming until you see why. A WAL exists to make page-based mutation crash-safe. DoltLite does not have pages; it has content-addressed chunks, and the repo's compatibility contract says so directly: it "uses chunks rather than pages, has no SQLite WAL or rollback journal, and cannot use an anonymous rowid as a version-controlled key because identity must be history-independent."
This is a consequence of the architecture, not an unfinished corner. The right question is not "where is the WAL" but "what replaces its guarantees," and the answer is a separate concurrency contract with its own multi-process test suites.
Still check this before you adopt. "Intentionally absent" and "safe" are different claims. If you are relying on SQLite's specific durability behaviour under power loss, a chunk store with different crash semantics is a migration you need to test, not a drop-in.
The number that sounds bad and is not
The obvious stick to beat this with is the divergence count. The project tracks where it differs from upstream SQLite's test suite in test/known_testfixture_divergences.txt, and that file is thousands of lines long. Quoted alone, "thousands of known test divergences in a database" is a damning headline.
We pulled the file and counted the classifications ourselves, because every entry is tagged:
| Class | Entries | Meaning |
|---|---|---|
intentional | 3,411 | Deliberate behaviour difference |
unsupported | 1,292 | Known boundary, not implemented |
harness | 1 | Test infrastructure artefact |
engine-gap | 1 | An actual bug, filed as issue 1884 |
4,705 classified entries. One is a real bug, and it carries an issue number, because the rules require that unsupported and engine-gap entries cite one. The rest are a documented map of where a version-controlled storage engine cannot behave like a pager, which is exactly the file you would want to exist.
Then we checked the other allowlist. test/known_sqllogictest_divergences.txt has zero non-comment lines. The header explains that the gate is bidirectional: a new divergence in any clean file fails CI, and a listed entry that starts matching stock SQLite again must be removed. You cannot quiet a failure by adding it to the list and you cannot leave a stale entry behind.
The guardrails are the deliverable
The repo ships an AGENTS.md, 333 lines and roughly 2,400 words, that governed those 2,000 PRs. Read as a document about working with agents rather than about SQLite, three rules stand out.
The first is blunt:
Never delete a test or disable a check. If a test or assertion fails, fix the change, not the guardrail.
The second requires proof rather than assertion. An observable bug fix needs a fail-before, pass-after test: one that fails on the unfixed engine and passes with the fix. Latent and defensive fixes are explicitly exempted, which is the detail that makes the rule survivable.
The third is the one we would steal outright. Describing the C test runner:
Either way not built and stale are counted separately from failed, so a skipped or out-of-date binary can never read as a pass.
A skipped or out-of-date binary can never read as a pass. That is the whole discipline in one line.
That is the failure mode every automated check dies of. A guard that silently stops running looks identical to a guard that is passing, and the gap between them is where you lose an afternoon or a database. Making not built and stale their own categories, distinct from failed, is the fix, and it costs nothing.
There is also a warning about the agents themselves. Under a heading telling contributors to write effectively no comments, the file says "agents over-comment by default; consciously resist it." Someone read a lot of generated diffs to write that sentence.
Where this leaves the objection
anon291 is right that validation is a database's product, and the interesting thing is that DoltHub appears to agree. The oracle suites run identical SQL against DoltLite and against real Dolt and diff the normalised output, so the reference implementation is a running program rather than a spec. SQLLogicTest is held at 100 percent parity with a corpus pinned to a fixed check-in. Divergences are classified, counted and required to cite issues.
None of that proves the code is correct. A test suite is a lower bound on correctness and 215 stars on a five-month-old fork is not production validation. We would not put a customer ledger on this next week, and the autocommit penalty alone rules out plenty of workloads.
But "built by agents" is doing less work in that headline than people think. The 2,000 PRs are not the story. The story is that somebody wrote the contract those PRs had to pass, made it bidirectional so it cannot be gamed from either side, and published it. The agent count is a fact about labour. The divergence file is a fact about engineering, and it is the one that should decide whether you try this.
What would change our mind: that engine-gap count going up faster than it gets closed, or the sqllogictest allowlist acquiring its first real entry. Both are one curl away, which is the point.


