# Event Stream Contract ## Status Planned for the next milestone. The Phase 1 web skeleton does not implement the event stream yet. ## Intended Endpoint - `GET /api/events/stream` ## Transport Choice - start with Server-Sent Events rather than websockets - keep the stream read-only and cursor-based - let the frontend use events to invalidate or refresh TanStack Query caches ## Query Parameters - `after_event_id`: optional monotonic cursor - `run_id`: optional run filter for run-scoped pages - `thread_id`: optional thread filter for timeline views ## Event Envelope Each SSE message should carry one JSON object with this shape: ```json { "event_id": 42, "event_type": "task_blocked", "run_id": "run_web_001", "task_id": "T1", "thread_id": "thr_123", "summary": "Need the API shape", "payload": { "source": "orch" }, "created_at": "2026-03-20T09:30:00Z" } ``` ## Expected First Event Types - `task_added` - `task_ready` - `task_dispatched` - `task_running` - `task_blocked` - `task_verifying` - `task_answered` - `task_verification_recorded` - `task_done` - `task_failed` - `thread_claim` - `thread_update` - `thread_reply` - `thread_result` - `council_tallied` - `council_report_persisted` ## Cursor Rules - `event_id` is the only resume cursor - the server should emit events in ascending `event_id` order - reconnect clients should pass the last processed `event_id` - the server should not depend on in-memory subscriptions for correctness ## Frontend Usage - run detail pages should subscribe with `run_id` - thread views may subscribe with `thread_id` - the client should prefer refetch/invalidate behavior over complex local reducers in the first realtime slice ## Backend Notes - source data should come from the existing shared `events` table - `orchd` should treat the stream as a projection over persisted events, not as a separate ephemeral bus - if filtering becomes expensive later, move filtering logic into a dedicated query layer without changing the wire contract