`test_fresh_palace_via_full_stack_gets_cosine` used `tempfile.Temporary-
Directory()` as a context manager, which tries to delete the temp path
on exit. On Windows, ChromaDB still holds SQLite file handles to
`chroma.sqlite3` when the context closes, producing:
PermissionError: [WinError 32] The process cannot access the file
because it is being used by another process: '...\\chroma.sqlite3'
NotADirectoryError: [WinError 267] The directory name is invalid
Other tests in the same file use pytest's `tmp_path` fixture, which
defers cleanup to session end (when the process is exiting and the
file-lock contention is moot). Align this one with the rest of the
file.
CLAUDE.md already documents the 80% Windows coverage allowance due to
"ChromaDB file lock cleanup" — the fix is to stop fighting the lock.
Three tightly-coupled search-quality fixes for v3.3.3:
1. CLI `mempalace search` now routes through the same `_hybrid_rank`
the MCP path already used. Drawers whose text contains every query
term but embed as file-tree noise (directory listings, diffs, log
fragments) were scoring cosine distance >= 1.0 — the display formula
`max(0, 1 - dist)` then floored every result to `Match: 0.0`, with
no way for the user to tell a lexical match from a total miss. BM25
catches these cleanly; the display surfaces both `cosine=` and
`bm25=` so users see which component is firing.
2. Legacy-palace distance-metric warning. Palaces created before
`hnsw:space=cosine` was consistently set silently use ChromaDB's
default L2 metric, which breaks the cosine-similarity formula (L2
distances routinely exceed 1.0 on normalized 384-dim vectors). The
search path now detects this at query time and prints a one-line
notice pointing at `mempalace repair`. Only fires for legacy
palaces; new palaces already set cosine correctly.
3. Invariant tests pinning `hnsw:space=cosine` on every collection-
creation path — legacy `get_or_create_collection`, legacy
`create_collection`, RFC 001 `get_collection(create=True)`, the
public `palace.get_collection`, and a round-trip through reopen.
Locks down the correctness that new-user palaces already have so a
future refactor can't silently regress it.
Also adds a `metadata` property to `ChromaCollection` so callers can
read the underlying hnsw:space without reaching into `_collection`.
Tests:
- New regression: simulate three candidates at distance 1.5 (cosine=0),
one containing query terms — must rank first with non-zero bm25.
- New: legacy metric (empty or non-cosine) produces stderr warning.
- New: correctly-configured palace produces no warning.
- New: all five creation paths pin cosine metadata.
All existing tests still pass.