SSE endpoint and the quest-completed transition #154

Member

Maestro task: 4d7bae00-c930-4124-8d8e-36db7a96a0a7 — "SSE endpoint and the quest-completed transition"

Done-condition: The API exposes an SSE endpoint (GET /api/v1/panel/events) that streams transitions, backed by ONE in-process publisher (api/events.py) that route handlers call. Completing an instance through the panel complete route publishes a quest-completed transition carrying the documented payload fields (child_id, child_name, instance_id, quest_title, window, due_date, due_time, occurred_at, was_on_time). A test subscribes to the SSE stream, completes an instance, and asserts the emitted event type and its payload fields.

Changes:

  • api/events.py (NEW): single in-process TransitionPublisher (per-app, app.state.publisher, installed in the lifespan); bounded per-subscriber asyncio queues, non-blocking publish with drop-on-full, clean teardown on disconnect.
  • api/routes_panel.py: GET /api/v1/panel/events returns text/event-stream and frames each event as event: <type>\ndata: <json>\n\n; the complete route publishes exactly once, only on result.appended, with the payload built by nestquest_core.events.build_quest_completed_event (never hand-built).
  • api/nestquest_core.py: exposes the core events module (core_events) following the existing importlib pattern.
  • tests/test_api_sse.py (NEW): streams from a real uvicorn app on an ephemeral port (httpx's ASGITransport buffers endless SSE bodies), asserts the completed event type and all documented payload fields, and that a no-op re-complete publishes nothing; all reads timeout-bounded.

Tests (TZ=UTC):

  • TZ=UTC uv run pytest -q → 1608 passed in 7.53s (baseline 1605 + 3 new)
  • TZ=UTC uv run pytest -q tests/test_api_sse.py → 3 passed in 1.10s
Maestro task: 4d7bae00-c930-4124-8d8e-36db7a96a0a7 — "SSE endpoint and the quest-completed transition" Done-condition: The API exposes an SSE endpoint (GET /api/v1/panel/events) that streams transitions, backed by ONE in-process publisher (api/events.py) that route handlers call. Completing an instance through the panel complete route publishes a quest-completed transition carrying the documented payload fields (child_id, child_name, instance_id, quest_title, window, due_date, due_time, occurred_at, was_on_time). A test subscribes to the SSE stream, completes an instance, and asserts the emitted event type and its payload fields. Changes: - api/events.py (NEW): single in-process TransitionPublisher (per-app, app.state.publisher, installed in the lifespan); bounded per-subscriber asyncio queues, non-blocking publish with drop-on-full, clean teardown on disconnect. - api/routes_panel.py: GET /api/v1/panel/events returns text/event-stream and frames each event as `event: <type>\ndata: <json>\n\n`; the complete route publishes exactly once, only on result.appended, with the payload built by nestquest_core.events.build_quest_completed_event (never hand-built). - api/nestquest_core.py: exposes the core events module (core_events) following the existing importlib pattern. - tests/test_api_sse.py (NEW): streams from a real uvicorn app on an ephemeral port (httpx's ASGITransport buffers endless SSE bodies), asserts the completed event type and all documented payload fields, and that a no-op re-complete publishes nothing; all reads timeout-bounded. Tests (TZ=UTC): - TZ=UTC uv run pytest -q → 1608 passed in 7.53s (baseline 1605 + 3 new) - TZ=UTC uv run pytest -q tests/test_api_sse.py → 3 passed in 1.10s
dev_agent added 1 commit 2026-09-22 23:00:55 -04:00
The panel needs live transitions, so add ONE in-process publisher
(api/events.py, installed on app.state.publisher in the lifespan) and
a GET /api/v1/panel/events SSE route on the panel router — it inherits
the single require_panel_token check. The complete route now builds
the event through the core builder and publishes it only when the
core's .appended flag says an actual transition happened; a no-op
re-complete publishes nothing. Tests stream from a real uvicorn
instance on an ephemeral port because httpx's ASGITransport buffers
endless SSE bodies.
review_agent left a comment
Member

Independent Claude Sonnet 5 review completed for this exact commit.

Independent Claude Sonnet 5 review completed for this exact commit.
dev_agent merged commit a07ce1b2f2 into feature/cd03735b-2498-4b34-b84c-c1cb2b271ba7-nestquest-api-service 2026-09-22 23:08:03 -04:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Maniacs_Mansion/NestQuest#154