f7dfeff256
Slice 3c — completes the server-side producer set. Publishes scan.run_started when InsertScanRun succeeds and scan.run_finished after FinishScanRun completes (success or with an error_message payload). Per-file progress events are intentionally NOT emitted — they'd flood the stream during large library scans without giving the admin scan card anything actionable. The two-beat signal gives the UI enough to invalidate scanStatusProvider at the right moments. Bus access uses a package-level setter on internal/library because threading bus through RunScan + TryStartScan + Scheduler + all their callers would touch ~10 sites without changing behavior at the boundaries that don't publish. Per-process singleton, matches the log.SetDefault idiom. cmd/minstrel/main.go calls library.SetEventBus(bus) once at startup; test contexts that never call it skip publishing safely (publishScanEvent is a no-op when bus is nil). This completes the server side of #392. Slice 4 wires the Flutter consumer: live_events_provider.dart (StreamProvider) + live_events_dispatcher.dart (event-kind → provider invalidation) + AppLifecycleState cold-start invalidation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>