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).
18 KiB
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 (sha36148179601453a0— 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 (sendconfig.role, response echoesmodelunremapped) 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.toml→canonical_drift.pygate); the prose markdown is thetolerate_driftreference. 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_session → cli.py,tui.py,web/server.py |
+ end_user_id, bifrost binding; 404→AgentNotFound, 502→BifrostHandshakeFailed. v0.21.2 (#19): ephemeral-template (Echo) create — config 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_history → cli.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_messages → cli.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:341 → tui.py:1472,web/server.py:100 |
Tier-1 roster; merged with local index |
GET /agents/{id}/persona_state |
✅ | sessions.py:384 → tui.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_me → cli.py --whoami |
identity/whoami probe; 401→SessionApiFailed |
GET /capabilities |
✅ | sessions.py get_capabilities → cli.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_tools → tui.py _hydrate_session_tools |
owner-scoped tool inventory in the TUI Tools pane (#183) |
GET /admin/sessions/{id}/bifrost |
✅ | sessions.py:428 get_session_bifrost → tui.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_events → tui.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_models → cli.py --characters |
character-capable model profiles (#161) |
POST /characters |
✅ | sessions.py create_character → cli.py --characters |
create transient character (#161) |
GET /characters/{id}/state |
✅ | sessions.py get_character_state → cli.py --characters |
live character PAD/emotions (#161) |
DELETE /characters/{id} |
✅ | sessions.py delete_character → cli.py --characters |
remove transient character (#161) |
POST /sessions/{id}/persona_state |
✅ | sessions.py set_persona_state → cli.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 /sessions—sessions.py:198list_sessionsexists, no caller: the startup session-picker (design-brief §4 v1) was never wired.POST /sessions/{id}/messages+Last-Event-ID—sse_client.py:524reconnect_turnexists, 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.x–v0.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_messagesvia--seed-first-message(v0.19.6; live-proof pending thesession.history.writegrant).
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_updateis 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:
- ✅ DONE — Session picker (
v0.18.7) + SSE-resume (v0.18.5/.6). - ✅ DONE —
GET /capabilities+GET /me(v0.18.8,--whoami). - ✅ 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-scopedGET /sessions/{id}/tools(item 5). - ✅ DONE — AdminEvents pane (
v0.18.11,GET /admin/eventsSSE, session-filtered; admin-key; live-auth-proven). #11's blocker was already satisfied (admin key carriesadmin.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):
- ✅ DONE —
GET /sessions/{id}/tools(v0.18.9, owner-scoped tool inventory in the TUI Tools pane). - ✅ DONE — Transient-characters CRUD (4 endpoints) +
POST /sessions/{id}/persona_state(v0.19.1,--characters+--set-persona-padone-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
- 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).
- 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.jsonas the authoritative drift gates (pinned in.corviduo-canonicals.toml,canonical_drift.pygreen); prose markdown kept astolerate_driftreference. Spec pin advanced f1b59f8 (v0.35.16) → 5810a26 (v1.0.0b2). This map now audits against a frozen, diffable target.