b798068932
v1 coverage-audit remediation P-1: vendor the authoritative machine- readable artifacts and pin them for drift-checking, advancing the spec pin from v0.35.16 (f1b59f8) to v1.0.0b2 (5810a26). - Vendor docs/conversation-api-openapi.json (OpenAPI 2.2.0, 40 path- groups) + docs/conversation-api-sse-events.schema.json (11 events). - Pin all three Conversation-API artifacts in .corviduo-canonicals.toml: OpenAPI + SSE schema as strict drift gates (canonical_drift.py), the prose markdown as tolerate_drift reference. Drift check green (10/10). - pyproject: worldtree-spec-rev -> 5810a26, worldtree-version -> v1.0.0b2 (was stale at v0.29.0), pinned-on -> 2026-06-30. - SPEC-PIN.md: current-pin table + history row + vendored-artifacts list. - coverage-map.md: P-1 marked remediated; the map now audits a frozen, diffable target. The prose markdown is byte-identical to v0.35.16 (last WT edit 2026-05-31); the b2 surface lives only in the OpenAPI. No client- facing code change (the b2 409/503 + unified error envelope were already consumed in v0.18.3/.4) -> pin-only, no version bump.
248 lines
14 KiB
Markdown
248 lines
14 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 | 7 | 11 | 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.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 |
|
||
|
||
**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 — the convergence frontier (debug-observability path)
|
||
|
||
**Tier 1 — the debug-observability core (design-brief'd for v1, unbuilt):**
|
||
|
||
| Endpoint | Status | Why in-scope |
|
||
|---|---|---|
|
||
| `GET /admin/events` | ⬜ | design-brief §5 v1 **AdminEvents pane**; = issue **#11**, **blocked** on `admin.events.read` scope (infra-ops) |
|
||
| `GET /admin/sessions/{id}/bifrost` | ⬜ | design-brief §5 v1 **BifrostState widget** — never built; admin-key-gated |
|
||
| `GET /admin/sessions/{id}/tools` | ⬜ | design-brief §5 v1 **Tools widget** — never built; admin-key-gated |
|
||
| `GET /capabilities` | ⬜ | server capability discovery — a turn flows through what's advertised |
|
||
| `GET /me` | ⬜ | whoami / key-identity — "which key am I against" is a debug primitive |
|
||
| (`GET /sessions` picker · resume) | ⬜ | sub-gaps above — presenter-wiring only, wrappers exist |
|
||
|
||
**Tier 2 — rounds out I/O coverage under A (postdates the design-brief):**
|
||
|
||
| Endpoint | Status | Why in-scope |
|
||
|---|---|---|
|
||
| `GET /sessions/{id}/tools` | ⬜ | Tier-3 owner-scoped tool introspection (#183) — reachable with the **consumer key** (no admin scope), the unblocked tool-introspection path |
|
||
| `POST /sessions/{id}/persona_state` (write) | ⬜ | affect-injection is debug-relevant; pairs with our provider affect plane |
|
||
| `POST /characters` · `DELETE /characters/{id}` · `GET /characters/{id}/state` · `GET /models/available-for-characters` | ⬜ | transient-characters (Echo) is a session-creation **routing path** a debug client should be able to drive a turn through |
|
||
|
||
### 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/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)
|
||
|
||
**Tier 1 — debug-observability core**, in dependency order:
|
||
|
||
1. **Session picker + SSE-resume** — wrappers exist (`list_sessions`,
|
||
`reconnect_turn`), need presenter wiring only. **Cheapest; unblocked.**
|
||
2. **`GET /capabilities` + `GET /me`** — cheap debug primitives. Unblocked.
|
||
3. **BifrostState + Tools widgets** (`GET /admin/sessions/{id}/{bifrost,tools}`)
|
||
— design-brief'd v1, unbuilt. Admin-key-gated reads.
|
||
4. **#11 — AdminEvents pane** — **blocked** on an `admin.events.read` scope grant
|
||
(infra-ops). The single externally-blocked item; everything else can ship
|
||
without it.
|
||
|
||
**Tier 2 — rounds out coverage** (lower priority):
|
||
|
||
5. **`GET /sessions/{id}/tools`** — owner-scoped tool introspection; consumer-key
|
||
reachable (no admin scope), so unblocked.
|
||
6. **Transient-characters routing** (4 endpoints) + **`POST /sessions/{id}/persona_state`**.
|
||
|
||
---
|
||
|
||
## 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.
|