Files
ratatoskr/persistent-memory.d/2026-07-19-worldtree-sdk-cutover-slice-4-complete.md
T

4.8 KiB
Raw Blame History

[2026-07-19] worldtree-sdk cutover SLICE-4 COMPLETE + committed (c62b4ee+aed9429+477d98f, v0.21.13.15, 475 green).

Agents/Tier-3 family onto the ratatoskr.wt adapter + the modelrole fold (scope B), full House Code Discipline end-to-end (contract error-map → TDD → rewire → delete → live smoke → coverage re-anchor → both heid gates → fixups).

What moved onto the SDK (client.agents.*, open-world dicts, errors mapped by route+(status,error_code) per INV-CUT-2): list_agents (agents.list), get_persona_state (agents.persona_state; 404 persona_not_configured / 404 agent_not_available / 403 auth_scope_denied dual-key), define_agent (agents.define; 429→Tier3QuotaExceeded(retry_after=0) status-only, 403 tier3_user_id_unsupported, 422 layer_deferred), patch_agent (agents.patch; bare-404→ Tier3AgentNotFound, 422 field_not_mutable), delete_agent (agents.delete; bare-404). Rewired the python -m ratatoskr.tier3 CLI + the web _agents_endpoint / _persona_state_endpoint. DELETED the hand-rolled sessions.list_agents / get_persona_state / AgentInfo and tier3.define/patch/delete_agent / Tier3AgentInfo / parse+extract helpers.

model→role fold: define/patch response echoes role (spec 1.2 / b128), read off the open-world dict; LocalAgentEntry.model.role, local-index _SCHEMA_VERSION 1→2 (old index discarded, no-compat). project_tier3_agents_model_to_role_pending is RESOLVED by this — the deferred scope-B work landed here (doing it standalone would have been throwaway).

KEY SLICE-4 FOOT-GUNS (for slices 5-7 + any tier3 work):

  • python -m double-module exception identity. Running tier3 as __main__ while wt imports ratatoskr.tier3 bound TWO copies of each Tier3* class → a raised Tier3AgentNotFound escaped the CLI's except as an uncaught traceback (exit 1, not the mapped exit 20). Unit tests call main() in-process (module is ratatoskr.tier3, not __main__) so they NEVER hit the split — the LIVE SMOKE caught it. Fix: the Tier3* exceptions live in sessions.py (never run as __main__) → single class identity, and it removed the wt→tier3 import edge. Rule: caller-semantic exceptions the adapter raises + a -m CLI catches must NOT live in the -m module.
  • Open-world presenters must degrade, never crash. The heid-bug-hunt panel (5/5/5, two 3/3 crash sites) caught the CLI (_run_define/_run_patch) and web (_agents_endpoint) HARD-INDEXING the open-world dicts (info["agent_id"], {a["agent_id"] for a in upstream}) → KeyError/TypeError on a partial/drifted 2xx response (incl. system_prompt: nullNone.splitlines() AttributeError). The wt tests + live smoke used FULL server dicts, so it never surfaced. Fix: _str_field (absent/null/non-str → default) in the CLI; well-formed-mapping filter in the web endpoint; a no-agent_id 2xx → controlled exit 20. The invariant "open-world reads degrade, never crash the presenter" must hold at EVERY presenter, not just the adapter.

Both heid gates cleared:

  • code-review (panel, zero adapter/error-map/model→role drift): 3 fixups (v0.21.14, aed9429) — persona endpoint now catches wt.SessionApiFailed (parity with 3 sibling endpoints; a latent PRE-cutover gap, NOT a slice-4 regression); dual-key NEGATIVE tests for define/patch + flat-field test; contract documents the ":" in agent_id PRE.
  • bug-hunt (panel 5/5/5, no false positives): 3 fixups (v0.21.15, 477d98f) — the two open-world presenter crash sites above + _error_field_from_body type-checks field is str. HELD (contract-intended, Heid-confirmed): the 429→quota / bare-404→not-found status-only maps (the arms flagged them spec-free; the § Error map specifies them; the SDK's ApiError floor drops Retry-After so retry_after=0 is canonical). Dual-keying define's 429 for full row consistency is an available tightening (contract amendment), surfaced not applied.

LIVE-SMOKE on personal :8081 (b128): valid agent roles are thoughtful-character / character / assistant (NOT the /models/available-for-characters char-rp — that's a character-model, a different vocab; define 422s on it). define→patch→list(6 agents: forseti/lofn/mask/mimir/vili/…)→persona_state(→PersonaNotConfigured mapped)→delete→index empty; non-existent-id patch via -m[agent_not_found] exit 20 (double-module fix proven). Throwaway ratatoskr:slice4-smoke deleted, server left clean.

NEXT = slice-5 (characters + me/capabilities/models — the remaining consumer reads); then slice-6 (admin: stream_admin_events + get_session_bifrost, admin_auth), slice-7 (teardown: delete residual hand-rolled, drop httpx-sse, retire contracts #2/#15, MINOR bump per DEC-6 w/ operator approval). Consumer layer ONLY; Bifrost provider planes untouched.