Files
ratatoskr/docs/coverage-map.md
T
vh b1fbadde3c docs(coverage): track v1 coverage against worldtree-sdk's 41-op ratified surface
worldtree-sdk v1.0.0 (wtsdk-dev, althing 01KXVF24WQD2T5ZCS49KKFCCMH) ratifies the
same 41-op surface from the identical OpenAPI 2.3.0 (sha 36148179601453a0) this
ledger already tracks — record it as the external parity authority. Ratatoskr is
the parallel Python/httpx reference-consumer (no TS adoption); the forthcoming
worldtree-sdk Python spine is the future consumable, noted as a repin candidate.

Fold in v0.21.2: POST /sessions row now notes ephemeral-Echo config passthrough
(role not model, W-4 cross-validated by the SDK); GET /capabilities row notes the
--whoami allowed_roles/default_role fix + spec v1.1. REST count unchanged (19/41 —
ephemeral is a depth enhancement to an already-covered route).

No version bump (docs-only coverage-ledger update).
2026-07-18 13:44:04 -07:00

18 KiB
Raw Permalink Blame History

Ratatoskr v1 coverage map

The v1 convergence-target ledger. Ratatoskr has no self-defined feature roadmap: v1 = consume all of Worldtree's I/O points, reached when Worldtree hits 1.0 (operator, 2026-06-19; auto-memory project-ratatoskr-v1-derived-from-worldtree-io-coverage). This file is that coverage map — every Worldtree v1-frozen I/O point × ratatoskr's coverage status, so "are we at v1?" is a ledger lookup, not a judgement call.

First authored: 2026-06-30 (the v1 coverage-audit kickoff).


Frozen target

Worldtree is at v1.0.0b2 — approaching 1.0, with its wire surfaces now FROZEN (Worldtree docs/v1-schema-freeze-manifest.md, #326). The audit anchors against the frozen machine-readable artifacts, NOT the prose markdown:

Worldtree v1 surface Frozen anchor Ratatoskr role
Conversation REST API OpenAPI info.version 2.3.0 (Worldtree/docs/conversation-api-openapi.json, sha 36148179…) — 41 path×method groups (2.3.0 added POST /sessions/{id}/history, #347) client (debug TUI / web)
Conversation SSE events conversation-api-sse-events.schema.json (sha 9deeebf4…) — 11 discriminated event types client
Bifrost wire (consumer protocol) wire v0.6 STABLE/FROZEN (bifrost==1.0.0) — memory + affect planes provider (Worldtree dispatches into us)

External ratification — worldtree-sdk v1.0.0 (wtsdk-dev, 2026-07-18, althing thread 01KXVF24WQD2T5ZCS49KKFCCMH). The TypeScript v0 consumer SDK wires all 41 ops as typed methods from the same OpenAPI 2.3.0 (sha 36148179601453a0 — matches our vendored pin exactly), so it is the independent parity authority for the surface this ledger tracks. Ratatoskr does not adopt the SDK — we are the parallel Python/httpx reference-consumer of the identical surface (no TS/npm surface exists to pin it into). Cross-check corroboration: the SDK's W-4 (send config.role, response echoes model unremapped) matches what ratatoskr shipped in v0.21.2 for ephemeral-Echo create.

Future consumable — worldtree-sdk Python spine (wtsdk-dev, next milestone). The TS v1 surface is our parity checklist; the forthcoming Python spine (same behavioral-spec + fixture corpus, TS decisions kept Python-portable per their surface-design §6) is what ratatoskr would actually consume when it lands. wtsdk-dev slots ratatoskr as THE reference consumer whose coverage column drives that spine — a future repin candidate, not yet available.

Pin drift (finding P-1) — REMEDIATED 2026-06-30. Ratatoskr formerly vendored only the prose markdown (docs/conversation-api-spec.md), which is byte-identical to live Worldtree's but frozen at v0.35.16-era content (last WT edit 2026-05-31) and does not document the b2 surface (7 endpoints below, the 409/503 on messages-POST #331, the unified error envelope #328, the SSE schema). Remediated: the OpenAPI 2.2.0 + SSE-schema JSON are now vendored and pinned (.corviduo-canonicals.tomlcanonical_drift.py gate); the prose markdown is the tolerate_drift reference. Spec pin advanced to 5810a26 (v1.0.0b2). See § Pin remediation.

7 endpoints new in b2 OpenAPI, absent from our vendored markdown: /admin/keys/bulk, /admin/persona/archive, /admin/persona/erase, /admin/usage, /embed, /judgments, /me/usage.


Scorecard

Scope mandate: A (ledger-mandate), locked by operator 2026-06-30. v1 "done" = every frozen I/O point is classified (covered or excluded-with-rationale), zero unaccounted. NOT "feature-complete client." All scope-pending rows are now resolved (§ Surface 1, scope-resolution table).

Surface Points covered-live gap (in-scope) 🚫 excluded-by-design
REST (OpenAPI 2.3.0, path groups) 41 19 0 22
SSE events 11 11 0 0
Bifrost provider planes 8 verbs 8 0 (10 gated verbs deferred)

Legend. consumed in code AND live-proven against real Worldtree · a debug-observability I/O point we should cover but don't yet (the convergence frontier) · 🚫 deliberate non-goal per the design-brief negative clauses + the A mandate. Counts are at the path-group level; mixed-method groups are footnoted (e.g. /sessions POST is but its GET picker is an unwired sub-gap).


Surface 1 — Conversation REST API (OpenAPI 2.3.0)

Covered — client path (ratatoskr's core identity)

Endpoint Status Where consumed Note
POST /sessions sessions.py create_sessioncli.py,tui.py,web/server.py + end_user_id, bifrost binding; 404→AgentNotFound, 502→BifrostHandshakeFailed. v0.21.2 (#19): ephemeral-template (Echo) createconfig passthrough (--system-prompt), role not model (W-4), kind/config captured; 422 ephemeral_requires_config now reachable-and-handled. Depth enhancement to an already-covered route — count unchanged
POST /sessions/{id}/messages (turn stream, SSE) sse_client.py:484 stream_turn → cli/tui/web the primary surface; 409→AgentNotAvailable, 503→TurnLaunchUnavailable (b2 #331)
POST /sessions/{id}/history (authored-history-write, #347) sessions.py:583 write_authored_historycli.py:758 --seed-first-message v1: author=assistant, effects=none, per-session idempotency; 404→AuthoredHistoryUnavailable (hide-existence: feature-absent, never probe); 409/422 mapped. LIVE-PROVEN 2026-07-06 on personal :8081 (grant applied via a rule-based Heimdall allow, worldtree-dev): create mimir session → seed → 201 (seq=0, phase=seeded, turn_id=1798) → GET /messages reads it back as a plain role=assistant turn (model-invisible provenance confirmed). Hide-404 for ungranted is unit+probe covered
GET /sessions/{id}/messages (history) sessions.py:635 get_session_messagescli.py:758 --seed-first-message read-back un-deferred as the #347 seed read-back — confirms model-invisible provenance (a seed reads back as a normal role=assistant turn)
POST /sessions/{id}/turns/{turn_id}/cancel sse_client.py:581 → cli/tui/web two-stage Ctrl-C; 404/409 mapped
GET /agents sessions.py:341tui.py:1472,web/server.py:100 Tier-1 roster; merged with local index
GET /agents/{id}/persona_state sessions.py:384tui.py:1132,web/server.py:386 persona hydrate; 404/403 mapped
POST /agents/define tier3.py:175_run_define Tier-3 create
PATCH /agents/{id} tier3.py:219_run_patch Tier-3 mutate (system_prompt/model)
DELETE /agents/{id} tier3.py:242_run_delete Tier-3 hard-delete
GET /me sessions.py:411 get_mecli.py --whoami identity/whoami probe; 401→SessionApiFailed
GET /capabilities sessions.py get_capabilitiescli.py --whoami Echo ephemeral-template discovery. v0.21.2: --whoami renderer reads allowed_roles/default_role (was the dead allowed_models/default_model) + tolerates malformed caps; matches conversation-api-spec v1.1 (b4a278c)
GET /sessions/{id}/tools sessions.py:411 get_session_toolstui.py _hydrate_session_tools owner-scoped tool inventory in the TUI Tools pane (#183)
GET /admin/sessions/{id}/bifrost sessions.py:428 get_session_bifrosttui.py _hydrate_bifrost_state admin-scoped BifrostState pane (#176); admin key (RATATOSKR_ADMIN_API_KEY); live-auth-proven
GET /admin/events (SSE) sse_client.py stream_admin_eventstui.py _stream_admin_events admin lifecycle SSE stream (#11), session-filtered AdminEvents pane; admin key; live-auth-proven
GET /models/available-for-characters sessions.py list_character_modelscli.py --characters character-capable model profiles (#161)
POST /characters sessions.py create_charactercli.py --characters create transient character (#161)
GET /characters/{id}/state sessions.py get_character_statecli.py --characters live character PAD/emotions (#161)
DELETE /characters/{id} sessions.py delete_charactercli.py --characters remove transient character (#161)
POST /sessions/{id}/persona_state sessions.py set_persona_statecli.py --set-persona-pad persona-state write / affect injection (freeform body — unpinned in the frozen surface)

Sub-gaps inside path groups (the method we use is live; a sibling method on the same path is an unwired frontier item — see frontier Tier 1):

  • GET /sessionssessions.py:198 list_sessions exists, no caller: the startup session-picker (design-brief §4 v1) was never wired.
  • POST /sessions/{id}/messages + Last-Event-IDsse_client.py:524 reconnect_turn exists, no caller: the reference SSE-resume impl (design-brief §8d) was never wired.
  • GET /agents/{id} — consumer-agent lookup (GET /agents/<owner>:<name> with the owner key) is manual-curl-only, not in code.

In-scope gaps — CONVERGED (re-closed 2026-07-06 after the #347 re-open)

Every in-scope REST I/O point is covered. The audit first converged 2026-07-01; Worldtree's #347 (authored-history-write, OpenAPI 2.3.0) then added one new in-scope path-group, re-opening the audit with a single gap — now closed (v0.19.6). The original frontier (design-brief §5 observability panes + presenter-wiring sub-gaps + Tier-2 tail) remains fully closed:

  • Session picker + SSE-resume — wired (v0.18.5.7).
  • Persona · Tools · BifrostState · AdminEvents panes — all built + live (v0.18.xv0.19.0).
  • Transient-characters CRUD + persona-state write — consumed via --characters / --set-persona-pad (v0.19.1).
  • Authored-history-write (#347) + messages read-back — write_authored_history + get_session_messages via --seed-first-message (v0.19.6; live-proof pending the session.history.write grant).

The only remaining not-consumed in-scope method is GET /agents/{id} (consumer- agent lookup, manual-curl-only) — a sub-method on an already- path group, not a path-group gap. Everything else is covered or excluded-by-design below.

Excluded by design — the design-brief negative clauses

Endpoint(s) Status Rationale (design-brief / memory)
PATCH /sessions/{id} · DELETE /sessions/{id} 🚫 §4: rename/delete happen outside the tool (sessions_cli.py)
GET /sessions/{id} 🚫 session detail — identity is footer-visible, no detail view
GET /sessions/{id}/tool-events 🚫 §5: tool calls observed inline from SSE tool_start/tool_result; persisted-events endpoint is opt-in only
GET /admin/sessions/{id}/tools 🚫 covered-by-alternative — the owner-scoped GET /sessions/{id}/tools () serves the Tools inventory; this admin variant is only for cross-user operator debug, out of the single-session focus (§6)
GET/POST /admin/keys · DELETE/POST /admin/keys/{id} · POST /admin/keys/{id}/rotate · DELETE/POST /admin/keys/bulk · POST /admin/keys/bulk/rotate 🚫 §6: NOT a Worldtree-admin tool (key mgmt)
POST /admin/sessions/{id}/retire 🚫 admin session mutation
POST /admin/persona/{archive,erase} 🚫 admin persona GDPR ops (new in b2)
POST /admin/users/{id}/tier 🚫 admin user mgmt
GET /me/usage · GET /admin/usage 🚫 usage metering — not turn-flow observability (new in b2)
GET /healthz · GET /readyz 🚫 liveness probes — low debug value (could become a connect preflight; park)
GET /search 🚫 §5: consumer-product feature, not turn-flow (was "defer to v2")
GET/POST /uploads · DELETE/GET /uploads/{id} 🚫 §6: no uploads — consumer-product feature
GET /pending · GET /sessions/{id}/pending 🚫 §5: poll-only, no turn-flow signal (was "optional, skip")
POST /embed 🚫 embedding utility — no turn flows through it
POST /judgments 🚫 LLM-as-judge A/B eval (response_a/response_b/rubric) — standalone eval utility, not turn-flow

Scope-resolution record (the 11 rows, resolved under A)

The 2026-05-20 design-brief deferred several surfaces; the 2026-06-19 reframe ("v1 = full I/O coverage") put them back in tension. Resolved 2026-06-30 under mandate A (debug-observability identity intact; classify, don't build-all):

Endpoint(s) Resolution
GET /search · uploads (×2) · pending (×2) · POST /embed · POST /judgments 🚫 excluded — consumer-product / eval / poll utilities, not turn-flow observability
characters (×4) · POST /sessions/{id}/persona_state in-scope (frontier Tier 2) — session-routing + affect-injection debug paths

Nothing remains . The -vs-🚫 line follows the debug-observability test: does a turn flow through it / is it a layer worth watching live?


Surface 2 — SSE events (11/11 )

Every frozen SSE event type is parsed in sse_client.py:_envelope_for_type (342-411) and rendered by all three presenters (cli/tui/web). Full coverage.

text · worker_phase · thinking · text_boundary · tool_start · tool_result · done · error · cancelled · awaiting_llm_first_token · affect_update

Caveat (not a gap): affect_update is wire-verified to emit zero events for consumer (Tier-3) agents — the persona-strip SSE path never populates for them (memory 2026-06-18). The handler is correct; the upstream emitter is silent. PAD for consumer agents is surfaced via our own provider read route (GET /affect/state/{id}, #18 D2), not this event.


Surface 3 — Bifrost provider planes (8/8 , live-proven)

Ratatoskr implements the provider side; Worldtree dispatches into it. Live-proven end-to-end through real WT turns (#17/#18 smokes; combined :8392 WT-driven smoke 2026-06-20).

Memory plane — covers the entire bifrost.memory.MemoryDataStore protocol (required: describe_store, get, get_many, search, upsert_many) plus delete_many:

Verb Where
describe_store memory_store.py:140 advertises caps (sync)
search memory_store.py:224 vector recall; scope_all AND / scope_any OR
get / get_many memory_store.py:293 / :305 point reads
upsert_many memory_store.py:150 idempotent batch write; optimistic lock
delete_many memory_store.py:314 transactional delete

Affect plane — covers bifrost…InMemoryAffectStore (emit, fetch):

Verb Where
emit affect_store.py:47 conduit-opaque snapshot upsert (LWW)
fetch affect_store.py:116 {found, snapshot}; mandatory since bifrost 0.10.0 strong-or-absent gate

Plus the non-wire PAD read route GET /affect/state/{agent_id} (affect_store.py:189) and the combined :8392 endpoint advertising both caps by store-presence (combined.py:46).

Deferred-gated (advertised-unsupported, correctly out-of-scope for the basic plane): scan, get_edges_for, upsert_edges, mark_invalid, mark_superseded, patch_many, commit_checkpoint, lease_job, read_checkpoint, health. These live only in the bifrost reference extended store, not the MemoryDataStore protocol; deferred per the #294 re-scope (memory 2026-06-15). Re-evaluate only if Worldtree's Tier-3 path starts exercising them.


Convergence frontier (the v1 to-do) — CLOSED 2026-07-01, re-closed 2026-07-06 (#347)

Every in-scope I/O point is covered. The frontier is empty: REST 19/41 with zero in-scope gaps (the other 22 REST path-groups are excluded-by-design), SSE 11/11, Bifrost provider planes 8/8. v1 convergence (per scope A: "every frozen I/O point classified, zero unaccounted") is met — ratatoskr cuts v1 when Worldtree tags 1.0. The arc, for the record:

Tier 1 — debug-observability core:

  1. DONE — Session picker (v0.18.7) + SSE-resume (v0.18.5/.6).
  2. DONEGET /capabilities + GET /me (v0.18.8, --whoami).
  3. DONE — BifrostState pane (v0.18.10, GET /admin/sessions/{id}/bifrost, admin-key; live-auth-proven). The Tools half was already covered by the owner-scoped GET /sessions/{id}/tools (item 5).
  4. DONE — AdminEvents pane (v0.18.11, GET /admin/events SSE, session-filtered; admin-key; live-auth-proven). #11's blocker was already satisfied (admin key carries admin.events.read). Tier 1 complete — the admin/debug-observability core (Persona · Tools · BifrostState · AdminEvents) is fully built.

Tier 2 — rounds out coverage (all that remains):

  1. DONEGET /sessions/{id}/tools (v0.18.9, owner-scoped tool inventory in the TUI Tools pane).
  2. DONE — Transient-characters CRUD (4 endpoints) + POST /sessions/{id}/persona_state (v0.19.1, --characters + --set-persona-pad one-shot probes). The last in-scope client I/O points.

Pin remediation (finding P-1) — DONE 2026-06-30

Re-pinned to the frozen machine-readable artifacts (the chosen option): conversation-api-openapi.json (2.2.0) + conversation-api-sse-events.schema.json are vendored under docs/ and pinned in .corviduo-canonicals.toml (worldtree-conversation-api-openapi-v2, -sse-events-v1), drift-gated by canonical_drift.py. The prose markdown stays as a tolerate_drift reference (-spec-v1). pyproject.toml spec pin advanced f1b59f8 → 5810a26 (v1.0.0b2); docs/SPEC-PIN.md records the bump. This map now audits a frozen, diffable target — re-running the audit is a canonical_drift.py check away.


Decisions

  1. Scope mandate — RESOLVED 2026-06-30: A (ledger-mandate). v1 "done" = every frozen I/O point classified, zero unaccounted; NOT feature-complete client. The -vs-🚫 line for the 11 formerly-pending rows is locked above (§ Scope-resolution record).
  2. Pin remediation (P-1) — RESOLVED 2026-06-30 (re-pin to machine-readable). Vendored conversation-api-openapi.json (2.2.0) + conversation-api-sse-events.schema.json as the authoritative drift gates (pinned in .corviduo-canonicals.toml, canonical_drift.py green); prose markdown kept as tolerate_drift reference. Spec pin advanced f1b59f8 (v0.35.16) → 5810a26 (v1.0.0b2). This map now audits against a frozen, diffable target.