Composite Bifrost endpoint + PAD read-endpoint — memory + live PAD in one session (#17 follow-on) #18
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Follow-on to #17 (Bifrost-binding the chat client — shipped this session, v0.17.8–v0.17.13, live-smoke proven). #17 delivered single-plane bind + the dispatch-layer op-feed, with two limits deliberately parked at v1:
POST /sessionsbinds exactly onebifrost.endpoint_url. Our memory (:8391) and affect (:8390) providers are separate processes, so a session binds memory or affect — not both. A Sindra session that should both remember and show live PAD can't do both at once.affect.emitto our:8390store and it persists (live-proven this session: op-feedop:"emit", session_id:"7f65a975…", status:"ok", stored:true). But the web persona pane still reads Worldtree'spersona_state(loadPersona, index.html:707), which 404s for every Tier-3 colon-id agent (ADR-0009 — persona_state is Tier-1-only). So the pane prints "persona telemetry isn't exposed for Tier-3 … yet" even though our store HAS the PAD. The pane was never wired to read from our own store — the "persona-pane payoff" the #17 affect-binding was meant to unlock.Goal
Make a single bound Sindra session both remember (memory plane) and show live PAD/valence in the persona pane (affect plane), in one seat. Two deliverables:
1. Composite Bifrost endpoint (un-parks #17 open-question)
A single ASGI facade (new port, e.g.
:8392) fronting BOTH our memory + affect stores, advertising BOTHmemory+affectcaps at the handshake. One session binds it → Worldtree dispatches both memory-calls AND affect-calls to it. Bifrost's own reference server already mounts both planes in one app (reference_server/server.py: handshake + memory-call + affect-call routes taking both stores), so this is a thin combined builder mirroring the two existing ones (build_memory_provider_app/build_affect_provider_app).Per the #17 contract's parking note, this is NOT just "handshake lists both caps":
instrument_provider_appcurrently takes a FIXEDplaneparam — for the combined app it must derive plane per-request from the path (/bifrost/memory-callvs/bifrost/affect-call), which it already half-does in_op_from.2. PAD read-endpoint → persona pane renders our store
A small non-bifrost read route on the affect provider (recommended over the web server opening
affect.dbdirectly — keeps the provider the owner of its store, no web↔SQLite coupling). The store already hasget(agent_id, end_user_id)→ the PAD/valence snapshot. Then:GET /api/affect/{agent_id}) proxies it server-side;loadPersona(or a sibling) renders PAD/valence from OUR store instead of (or as fallback to) the Tier-3-gatedpersona_state.affect.fetchover bifrost is reserved/blocked (Worldtree can't fetch) — irrelevant here: we own the store and read it directly, no Worldtree dependency. (WT #289affect.fetch/affect:readis an OPTIONAL mediated-read — their intelligence over our raw store — NOT a prerequisite.)Operator decisions (this session)
Acceptance
memory+affect.searchAND an affectemit, each correlated to the bound session_id.ratatoskr:sindra) from OUR:8390store — no "telemetry isn't exposed" for affect-bound sessions.Dependencies / scope
endpoint_url)..contract.mdcovering both pieces before code.