df841d342c
Follow-up on top of #65554 (@the3asic): - Lower FTS5 'usermerge' to its minimum of 2 (persisted in the config shadow table, applied once per SessionDB instance). Without this a positive-rank 'merge' skips any level holding fewer than 4 segments (SQLite FTS5 §6.8), so the fragmented-index case the cadence targets never converges. - Run up to _FTS_MERGE_COMMANDS_PER_PASS (4) bounded merge commands per index per cadence, stopping early on the documented no-progress signal (total_changes delta < 2). Each command is its own implicit transaction, so the write lock is released between commands. - Skip a missing messages_fts instead of raising: the chunked optimize-storage rebuild legitimately drops + backfills FTS tables while writers keep running; warning every 1000 writes for the whole backfill window would be noise, and optimize_fts() has always treated missing tables as skippable. - Replace traced-SQL shape assertions with behavioral tests: real fragmented-index convergence (automerge suppressed, 60 segments) and a 3-segment below-default-usermerge compaction test that fails against a bare positive-rank merge (sabotage-verified). Validation on a sqlite3.backup() copy of a real 10.7 GB production state.db (1.49M messages, 1.3 GB + 2.9 GB FTS shadow tables): worst per-command write-lock hold 41.8 ms (was 9.2 s / 18.1 s per index with 'optimize'), search results byte-identical, fts5 integrity-check and PRAGMA integrity_check clean, steady-state pass 0.0 ms.