diff --git a/archival-memory.md b/archival-memory.md index 8c2012f..937e659 100644 --- a/archival-memory.md +++ b/archival-memory.md @@ -63,6 +63,85 @@ The 2026-05-20 → 2026-05-29 cluster: the original debug-TUI/web build era, bef - `[2026-06-15]` **Providers run as dev-box BACKGROUND SHELLS, not infra-ops/systemd** (operator call — it's a dev box). `ratatoskr-provider` (affect) + `ratatoskr-memory-provider` as background processes; no productionization track. _Archived 2026-07-17._ + +The 2026-06-14 → 2026-06-18 cluster: the Bifrost-provider second-identity build era (#17/#18 self-drive+observe, #295/#296 cold-recall diagnosis, agent_self lattice). Archived 2026-07-18. + +`[2026-06-15]` **Affect plane shipped (v0.17.2) + LIVE-PROVEN end-to-end against real Worldtree v0.35.2.** Personal handshake 200 + `affect.emit` 200 from `10.250.50.152` → durable row persisted (opacity held). HS256 key = the consumer's Heimdall API-key STRING utf-8-encoded (NOT base64/raw — the tripwire); cross-subnet route + `BIFROST_CLIENT_ALLOWED_HOSTS` allowlist all held (infra-ops-owned). worldtree-dev confirmed ADR-0009 holding as designed. + +_Archived 2026-07-18._ + +`[2026-06-16]` **#295 cold-recall miss root-caused — UPSTREAM, scope-axis asymmetry.** A self-driven bound cold-recall probe captured the inbound pair via the observe log: Worldtree's recall filter carries `{end_user, agent_self}`; our chunks were `{end_user}`-only; AND-matching dropped everything on `agent_self` → 0 hits. Our store + search are SOUND; fix is Worldtree-side. F2 (question-promotion) → **#296**; F1 (recall-miss) → **#297**. + +_Archived 2026-07-18._ + +`[2026-06-16]` **agent_self → make it CANONICAL (operator decided A).** bifrost's reference lattice was `{end_user, group, tenant}` only (agent_self → `invalid_filter` 400); Worldtree emits agent_self (#248). Operator chose canonical-not-re-expressed; worldtree-dev filed the lattice-addition with bifrost-dev. Implication: our store's permissive axis-acceptance becomes CORRECT once bifrost adds agent_self. + +_Archived 2026-07-18._ + +`[2026-06-16]` **Self-drive auth identity: bound session-create uses the CONSUMER Heimdall key as bearer, NOT `WORLDTREE_API_KEY`.** Worldtree signs the Bifrost handshake JWT with the session-create bearer (canary key → handshake 401; consumer key → 200). Two keys, two identities. Proven by hand; documented in `docs/bifrost-self-test.md`; load-bearing for #17's Bind half. + +_Archived 2026-07-18._ + +`[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 (→ now #18). Observe = structured op-feed 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. Contract `docs/contracts/issues/17.contract.md` written + `/heid`-reviewed. + +_Archived 2026-07-18._ + +`[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 `{end_user,group,tenant,agent_self}` (#10, driven by our foot-gun flag); Worldtree pinned 0.7.0 (v0.35.11). We DID add `_validate_scope_filter` (4-axis) to match the reference (purely additive; out-of-lattice → InvalidFilter). + +_Archived 2026-07-18._ + +`[2026-06-16]` **Repinned bifrost 0.7.0→0.8.0 + reimplemented memory `search` to the v0.6 scope split (operator-directed).** `scope_filter` → `scope_all` (AND) + `scope_any` (OR/union over a list of conjunctive scopes), bifrost #11 — the canonical resolution of the #295/#297 silent-zero. The reference now does OR via `scope_any` (a NEW field — additive split, not a flip of AND). Store / contract (v1.2) / tests at parity with the v0.6 reference; provider bounced onto 0.8.0 with a wiped DB. Shipped v0.17.6 (`96d61a4`). **(SUPERSEDED the earlier "do NOT flip `_scope_matches` to OR" note.)** + +_Archived 2026-07-18._ + +`[2026-06-17]` **Worldtree spec pin bumped v0.29.0→v0.35.16 (`562001a`→`f1b59f8`); cold recall closed on the WIRE.** Worldtree shipped #297 (client-side per-scope-value union recall) + #298/#299 (adopt the bifrost v0.6 `scope_any`/`scope_all` wire) — emits `scope_any` on recall, pairing with our v0.17.6 provider. Re-vendored the spec; diff-reviewed the 285-commit catch-up — no client-breaking changes. `pin:`-only commit, no bump. + +_Archived 2026-07-18._ + +`[2026-06-17]` **End-to-end cold-recall proof RAN — our stack proven, #296 isolated.** Against personal WT v0.35.16 with restored `ratatoskr:sindra`: #297/#298 union recall, write path, and cold read ALL proven. Lone gap = upstream #296 extraction quality (the WIRE closed; fact-recall was #296-blocked). + +_Archived 2026-07-18._ + +`[2026-06-17]` **DELETE+redefine `ratatoskr:sindra` (operator-authorized; pre-v1 debug surface).** She SURVIVED the rebuild but was STALE (dead model + no memory block); memory is immutable post-define, so DELETE+redefine was the only path. v0.35.16 define takes **`role`** (capability), NOT `model`: `role:"character"` → first-healthy bind `mistral-small-4`; `memory:{}` trips the promotion gate (GET does NOT echo `memory_config`). Our `tier3.py` define is Phase-2.0-stale — untracked modernization follow-up. + +_Archived 2026-07-18._ + +`[2026-06-17]` **Promotion = 4-trigger hybrid (worldtree-dev, code-grounded):** salience (regex, 90s rate-limit) / `turn_count≥6` / context_pressure / **idle `≥10min` (unconditional on quality)**; per-turn `plan_promotion_run` for consumer_defined. **DELETE does NOT drain/promote** (delete-is-delete, #276) — idle `≥10min` is the deterministic flush. + +_Archived 2026-07-18._ + +`[2026-06-18]` **#296 Stages 1+2 closed.** Stage 1 (v0.35.19, recallability admission gate) validated live for normal turns; bisect localized the residual to verbose-persona VOLUME crowd-out. Stage 2 (v0.36.0, MERGED at worldtree-codex) = user-only one-call-per-turn extraction, the STRUCTURAL fix; hard-linguistic layer → Worldtree #305 (we handed over a live-validated eval fixture PAIR). Full-coverage re-smoke: verbose `sindra-probe` promoted the fact cleanly + cold-recalled @ 0.694 under v0.36.0. + +_Archived 2026-07-18._ + +`[2026-06-18]` **#17 implemented end-to-end via direct in-session TDD** (6 patch bumps `v0.17.8`→`v0.17.13`, suite 470 green). Slice order: bind primitive → op-feed → CLI → TUI → web(server) → web(UI). Tests drive the REAL bifrost dispatch via minted JWTs (`bifrost.core.dispatch_jwt.mint_dispatch_jwt`) — the "test against the shipped lib" posture, not hand-mocked envelopes. Op-feed reads `session_id` off the dispatch JWT `sub` claim (the contract open-q, resolved YES at the ASGI layer where the JWT is always present — `bifrost.reference_server._dispatch_auth.DispatchContext.session_id = payload["sub"]`). bifrost wire facts captured in-code: memory envelope `{operation, args}`→`memory_result(**payload)`={success,...}; verbs bare (search/upsert_many/get/get_many/delete_many); affect `{operation:"affect.emit"}`→`{success,stored}`; error envelope `{code, message}`; scopes `memory:read|write`. + +_Archived 2026-07-18._ + +`[2026-06-18]` **#17 live-smoke PROVEN — the whole thesis validated.** A self-driven bound CLI session showed, from the PROVIDER side, exactly which memory ops a turn produced (2 recall searches, exact bound session_id, real union-recall scopes). Negative (canary→auth_rejected) NOT live-constructible (Tier-1 agents aren't memory-bindable; a wrong key for an owner-scoped agent fails at agent-auth before the handshake) — covered by the unit test + prior hand-proof. + +_Archived 2026-07-18._ + +`[2026-06-18]` **Fixed a pre-existing test-isolation bug exposed by the #17 CLI tests** (`0bebad7`): `test_no_textual_import` did a live `importlib.reload(ratatoskr.cli)` that mutated the shared module in place, breaking class identity (`isinstance`/`pytest.raises`) for every test ordered after it. The real check is the static source-grep; the reload was vestigial → removed. Lesson: never `importlib.reload` a shared module in a test without restoring it. + +_Archived 2026-07-18._ + +`[2026-06-18]` **#18 filed (composite endpoint + PAD read-endpoint) — DEFERRED, tracked at Gitea #18.** Two pieces: (1) a composite Bifrost facade (new port e.g. `:8392`) fronting BOTH `:8390`+`:8391` advertising both caps at handshake → one session binds both planes (un-parks the #17 open-q; bifrost reference_server already mounts both planes in one app → thin combined builder; needs per-plane failure-status + the op-feed deriving plane PER-REQUEST from the path instead of its fixed `plane` param). (2) a non-bifrost PAD read-endpoint on the affect provider (recommended over web-reads-`affect.db`-directly) → web persona pane renders PAD/valence from OUR `:8390` store. **Composite half APPROVED by operator ("A is correct"); contract-first next.** **Persona-telemetry diagnosis (verified):** affect bind persists PAD (vuong: pleasure +0.146, familiarity 0.18→0.59 over 8 turns) but the pane reads Tier-3-404 `persona_state` AND Tier-3 emits ZERO `affect_update` SSE (wire-verified) — both WT sources dead, so #18's PAD-display half is the only path. `affect.fetch` over bifrost is RESERVED/blocked but irrelevant (we own the store). Proposed: fast-track the PAD-display half now (awaiting operator go), keep composite contract-first. + +_Archived 2026-07-18._ + +`[2026-06-18]` **#18 SPLIT; Deliverable 1 (composite) routed to bifrost — Option C (operator).** D2 (PAD read-endpoint, our-side only) fast-tracked; D1 (composite `:8392` endpoint) routed to bifrost-dev to add a PUBLIC `build_combined_app` rather than hand-roll one from bifrost privates — because ratatoskr is a debug surface that must exercise the CANONICAL surface ("don't go off the reservation"). The Heid framing-panel had unanimously recommended hand-rolling (Option B) — DISCARDED as wrong-grounded (the panel lacked the canonical-surface principle; their own finding that B reaches external/underscore-private names actually vindicated C). bifrost-dev confirmed: clean additive minor (~`v0.9.0`), design locked (advertise-by-store-PRESENCE handshake — no health probe; per-route call-time isolation within a shared ASGI process), slotted after WT #289. [principle → auto-memory `feedback-debug-surface-uses-canonical-surface-only`] + +_Archived 2026-07-18._ + +`[2026-06-18]` **FR-1 RESOLVED — the composite premise was unverified, now wire-proven: single-endpoint, caps-routed.** The Heid panel's sharpest catch (Regin): "advertise both caps → Worldtree dispatches both planes to one endpoint" was an ASSUMPTION about WT dispatch, stated as fact. worldtree-dev verified IN CODE: one `BifrostClient` per session (single `_endpoint_url`), handshake `capabilities_granted` parsed INDEPENDENTLY into memory+affect sets, both stores attach off the SAME endpoint iff their cap was granted (`service.py:2597/2703-2713/2745-2751`, `bifrost_client.py ~357-369`; tests `test_tier3_bifrost_{memory,affect}_routing.py`). So D1 is **bifrost-only, ZERO Worldtree change** — #18's "no WT change needed" assumption was correct. + +_Archived 2026-07-18._ + +`[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). + +_Archived 2026-07-18._ + ## Tried and abandoned (archived) The 2026-05-20 → 2026-05-28 cluster: original-build-era foot-guns. Archived 2026-06-18. @@ -85,3 +164,69 @@ The 2026-05-20 → 2026-05-28 cluster: original-build-era foot-guns. Archived 20 - `[2026-05-27]` **`get_persona_state` mocked flat error envelope; real Worldtree wraps in `detail`.** v0.12.0 tests used `{"error_code": "auth_scope_denied"}` but real wire (FastAPI default) returns `{"detail": {"error_code": "auth_scope_denied", "message": "…"}}`. The parser only checked top-level so the typed exception was never raised; calls fell through to `SessionApiFailed(403)`, which the web persona endpoint surfaced as HTTP 500. v0.15.1 patches both shapes. **Lesson: test-side mock envelopes must match the REAL wire shape; live smoke is load-bearing for envelope-shape verification, not just happy paths.** - `[2026-05-27]` **Mid-session `system_prompt` mutation: universal omission across surveyed mature systems.** brokkr-smithy R13 panel (3-arm, strong convergence) confirmed: no surveyed system ships live PATCH-on-active-session (OpenAI Assistants/Responses, Anthropic Messages, Vertex AI, MCP, LangChain, LlamaIndex, Ollama, vLLM). The omission IS the answer. 12 additional threat vectors beyond ratatoskr's initial 7. **Don't re-propose this for ratatoskr;** if a future production conversational shell wants iterative-prompt-tuning ergonomics, the consensus shape is fork-via-client (PATCH agent → new session → replay context). - `[2026-05-28]` **Browser-local turn_id used for upstream cancel URL — old cancel tests ENCODED the bug.** Web companion v0.15.x cancel paths posted to `/sessions/{sid}/turns/{LOCAL_ID}/cancel`. Tests mocked the local-id URL so they encoded the bug rather than detecting it. Hulda caught it in Heid pass 1. Fix in v0.16.0: capture upstream_turn_id from the first SSE event's `sse_id.turn_id`; all cancel paths use it; cancel before first event is `{"cancelled": false, "reason": "not_started"}`. **Rule: when designing cancel/match paths against an external service, test fixtures must mock what would actually be hit upstream — mocking your own derived id encodes the bug instead of catching it.** + +The 2026-06-14 → 2026-06-18 cluster (foot-guns from the same era). Archived 2026-07-18. + +`[2026-06-14]` **Artifact-only contract review can't validate against a dependency's ACTUAL behavior.** `/heid-contract-review` sees only the contract, never the external library (bifrost) — so "the consumer under-built against bifrost's real semantics" is invisible to it by construction (the affect idempotency model shipped wrong because of this). Real-lib TDD against the shipped library + the executable reference store + the #195 parity test are the gate. Don't treat a clean contract review as evidence the code matches the dependency. + +_Archived 2026-07-18._ + +`[2026-06-15]` **"Sindra hasn't been registered" was an under-verified inference — WRONG.** Concluded it from grepping ratatoskr's CODE (`sindra` absent from `src/`), but Tier-3 registration is SERVER-SIDE (`POST /agents/define`) — a code grep structurally can't see it. **Rule: to check whether a Tier-3 agent exists, query the Worldtree instance, never the consumer repo's code.** (Extended 2026-06-17: even `GET /agents` can't see consumer agents; only `GET /agents/:` with the owner key does.) + +_Archived 2026-07-18._ + +`[2026-06-15]` **"byte-equal" round-trip slip propagated affect→memory via copy-paste.** The affect contract's byte-identical→semantic fix reappeared in the memory contract's INV-001 (sibling copy). Only an INDEPENDENT `/heid-contract-review` of the memory contract re-caught it. **Paraphrase every sibling contract fresh — don't amortize one review across a family; copies carry the parent's slips.** (also a feedback auto-memory) + +_Archived 2026-07-18._ + +`[2026-06-15]` **Memory plane TDD'd + shipped** (commit `cd12951`, v0.17.3). Impl decisions worth keeping: vec0 `distance_metric=cosine` at table creation (`score = 1 − distance`); `search` over-fetches ALL candidates by cosine then scope-filters in Python so `top_k` counts IN-SCOPE hits; idempotency_id = reference 4-tuple `("default",verb,_ctx_actor(ctx),key)` pipe-joined as the SQLite PK, digest = sha256 canonical-JSON; `_ctx_actor` = `job_id|jwt_sub|session_id`. heid-code-review returned zero true drift; optimistic-lock semantics pinned to the reference via an `expected_revisions` parity test. + +_Archived 2026-07-18._ + +`[2026-06-15]` **Memory provider LIVE-PROVEN against personal v0.35.3; recall-injection is upstream.** worldtree-dev's Tier-3 promotion recipe: memory-call fires from Tier-3 PROMOTION, gated at `service.py:2623` on `ctx.kind=="consumer_defined"` AND `ctx.memory_config is not None` (agent DEFINED WITH a `memory` block, dim 1024) AND handshake-granted memory caps AND `embedding_dim==1024`. Binding = `POST /sessions BifrostBindingRequest{endpoint_url}`, handshake `caps=["affect","memory"]`, **`binding.scope` null** (per-op scopes auto-minted). A `BIFROST_CLIENT_ALLOWED_HOSTS` allowlist gates the endpoint (infra-ops added `:8391`). HTTP + HS256 both work in dev. + +_Archived 2026-07-18._ + +`[2026-06-15]` **Diagnostic: our recall-search is SOUND — the cross-session recall gap is UPSTREAM, and it caught an upstream bug.** Embedded the recall query via gateway `qwen3-embedding` + searched our live store directly → the fact recalls at cosine 0.60, correctly ranked. So the cold-session recall failure is Worldtree's recall-assembly/injection, NOT our search. ALSO found a latent UPSTREAM bug: a recall QUESTION got promoted as a durable chunk and ranked #1. **This is exactly #17's thesis — ratatoskr-as-provider caught an upstream bug invisible from the chat side.** + +_Archived 2026-07-18._ + +`[2026-06-15]` **"Wire 200 ≠ recall works" — prove recall efficacy at the model's answer in a COLD (history-free) session, not on the wire.** A `search`/memory-call returns 200 whether or not its results are injected, and same-session "recall" can be plain session history. Don't call cross-session recall proven from a clean wire. + +_Archived 2026-07-18._ + +`[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`, so the question can't crowd the fact out at search level. The real cause was the scope-axis asymmetry. Lesson: offer provider-side hypotheses, let the upstream owner check them against their code. + +_Archived 2026-07-18._ + +`[2026-06-16]` **#17 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` INV-005 promised; a `session_id` comment contradicted the dispatch-layer design. Cross-model paraphrase is load-bearing for catching an author's own contract-vs-intent drift. + +_Archived 2026-07-18._ + +`[2026-06-16]` **"No promotion" was checked TOO EARLY — Tier-3 promotion is ASYNC (lands AFTER the SSE turn-end).** Don't trust an immediate post-turn fixture snapshot to judge promotion; it lands after the turn completes. (The reason #17's contract pins a post-turn grace window + fixture before/after assertion.) + +_Archived 2026-07-18._ + +`[2026-06-17]` **"sindra is GONE" (infra-ops, from `GET /agents` + admin token) was a FALSE NEGATIVE.** Consumer-defined Tier-3 agents are OWNER-SCOPED (separate `consumer_agents` table) — invisible to the foundational `GET /agents` roster even with an admin token. To check, `GET /agents/:` with the OWNER key. + +_Archived 2026-07-18._ + +`[2026-06-17]` **"Promotion didn't fire → #296" was PREMATURE — twice over.** (1) Polled the op-feed only ~2min, but the upsert landed at ~4min — promotion is async + multi-trigger; watch a longer window. (2) It DID fire; the real bug is extraction QUALITY, not non-firing. "No upsert while a session is live and `<10min` idle" is WAD. + +_Archived 2026-07-18._ + +`[2026-06-18]` **Wiping our `:8391` store does NOT reset Worldtree's promotion-side dedup** — a same-agent re-smoke returned `reason_code=noop_duplicate` / `candidate_count=0`: the extractor NEVER RE-RAN, dedup short-circuited against an earlier promotion. **For a clean promotion smoke, use a BRAND-NEW agent + end_user (never-used names).** (Also: `llm_calls_used=0` is NOT the "did the extractor run" tell — `noop_duplicate` is.) + +_Archived 2026-07-18._ + +`[2026-06-18]` **`affect.emit` is POST-TURN ASYNC — checking the op-feed immediately after a turn MISSES it.** The Tier-3 affect appraise→emit→rehydrate loop runs AFTER the SSE `[done]`; the emit lands in our `:8390` store seconds later (op-feed grep right after `[done]` showed only the handshake; the `emit stored:true` appeared on a later read). Same family as the async-promotion timing trap. Watch a few-second window post-turn before concluding "no affect emitted." Also wire-verified the same turn: Tier-3 sindra emits ZERO `affect_update` SSE (the persona-strip SSE path never populates for consumer agents) — see the #18 PAD-display decision. + +_Archived 2026-07-18._ + +`[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.** + +_Archived 2026-07-18._ + +`[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.** + +_Archived 2026-07-18._ diff --git a/persistent-memory.d/2026-06-14-artifact-only-contract-review-can-t-validate.md b/persistent-memory.d/2026-06-14-artifact-only-contract-review-can-t-validate.md deleted file mode 100644 index 74134a3..0000000 --- a/persistent-memory.d/2026-06-14-artifact-only-contract-review-can-t-validate.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-14]` **Artifact-only contract review can't validate against a dependency's ACTUAL behavior.** `/heid-contract-review` sees only the contract, never the external library (bifrost) — so "the consumer under-built against bifrost's real semantics" is invisible to it by construction (the affect idempotency model shipped wrong because of this). Real-lib TDD against the shipped library + the executable reference store + the #195 parity test are the gate. Don't treat a clean contract review as evidence the code matches the dependency. diff --git a/persistent-memory.d/2026-06-15-affect-plane-shipped-v0-17-2-live-proven-end.md b/persistent-memory.d/2026-06-15-affect-plane-shipped-v0-17-2-live-proven-end.md deleted file mode 100644 index 301f038..0000000 --- a/persistent-memory.d/2026-06-15-affect-plane-shipped-v0-17-2-live-proven-end.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **Affect plane shipped (v0.17.2) + LIVE-PROVEN end-to-end against real Worldtree v0.35.2.** Personal handshake 200 + `affect.emit` 200 from `10.250.50.152` → durable row persisted (opacity held). HS256 key = the consumer's Heimdall API-key STRING utf-8-encoded (NOT base64/raw — the tripwire); cross-subnet route + `BIFROST_CLIENT_ALLOWED_HOSTS` allowlist all held (infra-ops-owned). worldtree-dev confirmed ADR-0009 holding as designed. diff --git a/persistent-memory.d/2026-06-15-byte-equal-round-trip-slip-propagated-affect.md b/persistent-memory.d/2026-06-15-byte-equal-round-trip-slip-propagated-affect.md deleted file mode 100644 index b3853d7..0000000 --- a/persistent-memory.d/2026-06-15-byte-equal-round-trip-slip-propagated-affect.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **"byte-equal" round-trip slip propagated affect→memory via copy-paste.** The affect contract's byte-identical→semantic fix reappeared in the memory contract's INV-001 (sibling copy). Only an INDEPENDENT `/heid-contract-review` of the memory contract re-caught it. **Paraphrase every sibling contract fresh — don't amortize one review across a family; copies carry the parent's slips.** (also a feedback auto-memory) diff --git a/persistent-memory.d/2026-06-15-diagnostic-our-recall-search-is-sound-the-cro.md b/persistent-memory.d/2026-06-15-diagnostic-our-recall-search-is-sound-the-cro.md deleted file mode 100644 index 840d7d0..0000000 --- a/persistent-memory.d/2026-06-15-diagnostic-our-recall-search-is-sound-the-cro.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **Diagnostic: our recall-search is SOUND — the cross-session recall gap is UPSTREAM, and it caught an upstream bug.** Embedded the recall query via gateway `qwen3-embedding` + searched our live store directly → the fact recalls at cosine 0.60, correctly ranked. So the cold-session recall failure is Worldtree's recall-assembly/injection, NOT our search. ALSO found a latent UPSTREAM bug: a recall QUESTION got promoted as a durable chunk and ranked #1. **This is exactly #17's thesis — ratatoskr-as-provider caught an upstream bug invisible from the chat side.** diff --git a/persistent-memory.d/2026-06-15-memory-plane-tdd-d-shipped.md b/persistent-memory.d/2026-06-15-memory-plane-tdd-d-shipped.md deleted file mode 100644 index 9345e3d..0000000 --- a/persistent-memory.d/2026-06-15-memory-plane-tdd-d-shipped.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **Memory plane TDD'd + shipped** (commit `cd12951`, v0.17.3). Impl decisions worth keeping: vec0 `distance_metric=cosine` at table creation (`score = 1 − distance`); `search` over-fetches ALL candidates by cosine then scope-filters in Python so `top_k` counts IN-SCOPE hits; idempotency_id = reference 4-tuple `("default",verb,_ctx_actor(ctx),key)` pipe-joined as the SQLite PK, digest = sha256 canonical-JSON; `_ctx_actor` = `job_id|jwt_sub|session_id`. heid-code-review returned zero true drift; optimistic-lock semantics pinned to the reference via an `expected_revisions` parity test. diff --git a/persistent-memory.d/2026-06-15-memory-provider-live-proven-against-personal.md b/persistent-memory.d/2026-06-15-memory-provider-live-proven-against-personal.md deleted file mode 100644 index a01557e..0000000 --- a/persistent-memory.d/2026-06-15-memory-provider-live-proven-against-personal.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **Memory provider LIVE-PROVEN against personal v0.35.3; recall-injection is upstream.** worldtree-dev's Tier-3 promotion recipe: memory-call fires from Tier-3 PROMOTION, gated at `service.py:2623` on `ctx.kind=="consumer_defined"` AND `ctx.memory_config is not None` (agent DEFINED WITH a `memory` block, dim 1024) AND handshake-granted memory caps AND `embedding_dim==1024`. Binding = `POST /sessions BifrostBindingRequest{endpoint_url}`, handshake `caps=["affect","memory"]`, **`binding.scope` null** (per-op scopes auto-minted). A `BIFROST_CLIENT_ALLOWED_HOSTS` allowlist gates the endpoint (infra-ops added `:8391`). HTTP + HS256 both work in dev. diff --git a/persistent-memory.d/2026-06-15-sindra-hasn-t-been-registered-was-an-under-ve.md b/persistent-memory.d/2026-06-15-sindra-hasn-t-been-registered-was-an-under-ve.md deleted file mode 100644 index 88ad160..0000000 --- a/persistent-memory.d/2026-06-15-sindra-hasn-t-been-registered-was-an-under-ve.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **"Sindra hasn't been registered" was an under-verified inference — WRONG.** Concluded it from grepping ratatoskr's CODE (`sindra` absent from `src/`), but Tier-3 registration is SERVER-SIDE (`POST /agents/define`) — a code grep structurally can't see it. **Rule: to check whether a Tier-3 agent exists, query the Worldtree instance, never the consumer repo's code.** (Extended 2026-06-17: even `GET /agents` can't see consumer agents; only `GET /agents/:` with the owner key does.) diff --git a/persistent-memory.d/2026-06-15-wire-200-recall-works-prove-recall-efficacy-a.md b/persistent-memory.d/2026-06-15-wire-200-recall-works-prove-recall-efficacy-a.md deleted file mode 100644 index 596f181..0000000 --- a/persistent-memory.d/2026-06-15-wire-200-recall-works-prove-recall-efficacy-a.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-15]` **"Wire 200 ≠ recall works" — prove recall efficacy at the model's answer in a COLD (history-free) session, not on the wire.** A `search`/memory-call returns 200 whether or not its results are injected, and same-session "recall" can be plain session history. Don't call cross-session recall proven from a clean wire. diff --git a/persistent-memory.d/2026-06-16-17-contract-drifted-from-its-own-design-in-tw.md b/persistent-memory.d/2026-06-16-17-contract-drifted-from-its-own-design-in-tw.md deleted file mode 100644 index 9e0b27a..0000000 --- a/persistent-memory.d/2026-06-16-17-contract-drifted-from-its-own-design-in-tw.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-16]` **#17 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` INV-005 promised; a `session_id` comment contradicted the dispatch-layer design. Cross-model paraphrase is load-bearing for catching an author's own contract-vs-intent drift. diff --git a/persistent-memory.d/2026-06-16-295-cold-recall-miss-root-caused-upstream-sco.md b/persistent-memory.d/2026-06-16-295-cold-recall-miss-root-caused-upstream-sco.md deleted file mode 100644 index 5db785a..0000000 --- a/persistent-memory.d/2026-06-16-295-cold-recall-miss-root-caused-upstream-sco.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-16]` **#295 cold-recall miss root-caused — UPSTREAM, scope-axis asymmetry.** A self-driven bound cold-recall probe captured the inbound pair via the observe log: Worldtree's recall filter carries `{end_user, agent_self}`; our chunks were `{end_user}`-only; AND-matching dropped everything on `agent_self` → 0 hits. Our store + search are SOUND; fix is Worldtree-side. F2 (question-promotion) → **#296**; F1 (recall-miss) → **#297**. diff --git a/persistent-memory.d/2026-06-16-agentself-lattice-shipped-both-sides-our-axis.md b/persistent-memory.d/2026-06-16-agentself-lattice-shipped-both-sides-our-axis.md deleted file mode 100644 index b6274af..0000000 --- a/persistent-memory.d/2026-06-16-agentself-lattice-shipped-both-sides-our-axis.md +++ /dev/null @@ -1 +0,0 @@ -`[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 `{end_user,group,tenant,agent_self}` (#10, driven by our foot-gun flag); Worldtree pinned 0.7.0 (v0.35.11). We DID add `_validate_scope_filter` (4-axis) to match the reference (purely additive; out-of-lattice → InvalidFilter). diff --git a/persistent-memory.d/2026-06-16-agentself-make-it-canonical-operator-decided.md b/persistent-memory.d/2026-06-16-agentself-make-it-canonical-operator-decided.md deleted file mode 100644 index 879bd16..0000000 --- a/persistent-memory.d/2026-06-16-agentself-make-it-canonical-operator-decided.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-16]` **agent_self → make it CANONICAL (operator decided A).** bifrost's reference lattice was `{end_user, group, tenant}` only (agent_self → `invalid_filter` 400); Worldtree emits agent_self (#248). Operator chose canonical-not-re-expressed; worldtree-dev filed the lattice-addition with bifrost-dev. Implication: our store's permissive axis-acceptance becomes CORRECT once bifrost adds agent_self. diff --git a/persistent-memory.d/2026-06-16-issue-17-v1-scope-locked-operator-1a-2a-singl.md b/persistent-memory.d/2026-06-16-issue-17-v1-scope-locked-operator-1a-2a-singl.md deleted file mode 100644 index ac0e5c3..0000000 --- a/persistent-memory.d/2026-06-16-issue-17-v1-scope-locked-operator-1a-2a-singl.md +++ /dev/null @@ -1 +0,0 @@ -`[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 (→ now #18). Observe = structured op-feed 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. Contract `docs/contracts/issues/17.contract.md` written + `/heid`-reviewed. diff --git a/persistent-memory.d/2026-06-16-my-295-coupling-hypothesis-the-promoted-quest.md b/persistent-memory.d/2026-06-16-my-295-coupling-hypothesis-the-promoted-quest.md deleted file mode 100644 index c924c4e..0000000 --- a/persistent-memory.d/2026-06-16-my-295-coupling-hypothesis-the-promoted-quest.md +++ /dev/null @@ -1 +0,0 @@ -`[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`, so the question can't crowd the fact out at search level. The real cause was the scope-axis asymmetry. Lesson: offer provider-side hypotheses, let the upstream owner check them against their code. diff --git a/persistent-memory.d/2026-06-16-no-promotion-was-checked-too-early-tier-3-pro.md b/persistent-memory.d/2026-06-16-no-promotion-was-checked-too-early-tier-3-pro.md deleted file mode 100644 index 152792a..0000000 --- a/persistent-memory.d/2026-06-16-no-promotion-was-checked-too-early-tier-3-pro.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-16]` **"No promotion" was checked TOO EARLY — Tier-3 promotion is ASYNC (lands AFTER the SSE turn-end).** Don't trust an immediate post-turn fixture snapshot to judge promotion; it lands after the turn completes. (The reason #17's contract pins a post-turn grace window + fixture before/after assertion.) diff --git a/persistent-memory.d/2026-06-16-repinned-bifrost-0-7-0-0-8-0-reimplemented-me.md b/persistent-memory.d/2026-06-16-repinned-bifrost-0-7-0-0-8-0-reimplemented-me.md deleted file mode 100644 index 8ac8e8c..0000000 --- a/persistent-memory.d/2026-06-16-repinned-bifrost-0-7-0-0-8-0-reimplemented-me.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-16]` **Repinned bifrost 0.7.0→0.8.0 + reimplemented memory `search` to the v0.6 scope split (operator-directed).** `scope_filter` → `scope_all` (AND) + `scope_any` (OR/union over a list of conjunctive scopes), bifrost #11 — the canonical resolution of the #295/#297 silent-zero. The reference now does OR via `scope_any` (a NEW field — additive split, not a flip of AND). Store / contract (v1.2) / tests at parity with the v0.6 reference; provider bounced onto 0.8.0 with a wiped DB. Shipped v0.17.6 (`96d61a4`). **(SUPERSEDED the earlier "do NOT flip `_scope_matches` to OR" note.)** diff --git a/persistent-memory.d/2026-06-16-self-drive-auth-identity-bound-session-create.md b/persistent-memory.d/2026-06-16-self-drive-auth-identity-bound-session-create.md deleted file mode 100644 index 40daeaa..0000000 --- a/persistent-memory.d/2026-06-16-self-drive-auth-identity-bound-session-create.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-16]` **Self-drive auth identity: bound session-create uses the CONSUMER Heimdall key as bearer, NOT `WORLDTREE_API_KEY`.** Worldtree signs the Bifrost handshake JWT with the session-create bearer (canary key → handshake 401; consumer key → 200). Two keys, two identities. Proven by hand; documented in `docs/bifrost-self-test.md`; load-bearing for #17's Bind half. diff --git a/persistent-memory.d/2026-06-17-delete-redefine-ratatoskr-sindra-operator-aut.md b/persistent-memory.d/2026-06-17-delete-redefine-ratatoskr-sindra-operator-aut.md deleted file mode 100644 index 4c7f2cf..0000000 --- a/persistent-memory.d/2026-06-17-delete-redefine-ratatoskr-sindra-operator-aut.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-17]` **DELETE+redefine `ratatoskr:sindra` (operator-authorized; pre-v1 debug surface).** She SURVIVED the rebuild but was STALE (dead model + no memory block); memory is immutable post-define, so DELETE+redefine was the only path. v0.35.16 define takes **`role`** (capability), NOT `model`: `role:"character"` → first-healthy bind `mistral-small-4`; `memory:{}` trips the promotion gate (GET does NOT echo `memory_config`). Our `tier3.py` define is Phase-2.0-stale — untracked modernization follow-up. diff --git a/persistent-memory.d/2026-06-17-end-to-end-cold-recall-proof-ran-our-stack-pr.md b/persistent-memory.d/2026-06-17-end-to-end-cold-recall-proof-ran-our-stack-pr.md deleted file mode 100644 index 359cc60..0000000 --- a/persistent-memory.d/2026-06-17-end-to-end-cold-recall-proof-ran-our-stack-pr.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-17]` **End-to-end cold-recall proof RAN — our stack proven, #296 isolated.** Against personal WT v0.35.16 with restored `ratatoskr:sindra`: #297/#298 union recall, write path, and cold read ALL proven. Lone gap = upstream #296 extraction quality (the WIRE closed; fact-recall was #296-blocked). diff --git a/persistent-memory.d/2026-06-17-promotion-4-trigger-hybrid-worldtree-dev-code.md b/persistent-memory.d/2026-06-17-promotion-4-trigger-hybrid-worldtree-dev-code.md deleted file mode 100644 index c5035ef..0000000 --- a/persistent-memory.d/2026-06-17-promotion-4-trigger-hybrid-worldtree-dev-code.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-17]` **Promotion = 4-trigger hybrid (worldtree-dev, code-grounded):** salience (regex, 90s rate-limit) / `turn_count≥6` / context_pressure / **idle `≥10min` (unconditional on quality)**; per-turn `plan_promotion_run` for consumer_defined. **DELETE does NOT drain/promote** (delete-is-delete, #276) — idle `≥10min` is the deterministic flush. diff --git a/persistent-memory.d/2026-06-17-promotion-didn-t-fire-296-was-premature-twice.md b/persistent-memory.d/2026-06-17-promotion-didn-t-fire-296-was-premature-twice.md deleted file mode 100644 index ba3e760..0000000 --- a/persistent-memory.d/2026-06-17-promotion-didn-t-fire-296-was-premature-twice.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-17]` **"Promotion didn't fire → #296" was PREMATURE — twice over.** (1) Polled the op-feed only ~2min, but the upsert landed at ~4min — promotion is async + multi-trigger; watch a longer window. (2) It DID fire; the real bug is extraction QUALITY, not non-firing. "No upsert while a session is live and `<10min` idle" is WAD. diff --git a/persistent-memory.d/2026-06-17-sindra-is-gone-infra-ops-from-get-agents-admi.md b/persistent-memory.d/2026-06-17-sindra-is-gone-infra-ops-from-get-agents-admi.md deleted file mode 100644 index 9196cf4..0000000 --- a/persistent-memory.d/2026-06-17-sindra-is-gone-infra-ops-from-get-agents-admi.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-17]` **"sindra is GONE" (infra-ops, from `GET /agents` + admin token) was a FALSE NEGATIVE.** Consumer-defined Tier-3 agents are OWNER-SCOPED (separate `consumer_agents` table) — invisible to the foundational `GET /agents` roster even with an admin token. To check, `GET /agents/:` with the OWNER key. diff --git a/persistent-memory.d/2026-06-17-worldtree-spec-pin-bumped-v0-29-0-v0-35-16-56.md b/persistent-memory.d/2026-06-17-worldtree-spec-pin-bumped-v0-29-0-v0-35-16-56.md deleted file mode 100644 index 1918f9e..0000000 --- a/persistent-memory.d/2026-06-17-worldtree-spec-pin-bumped-v0-29-0-v0-35-16-56.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-17]` **Worldtree spec pin bumped v0.29.0→v0.35.16 (`562001a`→`f1b59f8`); cold recall closed on the WIRE.** Worldtree shipped #297 (client-side per-scope-value union recall) + #298/#299 (adopt the bifrost v0.6 `scope_any`/`scope_all` wire) — emits `scope_any` on recall, pairing with our v0.17.6 provider. Re-vendored the spec; diff-reviewed the 285-commit catch-up — no client-breaking changes. `pin:`-only commit, no bump. diff --git a/persistent-memory.d/2026-06-18-17-implemented-end-to-end-via-direct-in-sessi.md b/persistent-memory.d/2026-06-18-17-implemented-end-to-end-via-direct-in-sessi.md deleted file mode 100644 index 67aec4c..0000000 --- a/persistent-memory.d/2026-06-18-17-implemented-end-to-end-via-direct-in-sessi.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **#17 implemented end-to-end via direct in-session TDD** (6 patch bumps `v0.17.8`→`v0.17.13`, suite 470 green). Slice order: bind primitive → op-feed → CLI → TUI → web(server) → web(UI). Tests drive the REAL bifrost dispatch via minted JWTs (`bifrost.core.dispatch_jwt.mint_dispatch_jwt`) — the "test against the shipped lib" posture, not hand-mocked envelopes. Op-feed reads `session_id` off the dispatch JWT `sub` claim (the contract open-q, resolved YES at the ASGI layer where the JWT is always present — `bifrost.reference_server._dispatch_auth.DispatchContext.session_id = payload["sub"]`). bifrost wire facts captured in-code: memory envelope `{operation, args}`→`memory_result(**payload)`={success,...}; verbs bare (search/upsert_many/get/get_many/delete_many); affect `{operation:"affect.emit"}`→`{success,stored}`; error envelope `{code, message}`; scopes `memory:read|write`. diff --git a/persistent-memory.d/2026-06-18-17-live-smoke-proven-the-whole-thesis-validat.md b/persistent-memory.d/2026-06-18-17-live-smoke-proven-the-whole-thesis-validat.md deleted file mode 100644 index c4014c4..0000000 --- a/persistent-memory.d/2026-06-18-17-live-smoke-proven-the-whole-thesis-validat.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **#17 live-smoke PROVEN — the whole thesis validated.** A self-driven bound CLI session showed, from the PROVIDER side, exactly which memory ops a turn produced (2 recall searches, exact bound session_id, real union-recall scopes). Negative (canary→auth_rejected) NOT live-constructible (Tier-1 agents aren't memory-bindable; a wrong key for an owner-scoped agent fails at agent-auth before the handshake) — covered by the unit test + prior hand-proof. diff --git a/persistent-memory.d/2026-06-18-18-d2-implemented-via-direct-in-session-tdd-s.md b/persistent-memory.d/2026-06-18-18-d2-implemented-via-direct-in-session-tdd-s.md deleted file mode 100644 index a55ea39..0000000 --- a/persistent-memory.d/2026-06-18-18-d2-implemented-via-direct-in-session-tdd-s.md +++ /dev/null @@ -1 +0,0 @@ -`[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). diff --git a/persistent-memory.d/2026-06-18-18-filed-composite-endpoint-pad-read-endpoint.md b/persistent-memory.d/2026-06-18-18-filed-composite-endpoint-pad-read-endpoint.md deleted file mode 100644 index e692100..0000000 --- a/persistent-memory.d/2026-06-18-18-filed-composite-endpoint-pad-read-endpoint.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **#18 filed (composite endpoint + PAD read-endpoint) — DEFERRED, tracked at Gitea #18.** Two pieces: (1) a composite Bifrost facade (new port e.g. `:8392`) fronting BOTH `:8390`+`:8391` advertising both caps at handshake → one session binds both planes (un-parks the #17 open-q; bifrost reference_server already mounts both planes in one app → thin combined builder; needs per-plane failure-status + the op-feed deriving plane PER-REQUEST from the path instead of its fixed `plane` param). (2) a non-bifrost PAD read-endpoint on the affect provider (recommended over web-reads-`affect.db`-directly) → web persona pane renders PAD/valence from OUR `:8390` store. **Composite half APPROVED by operator ("A is correct"); contract-first next.** **Persona-telemetry diagnosis (verified):** affect bind persists PAD (vuong: pleasure +0.146, familiarity 0.18→0.59 over 8 turns) but the pane reads Tier-3-404 `persona_state` AND Tier-3 emits ZERO `affect_update` SSE (wire-verified) — both WT sources dead, so #18's PAD-display half is the only path. `affect.fetch` over bifrost is RESERVED/blocked but irrelevant (we own the store). Proposed: fast-track the PAD-display half now (awaiting operator go), keep composite contract-first. diff --git a/persistent-memory.d/2026-06-18-18-split-deliverable-1-composite-routed-to-bi.md b/persistent-memory.d/2026-06-18-18-split-deliverable-1-composite-routed-to-bi.md deleted file mode 100644 index 5e905da..0000000 --- a/persistent-memory.d/2026-06-18-18-split-deliverable-1-composite-routed-to-bi.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **#18 SPLIT; Deliverable 1 (composite) routed to bifrost — Option C (operator).** D2 (PAD read-endpoint, our-side only) fast-tracked; D1 (composite `:8392` endpoint) routed to bifrost-dev to add a PUBLIC `build_combined_app` rather than hand-roll one from bifrost privates — because ratatoskr is a debug surface that must exercise the CANONICAL surface ("don't go off the reservation"). The Heid framing-panel had unanimously recommended hand-rolling (Option B) — DISCARDED as wrong-grounded (the panel lacked the canonical-surface principle; their own finding that B reaches external/underscore-private names actually vindicated C). bifrost-dev confirmed: clean additive minor (~`v0.9.0`), design locked (advertise-by-store-PRESENCE handshake — no health probe; per-route call-time isolation within a shared ASGI process), slotted after WT #289. [principle → auto-memory `feedback-debug-surface-uses-canonical-surface-only`] diff --git a/persistent-memory.d/2026-06-18-296-stages-1-2-closed.md b/persistent-memory.d/2026-06-18-296-stages-1-2-closed.md deleted file mode 100644 index 067b3c2..0000000 --- a/persistent-memory.d/2026-06-18-296-stages-1-2-closed.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **#296 Stages 1+2 closed.** Stage 1 (v0.35.19, recallability admission gate) validated live for normal turns; bisect localized the residual to verbose-persona VOLUME crowd-out. Stage 2 (v0.36.0, MERGED at worldtree-codex) = user-only one-call-per-turn extraction, the STRUCTURAL fix; hard-linguistic layer → Worldtree #305 (we handed over a live-validated eval fixture PAIR). Full-coverage re-smoke: verbose `sindra-probe` promoted the fact cleanly + cold-recalled @ 0.694 under v0.36.0. diff --git a/persistent-memory.d/2026-06-18-affect-emit-is-post-turn-async-checking-the-o.md b/persistent-memory.d/2026-06-18-affect-emit-is-post-turn-async-checking-the-o.md deleted file mode 100644 index 17e101a..0000000 --- a/persistent-memory.d/2026-06-18-affect-emit-is-post-turn-async-checking-the-o.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **`affect.emit` is POST-TURN ASYNC — checking the op-feed immediately after a turn MISSES it.** The Tier-3 affect appraise→emit→rehydrate loop runs AFTER the SSE `[done]`; the emit lands in our `:8390` store seconds later (op-feed grep right after `[done]` showed only the handshake; the `emit stored:true` appeared on a later read). Same family as the async-promotion timing trap. Watch a few-second window post-turn before concluding "no affect emitted." Also wire-verified the same turn: Tier-3 sindra emits ZERO `affect_update` SSE (the persona-strip SSE path never populates for consumer agents) — see the #18 PAD-display decision. diff --git a/persistent-memory.d/2026-06-18-fixed-a-pre-existing-test-isolation-bug-expos.md b/persistent-memory.d/2026-06-18-fixed-a-pre-existing-test-isolation-bug-expos.md deleted file mode 100644 index 2c4c32a..0000000 --- a/persistent-memory.d/2026-06-18-fixed-a-pre-existing-test-isolation-bug-expos.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **Fixed a pre-existing test-isolation bug exposed by the #17 CLI tests** (`0bebad7`): `test_no_textual_import` did a live `importlib.reload(ratatoskr.cli)` that mutated the shared module in place, breaking class identity (`isinstance`/`pytest.raises`) for every test ordered after it. The real check is the static source-grep; the reload was vestigial → removed. Lesson: never `importlib.reload` a shared module in a test without restoring it. diff --git a/persistent-memory.d/2026-06-18-fr-1-resolved-the-composite-premise-was-unver.md b/persistent-memory.d/2026-06-18-fr-1-resolved-the-composite-premise-was-unver.md deleted file mode 100644 index 79d11d7..0000000 --- a/persistent-memory.d/2026-06-18-fr-1-resolved-the-composite-premise-was-unver.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **FR-1 RESOLVED — the composite premise was unverified, now wire-proven: single-endpoint, caps-routed.** The Heid panel's sharpest catch (Regin): "advertise both caps → Worldtree dispatches both planes to one endpoint" was an ASSUMPTION about WT dispatch, stated as fact. worldtree-dev verified IN CODE: one `BifrostClient` per session (single `_endpoint_url`), handshake `capabilities_granted` parsed INDEPENDENTLY into memory+affect sets, both stores attach off the SAME endpoint iff their cap was granted (`service.py:2597/2703-2713/2745-2751`, `bifrost_client.py ~357-369`; tests `test_tier3_bifrost_{memory,affect}_routing.py`). So D1 is **bifrost-only, ZERO Worldtree change** — #18's "no WT change needed" assumption was correct. diff --git a/persistent-memory.d/2026-06-18-latent-sqlite-thread-safety-bug-in-the-affect.md b/persistent-memory.d/2026-06-18-latent-sqlite-thread-safety-bug-in-the-affect.md deleted file mode 100644 index 09d05ab..0000000 --- a/persistent-memory.d/2026-06-18-latent-sqlite-thread-safety-bug-in-the-affect.md +++ /dev/null @@ -1 +0,0 @@ -`[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.** diff --git a/persistent-memory.d/2026-06-18-rationalized-away-a-known-contract-invariant.md b/persistent-memory.d/2026-06-18-rationalized-away-a-known-contract-invariant.md deleted file mode 100644 index e88a938..0000000 --- a/persistent-memory.d/2026-06-18-rationalized-away-a-known-contract-invariant.md +++ /dev/null @@ -1 +0,0 @@ -`[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.** diff --git a/persistent-memory.d/2026-06-18-wiping-our-8391-store-does-not-reset-worldtre.md b/persistent-memory.d/2026-06-18-wiping-our-8391-store-does-not-reset-worldtre.md deleted file mode 100644 index 52ed091..0000000 --- a/persistent-memory.d/2026-06-18-wiping-our-8391-store-does-not-reset-worldtre.md +++ /dev/null @@ -1 +0,0 @@ -`[2026-06-18]` **Wiping our `:8391` store does NOT reset Worldtree's promotion-side dedup** — a same-agent re-smoke returned `reason_code=noop_duplicate` / `candidate_count=0`: the extractor NEVER RE-RAN, dedup short-circuited against an earlier promotion. **For a clean promotion smoke, use a BRAND-NEW agent + end_user (never-used names).** (Also: `llm_calls_used=0` is NOT the "did the extractor run" tell — `noop_duplicate` is.) diff --git a/persistent-memory.d/2026-07-18-worldtree-sdk-371-and-sillytavern-app.md b/persistent-memory.d/2026-07-18-worldtree-sdk-371-and-sillytavern-app.md new file mode 100644 index 0000000..23003cb --- /dev/null +++ b/persistent-memory.d/2026-07-18-worldtree-sdk-371-and-sillytavern-app.md @@ -0,0 +1,81 @@ +`[2026-07-18]` **Worldtree owns the official Conversation API SDK (#371); the SillyTavern-style app is a fresh TS sibling, NOT a fork of Rata.** + +## The question that started it + +Operator: "I want to create an app like SillyTavern that runs on Worldtree. Ratatoskr +is already most of the way there, so I'm thinking we fork it." → turned into a +structural decision about how a new Worldtree chat product should relate to Rata. + +## Why NOT fork Rata + +Rata splits into three layers: (1) a **Worldtree client spine** (~1.8k LOC — +`sse_client.py` SSE/resume, `sessions.py` session/agent/persona clients, `tier3.py` +agent lifecycle); (2) a Bifrost **provider** layer (`provider/*`); (3) thin +**front-ends** (debug web console + `--send` CLI). The reusable part is the client +spine; the part that diverges is the front-end — and Rata's front-end is a +*debug-observability console* you'd throw away for a chat product. **Forking gives +you the wrong 60% and makes you delete it**, and muddies Rata's reference-impl +identity. The ecosystem norm is already independent spec-pinned siblings (Skaldsong +Python, mead-hall TS, Rata) — not forks. + +## The decision (operator ruled YES 2026-07-17, via worldtree-dev) + +- **Worldtree owns an official Conversation API client SDK** — spec-author-ships-the-SDK + (Stripe/GitHub shape), keeps SDK↔spec in lockstep, avoids consumer-depends-on-peer-consumer + coupling. +- **Dedicated repo** (name settled at WT's shape pass), **elevated onto Worldtree's pre-v1 + work track** (NOT parked behind WT's two v1 pillars — agent bus + KB). +- **Shape:** TS-first (near-term demand); types/CRUD generated from WT's published OpenAPI + (`worldtree-conversation-api-openapi-v2`) + a hand-written SSE/resume/error spine **seeded + from Rata's `sse_client`/`sessions`/`tier3`**. TS+Python same-source keeps them in sync. + **Internal-registry-only distribution, permanently.** +- **TS package ships v0 first, Python the following milestone** → **Rata's own repin rides + the Python milestone** (not imminent; no schedule pressure on us). +- **SillyTavern-style chat app = fresh TS sibling** consuming the SDK, NOT a fork of Rata. + **template-dev stands up the app repo.** Its early TS client is the natural **seed for the + SDK's TS side** (Rata seeds Python, the app seeds TS) — so it isn't throwaway. +- **Bifrost held OUT of scope:** the provider Protocols already ship in the `bifrost` package; + Rata's `provider/*` is a reference *provider* (a different, smaller audience). Separate track + if ever warranted. + +## Rata's contributions this session + +- **Parity-matrix usage inventory** shipped to worldtree-dev (18 routes; SSE event/resume/ + heartbeat surface; full error-code → typed-exception table; auth scopes; **8 compat hacks + that must survive the repin**). The load-bearing one: **empty-`id` SSE frames treated as + keepalives** (a provider emits id-less events mid-stream — re-strictifying id parsing crashes + turns on it) → promoted to normative rule **B-KA-2** in WT's behavioral spec. W-4 (tier-3 + `role`/`model` drift) triggered a Worldtree-side spec fixup. Seeded WT `docs/sdk/parity-matrix.md` + (`698cfc3`), behavioral-spec (`c999b5f`). +- **Fixture-corpus gap caught** (verified, then sent): `issues/1.contract.md`'s TESTS predate + `awaiting_llm_first_token` (#201) + `affect_update` (#204) — its `full_event_vocab` stops at + the 8-event set. WT confirmed and patched the fixture-checklist (`87d2569`) to seed from + `_envelope_for_type` (authoritative 11-variant parse surface), not #1 alone. WT fixture corpus + landed independently: `3019ee6`, 66 fixtures, validator clean. +- **issue-#1 contract drift fixed our side** (`4bd9abd`) — re-canonicalized the SSE event vocab + (added both events to the Event union + TESTS via a dated amendment; docs-only, no bump). +- **Personal-WT endpoint shipped** for fixture/conformance capture: `http://10.250.50.152:8081` + (health-checked live; unified error envelope = current-gen deploy). No key provisioning needed + (operator env carries instance keys). **Capture run waits on explicit operator go + instance + choice (demo :8080 vs personal :8081).** + +## Pinned for our eventual repin (SDK diverges from Rata in 3 spots) + +- **D-1** — unknown SSE event `type` → **skip with a diagnostics hook**, vs Rata's current hard + `ValueError` in `_envelope_for_type`. Adopt only at repin. +- **D-2** — open-world enums. +- **D-3** — typed eager `410 session_retired`. + +## Open question (operator) + +How does this relate to the **arbo image-gen fork** (NEXT-MAJOR, 2026-07-17)? Different products +(arbo = image-gen off ComfyUI's REST API; SillyTavern = chat off Worldtree), so no literal +conflict — but this session established a don't-fork/fresh-sibling pattern for Worldtree consumers. +Is SillyTavern/SDK now the priority app, additive to arbo, or does the pattern change the arbo +plan? NOT resolved; both preserved in Current state. + +## Tracking + +Worldtree **#371** (their tracking issue). Rata-side: contract fix `4bd9abd`. Cross-references: +[[2026-07-18-368-silo-test-passed]] (the prior arc), the reference-impl posture (auto-memory +`feedback_ratatoskr_is_a_reference_impl_adopt_canonical`). diff --git a/persistent-memory.md b/persistent-memory.md index d1ea48c..f83c7ac 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -46,118 +46,89 @@ upstream API key stays server-side (INV-003). _As of 2026-07-18:_ -**✅ #368 (user, character) MEMORY SILO TEST — DONE, PASSED LIVE (2026-07-18, WT b127).** The reason for the clear; -now closed. Silo holds end-to-end: write-side promoted chunks carry conjunctive `{end_user, agent_self}` (proven on -3 live Sindra promotions under end_user `silo-368`); read-side isolation proven by cold-recall (fresh Betty session, -SAME end_user, got ZERO hits → "I don't know", did NOT bleed Sindra's coffee; Sindra recalled her own coffee). -Migration sentinel `worldtree:migration:agent-scope-v1` written on first bind (backfill a no-op on the born-empty -store). Zero consumer changes, as contracted. Throwaway `ratatoskr:betty` DELETED (WT + local); Sindra intact. Full -record → `persistent-memory.d/2026-07-18-368-silo-test-passed.md`. **Still QUEUED:** the backfill live-verify -(synthetic legacy corpus — exercises the backfill machinery a born-fresh store skips). +**▶️ NEW MAJOR — Worldtree Conversation API SDK (#371) + a SillyTavern-style chat app.** +Operator wants a SillyTavern-like chat app on Worldtree; the "fork Rata?" question resolved to +**don't fork**. Instead: **Worldtree owns an official Conversation API client SDK** (dedicated repo, +elevated onto WT's pre-v1 track, tracking issue **WT #371**), **seeded from Rata's client spine** +(`sse_client`/`sessions`/`tier3`); the chat app is a **fresh TS SIBLING** consuming that SDK, NOT a +fork of Rata. TS package ships first — **Rata's own repin rides the later Python milestone**; +internal-registry-only distribution. **template-dev is standing up the app repo** (consult expected). +Rata's contributions shipped this session: the parity-matrix usage inventory (→ WT +`docs/sdk/parity-matrix.md` `698cfc3`, behavioral-spec `c999b5f`, fixture corpus `3019ee6` = 66 +fixtures), the issue-#1 contract drift fix (`4bd9abd`), and the personal-WT endpoint for fixture +capture. WT-side pipeline: behavioral-spec ✓ / parity-matrix ✓ / fixture-corpus ✓ → next is the +**codegen spike (d)**. **Three SDK divergences pinned for our eventual repin:** D-1 (unknown SSE +event → skip+diagnostics-hook, vs our current hard-raise in `_envelope_for_type`), D-2 (open-world +enums), D-3 (typed eager `410 session_retired`). Full arc → +`persistent-memory.d/2026-07-18-worldtree-sdk-371-and-sillytavern-app.md`. + - **⚠️ OPEN Q for operator (priority):** how does this relate to the arbo-fork below? Different + products (arbo = image-gen off ComfyUI; SillyTavern = chat off Worldtree), so no literal conflict + — but is SillyTavern/SDK now the priority app, additive-to arbo, or does this session's + don't-fork/sibling pattern change the arbo-fork plan? NOT resolved this session; preserved both. + - **Awaiting operator:** explicit go on the fixture-capture run + which instance (demo vs personal) + — worldtree-dev is holding on your call. Also awaiting template-dev's app-repo-standup consult. -**▶️ NEXT MAJOR: FORK ratatoskr for arbo image-gen (operator call 2026-07-17).** The arbo debug-observability -idea did NOT become a feature bolted onto ratatoskr. Operator decision: **ratatoskr stays the Worldtree -debug surface; the arbo image-gen product becomes a FORK of ratatoskr** (clean chat-first surface, debug -panes hidden by default). The fork is the next major workstream; "get ready to assist with fork" is the -standing directive. +**▶️ NEXT MAJOR (operator call 2026-07-17, PRESERVED): FORK ratatoskr for arbo image-gen.** The +arbo debug-observability idea did NOT become a feature bolted onto ratatoskr; ratatoskr stays the +Worldtree debug surface, the **arbo image-gen product becomes a FORK of ratatoskr** (clean chat-first +surface, debug panes hidden by default). "Get ready to assist with fork" is the standing directive. +**arbo API intel** (comfy-dev 2026-07-17): validated-workflow-library REST API — `GET /workflows` → +`POST /workflows/{id}/run` → 202 JobHandle → **poll** `GET /jobs/{id}` (NO SSE) → `outputs[].url`; +Bearer via infra-ops `ENGINE_TOKEN`; deploy `irv-ml1:8201` WireGuard-only; canonical doc +`vh/arbo:docs/rest-api.md` v1.4.27 + `/openapi.json`. (See the OPEN Q above re: arbo vs SDK/sibling.) -**✅ Pre-fork UI prep SHIPPED (v0.20.17, committed c678853, NOT pushed).** Web console: left+right debug -panes (`#rail-left`, `#affect-console`) are now independently collapsible/hideable — chevron button folds -each to a 22px re-open strip, `#center` reflows to fill, state persists per-pane in localStorage. Both -collapsed = the clean chat surface the fork starts from. Playwright-verified. Web server bounced (now pid -3194361 on :8765, serving the updated HTML). TUI untouched by this change (web-only). +**✅ Pre-fork UI prep SHIPPED (v0.20.17, `c678853`, NOT pushed).** Web console left+right debug panes +independently collapsible/hideable (chevron → 22px re-open strip, `#center` reflows, per-pane +localStorage); both collapsed = the clean chat surface a fork starts from. Playwright-verified. -**⚠️ TUI DEPRECATED (operator call 2026-07-17) — rip pending, NOT a clean delete.** The textual TUI is -superseded by the web interface; per no-backwards-compat it gets ripped. BLAST RADIUS (mapped, not yet -executed): `tui.py` (textual app) + `tests/test_tui.py` delete cleanly. BUT `cli.py` is **SHARED, not -TUI-only** — `web/entrypoint.py` and `tier3.py` both import `USER_AGENT` from it, and `cli.py` holds -`ParsedArgs`/`_format_duration_ms`/`_format_usage` (used by tui) plus its own headless `CliPresenterState`. -So the rip is a surgical disentangle, not `rm tui.py`. OPEN SCOPE Q for operator: remove textual-only -(`tui.py`, keep `cli.py`'s headless CLI + `ratatoskr` entry) vs remove the whole terminal interface -(relocate shared consts out of `cli.py` first). Breaking public-surface removal → **minor bump (needs -operator approval)**. `textual`/`textual-dev` deps removable only after confirming tui.py is the sole user. +**⚠️ TUI DEPRECATED (operator 2026-07-17) — rip pending, surgical not a clean delete.** `tui.py` + +`tests/test_tui.py` delete cleanly, BUT `cli.py` is SHARED (`web/entrypoint.py` + `tier3.py` import +`USER_AGENT`; `cli.py` holds `ParsedArgs`/`_format_duration_ms`/`_format_usage`). OPEN SCOPE Q: remove +textual-only (`tui.py`) vs the whole terminal interface (relocate shared consts out of `cli.py` +first). Breaking public-surface removal → minor bump (operator approval). -**arbo API intel (from comfy-dev 2026-07-17, banked for fork planning):** validated-workflow-library REST -API, NOT raw txt2img. `GET /workflows` (~200 recipes: t2i/img2img/inpaint/video/…) → `POST /workflows/{id}/run` -→ 202 JobHandle → **poll** `GET /jobs/{id}` (NO SSE/websocket — coarser than WT's live stream) → `GET -outputs[].url` for the image. Thin RunRequest (prompt raw|compiled + optional seed/dims/image). Bearer -auth via infra-ops `ENGINE_TOKEN`. Deploy: `irv-ml1:8201`, WireGuard-only. Canonical doc (pinnable): -`vh/arbo:docs/rest-api.md` v1.4.27 + auto-served `/openapi.json`. comfy-dev offered a concrete t2i -walkthrough + will coordinate the token when we smoke. +**✅ #368 (user,character) memory silo test — DONE, PASSED live (WT b127).** The reason for the +pre-session clear; closed. Write+read isolation proven end-to-end (conjunctive `{end_user, +agent_self}`; cold-recall gave zero cross-character bleed); throwaway `ratatoskr:betty` deleted; +Sindra intact; stores scrubbed. **Still QUEUED:** the backfill live-verify (synthetic legacy corpus +— exercises machinery a born-fresh store skips). Full record → +`persistent-memory.d/2026-07-18-368-silo-test-passed.md`. Sibling issues WT-owned: **#369** +(recited-content re-promotion loop), **#367** (copula-absence bridge phrasing). -**#368 cross-agent Tier-3 bleed — ARC DONE from ratatoskr's side** (diagnosis → two-channel investigation → -both scrubs → marker repro → enforcement read → silo test). Full forensic history moved to -`persistent-memory.d/2026-07-18-368-silo-test-passed.md`. Net: WT b127 silos Tier-3 memory per (user,character) -via conjunctive `{end_user, agent_self}` write+read; silo test PASSED live (see the ✅ summary above); our stores -scrubbed clean afterward. tier3 CLI define/patch adapted to the b127 `role` schema (`860e0d5`). Sibling issues -WT-owned: **#369** (recited-content re-promotion feedback loop), **#367** (copula-absence bridge phrasing). +**Deferred/queued (all NON-URGENT):** (1) P06-driver **referential-frame A/B/C probe** +(operator-ratified, waits on WT b122 → `persistent-memory.d/2026-07-17-referential-frame-probe.md`); +(2) R39 Phase-2 **matched-quartets rebuild** (confirmatory, "whenever"); (3) bifrost **snapshot-cursor +adoption** (ruled normative, not blocking → `persistent-memory.d/2026-07-16-bifrost-cursor-conformance.md`). -**✅ Cross-session name-recall arc COMPLETE + SHIPPED (v0.20.11→v0.20.15, pushed).** person-prime `scan` -verb + `mark_superseded` verb both shipped, deployed to :8392, and live-verified; **WT #364 closed -end-to-end both sides; Sindra durably recalls Vuong's name** (the "name unknown" negatives are RETIRED -via mark_superseded, not just recency-evicted). Full arcs: -`persistent-memory.d/2026-07-16-person-prime-scan-shipped.md` + `…-2026-07-16-wt364-r39-name-recall.md`. - -**✅ Web-console UI shipped (v0.20.16, pushed):** code-block word-wrap + per-code-block & per-turn copy -buttons (hover-revealed; one delegated `#tw` handler + LAN-http clipboard fallback; Playwright-verified). -Served from disk → browser refresh picks it up, no :8765 restart for UI edits. - -**OBSOLETE — Sindra pristine baseline WIPED 2026-07-18.** The whole Tier-3 memory.db was scrubbed to 0 -chunks (operator full-scrub ruling for #368 — contamination noise, nothing worth keeping; no backup taken -per direct order). The old `sindra-baseline/*.db` checkpoints predate the scrub and no longer reflect a -"clean 5-fact" target — the store is now born-empty. Any future test drive starts from the born-fresh store. - -**Deferred/queued tasks (all NON-URGENT — tracked in Recent decisions):** (1) P06-driver **referential- -frame A/B/C probe** (operator-ratified; waits on WT b122; read-shape + brokkr's scoring rubric captured → -`persistent-memory.d/2026-07-17-referential-frame-probe.md`); (2) R39 Phase-2 **matched-quartets rebuild** -(confirmatory, "whenever"); (3) bifrost **snapshot-cursor adoption** (ruled normative, not blocking → -`persistent-memory.d/2026-07-16-bifrost-cursor-conformance.md`). - -**Substrate / environment:** branch `main` at **v0.21.1** — local commits ahead of origin's v0.20.16 (`5a1883a`): tier3 b127-role adaptation (`860e0d5`) + memory snapshots, UNPUSHED; origin -`git@gitea.phasefinal.com:vh/ratatoskr.git`. bifrost **`==1.1.4`** / wire v0.7 (maintenance-verb hasattr -backstop + scan/cursor conformance harness); Worldtree openapi vendored 2.3.0; **suite 644 green**. -Personal WT on **b127** (#368 (user,character) silo live + deployed both instances; #364 promotion-hygiene also live). The combined **:8392** provider (memory+affect) + -**:8765** web are THE surfaces, dev-box BACKGROUND SHELLS — restart via -`scratchpad/relaunch_by_pid.py ` (env-preserving, self-daemonizing; pid via `ss -ltnp | grep `); -web serves static `index.html` from disk. `env.sh` sets the REQUIRED `RATATOSKR_MEMORY_EMBEDDING_DIM=1024`. -Keys env-only mode-600 in `~/.config/ratatoskr/provider.env` + `RATATOSKR_ADMIN_API_KEY` (7 read scopes, +**Substrate / environment:** branch `main` at **v0.21.1**; local commits ahead of origin's v0.20.16 +— incl. tier3 b127-role adaptation (`860e0d5`), #368 memory snapshots + silo work, and this session's +contract-drift fix (`4bd9abd`) — **UNPUSHED**; origin `git@gitea.phasefinal.com:vh/ratatoskr.git`. +bifrost **`==1.1.4`** / wire v0.7; WT openapi vendored 2.3.0; **suite 644 green**. Personal WT on +**b127** (`http://10.250.50.152:8081`; #368 silo + #364 promotion-hygiene live both instances). The +combined **:8392** provider (memory+affect) + **:8765** web are THE surfaces, dev-box BACKGROUND +SHELLS — restart via `scratchpad/relaunch_by_pid.py ` (env-preserving; pid via +`ss -ltnp | grep `). `env.sh` sets the REQUIRED `RATATOSKR_MEMORY_EMBEDDING_DIM=1024`. Keys +env-only mode-600 in `~/.config/ratatoskr/provider.env` + `RATATOSKR_ADMIN_API_KEY` (7 read scopes, personal-:8081-only; Heimdall keys per-instance). **FOOT-GUN: check owner-scoped agents via -`GET /agents/:` with the OWNER key (`WORLDTREE_API_KEY`), NOT the admin key** — the admin -token gives a false 404. `graphify-out/` runs dirty (auto-regen, never stage). v1 = full Worldtree I/O -coverage, cuts when WT tags 1.0 (`docs/coverage-map.md`). +`GET /agents/:` with the OWNER key (`WORLDTREE_API_KEY`), NOT the admin key** — admin +gives a false 404. `graphify-out/` runs dirty (auto-regen on the commit hook, never stage). v1 = full +Worldtree I/O coverage, cuts when WT tags 1.0 (`docs/coverage-map.md`). -**Other live threads:** soong-lab = Tier-3 agent-authoring studio (v0.3.24 fixed the motivational -string→object export; our workaround droppable for v0.3.24+ bundles). Open/deferred: #10 subject- -migration watch; relational-dynamics-arc verify (bind via `--bifrost-url :8392`); P06 optimization-phase -re-drive (brokkr brings prereg); we-framing-conditional affect-egress re-vendor. WT #356 resume-durability -+ WT #367 (copula-absence bridge phrasing) = worldtree-owned. +**Other live threads:** soong-lab = Tier-3 agent-authoring studio (v0.3.24+; motivational +string→object export fixed upstream, our workaround droppable). Cross-session name-recall arc +COMPLETE + shipped (v0.20.11→.16; WT #364 closed both sides; Sindra durably recalls Vuong via +`mark_superseded` retirement, not recency-eviction). Sindra pristine baseline **WIPED 2026-07-18** +(full #368 scrub, born-empty store — old `sindra-baseline/*.db` predate it). Open/deferred: #10 +subject-migration watch; relational-dynamics verify (bind `--bifrost-url :8392`); WT #356 +resume-durability = WT-owned. ## Recent decisions Chronological log of decisions with `[YYYY-MM-DD]` prefix. One line per decision. Captures rationale that won't be obvious from code alone. -- `[2026-06-15]` **Affect plane shipped (v0.17.2) + LIVE-PROVEN end-to-end against real Worldtree v0.35.2** → `persistent-memory.d/2026-06-15-affect-plane-shipped-v0-17-2-live-proven-end.md` -- `[2026-06-16]` **#295 cold-recall miss root-caused — UPSTREAM, scope-axis asymmetry** → `persistent-memory.d/2026-06-16-295-cold-recall-miss-root-caused-upstream-sco.md` -- `[2026-06-16]` **agent_self → make it CANONICAL (operator decided A)** → `persistent-memory.d/2026-06-16-agentself-make-it-canonical-operator-decided.md` -- `[2026-06-16]` **Self-drive auth identity: bound session-create uses the CONSUMER Heimdall key as bearer, NOT `WORLDTREE_API_KEY`** → `persistent-memory.d/2026-06-16-self-drive-auth-identity-bound-session-create.md` -- `[2026-06-16]` **Issue #17 v1 scope locked (operator 1A/2A): single-plane bind + dispatch-layer op-feed** → `persistent-memory.d/2026-06-16-issue-17-v1-scope-locked-operator-1a-2a-singl.md` -- `[2026-06-16]` **agent_self lattice SHIPPED both sides → our axis-validation gap CLOSED (v0.17.5)** → `persistent-memory.d/2026-06-16-agentself-lattice-shipped-both-sides-our-axis.md` -- `[2026-06-16]` **Repinned bifrost 0.7.0→0.8.0 + reimplemented memory `search` to the v0.6 scope split (operator-directed)** → `persistent-memory.d/2026-06-16-repinned-bifrost-0-7-0-0-8-0-reimplemented-me.md` -- `[2026-06-17]` **Worldtree spec pin bumped v0.29.0→v0.35.16 (`562001a`→`f1b59f8`); cold recall closed on the WIRE** → `persistent-memory.d/2026-06-17-worldtree-spec-pin-bumped-v0-29-0-v0-35-16-56.md` -- `[2026-06-17]` **End-to-end cold-recall proof RAN — our stack proven, #296 isolated** → `persistent-memory.d/2026-06-17-end-to-end-cold-recall-proof-ran-our-stack-pr.md` -- `[2026-06-17]` **DELETE+redefine `ratatoskr:sindra` (operator-authorized; pre-v1 debug surface)** → `persistent-memory.d/2026-06-17-delete-redefine-ratatoskr-sindra-operator-aut.md` -- `[2026-06-17]` **Promotion = 4-trigger hybrid (worldtree-dev, code-grounded):** → `persistent-memory.d/2026-06-17-promotion-4-trigger-hybrid-worldtree-dev-code.md` - `[2026-06-17]` **#296 triage sent to worldtree-dev** (`01KVBBH0…`): extraction SUBJECT-INVERSION (promotes assistant prose, drops the user's fact) + META-DESCRIPTION-not-content; verbose-persona aggravator. WAD-vs-bug resolved to BUG (extraction quality), not idle-gating. - `[2026-06-18]` **Tier-3 memory PROVEN end-to-end live** — `ratatoskr:terse-probe` recalled a seeded user fact in a COLD history-free session (scope_any → 1 hit @ cosine 0.6994). Closes the opening "how far from Tier-3 memory" question for normal agents. -- `[2026-06-18]` **#296 Stages 1+2 closed** → `persistent-memory.d/2026-06-18-296-stages-1-2-closed.md` -- `[2026-06-18]` **#17 implemented end-to-end via direct in-session TDD** → `persistent-memory.d/2026-06-18-17-implemented-end-to-end-via-direct-in-sessi.md` -- `[2026-06-18]` **#17 live-smoke PROVEN — the whole thesis validated** → `persistent-memory.d/2026-06-18-17-live-smoke-proven-the-whole-thesis-validat.md` -- `[2026-06-18]` **Fixed a pre-existing test-isolation bug exposed by the #17 CLI tests** → `persistent-memory.d/2026-06-18-fixed-a-pre-existing-test-isolation-bug-expos.md` -- `[2026-06-18]` **#18 filed (composite endpoint + PAD read-endpoint) — DEFERRED, tracked at Gitea #18** → `persistent-memory.d/2026-06-18-18-filed-composite-endpoint-pad-read-endpoint.md` -- `[2026-06-18]` **#18 SPLIT; Deliverable 1 (composite) routed to bifrost — Option C (operator)** → `persistent-memory.d/2026-06-18-18-split-deliverable-1-composite-routed-to-bi.md` -- `[2026-06-18]` **FR-1 RESOLVED — the composite premise was unverified, now wire-proven: single-endpoint, caps-routed** → `persistent-memory.d/2026-06-18-fr-1-resolved-the-composite-premise-was-unver.md` -- `[2026-06-18]` **#18 D2 implemented via direct in-session TDD (suite 470→482)** → `persistent-memory.d/2026-06-18-18-d2-implemented-via-direct-in-session-tdd-s.md` - `[2026-06-19]` **#18 D2 SHIPPED (`v0.17.14`, `39eebd1`) and the full #17+#18 arc PUSHED to origin** → `persistent-memory.d/2026-06-19-18-d2-shipped-v0-17-14-39eebd1-and-the-full-1.md` - `[2026-06-19]` **bifrost repinned 0.8.0→0.10.0; `affect.fetch` became MANDATORY (strong-or-absent)** → `persistent-memory.d/2026-06-19-bifrost-repinned-0-8-0-0-10-0-affect-fetch-be.md` @@ -276,7 +247,10 @@ decision. Captures rationale that won't be obvious from code alone. - `[2026-07-18]` **#368 (user, character) memory silo test — DONE, PASSED live (WT b127).** Write + read isolation both proven end-to-end; betty (throwaway) deleted; the immediate post-clear action, closed. → `persistent-memory.d/2026-07-18-368-silo-test-passed.md` -_46 older entries (2026-05-* debug-TUI/web era + the 2026-06-14→16 Bifrost-provider build era) archived to archival-memory.md._ +- `[2026-07-18]` **Worldtree owns the official Conversation API SDK (#371) — Rata's client spine (`sse_client`/`sessions`/`tier3`) is the SEED; the SillyTavern-style app is a fresh TS SIBLING, NOT a fork of Rata.** TS package ships first (Rata's repin rides the later Python milestone); internal-registry-only; template-dev stands up the app repo. → `persistent-memory.d/2026-07-18-worldtree-sdk-371-and-sillytavern-app.md` +- `[2026-07-18]` **issue #1 contract drift fixed (`4bd9abd`) — SSE event vocab re-canonicalized: added `awaiting_llm_first_token` (#201) + `affect_update` (#204) to the Event union + TESTS (both were parsed by `_envelope_for_type` and tested in `test_sse_client.py`, but never in #1's contract; surfaced by the #371 parity pass). Contract-only, no bump.** + +_65 older entries (2026-05-* debug-TUI/web era + the 2026-06-14 → 06-18 Bifrost-provider build / #17+#18 / #295-296 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._ @@ -286,25 +260,9 @@ Log of approaches that were tried and rejected, with rationale. Future-self defense against re-attempting the same cul-de-sac. - `[2026-07-10]` **Reverting our provider to bifrost 1.0.0 to fix the bound-handshake `schema_validation_failed` — DISPROVEN, and it would've been the WRONG state** → `persistent-memory.d/2026-07-10-reverting-our-provider-to-bifrost-1-0-0-to-fi.md` -- `[2026-06-15]` **"Sindra hasn't been registered" was an under-verified inference — WRONG** → `persistent-memory.d/2026-06-15-sindra-hasn-t-been-registered-was-an-under-ve.md` -- `[2026-06-14]` **Artifact-only contract review can't validate against a dependency's ACTUAL behavior** → `persistent-memory.d/2026-06-14-artifact-only-contract-review-can-t-validate.md` -- `[2026-06-15]` **"byte-equal" round-trip slip propagated affect→memory via copy-paste** → `persistent-memory.d/2026-06-15-byte-equal-round-trip-slip-propagated-affect.md` - `[2026-06-15]` **Canonical sync retired the issue-scoped parser staleness.** `contract_parser.py` synced to v2.1 (commit `d85ab43`): now validates issue-scoped frontmatter + four v2.1 test categories. The old "treat parser ERROR-on-issue-scoped as expected" note no longer applies. -- `[2026-06-15]` **Memory plane TDD'd + shipped** → `persistent-memory.d/2026-06-15-memory-plane-tdd-d-shipped.md` -- `[2026-06-15]` **Memory provider LIVE-PROVEN against personal v0.35.3; recall-injection is upstream** → `persistent-memory.d/2026-06-15-memory-provider-live-proven-against-personal.md` -- `[2026-06-15]` **Diagnostic: our recall-search is SOUND — the cross-session recall gap is UPSTREAM, and it caught an upstream bug** → `persistent-memory.d/2026-06-15-diagnostic-our-recall-search-is-sound-the-cro.md` -- `[2026-06-15]` **"Wire 200 ≠ recall works" — prove recall efficacy at the model's answer in a COLD (history-free) session, not on the wire** → `persistent-memory.d/2026-06-15-wire-200-recall-works-prove-recall-efficacy-a.md` - `[2026-06-15]` **Issue #17 filed.** REVERSES design-brief §6's "no Bifrost-binding consumer support" — that negative clause predates ratatoskr's provider identity (2026-06-14), so the canary now owns both ends but its client couldn't drive its own provider. (Shipped 2026-06-18.) - `[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` 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** → `persistent-memory.d/2026-06-16-my-295-coupling-hypothesis-the-promoted-quest.md` -- `[2026-06-16]` **#17 contract drifted from its own design in two spots, caught only by `/heid-contract-review` (not same-author paraphrase):** → `persistent-memory.d/2026-06-16-17-contract-drifted-from-its-own-design-in-tw.md` -- `[2026-06-16]` **"No promotion" was checked TOO EARLY — Tier-3 promotion is ASYNC (lands AFTER the SSE turn-end)** → `persistent-memory.d/2026-06-16-no-promotion-was-checked-too-early-tier-3-pro.md` -- `[2026-06-17]` **"sindra is GONE" (infra-ops, from `GET /agents` + admin token) was a FALSE NEGATIVE** → `persistent-memory.d/2026-06-17-sindra-is-gone-infra-ops-from-get-agents-admi.md` -- `[2026-06-17]` **"Promotion didn't fire → #296" was PREMATURE — twice over** → `persistent-memory.d/2026-06-17-promotion-didn-t-fire-296-was-premature-twice.md` -- `[2026-06-18]` **Wiping our `:8391` store does NOT reset Worldtree's promotion-side dedup** → `persistent-memory.d/2026-06-18-wiping-our-8391-store-does-not-reset-worldtre.md` -- `[2026-06-18]` **`affect.emit` is POST-TURN ASYNC — checking the op-feed immediately after a turn MISSES it** → `persistent-memory.d/2026-06-18-affect-emit-is-post-turn-async-checking-the-o.md` -- `[2026-06-18]` **Rationalized away a KNOWN contract-invariant deviation during TDD — only the cross-model code-review caught it** → `persistent-memory.d/2026-06-18-rationalized-away-a-known-contract-invariant.md` -- `[2026-06-18]` **Latent SQLite thread-safety bug in the affect store, surfaced ONLY by the new HTTP read route** → `persistent-memory.d/2026-06-18-latent-sqlite-thread-safety-bug-in-the-affect.md` - `[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)** → `persistent-memory.d/2026-06-19-the-same-checksamethread-sqlite-bug-recurred.md` - `[2026-06-19]` **Full WT-driven `:8392` live-smoke is infra-gated — `:8392` not in WT's `BIFROST_CLIENT_ALLOWED_HOSTS` (bind 422s)** → `persistent-memory.d/2026-06-19-full-wt-driven-8392-live-smoke-is-infra-gated.md` @@ -336,4 +294,4 @@ defense against re-attempting the same cul-de-sac. - `[2026-07-16]` **`sortable_chunk_fields` advertised WITHOUT the required `type` field = whole-handshake deploy-breaker; only DRIVING the real bind caught it** → `persistent-memory.d/2026-07-16-sortablechunkfields-advertised-without-the-re.md` -_18 older entries (2026-05-* — the original debug-TUI/web build era) archived to archival-memory.md._ +_34 older entries (2026-05-* debug-TUI/web era + the 2026-06-14 → 06-18 foot-gun cluster) archived to archival-memory.md._