Writing a database by hand
Our reader parses SQLite files by hand, and building that parser felt like fluency. Then the editor needed the opposite direction — writing databases from scratch — and that fluency turned out to be reading-level only. A parser can shrug at things it doesn't understand; a writer's every byte is a claim the official engine will audit without mercy. That asymmetry is the whole story here.
The assembly line
Writing runs the read pipeline backwards. Each row's values are encoded into a record — serial types chosen frugally (a boolean costs zero bytes; the integer 300 costs two; text costs its length), with header varints in front. Records pack into 4KB leaf pages from the tail forward, a pointer array growing from the head, until a page is full and the next begins. If a table outgrows one leaf, an interior page is stacked above it, holding "rowids up to N live in page P" signposts. Finally page one is assembled — the schema table describing every other table, plus the 100-byte header with its magic string, page size, counts and the UTF-8 declaration. Every offset computed, nothing borrowed.
The bug that taught the lesson
First full test: the engine's integrity check returned a page of accusations — rowids out of order, a double reference to page one, an orphaned page. The cause was one line: pages are numbered from 1, arrays from 0, and our child pointers were recorded one page off from where the children actually lived. A parser with that bug misreads one file and you debug it; a writer with that bug builds a thousand confidently corrupt files. Which is why the certification bar is external: not "our tests pass" but the reference engine opens our output, walks 3,000 rows across 21 pages, answers aggregates correctly, and — the compliment we're proudest of — inserts its own rows into pages we laid out. When the engine starts building on your foundation, the design holds.