diff --git a/docs/contracts/bifrost_memory_provider.contract.md b/docs/contracts/bifrost_memory_provider.contract.md index 6eb2dd9..2f1f0b8 100644 --- a/docs/contracts/bifrost_memory_provider.contract.md +++ b/docs/contracts/bifrost_memory_provider.contract.md @@ -145,6 +145,20 @@ interpreted. `updated_at desc`), matching bifrost's cross-pagination conformance negative. The sort field is indexed (`json_extract(record_json, '$.updated_at')`) so the read stays within person-prime's 500 ms fail-open budget. + - **Cursor is v1-provisional (KNOWN DEVIATION — offset, not snapshot).** The cursor is a + bare integer offset into the re-derived global order. This is CORRECT and conformant for + the **single-page** person-prime call (`cursor=None`), which is the only shipped consumer. + It **diverges from bifrost's protocol snapshot-cursor contract on multi-page continuation**: + the dispatch engine (`bifrost.memory` scan branch) drops the `sort` arg on a cursor + continuation because "the cursor's snapshotted order is authoritative", and maps + `ScanCursorExpired → 410`. Our offset cursor (a) does NOT snapshot the order — a page taken + after a concurrent write can duplicate/drop rows relative to the first page (heid-bug-hunt + 2026-07-15, all 3 arms), and (b) never raises `ScanCursorExpired`. The `global_before_paginate` + / cursor test asserts **static-store** behavior only. The durable/conformant fix is to adopt + the reference `InMemoryMemoryStore`'s snapshot-cursor semantics (opaque token + frozen ordered + id-list + TTL + `ScanCursorExpired`); DEFERRED pending bifrost-dev's ruling on the conformance + gap (scan/cursor has NO conformance coverage today, so a non-snapshot cursor passes). Routed + to bifrost-dev 2026-07-15. ## Concurrency diff --git a/persistent-memory.md b/persistent-memory.md index a56752d..94a1e16 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -46,7 +46,7 @@ upstream API key stays server-side (INV-003). _As of 2026-07-15:_ -**▶️ IN-FLIGHT — person-prime `scan` build (THE "last push"): CODE LANDED; DEPLOY BLOCKED on operator restart.** WHY: Sindra didn't remember Vuong's name across sessions; worldtree-dev root-caused it — WT injects a recalled fact only if combined score (sim×salience) ≥ **0.45** (`auto_inject_combined_score_threshold`, `core/memory/context_promotion/config.py`), and recall is per-turn **query-gated**, so moderate-sim durable facts (name hit ~0.40) never inject. The designed **turn-0** fix is WT **#349 person-prime** — a query-LESS top-N-by-recency durable-fact injection — capability-gated on the store advertising **`updated_at` in `sort_fields`** at the Bifrost handshake, DARK for our provider until now. Fix (ZERO Worldtree change): implement the sorted `scan` verb + advertise the capability. **✅ CODE COMPLETE + COMMITTED (2026-07-15):** contract amended + validated; `scan` impl in `src/ratatoskr/provider/memory_store.py` (globally-ordered by indexed `updated_at`, live-only via `_is_live`, offset-cursor; `has_more` peek so an exact-fill page emits no empty trailing cursor = reference-parity; `describe_store` advertises `sortable_chunk_fields=[{updated_at}]`; expression index `idx_chunks_updated_at`); **TDD 7/7 GREEN** (scan_recency tracer + live_only + scope_isolation + unadvertised_sort + person_prime_record_shape + cursor pagination + **`parity_vs_reference` #195**); NUANCE resolved — the bifrost ref `InMemoryMemoryStore` **READS** `updated_at` from the record (never stamps it; its own comment confirms), identical to ours, so order-parity holds over the live set (ref does NOT lifecycle-filter, so parity is defined over live chunks only). **Full suite 638 green.** Committed as **two patches**: `8fc757a` scan (**v0.20.11**) + `a9c521a` Sindra holodesk first-message preset (**v0.20.12**, split out as a separate concern from the soong-lab redefine). **heid-bug-hunt RETURNED + TRIAGED** (panel Gróa/Hulda/Regin, thread `01KXK5XTYHV8TGEDRAZV8GRXWC`; base-ref 39050c3, code-only diff). **FIXED (v0.20.13, `25ccb5c`):** the non-dict `sort` crash — a truthy non-dict `sort` (`"updated_at"`/`["updated_at"]`/int) hit `(sort or {}).get(...)` → AttributeError instead of InvalidArguments; added isinstance guard (Gróa#1/Hulda#2, confirmed-from-code). **SURFACED (operator design decision, NOT auto-adopted):** offset-cursor cross-page duplicate/drop under concurrent mutation (all 3 arms) — real divergence from bifrost `InMemoryMemoryStore` (snapshots the ordered id-list per opaque cursor + `ScanCursorExpired` TTL); BUT person-prime uses a SINGLE page (no cursor) so the actual use isn't exercised → operator picks: adopt reference snapshot-cursors vs keyset vs accept + honestly scope INV-010 to a static store. **NOTED (reference-parity / accept-known-risk, no fork):** full-table materialize-then-Python-filter (matches ref's own O(store) iteration), unbound limit, falsy scope coercion `[]`→`{}` (parity w/ search + ref), lexical timestamp ordering (WT writes consistent ISO-8601 UTC); `_is_live` dead-marker completeness (Gróa#6 needs-repro) → confirm canonical marker set w/ worldtree-dev, don't guess. **REFUTED:** Regin's mixed-type ORDER BY "crash" (SQLite orders by storage class, doesn't raise) + its self-retracted `_is_live`/None item. **⛔ REMAINING (BLOCKED / operator-gated):** (5) **DEPLOY — restart the `:8392` combined provider** so it advertises `sortable_chunk_fields` at the handshake — **auto-mode classifier DENIED me killing the running :8392 provider (pid 982284, not started this session); NEEDS OPERATOR.** Restart via the env-preserving `scratchpad/relaunch_provider.py ` (captures /proc environ — byte-identical config; self-daemonizes) OR `source env.sh` + `export RATATOSKR_MEMORY_EMBEDDING_DIM=1024` (env.sh OMITS this REQUIRED var) + kill+`nohup .venv/bin/ratatoskr-combined-provider`. Editable install already reflects the new code (v0.20.13). (6) ping **worldtree-dev to verify** turn-1 recall against personal :8081 — standing by; GATED on (5). (7) resolve the F3 cursor design decision (above) → then adjust INV-010 wording to match the choice. +**▶️ IN-FLIGHT — person-prime `scan` build (THE "last push"): CODE LANDED; DEPLOY BLOCKED on operator restart.** WHY: Sindra didn't remember Vuong's name across sessions; worldtree-dev root-caused it — WT injects a recalled fact only if combined score (sim×salience) ≥ **0.45** (`auto_inject_combined_score_threshold`, `core/memory/context_promotion/config.py`), and recall is per-turn **query-gated**, so moderate-sim durable facts (name hit ~0.40) never inject. The designed **turn-0** fix is WT **#349 person-prime** — a query-LESS top-N-by-recency durable-fact injection — capability-gated on the store advertising **`updated_at` in `sort_fields`** at the Bifrost handshake, DARK for our provider until now. Fix (ZERO Worldtree change): implement the sorted `scan` verb + advertise the capability. **✅ CODE COMPLETE + COMMITTED (2026-07-15):** contract amended + validated; `scan` impl in `src/ratatoskr/provider/memory_store.py` (globally-ordered by indexed `updated_at`, live-only via `_is_live`, offset-cursor; `has_more` peek so an exact-fill page emits no empty trailing cursor = reference-parity; `describe_store` advertises `sortable_chunk_fields=[{updated_at}]`; expression index `idx_chunks_updated_at`); **TDD 7/7 GREEN** (scan_recency tracer + live_only + scope_isolation + unadvertised_sort + person_prime_record_shape + cursor pagination + **`parity_vs_reference` #195**); NUANCE resolved — the bifrost ref `InMemoryMemoryStore` **READS** `updated_at` from the record (never stamps it; its own comment confirms), identical to ours, so order-parity holds over the live set (ref does NOT lifecycle-filter, so parity is defined over live chunks only). **Full suite 638 green.** Committed as **two patches**: `8fc757a` scan (**v0.20.11**) + `a9c521a` Sindra holodesk first-message preset (**v0.20.12**, split out as a separate concern from the soong-lab redefine). **heid-bug-hunt RETURNED + TRIAGED** (panel Gróa/Hulda/Regin, thread `01KXK5XTYHV8TGEDRAZV8GRXWC`; base-ref 39050c3, code-only diff). **FIXED (v0.20.13, `25ccb5c`):** the non-dict `sort` crash — a truthy non-dict `sort` (`"updated_at"`/`["updated_at"]`/int) hit `(sort or {}).get(...)` → AttributeError instead of InvalidArguments; added isinstance guard (Gróa#1/Hulda#2, confirmed-from-code). **SURFACED (operator design decision, NOT auto-adopted):** offset-cursor cross-page duplicate/drop under concurrent mutation (all 3 arms) — real divergence from bifrost `InMemoryMemoryStore` (snapshots the ordered id-list per opaque cursor + `ScanCursorExpired` TTL); BUT person-prime uses a SINGLE page (no cursor) so the actual use isn't exercised → operator picks: adopt reference snapshot-cursors vs keyset vs accept + honestly scope INV-010 to a static store. **NOTED (reference-parity / accept-known-risk, no fork):** full-table materialize-then-Python-filter (matches ref's own O(store) iteration), unbound limit, falsy scope coercion `[]`→`{}` (parity w/ search + ref), lexical timestamp ordering (WT writes consistent ISO-8601 UTC); `_is_live` dead-marker completeness (Gróa#6 needs-repro) → confirm canonical marker set w/ worldtree-dev, don't guess. **REFUTED:** Regin's mixed-type ORDER BY "crash" (SQLite orders by storage class, doesn't raise) + its self-retracted `_is_live`/None item. **⛔ REMAINING (BLOCKED / operator-gated):** (5) **DEPLOY — restart the `:8392` combined provider** so it advertises `sortable_chunk_fields` at the handshake — **auto-mode classifier DENIED me killing the running :8392 provider (pid 982284, not started this session); NEEDS OPERATOR.** Restart via the env-preserving `scratchpad/relaunch_provider.py ` (captures /proc environ — byte-identical config; self-daemonizes) OR `source env.sh` + `export RATATOSKR_MEMORY_EMBEDDING_DIM=1024` (env.sh OMITS this REQUIRED var) + kill+`nohup .venv/bin/ratatoskr-combined-provider`. Editable install already reflects the new code (v0.20.13). (6) ping **worldtree-dev to verify** turn-1 recall against personal :8081 — standing by; GATED on (5). (7) **F3 cursor RESOLVED — operator ACCEPTED offset-for-v1** (keep offset cursor; person-prime single-page is correct/conformant). INV-010 amended to mark the cursor **v1-provisional / KNOWN DEVIATION** (multi-page continuation diverges from bifrost's protocol snapshot-cursor contract — the `bifrost.memory` dispatch drops `sort` on continuation citing "snapshotted order is authoritative" + maps `ScanCursorExpired→410`; our offset cursor doesn't snapshot → dup/drop under concurrent write, and never expires). **Durable fix = a BIFROST completeness concern (operator's catch), ROUTED to bifrost-dev** (thread `01KXK7MDTY0B6VEDF3SVGSKY9R`): bifrost's conformance suite has ZERO scan/cursor coverage, so a non-snapshot cursor passes silently — asked bifrost-dev to (1) confirm snapshot-cursor+ScanCursorExpired is the NORMATIVE store contract, (2) add scan/cursor conformance coverage; we'll adopt the reference snapshot-cursor semantics IF they rule it normative (offered to be their canary as first non-reference scan implementer). AWAITING bifrost-dev ruling; monitor armed. **✅ soong-lab interop ESTABLISHED (this session) — soong-lab is now our Tier-3 agent-authoring studio.** ratatoskr consumes soong-lab bundles → WT `agents.define`, and ships agents back as bundles. Sindra round-trip PROVEN (soong imported her `resume` half through real `import_bundle`). soong-lab export/importer contracts PINNED via canonical-sync (`canonical_source=soong-lab` @ f434016, commit `39050c3`). Role-grounding (worldtree-dev): the 4 soong-lab ROLE_CHOICES = WT model-role slugs 1:1 BY NAME — `character`/`thoughtful-character` need the `character` grant (HELD via Sindra → define-able now); `assistant`/`thoughtful-assistant` need `foundational` (confirm/mint routed to infra-ops, in flight; only for assistant-type bundles). `character-rp` ≡ `thoughtful-character` (same Deckard seat + grant). **Sindra REDEFINED from the bundle** (`/tmp/sindra.json`, operator "update to match"): persona immutable → DELETE+redefine (payload validated via a throwaway `sindra-probe2`→201 BEFORE deleting the real one), `memory:{}` preserved, motivational string→WT-object mapped (Tried/abandoned), role→`thoughtful-character`, first_message preset updated. Verified live (thoughtful-character, 25574-char prompt).