feat(#20): characters + me/capabilities/models onto the wt adapter (slice-5)
Slice-5 of the worldtree-sdk cutover: migrate the remaining consumer READS + transient-character CRUD off the hand-rolled httpx wrappers onto the `ratatoskr.wt` adapter over the SDK, and delete the retired path. Adapter (`wt.py`): add `get_me` / `get_capabilities` / `list_character_models` / `create_character` / `get_character_state` / `delete_character` over `client.me` / `client.capabilities` / `client.models` / `client.characters.*`. All six are open-world reads/acks returned verbatim; none carries a discriminated SDK error, so each maps any `ApiError` → the `SessionApiFailed` default (INV-CUT-2) — exact parity with the retired path. No new Error-map rows. Decisions (contract § slice-5 notes): `create_character` omits `state` when None (SDK-idiomatic inline literal, server-equivalent to the retired explicit null); `delete_character` returns the SDK's open ACK verbatim (`-> Mapping|None`, not normalized to None). CLI rewire (`cli.py`): `--whoami` (me + capabilities) and `--characters` (models → create → state → delete) build a `wt.build_client` over the injected probe transport and catch `wt.SessionApiFailed` + `ConnectFailed`. Open-world degrade-not-crash carried (cumulative cutover foot-gun): `_characters_probe` reads `items` null-safe and extracts `character_id` defensively (clean abort, no hard-index KeyError); `_format_whoami` widened to `Mapping`. Deleted the six hand-rolled `sessions.py` wrappers (net -5 mypy no-any-return); `endpoint_for_plane` + `get_session_bifrost` (slice-6) + the exception classes stay. Retired the corresponding `test_sessions.py` classes; added the slice-5 adapter tests + a CLI malformed-create-abort test. LIVE SMOKE (:8081, b128) — INV-CUT-5 / DEC-4 cleared: `--whoami` rendered real identity + capabilities; `--characters` drove the full lifecycle end-to-end (char-rp catalog → created char_8c00006e… → PAD read-back → deleted). Suite 483 green; ruff clean; mypy at the 2 pre-existing baseline errors. Patch bump 0.21.15 → 0.21.16 (the cutover MINOR is deferred to slice-7, DEC-6).
This commit is contained in:
@@ -1923,6 +1923,27 @@ class TestTier2Probes:
|
||||
assert "deleted: char_z" in out
|
||||
assert del_route.call_count == 1 # lifecycle cleaned up
|
||||
|
||||
@respx.mock
|
||||
def test_characters_probe_create_missing_id_aborts(
|
||||
self, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""create ACK without character_id → clean abort (exit 20), never a hard-index
|
||||
KeyError (open-world degrade-not-crash; slice-5 cutover foot-gun)."""
|
||||
respx.get("https://w.example/models/available-for-characters").mock(
|
||||
return_value=httpx.Response(200, json={"items": []})
|
||||
)
|
||||
# 201 but the open-world ACK omits character_id — the probe must degrade.
|
||||
respx.post("https://w.example/characters").mock(
|
||||
return_value=httpx.Response(201, json={"ttl_expires_at": "t"})
|
||||
)
|
||||
del_route = respx.delete(url__regex=r"https://w\.example/characters/.+").mock(
|
||||
return_value=httpx.Response(204)
|
||||
)
|
||||
rc = main(["--characters", "--api-key", "k", "--server", "https://w.example"])
|
||||
assert rc == 20
|
||||
assert "no character_id" in capsys.readouterr().err
|
||||
assert del_route.call_count == 0 # aborted before state/delete — nothing to clean
|
||||
|
||||
@respx.mock
|
||||
def test_set_persona_probe(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
"""set_persona_probe [happy,tracer]: POST pad to /sessions/{id}/persona_state; 204."""
|
||||
|
||||
Reference in New Issue
Block a user