9fd97e6925
Root cause: SQLite's default PASSIVE checkpoint mode cannot complete under sustained write load (4 nodes, hundreds of events/min) because active readers always block it. The WAL grows unbounded and eventually corrupts the main DB file. - Add dedicated checkpoint_wal() using PRAGMA wal_checkpoint(RESTART), which waits for readers to finish then checkpoints fully and resets the WAL back to the start. - Add background checkpointer task running every 30 minutes, separate from the 6-hour prune cycle. - Remove PASSIVE checkpoint from prune_old_events(), replace with call to checkpoint_wal() after pruning completes. - Set wal_autocheckpoint=500 on every connection (~2MB threshold) so SQLite's own auto-checkpoint also triggers more frequently. - Reduce HOT_DAYS from 30 to 7: at current data rate (~500k beacons/day) 30-day retention would grow to several GB before first prune runs. 7 days keeps the DB under ~200MB and queries fast permanently. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>