feat(#18): PAD read-endpoint — web pane renders live PAD/valence from our affect store (Deliverable 2)

The web persona pane now renders live PAD/valence for Tier-3 agents from our
:8390 affect store, closing the persona-telemetry gap (Worldtree persona_state
404s for Tier-3 per ADR-0009; Tier-3 emits no affect_update SSE).

- provider: non-bifrost GET /affect/state/{agent_id} on the affect-store-owning
  app (add_route — keeps /bifrost/* top-level + op-feed-skipped); explicit
  no_affect_snapshot 404 (never a zeroed PAD); busy_timeout + check_same_thread
  on the connection.
- web: GET /api/affect/{agent_id} proxy — end_user_id server-supplied (never the
  browser), colon-id round-trip, configured RATATOSKR_AFFECT_READ_URL.
- pane: honest affect render (pad + valence + emitted_at, labelled "affect", no
  fabricated Tier-1 fields); explicit empty-state; polls 2s post-turn.

Contract-first (docs/contracts/issues/18.contract.md, Deliverable-2-scoped;
Deliverable 1 / composite endpoint deferred — bifrost-blocked on a public
build_combined_app, WT dispatch confirmed single-endpoint caps-routed).
Heid-code-review panel: 1 INV-001 drift (strip fabricated "neutral") + 4
test-gaps fixed. Live-smoke PROVEN: web->provider->affect.db chain returns real
sindra/vuong PAD; Playwright DOM check confirms the pane render + the fix.

Suite 482 green.
This commit is contained in:
vh
2026-06-19 21:56:15 -07:00
parent f3bac46238
commit 39eebd1a55
10 changed files with 626 additions and 6 deletions
+35
View File
@@ -277,6 +277,41 @@ class TestOpFeedAffect:
assert ev.req_summary == {} # conduit-opaque
assert ev.resp_summary == {"stored": True}
async def test_pad_read_route_emits_no_opevent(self) -> None:
"""INV-004 (#18 D2): the non-bifrost PAD read route is OUTSIDE _BIFROST_PATHS,
so the op-feed passes it through and records NO OpEvent — observe is bifrost-
only and the read path adds no plane attribution."""
import json as _json
sink = _RecordingSink()
app, store = _wrapped_affect_app(sink)
blob = _json.dumps(
{
"agent_id": "ratatoskr:sindra",
"end_user_id": "vuong",
"pad": {"pleasure": 0.1, "arousal": 0.0, "dominance": 0.0},
"valence": [],
"emitted_at": "2026-06-18T00:00:00+00:00",
},
sort_keys=True,
separators=(",", ":"),
)
store._conn.execute(
"INSERT INTO affect_snapshots (agent_id, end_user_id, snapshot_json, arrived_at) "
"VALUES (?, ?, ?, ?)",
("ratatoskr:sindra", "vuong", blob, "0"),
)
store._conn.commit()
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(
transport=transport, base_url="http://provider"
) as client:
resp = await client.get(
"/affect/state/ratatoskr:sindra", params={"end_user_id": "vuong"}
)
assert resp.status_code == 200
assert sink.events == [] # op-feed recorded nothing for the non-bifrost route
class TestInstrumentGuards:
def test_unknown_plane_raises(self) -> None: