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.
This commit is contained in:
2026-07-19 13:16:58 -07:00
parent 11ae2f056e
commit 8274ed2d89
2 changed files with 126 additions and 9 deletions
@@ -0,0 +1,105 @@
# 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]].
+21 -9
View File
@@ -46,7 +46,7 @@ upstream API key stays server-side (INV-003).
_As of 2026-07-19:_
**🔨 ACTIVE MIGRATION — worldtree-sdk cutover (issue #20): SLICE-15 COMPLETE, slice-6 (admin) next.**
**🔨 ACTIVE MIGRATION — worldtree-sdk cutover (issue #20): SLICE-16 COMPLETE, slice-7 (teardown, the LAST) next.**
Operator ruled ADOPT (2026-07-18): ratatoskr cuts its CONSUMER client layer over to **worldtree-sdk (Python)
1.0.0**, retiring the hand-rolled httpx wrappers behind a thin `ratatoskr.wt` adapter. Design locked (6 DECs,
vor-cross'd, heid-panel-reviewed); contract `docs/contracts/worldtree_sdk_cutover.contract.md`. **SLICE-1+2
@@ -61,6 +61,15 @@ onto `client.me`/`.capabilities`/`.models`/`.characters.*` (all open-world reads
**NO new Error-map rows**), rewired `--whoami`/`--characters` (**CLI-only; no web caller**), and DELETED the 6
hand-rolled `sessions.py` wrappers (`endpoint_for_plane`+`get_session_bifrost` [slice-6]+exceptions stay). Full
arc → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-5-complete.md`.
**SLICE-6 (admin: bifrost inspection + admin-events stream) ✅ DONE**`de9a5ba` (feat) + `bba57e1`
(CR-fixups, no-bump docs+test) + `11ae2f0` (BH-fixups), v0.21.19.20; suite **494 green**; both heid gates
cleared. Migrated `get_session_bifrost``client.admin.sessions.bifrost` + `stream_admin_events``client.admin.
stream_events` (**WEB-only** — coverage-map's tui.py rows were STALE); admin auth moved to the client's
`admin_auth` (`_wt_client(admin_key=…)`); the admin-events adapter **re-wraps** the SDK's `AdminEvent`→ratatoskr's
(nan→0, non-str/None type→"", None/non-mapping data→{}) to keep the web filter + AdminEvent domain type stable;
deleted `sessions.get_session_bifrost` + `sse_client.stream_admin_events`, retired `test_sse_client.py` whole.
**LIVE SMOKE :8081**: web bifrost admin-authed 404 clean envelope + a real `session.created` event (id=34)
re-wrapped end-to-end. Full arc → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-6-complete.md`.
**KEY ADAPTER FACTS (foot-guns, cumulative for slices 6-7):** SDK reads = **open-world dicts** — presenters
MUST degrade not crash, guarded at THREE levels (slice-5 needed all three): **container-type** (a scalar `123`
is non-iterable → `for x in 123` TypeError; the `or []` idiom catches null/absent but NOT a truthy non-iterable
@@ -71,9 +80,10 @@ failure to `ConnectFailed(status=0)`** (NOT raw httpx) — every adapter caller
**caller-semantic exceptions the adapter raises + a `-m` CLI catches must NOT live in the `-m` module** (double-
module class-identity split → uncaught traceback; live smoke catches it, unit tests can't); `TurnEvent` `turn_id`
ABSENT on text/thinking frames; `consumer_key` is BOUND-create-only; envelope parser prefers nested `detail`.
**NEXT = slice-6** (admin: `admin.sessions.bifrost` + `admin.stream_events`, admin_auth — `get_session_bifrost`
in `sessions.py` + `stream_admin_events` in `sse_client.py`); then slice-7 (teardown: retire contracts #2/#15,
drop `httpx-sse`, MINOR bump per DEC-6 w/ operator approval). Scope: consumer layer ONLY; Bifrost provider
**NEXT = slice-7 (teardown, the LAST slice):** delete any residual hand-rolled paths, drop the `httpx-sse` dep
from `pyproject.toml` (SDK owns SSE parsing now — 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. Scope: consumer layer ONLY; Bifrost provider
untouched. Full design → auto-memory `project_worldtree_sdk_cutover`.
**✅ RESOLVED — tier3 agents `model``role` (scope B) folded into cutover slice-4** (`c62b4ee`, v0.21.13). The
@@ -122,14 +132,14 @@ Full record → `persistent-memory.d/2026-07-18-368-silo-test-passed.md`. Siblin
(2) R39 Phase-2 **matched-quartets rebuild** (confirmatory, "whenever"); (3) bifrost **snapshot-cursor
adoption** (ruled normative, not blocking → `persistent-memory.d/2026-07-16-bifrost-cursor-conformance.md`).
**Substrate / environment:** branch `main` at **v0.21.18** — slice-4 arc `c62b4ee``477d98f` + slice-5 arc
`deab762``4e20030` + this snapshot **COMMITTED, not-yet-pushed** (tags v0.21.13.18; push is the operator's
call); slice-13 (v0.21.3.12, `b1fbadd``4f92a21`) PUSHED to origin earlier. origin
**Substrate / environment:** branch `main` at **v0.21.20** — slices 1-5 (`b1fbadd``4e20030`, v0.21.3.18)
PUSHED to origin; **slice-6 arc `de9a5ba`→`11ae2f0` (v0.21.19.20) + this snapshot** committed then PUSHED this
session (tags through v0.21.20). origin
`git@gitea.phasefinal.com:vh/ratatoskr.git`. **NEW core dep:
`worldtree-sdk==1.0.0`** (gitea PyPI, `[tool.uv.sources]`; `httpx-sse` retires at slice-7). bifrost
**`==1.1.4`** / wire v0.7; WT openapi vendored 2.3.0, **conversation-api-spec re-synced to v1.1** (`b4a278c`);
**suite 488 green** (slice-5 added the characters/me/caps adapter tests + heid code-review/bug-hunt fixup
tests, ~offset by the deleted hand-rolled character/me/caps tests). Personal WT on **b128**
**suite 494 green** (slice-6 added the admin adapter tests + heid fixup tests, ~offset by the retired
hand-rolled admin tests + the whole `test_sse_client.py`). Personal WT on **b128**
(`http://10.250.50.152:8081`; #368 silo + #364 promotion-hygiene live both instances). The combined
**:8392** provider (memory+affect) + **:8765** web are THE surfaces, dev-box BACKGROUND SHELLS —
restart via `scratchpad/relaunch_by_pid.py <pid>` (pid via `ss -ltnp | grep <port>`). `env.sh` sets
@@ -284,6 +294,8 @@ decision. Captures rationale that won't be obvious from code alone.
- `[2026-07-19]` **worldtree-sdk cutover SLICE-5 COMPLETE (characters + me/capabilities/models, `deab762`+`d86d6df`+`4e20030`, v0.21.16.18, 488 green).** Last consumer reads + transient-character CRUD onto the wt adapter (CLI-only rewire, NO new Error-map rows — all six routes → SessionApiFailed default); live-smoke-proven on :8081/b128; both heid gates cleared — code-review caught the null/element open-world-presenter degrade holes, the cold bug-hunt caught the **container-type layer below** them (guard container-type + element-type + top-level-mapping). Slice-6 (admin) next. → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-5-complete.md`
- `[2026-07-19]` **worldtree-sdk cutover SLICE-6 COMPLETE (admin: bifrost inspection + admin-events SSE, `de9a5ba`+`bba57e1`+`11ae2f0`, v0.21.19.20, 494 green).** The two admin routes onto `client.admin.*` (**WEB-only** — the coverage-map's tui.py rows were stale); admin auth moved from a per-call header to the client's `admin_auth`; the admin-events adapter **re-wraps** the SDK's divergent `AdminEvent`→ratatoskr's (nan/None degraded) to preserve the web surface. Live-smoke-proven (real `session.created` event id=34 re-wrapped end-to-end). Both heid gates cleared — code-review 3/3 no-drift (only a stale docstring + a test-gap), the cold bug-hunt caught 4 real hardening gaps the CR couldn't (ConnectFailed unmapped on the admin stream; non-str type crash; `dict(non-mapping)` bifrost 500; a transport leak). Slice-7 (teardown, LAST) next. → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-6-complete.md`
- `[2026-07-19]` **wyrd-dev #368 silo-enforcement consult delivered — ratatoskr's store-side silo is CONVENTIONAL (query-time filter); wyrd going STRUCTURAL off the framing.** Answered artifact-only from the provider memory-store code; wyrd folded my foot-guns into their unit-4 contract + committed to one-DB-file-per-campaign + first-class partition columns. OPEN LOOP: I'll eyeball their data model once the contract's cut (they'll ping). → `persistent-memory.d/2026-07-19-wyrd-368-silo-consult-delivered.md`
_67 older entries (2026-05-* debug-TUI/web era + the 2026-06-14 → 06-18 Bifrost-provider build / #17+#18 / #295-296 era) archived to archival-memory.md._