diff --git a/pyproject.toml b/pyproject.toml index b8aa9d4..8fbe0b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "ratatoskr" -version = "0.20.12" +version = "0.20.13" description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard" readme = "README.md" requires-python = ">=3.12" diff --git a/src/ratatoskr/provider/memory_store.py b/src/ratatoskr/provider/memory_store.py index 937f91e..87bc495 100644 --- a/src/ratatoskr/provider/memory_store.py +++ b/src/ratatoskr/provider/memory_store.py @@ -383,6 +383,8 @@ class RatatoskrMemoryStore: scope_all = scope_all or {} scope_any = scope_any or [] _validate_scope(scope_all, scope_any) # PRE-002 (same lattice as search) + if sort is not None and not isinstance(sort, dict): # PRE-003: malformed sort => reject, never crash + raise InvalidArguments("sort must be an object with field and direction") field = (sort or {}).get("field", "updated_at") direction = (sort or {}).get("direction", "desc") if field not in _SORTABLE_FIELD_NAMES or direction not in ("asc", "desc"): # PRE-003 diff --git a/tests/test_provider_memory.py b/tests/test_provider_memory.py index ed42955..bf10c12 100644 --- a/tests/test_provider_memory.py +++ b/tests/test_provider_memory.py @@ -427,6 +427,14 @@ async def test_scan_unadvertised_sort_field_rejected(): await store.scan(scope_all={"end_user": "u1"}, limit=3, sort={"field": "salience", "direction": "desc"}) +async def test_scan_non_dict_sort_rejected(): + # PRE-003: a truthy non-dict sort (caller-controlled) -> InvalidArguments, never AttributeError. + store = open_memory_store(":memory:", embedding_dim=EMBEDDING_DIM) + for bad in ("updated_at", ["updated_at"], 5): + with pytest.raises(InvalidArguments): + await store.scan(scope_all={"end_user": "u1"}, limit=3, sort=bad) + + async def test_scan_records_carry_person_prime_filter_fields(): # The client _scan_filter_matches keys on agent_id + subject + worldtree_scope; a record # missing any is silently dropped -> the scan record must carry them verbatim. diff --git a/uv.lock b/uv.lock index b1553fc..bdfd356 100644 --- a/uv.lock +++ b/uv.lock @@ -1052,7 +1052,7 @@ wheels = [ [[package]] name = "ratatoskr" -version = "0.20.12" +version = "0.20.13" source = { editable = "." } dependencies = [ { name = "httpx" },