WHY: Feature 18 makes the integration a client of the NestQuest API
service — every database write must go through the API. The
complete_quest service still wrote the local database and fired the
completed event itself, which would double-fire once the SSE
subscription re-fires the API's transition (and stops being true the
moment the local database is dropped). The service now proxies to the
entry's ONE shared API client (complete_instance: the panel complete
route) and keeps only the old immediate-update behaviour: a successful
call refreshes the coordinator so the sensors reflect the completion
at once; the bus event comes solely from the SSE stream. Typed API
failures (and an unconfigured client, and a non-panel actor) surface
as HomeAssistantError with token-free messages instead of being
swallowed; the payload contract (instance_id, actor=panel,
actor_child_id) is unchanged for the panel cards.
Tests: new tests/test_complete_quest_proxy.py asserts the call reaches
the shared client with the right arguments, that no local write or
local event happens on the API path, that a non-2xx/transport failure
raises a service error chained to the typed error (never containing
the token), and that the unconfigured stub fails clearly. Existing
tests that drove complete_quest through the local database move to the
API path via the harness stand-in client (which performs the same core
completion and publishes the same SSE frames the API route does) and
assert their bus events after the frames are delivered through the
real SSE subscription; the shim-level tests drive the events shim
directly, the way its remaining callers invoke it.
Task 47c7c90e (complete_quest proxies to the API, Feature 18).
The completed event's was_on_time travels on the CompletionResult
(decided with the write) instead of re-reading latest state a racing
reversal could replace; unload clears coordinator listeners before
the database close.
complete_quest and uncomplete_quest force a coordinator refresh when
the call appended, so entity states land the moment the service call
returns; scheduled internals are unaffected.
complete/uncomplete return a CompletionResult str subclass carrying
appended (decided with the write under the lock), so duplicate
concurrent calls fire one transition event; day-complete only
evaluates for an instance due today.
quest_completed and quest_uncompleted fire on actual transitions with
documented payloads; child_day_complete fires when a completion clears
the child's day; quest_missed is defined as Feature 11's contract.
Payloads documented in the README for automation authors.
permission_gate wraps all registered services at registration time:
admin-only policy reads call.context.user_id against admin_allowlist
is_admin (fail closed on no context); complete_quest stays open.