docs(contract): re-canonicalize #1 SSE event vocab against code
Add awaiting_llm_first_token (#201) and affect_update (#204) to issue #1's Event union and TESTS via a dated amendment. Both events are parsed by _envelope_for_type and covered in tests/test_sse_client.py, but issue #1's Output union + full_event_vocab test were frozen at the v0.19.0 baseline's 8-event set — contract-vs-code drift surfaced during the Worldtree #371 SDK parity-matrix pass. Documentation-only: no code change, no version bump.
This commit is contained in:
@@ -345,3 +345,48 @@ TESTS:
|
||||
buffer_expired_propagates [error]: attempt 1 drops after 42:1; reconnect returns 412 → ResumeBufferExpired propagates (not retried as a transient drop).
|
||||
zero_budget_no_resume [adversarial]: max_reconnects=0; attempt 1 drops after 42:1 → SseConnectionDropped propagates immediately (no reconnect attempted).
|
||||
```
|
||||
|
||||
## Amendment 2026-07-18 — event-vocab catch-up: `awaiting_llm_first_token` + `affect_update` (contract-vs-code drift)
|
||||
|
||||
The §Data flow Output union and the `full_event_vocab` TEST (FN `stream_turn`)
|
||||
were frozen at the v0.19.0 baseline's 9-variant `Event` union and 8-event
|
||||
happy-path vocab. Two SSE events were added to the wire AFTER that baseline and
|
||||
are parsed by `_envelope_for_type` today, but their TESTS landed in the
|
||||
*consuming* contracts (cli #3, tui #4, affect web-proxy #18) and in
|
||||
`tests/test_sse_client.py` — never in this contract, so #1 under-described its own
|
||||
module. Surfaced during the Worldtree #371 SDK parity-matrix pass (worldtree-dev
|
||||
confirmed the gap: `full_event_vocab` stops at the 8-event set). This amendment
|
||||
re-canonicalizes #1 against the code. It is **documentation-only** — the code and
|
||||
its `test_sse_client.py` coverage already exist and pass; no code change, no
|
||||
version bump.
|
||||
|
||||
**Corrected `Event` union** (supersedes the §Data flow Output list — 9 → 11):
|
||||
|
||||
WorkerPhase | Thinking | Text | TextBoundary | ToolStart | ToolResult
|
||||
| Done | Error | Cancelled | AwaitingLlmFirstToken | AffectUpdate
|
||||
|
||||
The two additions (both **non-terminal** — they do NOT satisfy INV-001's terminal
|
||||
requirement; the stream still ends at exactly one `Done`/`Error`/`Cancelled`):
|
||||
|
||||
- `AwaitingLlmFirstToken(sse_id, turn_id, elapsed_ms_since_building_prompt: float)`
|
||||
— SSE `awaiting_llm_first_token` (Worldtree #201, v0.29.0). Heartbeat during the
|
||||
BuildingPrompt→CallingLLM gap (default 5 s); `elapsed_ms_since_building_prompt`
|
||||
is server-authoritative monotonic. First-gap only (INV-201-5) — tool round-trips
|
||||
do not re-fire.
|
||||
- `AffectUpdate(sse_id, status: str, turn_id, snapshot: dict | None)` — SSE
|
||||
`affect_update` (Worldtree #204, v0.28.0). `status="current"` at turn start
|
||||
carries the full persona snapshot; `status="scheduled"` after post-turn appraisal
|
||||
kickoff omits it (`snapshot is None`). Suppressed for persona-disabled agents,
|
||||
Tier-3 consumer-defined agents, and ephemeral sessions.
|
||||
|
||||
Unknown event `type` still raises `ValueError` in `_envelope_for_type` (the
|
||||
base-contract behavior). NOTE for the eventual repin: the #371 SDK pins a
|
||||
deliberate divergence here (D-1 — skip-with-diagnostics-hook instead of raising),
|
||||
which Ratatoskr adopts only when it repins onto the SDK, not before.
|
||||
|
||||
TESTS (already present in `tests/test_sse_client.py` — `TestAwaitingLlmFirstToken`
|
||||
+ `TestAffectUpdate`; mirrored here for contract completeness):
|
||||
single_heartbeat_parsed [tracer]: mock emits one `awaiting_llm_first_token` (turn_id, elapsed_ms_since_building_prompt=5012.3) → consumer yields AwaitingLlmFirstToken carrying both fields + parsed sse_id
|
||||
heartbeat_sequence_monotonic [trace]: mock emits a heartbeat sequence → each yields AwaitingLlmFirstToken; elapsed_ms_since_building_prompt is monotonically non-decreasing across the sequence
|
||||
current_status_parsed_with_snapshot [tracer]: mock emits `affect_update` status="current" with a full snapshot dict → consumer yields AffectUpdate(status="current", snapshot=<verbatim dict>)
|
||||
scheduled_status_parsed_no_snapshot [trace]: mock emits `affect_update` status="scheduled" (no snapshot) → consumer yields AffectUpdate(status="scheduled", snapshot is None)
|
||||
|
||||
Reference in New Issue
Block a user