diff --git a/persistent-memory.md b/persistent-memory.md index 8aed14b..b691a3a 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -47,7 +47,7 @@ _As of 2026-06-16:_ durable persistence provider Worldtree writes Tier-3 agent affect/persona + memory into — alongside the original debug-observability TUI/web. The Bifrost-consumer work lives in `src/ratatoskr/provider/` and depends on -`bifrost>=0.6.1` (a `provider` optional-extra from the gitea PyPI index), +`bifrost>=0.7.0` (a `provider` optional-extra from the gitea PyPI index), SEPARATE from the Worldtree conversation-API spec pin. **AFFECT plane: SHIPPED + LIVE-PROVEN** (v0.17.2). Running now as a dev @@ -58,8 +58,10 @@ durable row persisted, opacity held. **MEMORY plane: PROVIDER LIVE-PROVEN + recall-miss ROOT-CAUSED (upstream).** Store + dev shell shipped (v0.17.3, `cd12951`; running on `0.0.0.0:8391`). -`memory.db` holds 3 durable chunks (choc-fact `498ed752` sal 0.9, name `8241e569` -sal 0.8, promoted-question `c863bb6b` sal 0.9 — all scope `{end_user:smoke-user}`). +`memory.db` holds 5 durable chunks (all scope `{end_user:smoke-user}`): choc-fact +`498ed752`, name `8241e569`, promoted-question `c863bb6b`, + 2 LATE async promotions +from the cold-recall probe (probe-question `acc3d49`, model NON-ANSWER `4773704` — +salience promoted a "I don't have memory" refusal). All are #296 corpus, KEEP PINNED. **The #295 cold-recall miss is now ROOT-CAUSED and it's UPSTREAM, not ours** (2026-06-16 debug-assist with worldtree-dev): a self-driven bound cold-recall probe captured the inbound pair via our new observe log — Worldtree's recall sends @@ -67,16 +69,21 @@ probe captured the inbound pair via our new observe log — Worldtree's recall s chunks carry `{end_user}` ONLY; our AND `_scope_matches` (byte-faithful to bifrost reference `reference_server/memory.py:398`) drops everything on the unmatched `agent_self` axis → 0 hits → the model says "no memory". So **our store + search -are SOUND**; the fix is Worldtree-side. F2 (question-promotion) → research issue -**#296** (keyword-regex salience suspected fundamentally flawed; `c863bb6b` is a -corpus fixture, KEEP PINNED). F1 (recall-miss) → research issue **#297** -(Worldtree-local fix = per-visible-scope single-axis search unioned client-side; -HELD until the agent_self lattice question lands — see Recent decisions). +are SOUND**; the fix is Worldtree-side. F2 → research issue **#296** (keyword-regex +salience suspected fundamentally flawed; corpus = `c863bb6b` + the 2 late-promotions). +F1 → research issue **#297** (Worldtree-local fix = per-visible-scope single-axis +search unioned client-side). **The agent_self lattice question is RESOLVED:** +agent_self is now canonical in bifrost 0.7.0 / wire v0.5 (our foot-gun flag drove it; +worldtree-dev shipped it both sides — Worldtree v0.35.11) → #297 union build UNBLOCKED. +Our memory provider now runs **bifrost 0.7.0 + validates the 4-axis lattice** +`{end_user,group,tenant,agent_self}` (v0.17.5; restarted on it; out-of-lattice axis → +InvalidFilter, reference-parity restored). -**OBSERVE BRICK SHIPPED** (`memory_store.py`, uncommitted at snapshot time): +**OBSERVE BRICK SHIPPED** (`memory_store.py`, committed v0.17.4 `2fef6e3`): structured `[memory-provider]` request/response logging on the memory-call path — the first concrete brick of #17's observe half, and the lens that caught #295's -root cause. Live-verified. +root cause. Live-verified. (NOTE: this is the debug SHIM at the STORE method; #17's +real observe feed instruments the DISPATCH layer — see the contract INV-005.) **SELF-DRIVE PROVEN BY HAND** (2026-06-16): ratatoskr's own client drove a Bifrost-bound cold-recall end-to-end (bind → handshake 200 → turn → captured the @@ -92,7 +99,10 @@ v1 scope operator-locked: single-plane bind (composite endpoint PARKED) + dispatch-layer op-feed with session-level correlation (turn-pane UI PARKED). `/heid-contract-review` panel caught + fixed two real internal inconsistencies (OpEvent `turn_id` reservation made literal; the `session_id`-for-all-verbs -correction). NEXT: TDD tracer-bullet (bind affect plane → turn → assert OpEvent). +correction). NEXT: **TDD slice 1 = the `create_session` bind primitive** (BifrostBinding +dataclass + consumer-key per-request bearer override + missing-key precondition + +502→BifrostHandshakeFailed; respx-mocked), then endpoint_for_plane → dispatch-layer +op-feed → CLI/TUI/web → live smoke. **Sindra:** a REGISTERED Tier-3 agent (`ratatoskr:sindra`, was model `artemis-31b-v1i`) — registration is REQUIRED to use a Tier-3 character (a @@ -108,9 +118,10 @@ registration. `~/.config/ratatoskr/provider.env` (mode 600, nh3-dev) — `consumer="ratatoskr"`, HS256 = the API-key STRING utf-8-encoded; rotate via infra-ops. -**Committed (2026-06-16, NOT yet pushed):** the observe brick (logger, patch -bump), `docs/bifrost-self-test.md`, the #17 contract, + this snapshot. Push is -the operator's call. `graphify-out/GRAPH_REPORT.md` still runs dirty +**Committed (2026-06-16, NOT yet pushed):** observe brick (logger, v0.17.4 `2fef6e3`), +`docs/bifrost-self-test.md` + #17 contract (`ca02c70`), 4-axis-validation parity +(v0.17.5, tag `v0.17.5`), + this snapshot. Tags `v0.17.4`/`v0.17.5`. Push is the +operator's call. `graphify-out/GRAPH_REPORT.md` still runs dirty (auto-regenerated artifact, not chased). **Still standing from before:** Worldtree spec pin v0.29.0 (`562001a`) for the @@ -179,6 +190,9 @@ decision. Captures rationale that won't be obvious from code alone. - `[2026-06-16]` **Issue #17 v1 scope locked (operator 1A/2A): single-plane bind + dispatch-layer op-feed.** `BifrostBindingRequest` is one `endpoint_url` (one plane per session); composite-both-planes endpoint PARKED. Observe = structured op-feed instrumented at the DISPATCH layer (bifrost passes ctx to upsert_many but NOT search/get/delete — `memory.py:244`), session-level correlation; turn-correlated pane UI PARKED (needs turn_id, TBD). Direct in-session TDD (live-smoke load-bearing). Contract `docs/contracts/issues/17.contract.md` written, `/heid`-design-consulted + `/heid-contract-review`-panel'd + fixed (validates OK). NEXT: TDD. - `[2026-06-16]` **Provider stores confirmed byte-faithful to bifrost's AND reference** (`reference_server/memory.py:398` `_matches_scope` = `all(...)`, identical to ours). OR-union was considered + rejected (ecosystem-wide change); flagged the silent-zero foot-gun to bifrost-dev (docs-only landed, bifrost stays 0.6.4). Do NOT flip `_scope_matches` to OR. +- `[2026-06-16]` **agent_self lattice SHIPPED both sides → our axis-validation gap CLOSED (v0.17.5).** bifrost 0.7.0 / wire v0.5 adds agent_self to the scope lattice `{end_user,group,tenant,agent_self}` (#10, driven by our foot-gun flag via bifrost-dev); Worldtree pinned 0.7.0 + canonical-synced the v0.5 spec (v0.35.11, `c860fb0`). **SUPERSEDES the prior "HELD, do NOT add axis-validation" note** — we DID add `_validate_scope_filter` (4-axis) to match the reference (bifrost-dev's recommendation, purely additive; out-of-lattice → InvalidFilter). #297 union build unblocked. Memory provider restarted on 0.7.0. +- `[2026-06-16]` **#17 contract reviewed + the debug-assist arc fully closed.** `/heid` design consult + `/heid-contract-review` panel both run on `docs/contracts/issues/17.contract.md` (validates OK). The #295 debug-assist that opened the session is closed end-to-end: root cause (scope-axis asymmetry) → #296/#297 research issues + corpus → a shipped bifrost protocol change (agent_self canonical) → our store at parity. NEXT durable step: #17 TDD (tracked: Gitea #17 + the contract). + _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._ ## Tried and abandoned @@ -217,3 +231,4 @@ defense against re-attempting the same cul-de-sac. - `[2026-06-16]` **`scripts/contract_drift_check.py` defaults `GITEA_REPO` to "Worldtree"** (line 74), so a bare run in ratatoskr false-positives DRIFT by hashing Worldtree's same-numbered issue. Always `export GITEA_REPO=ratatoskr GITEA_OWNER=vh` (env.sh leaves the GITEA vars commented out) before running the drift-checker here. - `[2026-06-16]` **My #295 coupling hypothesis (the promoted question crowds out the fact at small top_k) was REFUTED** — worldtree-dev's recall over-fetches `top_k=128` (`injector.py:203`/`_store_helpers.py:101`), so the question can't crowd the fact out at search level. Reasonable cross-frontier hypothesis, correctly framed as a hypothesis not a conclusion; the real cause was the scope-axis asymmetry. Lesson: offer provider-side hypotheses, let the upstream owner check them against their code. - `[2026-06-16]` **Contract drifted from its own design in two spots, caught only by `/heid-contract-review` (not same-author paraphrase):** the `OpEvent` dataclass omitted the `turn_id` that INV-005 promised; the `session_id` comment said "None for search/get/delete" contradicting the dispatch-layer design (the JWT carries session_id for all verbs at dispatch). Cross-model paraphrase is load-bearing for catching an author's own contract-vs-intent drift. +- `[2026-06-16]` **"No promotion" was checked TOO EARLY — Tier-3 promotion is ASYNC (lands AFTER the SSE turn-end).** The cold-recall probe's immediate post-turn fixture check showed 3 chunks (no promotion); a later check (during the v0.17.5 provider restart) found 5 — the probe HAD promoted 2 chunks (its question `acc3d49` + the model's non-answer `4773704`), just late. Don't trust an immediate post-turn fixture snapshot to judge promotion; it lands after the turn completes. (Same family as the "wire-200 ≠ recall, prove it in a cold session" lesson, extended to promotion timing — and the reason #17's contract pins a post-turn grace window + fixture before/after assertion.)