Files
ratatoskr/persistent-memory.d/2026-07-16-bifrost-cursor-conformance.md

3.0 KiB

[2026-07-16] bifrost scan/cursor conformance gap → snapshot-cursor ruled NORMATIVE

The gap (I surfaced it; operator's catch that it was bifrost's to fix)

The heid-bug-hunt flagged our offset cursor's cross-page dup/drop under mutation. On verifying before routing, it turned out sharper than "robustness": bifrost's PROTOCOL already mandates snapshot cursors — the dispatch engine (bifrost/memory.py:311-314) drops sort on a cursor continuation with the comment "the cursor's snapshotted order is authoritative", and maps ScanCursorExpired → 410. The reference InMemoryMemoryStore implements it (frozen ordered id-list per opaque token + TTL). But bifrost's conformance suite had ZERO scan/cursor coverage — so our non-snapshot offset cursor passed conformance while violating the protocol contract. That coverage hole is the real completeness concern. Routed to bifrost-dev (thread 01KXK7MDTY…).

bifrost-dev's ruling

  1. Snapshot-cursor is NORMATIVE, not opaque/per-store. Operator ruled: cursor snapshots a frozen ordered id-list, continuation ignores sort, stale/unknown cursor → ScanCursorExpired → 410. Offset-with-documented-limits is NOT blessed.
  2. Conformance coverage added in bifrost 1.1.3 (bifrost.conformance. memory_store_conformance, 4 probes: scan_snapshot_order_authoritative, scan_no_dup_or_drop, scan_unknown_cursor_expired, scan_snapshot_stable_under_write opt-in). Our offset bug ships as their negative-canary fixture (fixtures.v0_7.offset_cursor_store.OffsetCursorMemoryStore). main() grades import failure as exit 2 (setup error) vs exit 1 (conformance FAIL).

Our status + the TODO (operator-sequenced, NOT urgent)

Single-page person-prime (cursor=None) is already conformant — nothing shipped is broken; it's only multi-page continuation that's non-conformant. Our contract INV-010 currently marks the offset cursor v1-provisional / KNOWN DEVIATION (commit 8199774).

TODO when sequenced:

  1. Bump pin bifrost 1.1.1 → 1.1.4 (1.1.4 supersedes 1.1.3: adds the hasattr-gate backstop for the maintenance verbs — mark_superseded/mark_invalid/patch_many/delete_many/upsert_edges/get_edges_for degrade to memory.unsupported_capability 400 not AttributeError/500 — on top of 1.1.3's scan/cursor conformance harness. One bump gets both).
  2. Replace the offset cursor with the reference snapshot semantics (frozen id-list per opaque token + ScanCursorExpired → 410).
  3. Run run_all(store_factory=..., include_optional=True) against our SQLite store — expect P1/P2/P3 RED → green (the before/after IS the validation). Report to bifrost-dev (we're their canary — first non-reference scan implementer).
  4. Flip INV-010 from v1-provisional to snapshot semantics.

TTL-duration expiry NOT asserted by the harness (no portable clock hook via store_factory); bifrost-dev offered a clock_control opt-in if we ever need time-based expiry certified — parked, not needed yet.