67 lines
4.8 KiB
Markdown
67 lines
4.8 KiB
Markdown
`[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 `model`→`role` 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:
|
||
null` → `None.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.
|