b1fbadde3c
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).
286 lines
18 KiB
Markdown
286 lines
18 KiB
Markdown
# 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.
|