Compare commits

...

2 Commits

Author SHA1 Message Date
vh 3ef3a5ef83 chore(deps): repin bifrost 1.1.4→1.1.5 + v0.22.1
Adopt bifrost 1.1.5 (reference-impl posture — take the dep's canonical
hardening). 1.1.5 is a clean patch on the frozen v0.6 wire (no schema /
handshake change):

- fix: gate ALL optional store verbs → clean `unsupported_capability`
  (not 500), extending 1.1.4's maintenance-verb hasattr-backstop to the
  full optional-verb set. Directly improves our provider's error
  semantics on any unimplemented optional verb.
- feat: v0.6 memory verb-floor conformance harness (test infra).
- two docs clarifications (no runtime impact).

Verified: uv lock + sync resolved 1.1.5 from the gitea index; suite
494 green on 1.1.5; the :8392 combined provider restarted onto 1.1.5
(clean uvicorn boot, stores preserved). Patch bump v0.22.0→v0.22.1.
2026-07-19 18:01:21 -07:00
vh 5775ce2210 memory: /snapshot — worldtree-sdk cutover COMPLETE (all 7 slices, v0.22.0)
Slice-7 teardown landed (ec68b1f); the whole #20 cutover is done. Collapsed
the now-complete cutover in-flight block to a COMPLETE summary + foot-gun
carry-forward, added the slice-7 Recent-decisions entry + detail file, and
flagged the two operator-gated post-ship actions (push + althing announce).
Archival drain deferred (file ~33 over soft cap; standing-posture entries
interleave the settled ones — a dedicated pass, not a milestone-checkpoint cut).
2026-07-19 13:47:26 -07:00
4 changed files with 108 additions and 51 deletions
@@ -0,0 +1,69 @@
# worldtree-sdk cutover — SLICE-7 (teardown, the LAST slice) COMPLETE
`[2026-07-19]` Commit `ec68b1f` `feat(#20): worldtree-sdk cutover teardown (slice-7) + v0.22.0`,
tag **v0.22.0** (lightweight). Closes issue #20's implementation: all 7 slices done, suite **494 green**.
This slice is **teardown only — zero runtime-logic change**; the green suite is the regression gate.
## What slice-7 did
1. **Dropped `httpx-sse`** from `pyproject.toml` + lockfile. Slice-6 deleted its last user
(`sse_client.stream_admin_events`); a tree grep confirmed nothing imports `httpx_sse`. `uv sync`
physically pruned it from the venv; suite green with the module absent (empirical safety proof — a
heid bug-hunt would find nothing on a proven-unused dep removal, so it was skipped with that reasoning).
2. **Module-boundary decision — KEEP (operator decision A1, surfaced via /elitk + AskUserQuestion).**
Post-cutover `sessions.py` (227 LOC) and `sse_client.py` (170 LOC) hold NO client — only ratatoskr's
caller-semantic exception surface + a couple of dataclasses (`BifrostBinding`; `SseId`, `AdminEvent`) +
the `endpoint_for_plane` provider helper. Three options weighed:
- **A1 (chosen)** — keep as-is, fix the misleading docstrings. Zero import churn, zero risk.
- A2 — rename to `session_errors.py`/`stream_errors.py`, re-point ~7 importers. Honest names but pure
polish nobody asked for, right before a milestone.
- A3 — consolidate into one `errors.py` / fold into `wt.py`. **Blocked** by the `AgentNotAvailable`
name collision (two distinct classes: persona-404 in `sessions` vs eager-turn-409 in `sse_client`)
which would force renaming a contract-level caller-semantic type + its §Error-map rows + all catch
sites; folding into `wt.py` also mis-homes `endpoint_for_plane` (provider-side) and balloons the
adapter. Naming-honesty (principle-2) addressed by a one-line docstring note instead.
- **Resolves the slice-6 deferred item** (contract line ~326): the ratatoskr `AdminEvent`/`SseId` +
exceptions stay in `sse_client.py`; the session/tier3 exceptions + `BifrostBinding` stay in `sessions.py`.
- The class-identity foot-gun holds: exception homes must never be run as `__main__` (else a `-m` CLI's
`except` binds a second copy). `sessions.py`/`sse_client.py`/`wt.py` are all safe; `tier3.py`/`cli.py` are not.
3. **Retired wire contracts #2 (sessions) + #15 (tier3)** — DEC-1 phase-2. Files DELETED
(`docs/contracts/issues/{2,15}.contract.md`). Their normative authority transferred to the cutover
contract at authoring; the code they specified is gone. **#1 (SSE event vocabulary) NOT retired** —
stays current (amended `4bd9abd` 2026-07-18) as ratatoskr's SSE-rendering reference; **`first_message`
NOT retired** (ratatoskr-owned usage contract). **Accepted side-effect:** `issues/5.contract.md`'s
historical "amended #2/#3/#4 in-place" line now points at a deleted #2 — left as-is (frozen issue-record
of a past action; NOT expanding DEC-1's #2/#15 retirement scope, which was heid-reviewed + operator-set).
4. **Final coverage-map re-anchor.** `GET /sessions/{id}/tools``wt.py get_session_tools` (SDK
`sessions.tools`) → `web/server.py` (old `sessions.py``tui.py` row was stale; TUI already deleted).
`list_sessions` re-homed to `wt.py`, still caller-less (picker was a TUI frontier, now moot). The
`Last-Event-ID` SSE-resume sub-gap CLOSED — `reconnect_turn` deleted, resume folded into `wt.py
stream_turn` auto-resume. Surface-2 SSE parsing re-anchored to the SDK (`_envelope_for_type` gone).
5. **Stale doc-rot fix** — the `cli.py` transport comment no longer calls `seed_preset_first_message`
"not-yet-migrated hand-rolled" (it rides `wt.write_authored_history` since slice-3).
6. **v0.22.0** (minor, DEC-6, operator-approved 2026-07-19) — publishes the full 6-slice cutover milestone.
## Verification
- Suite **494 green** (clean env: `env -u RATATOSKR_ADMIN_API_KEY -u RATATOSKR_API_KEY -u WORLDTREE_API_KEY`
or venv-active with those keys unset).
- `httpx_sse` gone from the venv (`import httpx_sse` → ModuleNotFoundError); 0 refs in `uv.lock`; 0 in `src/`.
- Cutover contract still validates (only the expected no-FN-block warnings for a migration contract).
- No residual hand-rolled consumer HTTP paths: every `httpx.AsyncClient` construction outside `wt.py`
(cli/tier3/web/entrypoint) feeds `wt.build_client(transport=…)` per INV-CUT-1; zero raw `.get`/`.post`
bypass calls. Bifrost PROVIDER planes stay hand-rolled (INV-CUT-3, untouched).
## Pending (non-blocking, operator's call)
- `git push` origin — `ec68b1f` + the **v0.22.0 tag are LOCAL only**.
- althing announce of v0.22.0 to worldtree-dev / wtsdk-dev per the SemVer push-notify (post-push; Rata is
the SDK reference consumer, wt #371).
## Discovered during the slice (not bugs)
- Phantom stale-LSP diagnostics flagged `test_sse_client.py` + `slice6_smoke.py` — neither exists on disk
or in git (both deleted in slice-6). The in-venv 494-green suite is authoritative over Pyright, which
resolves against system Python (all the `worldtree_sdk`/`starlette`/`respx` "unresolved import" noise).
See per-slice arc files `[[2026-07-19-worldtree-sdk-cutover-slice-5-complete]]`,
`[[2026-07-19-worldtree-sdk-cutover-slice-6-complete]]` for the earlier slices; design in auto-memory
`project_worldtree_sdk_cutover`.
+32 -44
View File
@@ -46,45 +46,31 @@ upstream API key stays server-side (INV-003).
_As of 2026-07-19:_
**🔨 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
(foundation + sessions/turn) ✅ PUSHED** origin `aba1730`. **SLICE-3 (persona + authored-history + first-message)
✅ DONE** `ca9a339`+`fc256bb`. **SLICE-4 (agents/Tier-3 + `model`→`role` fold) ✅ DONE** `c62b4ee``477d98f`
(v0.21.13.15). **SLICE-5 (characters + me/capabilities/models) ✅ DONE**`deab762` (feat) + `d86d6df`
(heid-code-review fixups) + `4e20030` (heid-bug-hunt fixups), tags v0.21.16.18; full House Code Discipline,
both heid panels cleared. Suite **488 green**; **LIVE SMOKE on :8081/b128** drove `--whoami` (identity+caps) +
`--characters` (models→create→PAD read-back→delete) end-to-end. Slice-5 migrated
`get_me`/`get_capabilities`/`list_character_models`/`create_character`/`get_character_state`/`delete_character`
onto `client.me`/`.capabilities`/`.models`/`.characters.*` (all open-world reads → `SessionApiFailed` default,
**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
— the heid CODE-REVIEW caught null/element, the cold BUG-HUNT caught the container layer below it, run BOTH),
**element-type** (`isinstance(m, dict)`), **top-level-mapping** (`isinstance(_, Mapping)` before any `.get`; a
non-mapping passthrough → AttributeError); never hard-index `info["x"]`. The SDK **normalizes ANY transport
failure to `ConnectFailed(status=0)`** (NOT raw httpx) — every adapter caller `except ConnectFailed`.
**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-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`.
**✅ COMPLETE — worldtree-sdk cutover (issue #20): ALL 7 SLICES DONE, shipped as v0.22.0 (2026-07-19).**
Ratatoskr's CONSUMER client layer is fully cut over from hand-rolled httpx wrappers to **worldtree-sdk (Python)
1.0.0** behind the thin `ratatoskr.wt` adapter (operator ADOPT ruling 2026-07-18; 6 DECs, vor-cross'd +
heid-panel-reviewed; contract `docs/contracts/worldtree_sdk_cutover.contract.md` with slice-1..7 notes canonical).
Six route-family slices (foundation / sessions-turn / persona-authored / agents-tier3 / characters-me / admin) +
slice-7 teardown, each through the full House Code Discipline (adapter→cli/web→delete→live-smoke→both heid gates).
Suite **494 green**. Per-slice arcs → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-{1-2,3,4,5,6,7}-complete.md`.
**Slice-7 teardown** (`ec68b1f`, v0.22.0, zero runtime-logic): dropped `httpx-sse` (SDK owns SSE parsing; venv
pruned, nothing imported it); KEPT `sessions.py`+`sse_client.py` as pure caller-semantic type/exception homes
(operator decision A1 — no rename/fold; A3 blocked by the `AgentNotAvailable` name-collision + `endpoint_for_plane`
provider-homing); retired wire contracts #2/#15 (files deleted; **#1 SSE-vocab + first_message KEPT**, not retired);
final coverage-map re-anchor.
**KEY ADAPTER FOOT-GUNS (cumulative — still live for ANY future consumer/SDK work):** SDK reads = **open-world
dicts**, presenters MUST degrade-not-crash guarded at THREE levels — **container-type** (`isinstance(_, list)`: a
scalar `123` is non-iterable, `or []` catches null but NOT a truthy non-iterable), **element-type**
(`isinstance(m, dict)`), **top-level-mapping** (`isinstance(_, Mapping)` before any `.get`); never hard-index. The
SDK **normalizes ANY transport failure to `ConnectFailed(status=0)`** (not raw httpx) — every adapter caller
`except ConnectFailed`. **Caller-semantic exceptions the adapter raises + a `-m` CLI catches must NOT live in the
`-m` module** (double-module class-identity split → uncaught traceback; homed in `sessions.py`, never run as
`__main__`). `TurnEvent.turn_id` ABSENT on text/thinking frames; `consumer_key` is BOUND-create-only. Scope was
consumer layer ONLY; Bifrost provider untouched. Design → auto-memory `project_worldtree_sdk_cutover`.
**▶️ POST-CUTOVER PENDING (non-blocking, operator's call):** (a) `git push` origin — the `ec68b1f` commit +
**v0.22.0 tag are LOCAL only**; (b) althing announce of v0.22.0 to worldtree-dev / wtsdk-dev per the SemVer
push-notify (post-push; Rata is the SDK reference consumer). Nothing else pending on the cutover.
**✅ RESOLVED — tier3 agents `model``role` (scope B) folded into cutover slice-4** (`c62b4ee`, v0.21.13). The
deferred deploy-gated scope-B work (response `model``role` per spec 1.2 / b128, `LocalAgentEntry`, index schema
@@ -132,11 +118,11 @@ 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.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
**Substrate / environment:** branch `main` at **v0.22.0** — slices 1-5 (`b1fbadd``4e20030`, v0.21.3.18)
PUSHED to origin; **slice-6 arc `de9a5ba``11ae2f0` (v0.21.19.20) AND slice-7 `ec68b1f` (v0.22.0) + this
snapshot are LOCAL — NOT yet pushed** (operator's call). origin
`git@gitea.phasefinal.com:vh/ratatoskr.git`. **Core dep:
`worldtree-sdk==1.0.0`** (gitea PyPI, `[tool.uv.sources]`; **`httpx-sse` REMOVED at slice-7** — SDK owns SSE). bifrost
**`==1.1.4`** / wire v0.7; WT openapi vendored 2.3.0, **conversation-api-spec re-synced to v1.1** (`b4a278c`);
**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**
@@ -298,6 +284,8 @@ decision. Captures rationale that won't be obvious from code alone.
- `[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`
- `[2026-07-19]` **worldtree-sdk cutover SLICE-7 (teardown, the LAST) COMPLETE — the whole cutover shipped as v0.22.0 (`ec68b1f`, minor, operator-approved).** Zero runtime-logic teardown: httpx-sse dropped (SDK owns SSE), wire contracts #2/#15 retired (files deleted; #1+first_message kept), `sessions.py`/`sse_client.py` KEPT as pure type/exception homes (operator decision A1 — no rename/fold), coverage-map re-anchored. All 7 slices done, 494 green; push + althing-announce pending (operator's call). → `persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-7-complete.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._
_For per-issue TDD implementation notes, Volva findings, and contract amendments, see the git log — every per-issue commit carries a structured message capturing the trail._
+2 -2
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "ratatoskr"
version = "0.22.0"
version = "0.22.1"
description = "Worldtree Conversation API debug console (web + headless CLI) — multi-pane observability"
readme = "README.md"
requires-python = ">=3.12"
@@ -29,7 +29,7 @@ web = [
# from the debug TUI. Recipe: bifrost/docs/implementing-a-consumer.md.
provider = [
"ratatoskr[web]", # reuse the starlette + uvicorn ASGI stack
"bifrost==1.1.4", # consumer engines + library. 1.1.4 = hasattr-gate backstop for the maintenance verbs (mark_superseded/mark_invalid/patch_many/delete_many/upsert_edges/get_edges_for → unimplemented verb degrades to unsupported_capability 400, never AttributeError/500/retry-storm; we surfaced it via WT #364) + 1.1.3 scan/cursor conformance harness + 1.1.2 frozen-v0.6 fix. 1.1.1 = frozen-wire serialization fix (ADR-0008): additive capability fields are gated on the NEGOTIATED wire, so a v0.6-negotiated describe_store handshake stays v0.6-clean. 1.1.0 leaked the v0.7-additive `sortable_chunk_fields` into v0.6 StoreCapabilities → a strict v0.6 client (additionalProperties:false) rejects our server's handshake. Wire schemas + pins UNCHANGED (serialization-correctness only); our v0.7 handshake with Worldtree b47 is unaffected. (1.1.0 = wire v0.7 additive: memory.scan sort + sortable_chunk_fields; 1.0.0 = first STABLE, wire v0.6 FROZEN; 0.8.0/v0.6 scope_all/scope_any #11; 0.7.0/v0.5 agent_self)
"bifrost==1.1.5", # consumer engines + library. 1.1.5 = gate ALL optional store verbs → clean unsupported_capability (not 500), extending 1.1.4's maintenance-verb backstop to the full optional-verb set + a v0.6 memory verb-floor conformance harness (frozen wire v0.6, no schema change). 1.1.4 = hasattr-gate backstop for the maintenance verbs (mark_superseded/mark_invalid/patch_many/delete_many/upsert_edges/get_edges_for → unimplemented verb degrades to unsupported_capability 400, never AttributeError/500/retry-storm; we surfaced it via WT #364) + 1.1.3 scan/cursor conformance harness + 1.1.2 frozen-v0.6 fix. 1.1.1 = frozen-wire serialization fix (ADR-0008): additive capability fields are gated on the NEGOTIATED wire, so a v0.6-negotiated describe_store handshake stays v0.6-clean. 1.1.0 leaked the v0.7-additive `sortable_chunk_fields` into v0.6 StoreCapabilities → a strict v0.6 client (additionalProperties:false) rejects our server's handshake. Wire schemas + pins UNCHANGED (serialization-correctness only); our v0.7 handshake with Worldtree b47 is unaffected. (1.1.0 = wire v0.7 additive: memory.scan sort + sortable_chunk_fields; 1.0.0 = first STABLE, wire v0.6 FROZEN; 0.8.0/v0.6 scope_all/scope_any #11; 0.7.0/v0.5 agent_self)
"jsonschema>=4", # bifrost runtime dep — envelope validation
"sqlite-vec>=0.1.6", # vector index for the memory plane (vec0 virtual table)
]
Generated
+5 -5
View File
@@ -70,14 +70,14 @@ wheels = [
[[package]]
name = "bifrost"
version = "1.1.4"
version = "1.1.5"
source = { registry = "https://gitea.phasefinal.com/api/packages/vh/pypi/simple/" }
dependencies = [
{ name = "jsonschema" },
]
sdist = { url = "https://gitea.phasefinal.com/api/packages/vh/pypi/files/bifrost/1.1.4/bifrost-1.1.4.tar.gz", hash = "sha256:498d156035a93bf37a6fc1e9c09b468aac61e869fd2a5353e2695dc823f57e9a" }
sdist = { url = "https://gitea.phasefinal.com/api/packages/vh/pypi/files/bifrost/1.1.5/bifrost-1.1.5.tar.gz", hash = "sha256:8554b73e5b4f9d285cf91bb3d5e2e668ae7880b91601637394433cd9b9a149f8" }
wheels = [
{ url = "https://gitea.phasefinal.com/api/packages/vh/pypi/files/bifrost/1.1.4/bifrost-1.1.4-py3-none-any.whl", hash = "sha256:d67278528f12729eef0c19d875d36a2f1da6fa97737396ba130d525fee8d0b14" },
{ url = "https://gitea.phasefinal.com/api/packages/vh/pypi/files/bifrost/1.1.5/bifrost-1.1.5-py3-none-any.whl", hash = "sha256:1cdcc893a5e04f58bfa1fac33fad62c930ba58b3594a9fd6eb54162a761160d2" },
]
[[package]]
@@ -463,7 +463,7 @@ wheels = [
[[package]]
name = "ratatoskr"
version = "0.22.0"
version = "0.22.1"
source = { editable = "." }
dependencies = [
{ name = "httpx" },
@@ -495,7 +495,7 @@ web = [
[package.metadata]
requires-dist = [
{ name = "bifrost", marker = "extra == 'provider'", specifier = "==1.1.4", index = "https://gitea.phasefinal.com/api/packages/vh/pypi/simple/" },
{ name = "bifrost", marker = "extra == 'provider'", specifier = "==1.1.5", index = "https://gitea.phasefinal.com/api/packages/vh/pypi/simple/" },
{ name = "httpx", specifier = ">=0.27" },
{ name = "jsonschema", marker = "extra == 'provider'", specifier = ">=4" },
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.11" },