Replace the two _ = discards in executeRun (SaveJobs + CleanupLogs after
an async run) and the _ = in RunDue (SaveJobs before a scheduled run)
with captured errors emitted as ErrorOccurred events after the state
lock is released. The UI subscriber in mainwindow.go handles the new
event by appending an "Error" record to History so failed saves are
visible to the user instead of silently dropped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add src/app/events.go: a sealed Event interface with three concrete
types (JobChanged, RunRecorded, SchedulerStateChanged), an Observer
interface plus ObserverFunc adapter, and Subscribe/emit on the Service.
This replaces the scheduler's single onChange callback with typed
events the UI can exhaustively type-switch over.
Dispatch is serialized by a dedicated dispatchMu (separate from the
state lock): observers never run concurrently, emit must be called
without holding s.mu so observers can read Service state, and observers
must not re-enter an emitting method. emit is wired to mutating ops in
T3.3. Adds tests for ordered multi-observer delivery, empty-observer
no-op, and observer-reads-state-without-deadlock.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>