Implement ratatoskr.sse_client — SSE consumer, reconnect, cancel #1

Closed
opened 2026-05-20 20:56:35 -07:00 by vh · 1 comment
Owner

Problem

Ratatoskr needs a Python SSE consumer module for the Worldtree Conversation API. This module is the integration seam between Ratatoskr (separate-repo, separate-team, spec-only-consumer) and Worldtree — every higher feature (Textual TUI, --send non-interactive mode, two-stage Ctrl-C, scripted CI probes) lands on top of it.

The Worldtree spec emits SSE events with a composite {turn_id}:{seq} id: wire field (spec INV-014) that is load-bearing for:

  • Server-side turn cancellation (POST /sessions/{id}/turns/{turn_id}/cancelturn_id is parsed from the SSE wire id:).
  • In-process reconnect via the Last-Event-ID HTTP header.
  • Stream correlation (every event of a turn shares the same turn_id prefix).

Hand-rolled data:-only SSE parsing (the skaldsong pattern) silently drops the id: line and breaks both flows invisibly. docs/design-brief.md §3 names this as the foot-gun this module must close.

Solution

Implement src/ratatoskr/sse_client.py per the contract at docs/contracts/issues/1.contract.md. The contract specifies four functions and the typed Event envelope:

  • stream_turn(client, session_id, content) -> AsyncIterator[Event] — POST a user message and consume the SSE response stream.
  • reconnect_turn(client, session_id, last_event_id) -> AsyncIterator[Event] — re-attach to an in-flight turn via Last-Event-ID.
  • cancel_turn(client, session_id, turn_id, *, persist_partial=False) -> CancelResult — server-side cancel.
  • _parse_sse_id(raw) -> SseId — strict parser for the composite wire id: field.

Hard invariants (per contract):

  • INV-002: every yielded Event carries a parsed SseId(turn_id, seq) — impossible to silently drop the id: line.
  • INV-003: a turn's turn_id is stable across events; a mid-stream flip raises TurnIdFlip.
  • INV-005: no core.* / worldtree.* imports (boundary already enforced by tests/test_no_worldtree_imports.py).

TDD via tracer tests per function block. Recommended vertical slice: _parse_sse_idstream_turnreconnect_turncancel_turn. Each function's first listed test (tagged [..,tracer]) is the RED test for that function; remaining tests follow one at a time per the tdd skill.

Benefits

  • Closes the foot-gun docs/design-brief.md §3 names (silent id: drop).
  • Makes Ratatoskr the reference Python SSE-resume implementation other Corviduo consumers can crib from (per design-brief §3).
  • Shared API-consumption code for both presenters (Textual TUI + --send stdout) — neither forks the SSE parsing.
  • Two-stage Ctrl-C UX (design-brief §8c) becomes implementable once cancel_turn is GREEN.

Acceptance

  • All tests in the contract's TESTS sections pass (uv run pytest tests/).
  • Boundary smoke test tests/test_no_worldtree_imports.py continues to pass.
  • Recorded-SSE snapshot fixtures replay cleanly (see tests/snapshots/README.md); fixture recording can land in a follow-up issue.
  • Spec pin (docs/SPEC-PIN.md) unchanged; this module is built against Worldtree v0.19.0 / repo SHA 55101e909abcd2219833266b6f905c5bc956e0f0.

Dependencies

None. This is the first module in the dependency graph — every other module imports Event and/or invokes the three entry points.

## Problem Ratatoskr needs a Python SSE consumer module for the Worldtree Conversation API. This module is the integration seam between Ratatoskr (separate-repo, separate-team, spec-only-consumer) and Worldtree — every higher feature (Textual TUI, `--send` non-interactive mode, two-stage Ctrl-C, scripted CI probes) lands on top of it. The Worldtree spec emits SSE events with a composite `{turn_id}:{seq}` `id:` wire field (spec INV-014) that is load-bearing for: - Server-side turn cancellation (`POST /sessions/{id}/turns/{turn_id}/cancel` — `turn_id` is parsed from the SSE wire `id:`). - In-process reconnect via the `Last-Event-ID` HTTP header. - Stream correlation (every event of a turn shares the same `turn_id` prefix). Hand-rolled `data:`-only SSE parsing (the skaldsong pattern) silently drops the `id:` line and breaks both flows invisibly. `docs/design-brief.md` §3 names this as the foot-gun this module must close. ## Solution Implement `src/ratatoskr/sse_client.py` per the contract at `docs/contracts/issues/1.contract.md`. The contract specifies four functions and the typed `Event` envelope: - `stream_turn(client, session_id, content) -> AsyncIterator[Event]` — POST a user message and consume the SSE response stream. - `reconnect_turn(client, session_id, last_event_id) -> AsyncIterator[Event]` — re-attach to an in-flight turn via `Last-Event-ID`. - `cancel_turn(client, session_id, turn_id, *, persist_partial=False) -> CancelResult` — server-side cancel. - `_parse_sse_id(raw) -> SseId` — strict parser for the composite wire `id:` field. Hard invariants (per contract): - **INV-002**: every yielded `Event` carries a parsed `SseId(turn_id, seq)` — impossible to silently drop the `id:` line. - **INV-003**: a turn's `turn_id` is stable across events; a mid-stream flip raises `TurnIdFlip`. - **INV-005**: no `core.*` / `worldtree.*` imports (boundary already enforced by `tests/test_no_worldtree_imports.py`). TDD via tracer tests per function block. Recommended vertical slice: `_parse_sse_id` → `stream_turn` → `reconnect_turn` → `cancel_turn`. Each function's first listed test (tagged `[..,tracer]`) is the RED test for that function; remaining tests follow one at a time per the `tdd` skill. ## Benefits - Closes the foot-gun `docs/design-brief.md` §3 names (silent `id:` drop). - Makes Ratatoskr the **reference Python SSE-resume implementation** other Corviduo consumers can crib from (per design-brief §3). - Shared API-consumption code for both presenters (Textual TUI + `--send` stdout) — neither forks the SSE parsing. - Two-stage Ctrl-C UX (design-brief §8c) becomes implementable once `cancel_turn` is GREEN. ## Acceptance - All tests in the contract's TESTS sections pass (`uv run pytest tests/`). - Boundary smoke test `tests/test_no_worldtree_imports.py` continues to pass. - Recorded-SSE snapshot fixtures replay cleanly (see `tests/snapshots/README.md`); fixture recording can land in a follow-up issue. - Spec pin (`docs/SPEC-PIN.md`) unchanged; this module is built against Worldtree v0.19.0 / repo SHA `55101e909abcd2219833266b6f905c5bc956e0f0`. ## Dependencies None. This is the first module in the dependency graph — every other module imports `Event` and/or invokes the three entry points.
vh added the enhancementtasksse-client labels 2026-05-20 20:56:35 -07:00
Author
Owner

Closed — shipped. ratatoskr.sse_client implemented via TDD (commit 02f2a04, Volva-reviewed in c17af18). Live against personal Worldtree since v0 milestone.

Closed — shipped. ratatoskr.sse_client implemented via TDD (commit 02f2a04, Volva-reviewed in c17af18). Live against personal Worldtree since v0 milestone.
vh closed this issue 2026-05-29 23:27:31 -07:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vh/ratatoskr#1