Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f46ccbae1c | |||
| 772fad18b4 | |||
| 8199774405 | |||
| 66ba06875e |
@@ -119,10 +119,14 @@ interpreted.
|
||||
`describe_store` advertises ONLY what is implemented: `relational_edges_supported=False`,
|
||||
`atomic_supersede_supported=False`, `transaction_supported=False`,
|
||||
`optimistic_locking_supported=True`, `filterable_metadata_fields=[]`,
|
||||
**`sortable_chunk_fields=[{"name": "updated_at"}]`** (the ONLY globally-sortable field;
|
||||
gates `scan`'s sort at the bifrost dispatch `_validate_scan_sort` AND Worldtree's #349
|
||||
person-prime Branch-A `"updated_at" in caps.sort_fields_supported` — advertising it is
|
||||
what lights up turn-1 durable-fact injection).
|
||||
**`sortable_chunk_fields=[{"name": "updated_at", "type": "timestamp"}]`** (the ONLY
|
||||
globally-sortable field; gates `scan`'s sort at the bifrost dispatch `_validate_scan_sort`
|
||||
AND Worldtree's #349 person-prime Branch-A `"updated_at" in caps.sort_fields_supported` —
|
||||
advertising it is what lights up turn-1 durable-fact injection). Both `name` AND `type`
|
||||
are REQUIRED by the bifrost `handshake_response` `SortableChunkField` schema
|
||||
(`additionalProperties:false`) — omitting `type` fails wire-schema validation and breaks
|
||||
the ENTIRE handshake (memory + affect bind), not just the sort; `type` is advisory-only
|
||||
(the wire never interprets it).
|
||||
(`transaction_supported` is the bifrost **wire-level** multi-op transaction
|
||||
capability — NOT our internal SQLite transactions, which we use for atomic
|
||||
batches.) The client gates the gated verbs off these.
|
||||
@@ -145,6 +149,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
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "ratatoskr"
|
||||
version = "0.20.13"
|
||||
version = "0.20.14"
|
||||
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -132,7 +132,10 @@ def _validate_injection(record: dict) -> None:
|
||||
raise InvalidArguments("injection_source only valid for injected_context origin")
|
||||
|
||||
|
||||
_SORTABLE_CHUNK_FIELDS: list[dict] = [{"name": "updated_at"}]
|
||||
# bifrost handshake_response SortableChunkField requires BOTH name + type
|
||||
# (additionalProperties:false); omitting `type` fails wire-schema validation and
|
||||
# breaks the whole handshake. `type` is advisory-only (the wire never interprets it).
|
||||
_SORTABLE_CHUNK_FIELDS: list[dict] = [{"name": "updated_at", "type": "timestamp"}]
|
||||
_SORTABLE_FIELD_NAMES = frozenset(f["name"] for f in _SORTABLE_CHUNK_FIELDS)
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,15 @@ def test_fresh_db_advertises_v1_caps_and_schema():
|
||||
assert caps["atomic_supersede_supported"] is False
|
||||
assert caps["transaction_supported"] is False
|
||||
assert caps["filterable_metadata_fields"] == []
|
||||
# bifrost handshake_response SortableChunkField requires BOTH name + type
|
||||
# (additionalProperties:false) — omitting `type` fails wire-schema validation and
|
||||
# breaks the ENTIRE Bifrost bind (regression guard: the deploy-breaker of 2026-07-15).
|
||||
scf = caps["sortable_chunk_fields"]
|
||||
assert scf == [{"name": "updated_at", "type": "timestamp"}]
|
||||
for entry in scf:
|
||||
assert set(entry) == {"name", "type"} # required exactly, no extra keys
|
||||
assert isinstance(entry["name"], str) and entry["name"]
|
||||
assert isinstance(entry["type"], str) and entry["type"]
|
||||
# tables + vec index queryable
|
||||
store._conn.execute("SELECT * FROM memory_chunks")
|
||||
store._conn.execute("SELECT * FROM memory_idempotency")
|
||||
|
||||
Reference in New Issue
Block a user