diff --git a/.superpowers/sdd/2026-08-08-internal-qdrant-ollama/task-6-report.md b/.superpowers/sdd/2026-08-08-internal-qdrant-ollama/task-6-report.md index a70b3c9b..8b70773e 100644 --- a/.superpowers/sdd/2026-08-08-internal-qdrant-ollama/task-6-report.md +++ b/.superpowers/sdd/2026-08-08-internal-qdrant-ollama/task-6-report.md @@ -112,3 +112,33 @@ Notes: - Legacy HTTP/vector-rest deployments do not gain a new destructive RPC surface from this fix; they keep their previous behavior and continue to fail closed for unsupported cleanup. - The broader suite still emits the same pre-existing deprecation and serializer warnings already noted above; this round did not introduce new warnings. + +## Fix round 3 (2026-08-08) + +Scope: + +- Added an adapter-level Qdrant regression for mixed semantic kinds within one workspace plus a second workspace memory point. +- Verified that `delete_kinds("memory", ["memory"])` emits the real adapter filter with both `workspace_id=demo` and `record_kind=memory`. +- Verified that non-memory semantic kinds in the same workspace and memory from another workspace survive the delete. + +RED evidence: + +- Re-review identified a test gap rather than a confirmed runtime bug: + - existing coverage asserted only the CLI mock call shape for qdrant memory clear + - there was no adapter-level regression proving the real Qdrant delete filter and resulting fake-Qdrant state across mixed semantic kinds/workspaces +- Added regression: + - `tests/test_qdrant_vector_store.py::test_delete_kinds_is_workspace_scoped_and_preserves_other_semantic_kinds` + +GREEN evidence: + +- Requested focused suite: + - `cd harness && .venv/bin/pytest tests/test_qdrant_vector_store.py tests/test_qdrant_cli_commands.py tests/test_semantic_kind_isolation.py -q` + - Result: `18 passed` +- Ruff on changed files: + - `cd harness && .venv/bin/ruff check tests/test_qdrant_vector_store.py` + - Result: clean + +Notes: + +- This round required no production change; the new adapter regression passed against the existing Qdrant implementation. +- The focused suite still emits the same pre-existing `testcontainers.postgres` deprecation warning from `tests/conftest.py`; no new warnings were introduced. diff --git a/harness/tests/test_qdrant_vector_store.py b/harness/tests/test_qdrant_vector_store.py index ab5789c4..5da91c5b 100644 --- a/harness/tests/test_qdrant_vector_store.py +++ b/harness/tests/test_qdrant_vector_store.py @@ -307,6 +307,51 @@ def test_existing_hashes_health_and_exact_generation_inventory_and_delete(): assert health.dimension_compatible is True +def test_delete_kinds_is_workspace_scoped_and_preserves_other_semantic_kinds(): + fake = FakeQdrantHttp() + store = _store(fake) + + store.upsert("memory", [_write_record("memory:1", "memory")]) + store.upsert("memory", [_write_record("solved:1", "solved_question")]) + store.upsert("schema_records", [_write_record("schema_table:patients", "schema_table")]) + + other_workspace_memory = next( + point for point in fake.points.values() if point["payload"]["record_key"] == "memory:1" + ).copy() + other_workspace_memory["id"] = point_id("other", "memory", "memory:other") + other_workspace_memory["payload"] = { + **other_workspace_memory["payload"], + "workspace_id": "other", + "record_key": "memory:other", + "title": "title:memory:other", + "content": "content:memory:other", + "ref": "ref:memory:other", + } + fake.points[other_workspace_memory["id"]] = other_workspace_memory + + assert store.delete_kinds("memory", ["memory"]) == 1 + + delete_call = next( + call + for call in fake.calls + if call[0] == "POST" and call[1].endswith("/points/delete?wait=true") + ) + assert delete_call[2]["filter"] == { + "must": [ + {"key": "workspace_id", "match": {"value": "demo"}}, + {"key": "record_kind", "match": {"any": ["memory"]}}, + ] + } + assert { + point["payload"]["record_key"]: point["payload"]["record_kind"] + for point in fake.points.values() + } == { + "solved:1": "solved_question", + "schema_table:patients": "schema_table", + "memory:other": "memory", + } + + def test_sanitizes_timeout_and_malformed_responses(): fake = FakeQdrantHttp() store = _store(fake)