docs/http-api.md lists device_availability as an SSE event type on GET /v0/events, and state_handlers.cpp serializes DeviceAvailabilityEvent into event: device_availability. Nothing in the runtime constructs one:
grep -rn "DeviceAvailabilityEvent" core/ | grep -v "event_types.hpp\|event_emitter.cpp\|state_handlers.cpp"
returns nothing on main. A client subscribing for device availability gets a documented event that never fires.
Where the edges are
StateCache now has a per-device reachability sink (#299) that fires on the provider_available edge in poll_device (loss) and update_device_state (recovery). That is the natural place to emit, but it is not sufficient on its own: mark_provider_devices_unavailable — the supervised-provider-restart path — sets provider_available = false directly, bypassing the sink. Emitting only at the sink would produce an available: true event with no preceding available: false after every provider restart. #299 deliberately left the event unwired rather than ship that.
What a correct wiring needs
- Emit
available: false from both loss paths: the per-device read failure in poll_device and the per-provider mark_provider_devices_unavailable.
- Emit
available: true from update_device_state on the recovery edge only (provider_available initializes to true, so first-poll does not fire).
- Decide whether emission belongs in
StateCache directly (it already owns event_emitter_ and emits StateUpdateEvent / QualityChangeEvent from the same place) or via the sink. Direct is simpler and reaches both loss paths; the sink is for the loss latch, which must not engage on provider restart.
- A test that a provider restart produces a false/true pair, not a lone true.
Not in scope
The device-loss latch (#299) classifies losses by status code and does not latch on provider restart. The availability event should mirror provider_available exactly and carry no classification.
docs/http-api.mdlistsdevice_availabilityas an SSE event type onGET /v0/events, andstate_handlers.cppserializesDeviceAvailabilityEventintoevent: device_availability. Nothing in the runtime constructs one:returns nothing on
main. A client subscribing for device availability gets a documented event that never fires.Where the edges are
StateCachenow has a per-device reachability sink (#299) that fires on theprovider_availableedge inpoll_device(loss) andupdate_device_state(recovery). That is the natural place to emit, but it is not sufficient on its own:mark_provider_devices_unavailable— the supervised-provider-restart path — setsprovider_available = falsedirectly, bypassing the sink. Emitting only at the sink would produce anavailable: trueevent with no precedingavailable: falseafter every provider restart. #299 deliberately left the event unwired rather than ship that.What a correct wiring needs
available: falsefrom both loss paths: the per-device read failure inpoll_deviceand the per-providermark_provider_devices_unavailable.available: truefromupdate_device_stateon the recovery edge only (provider_availableinitializes totrue, so first-poll does not fire).StateCachedirectly (it already ownsevent_emitter_and emitsStateUpdateEvent/QualityChangeEventfrom the same place) or via the sink. Direct is simpler and reaches both loss paths; the sink is for the loss latch, which must not engage on provider restart.Not in scope
The device-loss latch (#299) classifies losses by status code and does not latch on provider restart. The availability event should mirror
provider_availableexactly and carry no classification.