Files
ratatoskr/persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-6-complete.md
vh 8274ed2d89 memory: /snapshot — worldtree-sdk cutover slice-6 complete (de9a5ba→11ae2f0)
Slice-6 (admin: bifrost inspection + admin-events SSE) done through the full House
Code Discipline, v0.21.19–.20, suite 494 green, live-proven (real session.created
event re-wrapped end-to-end on :8081), both heid gates cleared. Current state advanced
to slice-7 (teardown, LAST) next; Recent-decisions index entry + detail file added;
substrate at v0.21.20. Consumer client layer now fully cut over (6/7 slices).

persistent-memory.md stays ~345 lines (over the ~300 soft cap): dominated by the
non-archivable Current state block + <30-day July entries (guarded), so archival can't
reach the 250 target — left as-is per the stop-where-the-guards-stop rule.
2026-07-19 13:16:58 -07:00

6.7 KiB
Raw Permalink Blame History

worldtree-sdk cutover — SLICE-6 COMPLETE (admin: bifrost inspection + admin-events SSE)

[2026-07-19] Slice 6 of 7 of the worldtree-sdk consumer cutover (issue #20; contract docs/contracts/worldtree_sdk_cutover.contract.md). Full House Code Discipline; both heid panels cleared. Suite 494 green. The meatiest slice — SSE stream + admin auth + an event-shape decision.

Commits (v0.21.19–.20, on main, PUSHED this session)

  • de9a5ba feat — the two admin routes onto ratatoskr.wt, hand-rolled deleted.
  • bba57e1 fix — heid-code-review fixups (stale docstring + None-cursor test). NO version bump (docs + test only, SemVer skip rule).
  • 11ae2f0 fix — heid-bug-hunt fixups (admin-stream + bifrost hardening).

What migrated (WEB-only)

get_session_bifrost → client.admin.sessions.bifrost(id) (open-world dict verbatim, any ApiError → SessionApiFailed default — no new Error-map row). stream_admin_events → client.admin.stream_events(last_event_id=…). Both consumed ONLY by web/server.py (_session_bifrost_endpoint + _admin_events_endpoint) — the coverage-map's tui.py rows were STALE (the grep found zero TUI callers), so the TUI-deprecation wrinkle was moot. Corrected the coverage-map rows to web/server.py.

Deleted the hand-rolled sessions.get_session_bifrost + sse_client.stream_admin_events (+ ruff-cleaned the orphaned httpx/httpx_sse/json/AsyncIterator imports). Retired test_sse_client.py WHOLESALE (its last test was the admin stream; slice-2 had already removed the turn-stream tests) + test_sessions.py's TestGetSessionBifrost. sessions.py is now down to endpoint_for_plane + exception classes; sse_client.py to SseId + AdminEvent + exception classes.

Decisions made at TDD (contract § slice-6 notes)

  • Admin auth moves from a per-call Authorization header to the client's admin_auth. The SDK's admin.* routes use admin_auth (set via build_client(admin_key=…)), NOT a header. So _wt_client(client, *, admin_key=None, …) was extended, and the two web endpoints pass admin_key. The web already guards if not admin_key: 400, so the SDK's pre-HTTP ConfigurationError (W-5) is unreachable from the surface.
  • AdminEvent re-wrap (chosen over yield-through). The SDK's AdminEvent diverges from ratatoskr's: admin_id: int|float (nan for id-less) vs id: int; None-able type/data vs a dotted-str / {}-default dict. The web filter + SSE formatter read ev.id/ev.type/ev.data. The adapter re-wraps at the boundary — id = admin_id if int else 0, type = ev.type if isinstance str else "", data = dict if Mapping else {} — degrading the open-world None/nan ONCE and keeping the web endpoint + filter + the AdminEvent domain type UNCHANGED (preserves the web surface). Rejected: yield SDK events through + rewire the web filter (heavier churn; scatters the None/nan hardening). This is implementation-level (reversible, no module-boundary change), decided autonomously + flagged to the operator with the rejected alternative.

The ApiError-not-ConnectFailed gotcha (TDD → integration test)

First mapped the admin-stream non-200 as ConnectFailed. The web INTEGRATION test (respx mocking a real 500) exposed that the SDK admin stream raises ApiError("admin_stream_failed", status=…) on a non-200 open — the unit fake couldn't model it. Fixed to ApiError → SseConnectFailed. Lesson: a web integration test catches what the adapter unit fake structurally can't.

LIVE SMOKE (:8081, readonly-admin key — INV-CUT-5 / DEC-4 cleared)

Drove the WEB surface (via httpx.ASGITransport over create_app(client_factory, admin_key=…)) against real :8081. The bifrost endpoint returned an admin-authed clean 404 session_not_bifrost_bound envelope (auth + route + mapping proven — a 404 not a 401/403 = the admin key authenticated). :8081's admin stream is idle (no heartbeats in 15s raw), so I generated activity: streamed admin events while concurrently creating a session (POST /sessions {agent_id: mimir, end_user_id: …} → 201) and observed the real session.created admin event (id=32/34) re-wrapped cleanly (id int, type str, data dict); threwaway session cleaned up (DELETE → 204). NOTE: a raw POST /sessions needs end_user_id (422 without it).

heid-code-review (thread 01KXXYRNNY…) — 3/3 no drift

Gróa + Regin zero; Hulda "no slice-6 implementation drift." Only minor doc/test looseness: a stale _session_bifrost_endpoint docstring ("overrides the Authorization header" → corrected to "rides on the client's admin_auth"), and an admin-stream None-cursor test-gap (added). Hulda's "web endpoints under-tested" was source-VOIDED by Heid — those tests live in test_web_server.py, which wasn't in the consult embed (excerpt-elides-tests trap).

heid-bug-hunt (thread 01KXXZBW74…) — 4 real findings, all fixed

The cold spec-free hunt earned its keep: the CR found the admin surface CONFORMANT, but judging against the general ConnectFailed floor + the degrade-never-crash promise it surfaced 4 hardening gaps:

  • [bug, 3/3] stream_admin_events never mapped ConnectFailed — the SDK admin-stream open DOES raise it (connect-time / auth-resolution; confirmed in SDK source), stream_turn
    • the bifrost GET both catch it, and this endpoint's OWN :633 comment claimed it did. An unmapped ConnectFailed escaped the web gen's except (Sse*) → aborted SSE with no stream_error. Now mapped → SseConnectFailed.
  • [bug, 2/3] non-str type crashed the web filter — ev.type or "" (falsy-only) let a truthy non-str type (123) reach .startswith → AttributeError. Now isinstance-guarded (matches admin_id/data). Same container-type class as the slice-5 bug-hunt.
  • [robustness] dict(bstate) 500 on a non-mapping bifrost body — I introduced it in slice-6 (JSONResponse(bstate) → dict(bstate)). Now degrades to {}.
  • [robustness] transport leak — _wt_client ran before the try/finally in the SSE gen; a construction failure would leak the httpx transport. Moved inside the try. Voided (Heid): Regin's dict(ev.data) TypeError — the isinstance(_, Mapping) guard already handles it.

Next: slice-7 (teardown, the LAST slice)

Drop the httpx-sse dep from pyproject.toml (SDK owns SSE parsing — verify nothing else imports it), retire wire contracts #2/#15, relocate the AdminEvent/exception classes if sse_client.py/sessions.py end up ~empty, final coverage-map re-anchor, and the MINOR bump per DEC-6 (needs operator approval) publishing the cutover milestone.

See also 2026-07-19-worldtree-sdk-cutover-slice-5-complete (the container-type degrade-not-crash lesson) and 2026-07-19-worldtree-sdk-cutover-slice-4-complete.