9657f6e343
Post-merge follow-up to #71770. Both defects were found by @helix4u in review and reproduced against merged main before fixing. 1. Check/use race in _copy_source_bundle (my bug, from the #71770 follow-up commit). It called has_live_connection(), released the registry lock, and only then ran shutil.copy2() over the bundle. A tracked connection could open in that window; the copy's close() then cancels its POSIX advisory locks -- the exact class #71724 closed. Measured on main: a racer thread opened a connection mid-copy after blocking 0.000s. Adds sqlite_safe_read.offline_file_access(), a context manager that holds the connection-lifecycle lock across an entire multi-step raw access, and routes the bundle copy through it. Same racer now blocks 10.0s until every raw descriptor is closed. Any future raw read of a database file (hashing, moving a bundle aside) should use this rather than a bare pre-check. 2. _copy_state_meta_salvage assumed a 'key' column. A damaged state_meta can keep 'value' and lose 'key'; columns.index("key") then raised ValueError and aborted the whole partial recovery. The mirror case (key without value) would have copied key-only rows and reported the table complete. Now requires both, matching the non-partial _copy_state_meta, so an unusable optional table is recorded as missing/failed and --allow-partial still recovers sessions and messages. Both regression tests verified by sabotage: reinstating the bare pre-check fails the race test, removing the key/value requirement fails the other. 937 targeted tests green.