The last slice of the consumer-layer cutover. Teardown only — zero runtime-logic change; the 494-green suite is the regression gate. - Drop `httpx-sse` from pyproject + lockfile: slice-6 deleted its last user, nothing imports `httpx_sse`, the SDK owns SSE parsing now. - Module boundary (operator decision): KEEP `sessions.py` + `sse_client.py` as pure caller-semantic type/exception homes (no rename, no fold — A3 was blocked by the `AgentNotAvailable` name collision + `wt.py` would mis-home `endpoint_for_plane`). Docstrings updated to stop claiming "client"; the `AdminEvent`/`SseId`/exception homes stay put (resolves the slice-6 deferred-home item). - Retire wire contracts #2 (sessions) + #15 (tier3): DEC-1 phase-2 — normative authority already transferred to the cutover contract; the code they specified is gone, so the files are deleted. #1 (SSE event vocab) and `first_message` stay (ratatoskr-owned, not retired). - Final coverage-map re-anchor: tools/list_sessions re-homed to `wt.py`; the Last-Event-ID SSE-resume sub-gap CLOSED (folded into `stream_turn` auto-resume); Surface-2 SSE parsing re-anchored to the SDK. - Stale doc-rot fix: the cli.py transport comment no longer calls `seed_preset_first_message` "not-yet-migrated" (it rides `wt`). - v0.22.0 (minor, DEC-6, operator-approved): publishes the full 6-slice cutover milestone.
24 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 |
✅ | wt.py create_session (SDK sessions.create) → cli.py,web/server.py |
wt-adapter re-anchored (slice-2, #20) — + end_user_id, bifrost binding (consumer-key via SDK per-request auth), config passthrough; 404→AgentNotFound, bound-502→BifrostHandshakeFailed. Ephemeral-template (Echo) create (#19) carried through the adapter. Depth enhancement to an already-covered route — count unchanged |
POST /sessions/{id}/messages (turn stream, SSE) |
✅ | wt.py stream_turn (SDK resilient sessions.stream_turn, auto-resume) → cli/web |
wt-adapter re-anchored (slice-2, #20) — the primary surface; 409→AgentNotAvailable, 503→TurnLaunchUnavailable, drop→SseConnectionDropped, protocol→same-named; absorbs the old reconnect_turn |
POST /sessions/{id}/history (authored-history-write, #347) |
✅ | wt.py write_authored_history (SDK sessions.write_history) → cli.py --seed-first-message, first_message.py seed_preset_first_message (create-path seed) |
wt-adapter re-anchored (slice-3, #20) — SDK owns the entry shape; v1 author=assistant; 404→AuthoredHistoryUnavailable (hide-existence, route is the discriminator, never probe); 409/422→SessionApiFailed default. LIVE-SMOKE 2026-07-19 on personal :8081 (b128): --seed-first-message on a sindra session → 201 (seq=0, phase=seeded, turn_id=2294) → read-back verbatim; create-path preset seed observed via --new. (Prior 2026-07-06 hand-rolled proof superseded.) |
GET /sessions/{id}/messages (history) |
✅ | wt.py get_session_messages (SDK sessions.messages) → cli.py --seed-first-message read-back, web/server.py messages proxy |
wt-adapter re-anchored (slice-3, #20) — the #347 seed read-back; open-world passthrough. LIVE-SMOKE 2026-07-19: read-back rendered the seeded seq-0 turn as a plain role=assistant message (model-invisible provenance confirmed) |
POST /sessions/{id}/turns/{turn_id}/cancel |
✅ | wt.py cancel_turn (SDK sessions.cancel_turn) → cli/web |
wt-adapter re-anchored (slice-2, #20) — two-stage Ctrl-C; 404→CancelTurnNotFound, 409→CancelAlreadyCompleted, late-cancel 200 (cancelled=False) is a benign result, not an error |
GET /agents |
✅ | wt.py list_agents (SDK agents.list) → web/server.py _agents_endpoint |
wt-adapter re-anchored (slice-4, #20) — open-world array verbatim (no AgentInfo normalization), merged with the local tier3 index (remote-wins); error→SessionApiFailed default, transport→ConnectFailed→502. LIVE-SMOKE 2026-07-19 on personal :8081 (b128): 6 agents returned (forseti/lofn/mask/mimir/vili/…) |
GET /agents/{id}/persona_state |
✅ | wt.py get_persona_state (SDK agents.persona_state) → web/server.py _persona_state_endpoint |
wt-adapter re-anchored (slice-4, #20) — open-world snapshot; dual-key (status,error_code) map: 404 persona_not_configured→PersonaNotConfigured, 404 agent_not_available→AgentNotAvailable, 403 auth_scope_denied→AuthScopeDenied, else default. LIVE-SMOKE 2026-07-19: a tier3 agent → correctly mapped PersonaNotConfigured (route+code adapter proven) |
POST /agents/define |
✅ | wt.py define_agent (SDK agents.define) → tier3.py _run_define |
wt-adapter re-anchored (slice-4, #20) — sends AgentDefineInput {agent_name,role,system_prompt}, returns open-world DefinedAgent (echoes role, b128); slug pre-validated; 429→Tier3QuotaExceeded(retry_after=0, header-less floor), 403→Tier3UserIdUnsupported, 422 layer_deferred→Tier3LayerDeferred. LIVE-SMOKE 2026-07-19: define --role thoughtful-character → defined ratatoskr:slice4-smoke (thoughtful-character) |
PATCH /agents/{id} |
✅ | wt.py patch_agent (SDK agents.patch) → tier3.py _run_patch |
wt-adapter re-anchored (slice-4, #20) — Tier-3 mutate (system_prompt/role, model→role folded in); 404→Tier3AgentNotFound, 422 field_not_mutable→Tier3FieldNotMutable. LIVE-SMOKE 2026-07-19: patched ratatoskr:slice4-smoke; a non-existent id via python -m → [agent_not_found] (exit 20, class-identity fix proven) |
DELETE /agents/{id} |
✅ | wt.py delete_agent (SDK agents.delete) → tier3.py _run_delete |
wt-adapter re-anchored (slice-4, #20) — 204→None; 404→Tier3AgentNotFound (route-discriminated, NOT hide-existence). LIVE-SMOKE 2026-07-19: deleted ratatoskr:slice4-smoke + local index → [] |
GET /me |
✅ | wt.py get_me (SDK me.get) → cli.py --whoami |
wt-adapter re-anchored (slice-5, #20) — open-world identity dict verbatim; any error→SessionApiFailed default (401 on a bad/absent key), transport→ConnectFailed→exit 21. LIVE-SMOKE 2026-07-19 on personal :8081 (b128): identity rendered (user_id ratatoskr, tier user, scopes incl. character.*, key_id c990f0be) |
GET /capabilities |
✅ | wt.py get_capabilities (SDK capabilities.get) → cli.py --whoami |
wt-adapter re-anchored (slice-5, #20) — open-world advertisement verbatim; _format_whoami reads allowed_roles/default_role and degrades on a null/non-mapping template (slice-4 hardening); matches conversation-api-spec v1.1 (b4a278c). LIVE-SMOKE 2026-07-19: ephemeral_template echo: default=echo max_bytes=32768 roles=[echo] |
GET /sessions/{id}/tools |
✅ | wt.py get_session_tools (SDK sessions.tools) → web/server.py _session_tools_endpoint |
wt-adapter re-anchored (slice-7 teardown, #20) — owner-scoped tool inventory (#183); consumer bearer (no admin scope), open-world dict verbatim, any error→SessionApiFailed default. (Consumer is web/server.py; the old sessions.py→tui.py row was stale — the TUI is deleted.) |
GET /admin/sessions/{id}/bifrost |
✅ | wt.py get_session_bifrost (SDK admin.sessions.bifrost) → web/server.py _session_bifrost_endpoint |
wt-adapter re-anchored (slice-6, #20) — admin-scoped BifrostState (#176); admin_auth rides on the wt client (_wt_client(admin_key=…)), NOT a per-call header; open-world dict verbatim, any error→SessionApiFailed default. LIVE-SMOKE 2026-07-19 on :8081 (readonly-admin key): admin-authed end-to-end (404 session_not_bifrost_bound clean envelope — auth + route + mapping proven). (Consumer is web/server.py, not tui.py — the old row was stale.) |
GET /admin/events (SSE) |
✅ | wt.py stream_admin_events (SDK admin.stream_events) → web/server.py _admin_events_endpoint |
wt-adapter re-anchored (slice-6, #20) — admin lifecycle SSE (#11), session-filtered; admin_auth on the wt client; the adapter re-wraps the SDK's AdminEvent→ratatoskr's (nan admin_id→id 0, None type/data→""/{}), non-200 open ApiError→SseConnectFailed, ConnectionDropped→SseConnectionDropped. LIVE-SMOKE 2026-07-19: a real session.created event (id=32) re-wrapped cleanly on live wire. (Consumer is web/server.py, not tui.py — stale row corrected.) |
GET /models/available-for-characters |
✅ | wt.py list_character_models (SDK models.available_for_characters) → cli.py --characters |
wt-adapter re-anchored (slice-5, #20) — open-world catalog verbatim; the probe reads items null-safe (or []); any error→SessionApiFailed default. LIVE-SMOKE 2026-07-19: character models: char-rp |
POST /characters |
✅ | wt.py create_character (SDK characters.create) → cli.py --characters |
wt-adapter re-anchored (slice-5, #20) — body {character} (+state only when set — SDK-idiomatic, drops the redundant explicit null); open-world create ACK verbatim; the probe degrades on a missing character_id (no hard-index). LIVE-SMOKE 2026-07-19: created char_8c00006e… |
GET /characters/{id}/state |
✅ | wt.py get_character_state (SDK characters.state) → cli.py --characters |
wt-adapter re-anchored (slice-5, #20) — open-world live PAD/emotions verbatim; TTL-refreshing read. LIVE-SMOKE 2026-07-19: state pad=[0.234, -0.136, 0.065] read back |
DELETE /characters/{id} |
✅ | wt.py delete_character (SDK characters.delete) → cli.py --characters |
wt-adapter re-anchored (slice-5, #20) — returns the SDK's open ACK verbatim (`-> Mapping |
POST /sessions/{id}/persona_state |
✅ | wt.py set_persona_state (SDK sessions.set_persona_state, PadState) → cli.py --set-persona-pad |
wt-adapter re-anchored (slice-3, #20) — SDK owns the canonical {"pad": {...}} wire (#317); CLI passes the 3 PAD axes (finiteness pre-validated); 204→None, else SessionApiFailed default. LIVE-SMOKE 2026-07-19 on personal :8081: --set-persona-pad 0.4,0.1,-0.2 → 204 |
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—wt.py:234list_sessions(SDKsessions.list) exists, no caller: the startup session-picker (design-brief §4 v1) was a TUI feature and the TUI is now deleted, so the frontier is moot unless a web picker is wired.POST /sessions/{id}/messages+Last-Event-ID(SSE-resume) — CLOSED (slice-2 teardown): the old hand-rolledsse_client.reconnect_turnis deleted; resume is now folded intowt.py:280stream_turn(the SDK's resilient auto-resume), which IS the wired presenter default. No longer an unwired sub-gap.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 now parsed by worldtree-sdk (sessions.stream_turn,
yielding TurnEvents — the hand-rolled sse_client._envelope_for_type is deleted) and
rendered by both presenters (cli/web; the TUI is deleted). 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.