44138590ad
Step 3 of the Worldtree #204 integration: visible persona-state UX. Pairs with v0.11.0's AffectUpdate SSE event + v0.12.0's get_persona_state HTTP client — together those gave the data; this bump surfaces it. Two surfaces (Option C: both): Sticky persona-header (top of screen, dock=top, height=1): - Shape: `agent_id · dominant_emotion · pad(P, A, D) · N emotions active` — concise enough for at-a-glance scan above the chat - Starts hidden via `.empty` CSS class; height collapses to 0 when the agent has no persona surface - Refreshes on AffectUpdate(status="current") snapshots Persona TabPane (Ctrl+4): - Full snapshot detail: dominant emotion, PAD axes with baseline + delta, mood drift, active emotions with intensity + decay (minutes-rounded), last_updated_at footer - Replaced (not appended) on each new snapshot — snapshots are absolute state, not incremental Lifecycle: - on_mount spawns a Textual worker that calls get_persona_state to hydrate header + pane before turn 1 - PersonaNotConfigured (domari, muninn, Tier 3) → pane carries an italic placeholder, header stays empty - AgentNotAvailable / AuthScopeDenied / network error → italic failure placeholder; audit-logged; never crashes - Presenter's render() takes an optional `on_persona_snapshot` callback so AffectUpdate(current) refreshes both surfaces during a live turn (no widget coupling — App owns the callback) Tests: 10 new (4 formatters, 3 presenter callback, 4 layout/binding/ hydration). Full suite: 313 passing.