d47fe28fc5
`hermes import` published every zip member, including `state.db`, with `_extract_member_atomically` — a rename that swaps the file's inode. Any gateway, dashboard, or WebUI process holding the database open keeps its descriptor on the now-unlinked inode: it goes on serving pre-import pages and writing sessions no other process can see, while the sidecar WAL left beside the new file describes the database that was just unlinked. Nothing raises, so the import prints "Import complete" and the sessions are simply absent from the database everyone opens next. The live-safe path already exists: `/snapshot restore` has routed `.db` files through `_safe_restore_db()` since #65942, writing snapshot pages into the existing file so every open connection converges. `hermes import` — the disaster-recovery path, reached by users who already lost something once — never got that treatment. Route `.db` members through it. A target that does not exist yet has no holders and no inode worth preserving, so it keeps the ordinary atomic publish. A refused or failed live-safe restore now raises, so the import reports a skipped file instead of counting a silent success, and the existing database is left untouched. Importing an older backup over newer work stays allowed but no longer silent: the summary reports the session/message counts the import replaced, the same before/after evidence `restore_cron_jobs_if_emptied` uses for `cron/jobs.json`. Closes #100960 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ShTVU941HYygvypY9JMYE (cherry picked from commit 8ff260a312341cb85bdf7cdd570de5ff888c6013)