diff --git a/persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-1-2-complete.md b/persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-1-2-complete.md new file mode 100644 index 0000000..a0f71a9 --- /dev/null +++ b/persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-1-2-complete.md @@ -0,0 +1,57 @@ +`[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`. diff --git a/persistent-memory.md b/persistent-memory.md index 1a76238..13e1d02 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -1,6 +1,6 @@ # Persistent memory — ratatoskr -_Last updated: 2026-07-18_ +_Last updated: 2026-07-19_ > **Always check for `/tmp/ratatoskr-dev-handoff.md`** — if it exists and its > `Written:` stamp is under an hour old, read it (it carries the in-flight @@ -44,34 +44,38 @@ upstream API key stays server-side (INV-003). ## Current state / in-flight -_As of 2026-07-18 (evening):_ +_As of 2026-07-19:_ -**🔨 ACTIVE MIGRATION — worldtree-sdk cutover (issue #20), starting slice-1.** Operator ruled ADOPT -(2026-07-18): ratatoskr cuts its CONSUMER client layer over to consume **worldtree-sdk (Python) 1.0.0** — -retire the hand-rolled httpx wrappers (`sessions`/`sse_client`/`tier3`) behind a thin `ratatoskr.wt` -adapter over the SDK. Both TS + Python SDK 1.0.0 are GA (**Python live + pip-installable on the gitea -PyPI** — DEC-5 gate cleared). Ratatoskr's own **parity pass shaped the Python spine** (open-world reads, -caller-injected transport, per-wire role/model). Design locked (6 DECs, vor-cross'd with worldtree-codex, -heid-panel-reviewed → error-map table added); contract `docs/contracts/worldtree_sdk_cutover.contract.md` -(committed `e45640c`). **SLICE-1 IN PROGRESS:** ✅ dep integrated + DEC-5 verified + committed (`29c4fda`) — `worldtree-sdk==1.0.0` -installs from the gitea registry (reuses bifrost's index auth, NO new token; core dep + `[tool.uv.sources]`), -`WorldtreeClient` constructs with an injected transport (`_owns_client=False`, INV-CUT-1 confirmed live), -suite 534 green. **NEXT = the adapter** `src/ratatoskr/wt.py` (TDD): `build_client(base_url, *, api_key, -admin_key, transport)` → `WorldtreeClient(auth=, admin_auth=, transport=)` (DESIGN CARE: SDK does per-request -auth via the providers; our injected `httpx.AsyncClient` carries base_url/UA/timeout, NOT the Authorization -header — read the SDK `client.py` @ `~/development/worldtree-sdk` for the split, INV-CUT-1) + `translate_error` -DEFAULT (SDK `ApiError`→`SessionApiFailed`, discriminated `WorldtreeError` subclasses passthrough; route-specific -rows come in later slices). Unit-test; NO surface wiring (slice-2). Then slices 2-7 (route-family + deletions, -live-smoke per slice). Scope: consumer layer ONLY; -Bifrost provider planes untouched. Multi-session grind. Full design → auto-memory -`project_worldtree_sdk_cutover`. This SUPERSEDES the #371 "repin rides the later Python milestone" framing -below (that milestone shipped; we're adopting, not just repinning). +**🔨 ACTIVE MIGRATION — worldtree-sdk cutover (issue #20): SLICE-1 + SLICE-2 COMPLETE + PUSHED, slice-3 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 +(adapter foundation) ✅ + SLICE-2 (sessions/turn) ✅ DONE + PUSHED** — origin at `aba1730` (12-commit arc +`b1fbadd`→`aba1730`, tags v0.21.3–.10, pushed 2026-07-19): adapter (`b907a7b`, all 6 route families +create/list/messages/tools/stream/cancel + the § Error-map mapping) → CLI rewire (`e3a10ad`) → web rewire +(`5c595b8`) → orphan deletion (`59602fe`, sse_client 714→224 + sessions 677→608; DEC-4 LIVE-SMOKE PASSED on +:8081 create+stream+cancel FIRST) → heid-code-review fixup (`74d41eb`) → heid-bug-hunt fixup (`aba1730`). +Suite **497 green**. **KEY ADAPTER FACTS (foot-guns for slices 3-7):** the SDK returns **open-world dicts** +(not typed objects) for session reads → presenters read mappings, `info["session_id"]`; SDK events +(`TurnEvent`) carry `sse_id: str` + a **body-derived `turn_id` that's ABSENT on text/thinking frames** → parse +the cancel target's turn from the composite `sse_id`, NOT `event.turn_id`; the SDK **normalizes transport +failures to `ConnectFailed(status=0)`** (not raw httpx) → presenters catch it; the adapter re-wraps SDK stream +errors → ratatoskr caller-semantic exceptions (DEC-2, keep ratatoskr's typed exceptions); `consumer_key` is +BOUND-create-only (adapter nulls it when unbound, else the SDK auths as the consumer); the SDK's error-envelope +parser **prefers the nested `detail`** so a top-level `error_code` doesn't surface (why create's bound-502 is +NOT gated on error_code, unlike list's 422). **NEXT = slice-3** (persona `set_persona_state` + authored-history +`write_history` + first_message presets — retires the still-hand-rolled `create_session`/`get_session_messages` +that the `--seed-first-message` probe uses); then slice-4 (agents/tier3, FOLDS the `model`→`role` cutover), +slice-5 (characters/me/caps), slice-6 (admin — `stream_admin_events`+`get_session_bifrost` still hand-rolled), +slice-7 (teardown: retire contracts #2/#15, drop `httpx-sse`, minor bump per DEC-6 w/ operator approval). +Scope: consumer layer ONLY; Bifrost provider planes untouched. Full design → auto-memory +`project_worldtree_sdk_cutover`. -**⏸️ DEFERRED — tier3 agents `model`→`role` (scope B), on worldtree-dev's deploy flag.** WT renames the -agents-RESPONSE selector `model`→`role` (spec 1.2, commit `387c67b`, NOT yet deployed). `_parse_tier3_agent_info` -reads `body["model"]` → KeyErrors post-deploy. Operator chose scope B (full tier3 `model`→`role` incl. -contract #15 + CLI `--model`→`--role`). Implement ON the deploy flag, not before (breaks the current demo); -folds into cutover slice-4. Auto-memory `project_tier3_agents_model_to_role_pending`. +**⏸️ DEFERRED — tier3 agents `model`→`role` (scope B), folds into cutover slice-4.** WT renamed the +agents-RESPONSE selector `model`→`role` (spec 1.2). **DEPLOY NOW LIVE** on :8080/:8081 (v1.0.0b128, +worldtree-dev confirmed 2026-07-19 — was the deploy-flag gate; acked). Operator chose scope B (full tier3 +`model`→`role` incl. contract #15 + CLI `--model`→`--role`); lands in cutover slice-4 where tier3.py routes +through the SDK (doing it standalone now = throwaway). Auto-memory `project_tier3_agents_model_to_role_pending`. **✅ RESOLVED — the "app product" workstreams leave Rata entirely (operator 2026-07-18).** **No arbo fork, no SillyTavern-on-Rata** — a NEW repo (template-dev standing up) takes over BOTH @@ -115,13 +119,12 @@ 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.2**. **origin at `b1fbadd`** (pushed 2026-07-18: -canonical syncs `5d06a27`/`80c8d58`, ephemeral-Echo `c7016f2` #19, coverage-map→SDK-surface `b1fbadd`). -**UNPUSHED local commits** (cutover work — operator hasn't pushed): cutover contract `e45640c` #20, the -`memory:` snapshot, dep-integration `29c4fda` (worldtree-sdk==1.0.0), + this snapshot — **push is the -operator's call**. origin `git@gitea.phasefinal.com:vh/ratatoskr.git`. **NEW core dep: `worldtree-sdk==1.0.0`** -(gitea PyPI, `[tool.uv.sources]`). bifrost **`==1.1.4`** / wire v0.7; WT openapi vendored 2.3.0, -**conversation-api-spec re-synced to v1.1** (`b4a278c`); **suite 534 green**. Personal WT on **b127** +**Substrate / environment:** branch `main` at **v0.21.10**, **fully PUSHED to origin at `aba1730`** (slice-2 +cutover arc `b1fbadd`→`aba1730` + tags v0.21.3–.10 pushed 2026-07-19). 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 497 green** (was 534 pre-cutover; net +delta = adapter/rewire tests added, ~80 deleted hand-rolled turn-stream tests). 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 via `ss -ltnp | grep `). `env.sh` sets @@ -271,6 +274,7 @@ decision. Captures rationale that won't be obvious from code alone. - `[2026-07-18]` **ephemeral-template (Echo) create SHIPPED (`v0.21.2`, `c7016f2`, #19)** — `config` passthrough + `--system-prompt` + `SessionInfo.kind/config` + `--whoami` roles fix. Diagnosed from the ignored `session_api_failed` startup line; role/model drift resolved w/ worldtree-dev (spec re-synced v1.1). TDD + heid contract-review + bug-hunt. - `[2026-07-18]` **Rata = THE reference consumer of the worldtree-sdk Python spine** — parity pass (12 grounded findings) shaped its contract BEFORE build (open-world reads, caller-injected transport, per-wire role/model — adopted); coverage-map re-anchored to the SDK's 41-op ratified surface (`b1fbadd`); 4 ergonomics items parked post-v1 with wtsdk-dev. - `[2026-07-18]` **worldtree-sdk cutover DECIDED — adopt the Python SDK for the consumer client layer** (operator, overriding "stay hand-rolled"). Issue #20; contract `docs/contracts/worldtree_sdk_cutover.contract.md` (`e45640c`, vor-cross'd + heid-reviewed); auto-memory `project_worldtree_sdk_cutover`. Consumer layer only, Bifrost provider untouched; slice-1 foundation next. See in-flight. +- `[2026-07-19]` **worldtree-sdk cutover SLICE-1 + SLICE-2 COMPLETE + PUSHED (origin `aba1730`, v0.21.10, 497 green).** The biggest, riskiest cutover slice done end-to-end through the full House Code Discipline (adapter→cli→web→delete→live-smoke→both heid gates); the bug-hunt caught 4 real confirmed bugs the conformance lens couldn't, and a convergent code-review finding was correctly REJECTED as category-5 (SDK envelope-parser behavior). Slice-3 next. → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-1-2-complete.md` _65 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._