Commit Graph

3 Commits

Author SHA1 Message Date
vh ca6af6bdaa feat(#18): affect.fetch — adopt bifrost 0.10.0 mandatory fetch (D1 prerequisite)
bifrost 0.10.0's _supports_affect_plane (bifrost/affect.py:75-80) now requires a
callable fetch for the affect capability to advertise/dispatch at all (INV-012
strong-or-absent), so an emit-only store 400s on EVERY affect op — repinning past
the affect.fetch release (#12/#13) breaks our shipped affect plane until fetch
exists. Implement affect.fetch as a thin async wrapper over the existing get()
read seam, conformed verbatim to the reference InMemoryAffectStore.fetch:
{"found": False} or {"found": True, "snapshot": <verbatim>}, AffectInvalidArguments
on empty ids, opaque (INV-001 — never reads pad/valence).

This is the forced prerequisite for the #18 D1 composite (build_combined_app),
and a new Worldtree I/O point consumed (affect read-back over bifrost).

- Repin bifrost>=0.8.0 -> >=0.10.0 (uv lock: 0.8.0 -> 0.10.0)
- affect_store.py: add async fetch() over get()
- contract bifrost_affect_provider v1.2: fetch FN block + INV-010 (cap = supported+emit+fetch)
- tests: 3 fetch unit + parity_vs_reference_fetch through dispatch_affect_call
- suite 482 -> 486 green
2026-06-19 22:59:59 -07:00
vh 39eebd1a55 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.
2026-06-19 21:56:15 -07:00
vh d90a58dc48 feat(provider): SQLite affect store + ASGI wiring — #195 parity green
The first slice of ratatoskr-as-Tier-3-Bifrost-consumer: a SQLite-backed,
conduit-opaque affect store Worldtree emits Tier-3 persona/affect snapshots
into, plus the thin build_affect_provider_app wiring. The bifrost library
owns the wire; this owns only the store + glue.

- ratatoskr.provider.affect_store: open_affect_store / emit / get /
  build_affect_provider_app. Two-table SQLite schema (snapshots + idempotency).
- Conduit-opaque (INV-001): reads only the two addressing keys; whole-blob
  hash only. LWW-by-arrival across distinct keys; replay-or-conflict
  idempotency (actor-scoped from ctx), raising bifrost's typed exceptions.
- Conformance: #195 parity vs InMemoryAffectStore through the real
  dispatch_affect_call engine. 17 provider tests; 395 full suite.
- Contract v1.1: idempotency model corrected to bifrost's actual semantics
  (caught by real-lib TDD; the artifact-only review structurally could not).
  Heid-panel reviewed (contract + code); idempotency-cache TTL pruning,
  memory.* plane, and the combined two-plane server deferred (see Out of scope).
2026-06-14 15:24:00 -07:00