From 4f16ba588dda51368425573d740ca4933360ae4c Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Fri, 19 Jun 2026 23:57:01 -0700 Subject: [PATCH] =?UTF-8?q?memory:=20snapshot=20=E2=80=94=20#18=20CLOSED?= =?UTF-8?q?=20end-to-end=20+=20v0.18.0=20(composite=20both-plane=20binding?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #18 D1 SHIPPED: build_combined_provider_app on :8392 wraps bifrost 0.10.0's public build_combined_app over both stores + the shared affect read route; one bound WT session drives memory.* AND affect.* through one endpoint; op-feed plane='combined' per-path. Shipped v0.17.15 (affect.fetch, the strong-or-absent prerequisite) -> v0.17.16 (composite) -> v0.17.17 (#17 op-feed field-name fix) -> v0.18.0 (publishing minor). Suite 503 green. Live-smoke PROVEN at wire+dispatch (real stores + bifrost 0.10.0 on a running :8392): handshake grants both caps, PAD read route serves real sindra PAD, both planes dispatch at one bound session_id. WT-driven turn gated on infra-ops adding :8392 to WT's BIFROST_CLIENT_ALLOWED_HOSTS (requested). New decisions: reference-impl-adopt-canonical (operator); v1-derived-from-WT-I/O-coverage (operator). New foot-guns: memory-store check_same_thread bug (same as affect D2, exposed by the contract-mandated search test via TestClient); :8392 infra-allowlist gate; heid-review test-fidelity nudge cascaded into 2 latent-bug fixes. --- persistent-memory.md | 88 +++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/persistent-memory.md b/persistent-memory.md index 746d762..3ff37ab 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -1,6 +1,6 @@ # Persistent memory — ratatoskr -_Last updated: 2026-06-18_ +_Last updated: 2026-06-19_ This file captures durable intent and supporting evidence (goals, decisions, foot-gun warnings, in-flight state) across context resets. Read it at session @@ -41,37 +41,39 @@ upstream API key stays server-side (INV-003). _As of 2026-06-19:_ -**#18 DELIVERABLE 2 SHIPPED + PUSHED — the persona-telemetry gap is CLOSED.** The web -pane now renders live PAD/valence for Tier-3 agents from OUR `:8390` affect store -(`v0.17.14`, `39eebd1`, suite **482 green**, **pushed to origin**). Three pieces: -provider read route `GET /affect/state/{agent_id}` (non-bifrost, added to the affect app -via `app.add_route` — keeps `/bifrost/*` top-level + op-feed-skipped); web proxy -`GET /api/affect/{agent_id}` (server-supplied `end_user_id`, colon-id `quote()`'d, -`RATATOSKR_AFFECT_READ_URL` config, default `127.0.0.1:8390`); pane affect-render -(`renderAffectPane`/`loadAffect`, honest pad+valence+emitted_at, labelled "affect", NO -fabricated Tier-1 fields, explicit empty-state, 2s post-turn poll). Live-smoke + a -Playwright DOM check PROVEN against real sindra/vuong PAD. The push also published the -previously-held **#17** arc (`v0.17.8`→`v0.17.13`) — origin/main is now fully caught up. +**#18 CLOSED end-to-end — the composite ships (`v0.18.0`).** Both deliverables landed. +**D2** (PAD read-endpoint, `v0.17.14`) renders live PAD/valence in the web pane from our +`:8390` store. **D1** (composite endpoint, `v0.17.16` `7f4ceaa`) — `build_combined_provider_app` +(`provider/combined.py`) on `:8392` wraps bifrost's public `build_combined_app` over BOTH +stores + mounts the SHARED affect read route (`add_affect_read_route`); one bound Worldtree +session drives memory.* AND affect.* through ONE endpoint. Op-feed derives plane per request +path (`plane='combined'`). Published as **`v0.18.0`** (`359dbb1`). Suite **503 green**. -**#18 DELIVERABLE 1 (composite `:8392` endpoint) — PARKED on bifrost** (tracked Gitea #18). -Routed to bifrost-dev for a public `build_combined_app` rather than hand-rolled from -bifrost privates (debug-surface-uses-canonical principle). bifrost-dev confirmed it: clean -additive minor (~`v0.9.0`), design locked (advertise-by-presence handshake, per-route -call-time isolation), slotted AFTER WT #289. FR-1 RESOLVED — composite is bifrost-only, -ZERO Worldtree change (single-endpoint caps-routed, worldtree-dev code-verified). NEXT: -when bifrost ships `build_combined_app`, **repin + reimplement D1 against it** (per-plane -failure status + op-feed plane-per-request derivation already specced in the issue). -Nothing blocks on our side. +**LIVE-SMOKE PROVEN at wire+dispatch** (real stores + bifrost 0.10.0 on a running `:8392`): +handshake grants BOTH caps by store presence; the PAD read route serves real sindra PAD; a +memory `search` + an affect `emit` both dispatch through the one endpoint at a single bound +`session_id` with the op-feed deriving plane per path. **The only un-run leg is a +Worldtree-DRIVEN turn — GATED on infra-ops adding `http://10.100.10.50:8392` to WT's +`BIFROST_CLIENT_ALLOWED_HOSTS`** (`:8390/:8391` allowlisted, `:8392` not → bind 422s; requested +via infra-ops `01KVHWJGTT…`). FR-1 already code-verified the WT side (one BifrostClient, both +stores attach off one endpoint iff caps granted), so this is empirical confirmation of +known-good behavior, not open risk. -**OPERATOR SESSION STATE — running shells are PRE-#18 code (foot-gun).** web `:8765` + -affect `:8390` + memory `:8391` are the prior session's background shells running OLD code -(no read route; web has no `RATATOSKR_AFFECT_READ_URL`). To see D2 live in the operator's -own session, RESTART `:8390` (affect provider, new code → gains the read route) + `:8765` -(web, new code + `RATATOSKR_AFFECT_READ_URL=http://127.0.0.1:8390` + `RATATOSKR_END_USER_ID`). -This session's live-smoke used THROWAWAY `:8393`/`:8766` instances vs the same `affect.db` to -avoid disrupting them. Consumer/owner key = `wt_live_d81b…`; providers SQLite + sqlite-vec, -`memory.db`/`affect.db` at repo root (affect.db has live sindra PAD: vuong pleasure 0.146, -familiarity 0.589, interaction_count 8). +**bifrost repinned 0.8.0 → 0.10.0** (floor, `provider` extra). 0.10.0 made `affect.fetch` +MANDATORY (strong-or-absent: `_supports_affect_plane` requires `affect_supported`+`emit`+`fetch`, +gating EVERY affect op incl. emit) — so the repin FORCED `affect.fetch` (`v0.17.15`, conformed +to bifrost's reference `InMemoryAffectStore.fetch` → `{found, snapshot?}`) or our shipped affect +plane would 400. The composite's affect cap depends on it. + +**OPERATOR SESSION STATE — running shells are PRE-#18 code (foot-gun).** web `:8765` + affect +`:8390` + memory `:8391` are the prior session's background shells running OLD code. A +throwaway **`:8392` composite smoke instance is RUNNING** (background, NEW code, shared +`affect.db`/`memory.db` — the two-process topology, proven). To see the full stack on new code, +RESTART `:8390`/`:8765` from current code (D2 web needs `RATATOSKR_AFFECT_READ_URL`); the `:8392` +smoke instance is ready for the WT-turn once `:8392` is allowlisted. Consumer/owner key = +`wt_live_d81b…` (`~/.config/ratatoskr/provider.env`, mode 600, rotate via infra-ops); providers +SQLite + sqlite-vec, `memory.db`/`affect.db` at repo root (affect.db: live sindra PAD vuong +pleasure 0.146, familiarity 0.589, 8 interactions). **Tier-3 memory PROVEN end-to-end** (earlier this session): `ratatoskr:terse-probe` cold-recalled a seeded user fact (scope_any → 1 hit @ cosine 0.6994), and the verbose @@ -84,15 +86,15 @@ linguistic layer → Worldtree #305). `:8081` runs v0.36.0. (separate `consumer_agents` table) — invisible to `GET /agents`; check via `GET /agents/:` with the owner key. -**Standing:** Worldtree spec pin v0.35.16 (`f1b59f8`); bifrost 0.8.0 / wire v0.6 -(`scope_all`+`scope_any`); WT handshake now advertises `bifrost_version 0.6.0` (worldtree-dev -honesty-fix FYI `858ba58` — we don't pin/assert it, no-op our side). Heimdall key env-only -at `~/.config/ratatoskr/provider.env` (mode 600); rotate via infra-ops. `graphify-out/` -runs dirty (auto-regen, not chased). Open issues: #10 (subject migration), #11 (AdminEvents -pane) — deferred; **#18** (D2 PAD-read SHIPPED `v0.17.14`; D1 composite PARKED on bifrost -`build_combined_app`). Codex-first pilot dormant. +**Standing:** Worldtree spec pin v0.35.16 (`f1b59f8`); **bifrost 0.10.0 / wire v0.6** +(`scope_all`+`scope_any`). Heimdall key env-only at `~/.config/ratatoskr/provider.env` (mode +600); rotate via infra-ops. `graphify-out/` runs dirty (auto-regen, not chased). Open issues: +#10 (subject migration), #11 (AdminEvents pane — the next-reachable Worldtree-I/O coverage gap, +unblockable via an `admin.events.read` scope request) — deferred; **#18 CLOSED** (D1+D2 shipped, +`v0.18.0`). Codex-first pilot dormant. -Branch: `main` (== `origin/main` @ `39eebd1`). Remote: `origin → git@gitea.phasefinal.com:vh/ratatoskr.git`. +Branch: `main` (== `359dbb1`, tag `v0.18.0`) — **4 commits AHEAD of `origin/main` (`39eebd1`), +not yet pushed** (push is the operator's call). Remote: `origin → git@gitea.phasefinal.com:vh/ratatoskr.git`. ## Recent decisions @@ -127,6 +129,12 @@ decision. Captures rationale that won't be obvious from code alone. - `[2026-06-18]` **#18 D2 implemented via direct in-session TDD (suite 470→482).** Provider read route `GET /affect/state/{agent_id}` added via `app.add_route` (NOT an outer `Mount` — keeps `/bifrost/*` top-level so the existing route test + the op-feed path-check stay valid); web `GET /api/affect/{agent_id}` proxy (server-supplied `end_user_id`, colon-id `quote()`'d, `RATATOSKR_AFFECT_READ_URL`); pane renders the affect-emit shape honestly. Contract `docs/contracts/issues/18.contract.md` (D2-scoped; D1 deferred). **heid-code-review panel (Gróa 5 / Hulda 3 / Regin 0): 1 real INV-001 drift + 4 test-gaps, all fixed.** No contract amendments (code was wrong, contract was right). - `[2026-06-19]` **#18 D2 SHIPPED (`v0.17.14`, `39eebd1`) and the full #17+#18 arc PUSHED to origin.** Live-smoke PROVEN against real data (throwaway `:8393`/`:8766` vs the real `affect.db` → real sindra/vuong PAD through the full web→provider chain; Playwright DOM check confirmed the pane render + the F1 fix — no fabricated "neutral"). The push carried 9 previously-held commits incl. the deliberately-unpushed #17 (`v0.17.8`→`v0.17.13`); origin/main now == `39eebd1`, tag `v0.17.14`. +- `[2026-06-19]` **bifrost repinned 0.8.0→0.10.0; `affect.fetch` became MANDATORY (strong-or-absent).** 0.10.0's `_supports_affect_plane` requires `affect_supported`+`emit`+`fetch` and gates EVERY affect op — an emit-only store 400s. Implemented `affect.fetch` (`v0.17.15`, `ca6af6b`) conformed to bifrost's reference `InMemoryAffectStore.fetch` (`{found, snapshot?}`): the forced D1 prerequisite + a new Worldtree I/O point consumed. Flagged the now-stale consumer-guide line to bifrost-dev (fixed `a2e6d62`). +- `[2026-06-19]` **#18 D1 SHIPPED — composite `build_combined_app` on `:8392` (`v0.17.16`, `7f4ceaa`); #18 CLOSED; published `v0.18.0` (`359dbb1`).** `build_combined_provider_app` wraps bifrost's public builder over both stores + the shared read route; op-feed `plane='combined'` per-path. Direct in-session TDD; heid-code-review panel (Gróa/Hulda/Regin) returned ZERO drift. Live-proven at wire+dispatch; WT-turn gated on infra-ops `:8392` allowlist. +- `[2026-06-19]` **op-feed handshake field-name fix (#17, `v0.17.17` `d60b77d`): `capabilities_requested`→`capabilities`.** The summary read a field that never exists on the wire (bifrost reads `capabilities`, `_protocol.py:181`) → caps_requested was always null. Surfaced by the heid panel (Regin) during the D1 review — a latent #17 bug, not D1 drift. +- `[2026-06-19]` **Ratatoskr is a REFERENCE implementation of the Worldtree/Bifrost standard (operator).** Adopt the dep's canonical way (even if ours works); INFORM of drift/gaps; ADVISE a different approach only when ours is genuinely better (dep owner decides), never unilaterally fork. [auto-memory `feedback-ratatoskr-is-a-reference-impl-adopt-canonical`] +- `[2026-06-19]` **Ratatoskr v1 is DERIVED from Worldtree I/O coverage (operator) — no self-defined feature ROADMAP.** v1 = consume all of Worldtree's I/O points, reached when Worldtree hits 1.0; the convergence target is a coverage map, not a 3–7 capability list. [auto-memory `project-ratatoskr-v1-derived-from-worldtree-io-coverage`] + _41 older entries (2026-05-* — the original debug-TUI/web build era) archived to archival-memory.md._ _For per-issue TDD implementation notes, Volva findings, and contract amendments, see the git log — every per-issue commit carries a structured message capturing the trail._ @@ -156,4 +164,8 @@ defense against re-attempting the same cul-de-sac. - `[2026-06-18]` **Rationalized away a KNOWN contract-invariant deviation during TDD — only the cross-model code-review caught it.** #18 D2's `loadAffect` called `setPersonaStrip(snap)`, which renders `dominant_emotion || "neutral"`; the affect snapshot has no `dominant_emotion`, so it fabricated a "neutral" emotion — violating the very INV-001 ("no synthesized Tier-1 fields") I had WRITTEN. I knew the strip did this and talked myself into it as acceptable. Neither the design panel nor TDD caught it (unit tests don't exercise the JS render); the post-implementation `/heid-code-review` did (Gróa + Hulda both). **Lesson: a known deviation from a contract invariant is drift even when you've rationalized it — flag it, don't argue yourself past it; the post-implementation cross-model review is the backstop for author-rationalized drift, distinct from the design-stage panel.** - `[2026-06-18]` **Latent SQLite thread-safety bug in the affect store, surfaced ONLY by the new HTTP read route.** `open_affect_store` created the connection without `check_same_thread=False`; the bifrost emit path never tripped it (uvicorn's loop ran on the connection's creating thread), but the `TestClient`-driven read route runs handlers off a worker thread → `sqlite3.ProgrammingError`. Fix: `check_same_thread=False` (safe — the event loop serializes access) + explicit `PRAGMA busy_timeout=5000` (don't rely on sqlite3's `timeout=5.0` default). **Lesson: a sqlite-backed ASGI app needs `check_same_thread=False`; the HTTP-layer test exposed what the direct-store-method tests structurally couldn't.** +- `[2026-06-19]` **The SAME `check_same_thread` sqlite bug recurred in the MEMORY store — exposed by the contract-mandated `search` dispatch test (TestClient = worker thread).** Heid's test-fidelity finding (the D1 dispatch test used `describe_store` where the contract says `search`) → fixing it to `search` tripped `sqlite3.ProgrammingError` because `open_memory_store` also lacked `check_same_thread=False`. Fixed (mirrors affect INV-006). **Lesson: this bug is PER-STORE — every sqlite-backed ASGI store needs `check_same_thread=False`; an HTTP-layer (TestClient) test exposes what direct-store tests can't, and the composite serving memory over HTTP makes it bite.** +- `[2026-06-19]` **Full WT-driven `:8392` live-smoke is infra-gated — `:8392` not in WT's `BIFROST_CLIENT_ALLOWED_HOSTS` (bind 422s).** New provider ports are NOT auto-allowlisted (only `:8390/:8391` are). Self-driven dispatch (minted consumer-key JWTs → `:8392`) is the wire-proof; the WT-turn needs infra-ops to add `:8392` (requested `01KVHWJGTT…`). +- `[2026-06-19]` **heid-code-review pulled MORE weight than its own "marginal" self-assessment.** The panel returned zero drift, but its single test-fidelity finding CASCADED into 2 real latent-bug fixes when applied (the memory `check_same_thread` bug + Regin's op-feed field-name bug). **Lesson: a contract-fidelity nudge can transitively expose bugs the test never reached — don't dismiss a "marginal" finding by its count.** + _18 older entries (2026-05-* — the original debug-TUI/web build era) archived to archival-memory.md._