`[2026-07-19]` **worldtree-sdk cutover — SLICE-1 + SLICE-2 COMPLETE + PUSHED (origin `aba1730`, v0.21.10).**

The consumer client layer's biggest, riskiest slice (sessions/turn) is fully migrated onto
`worldtree-sdk (Python) 1.0.0` behind the `ratatoskr.wt` adapter, run end-to-end through the House Code
Discipline. Pushed 2026-07-19 (12-commit arc `b1fbadd`→`aba1730`, tags v0.21.3–.10). Suite **497 green**.

## The arc (all on origin/main)

| Part | Commit | What |
|---|---|---|
| Slice-1 foundation | `12cd864` (v0.21.3) | `build_client` + `translate_error` DEFAULT (ApiError→SessionApiFailed) + passthrough; INV-CUT-1 (injected transport, `_owns_client=False`) test-proven |
| Slice-2 adapter (read/create) | `bb158ae` (v0.21.4) | create/list/messages/tools over `client.sessions.*`, per-route error mapping |
| Slice-2 adapter (stream/cancel) | `b907a7b` (v0.21.5) | resilient `stream_turn` + `cancel_turn`; SDK stream errors re-wrapped → ratatoskr caller-semantic exceptions (DEC-2) |
| Slice-2 CLI rewire | `e3a10ad` (v0.21.6) | `_amain`/`_run_turn`/render onto SDK `TurnEvent`s + open dicts |
| Slice-2 web rewire | `5c595b8` (v0.21.7) | Starlette endpoints + SSE→JSON serialization; browser contract preserved |
| Slice-2 deletion | `59602fe` (v0.21.8) | sse_client 714→224, sessions 677→608; orphaned turn-stream family + Event model removed; **DEC-4 live-smoke PASSED first** |
| heid-code-review fixup | `74d41eb` (v0.21.9) | INV-CUT-2 completeness (cancel/stream ApiError default) + contract error-map amendments |
| heid-bug-hunt fixup | `aba1730` (v0.21.10) | 4 confirmed bugs + open-world render hardening |

## KEY ADAPTER FACTS (foot-guns for slices 3-7)

- **The SDK returns open-world dicts** (`Mapping`, NOT typed objects) for session reads → presenters read
  mappings (`info["session_id"]`), never attributes. Ratatoskr's typed `SessionInfo`/`SessionPage` retired.
- **SDK `TurnEvent` shape:** `sse_id: str` (the composite `"{turn}:{seq}"`) + a **body-derived `turn_id`
  that is ABSENT (None) on text/thinking frames.** The mid-stream cancel target must parse the turn from
  `sse_id`, NOT read `event.turn_id`. (This was a live bug — sigint-cancel saw "no event".)
- **The SDK normalizes transport failures to `ConnectFailed(status=0)`** (not raw httpx errors) → presenters
  catch `wtsdk.ConnectFailed` for network paths; a stream `ConnectFailed` maps → `SseConnectFailed`.
- **The adapter re-wraps SDK stream errors → ratatoskr's caller-semantic exceptions** (DEC-2 keeps ratatoskr's
  typed exceptions; SessionRetired→SessionApiFailed, ConnectionDropped→SseConnectionDropped, Malformed*/
  TurnIdFlip→same-named, ConnectFailed/terminal ResumeError→SseConnectFailed).
- **`consumer_key` is BOUND-create-only** — the adapter nulls it when `bifrost is None`, else the SDK's
  credential precedence auths as the consumer instead of the default bearer.
- **The SDK's error-envelope parser PREFERS the nested `detail`** dict when present, so a top-level
  `error_code` doesn't surface. That's why create's bound-502 is NOT gated on error_code (unlike list's
  422+cursor_invalid, whose flat body surfaces the code) — INV-002 also makes the handshake the sole
  bound-502 cause. This is a genuine cross-frontier triage win: 2/3 heid arms flagged the missing gate; it
  was correctly REJECTED as category-5 wrong-grounding (the SDK parser wasn't in the arms' file set).

## The two heid gates (the value proof)

- **heid-code-review** (Gróa+Hulda substantive, Regin zero=weak): adopted the cancel/stream ApiError-default
  completeness + error-map table amendments; correctly rejected the bound-502-gate finding (above).
- **heid-bug-hunt** (Gróa 8 / Hulda 6 / Regin 6; Heid source-checked, refuted 2 Regin FPs): caught **4 real
  confirmed bugs the conformance lens structurally could not see** — SessionRetired(410) uncaught by both
  presenters (crash/dropped-stream), cli consumer_key forwarded on unbound create (auth divergence), sse_id
  None-crash, cancel never-raise gap — plus open-world render hardening. Rejected 4 as verified FPs (incl.
  Hulda's "deleted funcs break callers" — grep-verified zero callers pre-deletion). Both surfaces re-smoked
  live after fixup: create+stream+cancel round-trip clean on :8081 (b128).

## What's still hand-rolled (later slices)

`create_session`/`get_session_messages` (the `--seed-first-message` probe uses them — retire in slice-3);
`set_persona_state`/`write_authored_history`/`first_message` (slice-3); agents/tier3 incl. `model`→`role`
(slice-4, deploy live b128); characters/me/capabilities/models (slice-5); `stream_admin_events`/
`get_session_bifrost` (slice-6). Slice-7 teardown: retire contracts #2/#15, drop `httpx-sse`, minor bump
(DEC-6, operator approval). Full design → auto-memory `project_worldtree_sdk_cutover`.
