If extract_all() returns None (Dashboard init failure), appending it to
store["records"] would cause _accept_loop to crash with AttributeError on
the next client connection. Since only OSError was caught, the thread
died permanently — no further clients could join push_clients, so live
broadcasts reached nobody.
- collect.py: skip None snapshots instead of appending them
- network.py: filter None records in _accept_loop; catch all exceptions
during history send (not just OSError) so the thread survives
- storage_helpers.py: filter None records when loading from disk
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>