af07a2329a
v1 coverage-audit: the last in-scope client I/O points. The audit now CONVERGES — REST 17/40 covered with zero in-scope gaps (23 excluded-by- design), SSE 11/11, Bifrost planes 8/8. - sessions.py: list_character_models / create_character / get_character_state / delete_character (#161, character.read/write) + set_persona_state (POST /sessions/{id}/persona_state — freeform body, unpinned in the frozen surface). 200/201 -> dict (or None on 204), off-status -> SessionApiFailed. - cli.py: two one-shot probes (mirror --whoami): --characters (CRUD lifecycle report) + --set-persona-pad "p,a,d" (requires --session). New ParsedArgs.characters/set_persona_pad + probe mutual-exclusion. - Contract #2 amended (5 FNs) + validated. TDD: 7 wrapper + 5 cli tests. Suite 573 green; touched code ruff-clean. - Char read side live-proven (GET /models/available-for-characters -> 200). Coverage-map: convergence frontier CLOSED — scope-A "done" (every frozen I/O point classified) is met; ratatoskr cuts v1 when Worldtree tags 1.0.
263 lines
16 KiB
Markdown
263 lines
16 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.2.0** (`Worldtree/docs/conversation-api-openapi.json`, sha `dbdf4e24…`) — **40 path×method groups** | **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) |
|
||
|
||
> **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.2.0, path groups) | 40 | 17 | 0 | 23 |
|
||
| 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.2.0)
|
||
|
||
### Covered — client path (ratatoskr's core identity)
|
||
|
||
| Endpoint | Status | Where consumed | Note |
|
||
|---|---|---|---|
|
||
| `POST /sessions` | ✅ | `sessions.py:307` → `cli.py:482`,`tui.py:1508`,`web/server.py:155` | + `end_user_id`, `bifrost` binding; 404→AgentNotFound, 502→BifrostHandshakeFailed |
|
||
| `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}/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:428` `get_capabilities` → `cli.py` `--whoami` | Echo ephemeral-template discovery |
|
||
| `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 (zero remaining, 2026-07-01)
|
||
|
||
**Every in-scope REST I/O point is now covered.** The frontier that opened this
|
||
audit (the design-brief §5 observability panes + the presenter-wiring sub-gaps +
|
||
the Tier-2 tail) is 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`).
|
||
|
||
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}/messages` (history) | 🚫 | §6: single-session live transcript, no history fetch |
|
||
| `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
|
||
|
||
**Every in-scope I/O point is covered.** The frontier is empty: REST 17/40 ✅
|
||
with **zero in-scope gaps** (the other 23 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.
|