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

286 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.toml` → `canonical_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_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:198` `list_sessions` exists, **no caller**: the
startup session-picker (design-brief §4 v1) was never wired.
- `POST /sessions/{id}/messages` + `Last-Event-ID``sse_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.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_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.**DONE**`GET /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):**
5.**DONE**`GET /sessions/{id}/tools` (`v0.18.9`, owner-scoped tool inventory
in the TUI Tools pane).
6.**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.