github MemPalace/mempalace v3.3.5
v3.3.5 — integrity, recovery, and cross-process correctness

3 hours ago

3.3.5 is a fixes release. No new user-facing features — the cycle's energy went into data integrity, recovery, and cross-process correctness. If your palace ever hit Internal error: Error finding id after a big mine, or you had a knowledge-graph triple silently disappear into the void, or you're running on Windows / a multi-tenant host — this release is for you.

The headline fixes:

  • mempalace repair --mode from-sqlite recovers palaces stuck on apply_logs. When ChromaDB's HNSW segment writer wedges (the corruption class reported upstream and in #1308), --mode legacy and the inline repair both call Collection.count() as their first read — which is exactly the call that raises. The old failure mode was a repair printing Cannot recover — palace may need to be re-mined from source files even though the SQLite tables were fully intact. The new mode reads (id, document, metadata) directly from chroma.sqlite3 via the metadata segment join, never opens a chromadb client against the corrupt palace, and re-upserts everything into a fresh palace under your configured embedding function. Verified end-to-end on a 52,300-row real-world corrupt palace. (#1308)

  • Two silent-loss classes in the knowledge graph are now write-time errors. Triples with valid_to < valid_from used to be durably stored but unreachable (the temporal filter matched no as_of); add_triple() now rejects inverted intervals at write time. And date strings forwarded to SQLite are validated up front via sanitize_iso_temporal() (alias sanitize_iso_date()), which accepts YYYY-MM-DD and canonical UTC datetimes (YYYY-MM-DDTHH:MM:SSZ / +00:00) and rejects everything else — natural-language inputs like "yesterday" no longer silently produce empty result sets. Behavior change: partial-date inputs that worked in 3.3.4 ("2026", "2026-05") now error; pass a full date or canonical UTC datetime instead. (#1214, #1164, #1167, #1374, #1417)

  • EntityRegistry.save() is now crash-safe. The people/projects map is months of mining data, and the old code did a non-atomic Path.write_text() — a power loss, OOM, or kill -9 between truncate and full-flush would silently wipe it (the registry's load() swallows JSONDecodeError). Save now writes to a sibling temp file, fsyncs, and os.replace()s into place. The previous registry stays intact on any crash before the rename returns. (#1215)

  • MCP tool_search rides through the post-bulk-mine HNSW flush window. After a CLI mine, ChromaDB's HNSW segment metadata can be unflushed for ~30-60s; wing-scoped MCP search was hitting Internal error: Error finding id for that entire window. tool_search now detects this specific transient, drops both the MCP-local and shared-backend caches for the palace, sleeps 2s, and retries once. Successful retries are tagged with index_recovered: true so callers can observe when it fired. Partial fix for the broader #1315 cluster — tool_check_duplicate and other index-touching tools still need the same wrapper. (#1396)

  • Cross-process correctness on Windows and multi-tenant hosts. ChromaBackend.close_palace() now properly releases ChromaDB's rust-side SQLite file lock — reopening the same palace path after shutil.rmtree no longer fails with SQLITE_READONLY_DBMOVED. And the MCP server's _kg is no longer a module-level singleton: multi-tenant hosts rotating MEMPALACE_PALACE_PATH between tool calls now hit the right SQLite file via a lazy per-path cache. (#1067, #1105, #1136, #1160)

  • Windows stdio is UTF-8 across the board. mempalace search > out.txt and piped fact_checker invocations no longer mojibake Cyrillic / CJK drawer text at the process boundary. mcp_server, hooks_cli, and cli/fact_checker all reconfigure identically. (#1282)

  • miner.detect_room routing fix for monorepos. The substring check was bidirectional, so any token that was a substring of a room name or vice versa matched — views/billing-page/src/Foo.test.tsx routed to an interviews room because "views" in "interviews" matched first. Now uses separator-bounded token matching (-, _, ., /). (#1004, closes #1002)

  • mempalace compress no longer crashes on palaces above ~32k drawers. Drawer fetch is now paginated, mirroring the #851 fix in miner.py. (#1073, #1107)

Internal: test reliability got a pass too — tests/test_palace_locks.py and tests/test_chroma_collection_lock.py switched from fork to spawn for subprocess spawning (Python 3.13 + multi-threaded parent = deadlock under fork, and macOS forbids fork-without-exec via CoreFoundation), and several test files now wrap sqlite3.connect(...) in contextlib.closing() so connections release on the failure path too. (#1430, #1431)

The promises haven't moved. Every word stored exactly. Everything stays on your machine. No telemetry. No fallbacks to cloud APIs we didn't tell you about. Each release sharpens what's already there.

What's Changed

  • fix(mcp_server): pass embedding_function= on collection reopen (#1299) by @igorls in #1303
  • fix(hooks): quote plugin-root paths with spaces (#1076) by @mvalentsev in #1077
  • fix(cli): cmd_compress writes to mempalace_closets (#1244) by @igorls in #1319
  • fix(mcp): case-insensitive agent name in diary read/write (#1243) by @igorls in #1323
  • fix(backends/chroma): wire quarantine_stale_hnsw into _client() (#1121 #1132 #1263) by @igorls in #1322
  • fix(mcp): omit absolute filesystem paths from MCP tool responses by @igorls in #1325
  • feat(searcher): candidate_strategy="union" — BM25 candidates joined with vector pool before hybrid rerank by @igorls in #1306
  • fix(mcp): forward valid_to and source params in kg_add/kg_invalidate (#1314) by @igorls in #1320
  • fix(cli): honor --palace flag in cmd_init (#1313) by @igorls in #1321
  • fix(kg): reject inverted intervals in add_triple (valid_to < valid_from) by @arnoldwender in #1214
  • fix(backends/chroma): release SQLite file lock on close_palace/close (#1067) by @mvalentsev in #1105
  • fix(entity_registry): atomic write to prevent partial corruption on crash by @arnoldwender in #1215
  • fix: paginate closet_llm col.get (#1073) by @sha2fiddy in #1107
  • fix(cli, fact-checker): reconfigure stdio to UTF-8 on Windows by @mvalentsev in #1282
  • fix(kg): validate ISO-8601 date formats at MCP boundary by @arnoldwender in #1167
  • fix(mcp): lazy per-path KnowledgeGraph cache (#1136) by @mvalentsev in #1160
  • docs(changelog): batch entries for 7 v3.3.5 fixes by @igorls in #1370
  • fix: serialize ChromaCollection writes through palace lock by @imtylervo in #1162
  • fix(hooks): treat absent ~/.mempalace as auto-save off by @lcatlett in #1305
  • fix: add total count to tool_list_drawers pagination response by @Sathvik-1007 in #1114
  • fix(mcp): force UTF-8 on stdio to fix -32000 on non-ASCII payloads (Windows) by @alonehobo in #1060
  • fix: guard None metadata/doc in tool_check_duplicate and Layer1/Layer2 by @eldar702 in #1030
  • fix(lint): hoist hooks_cli_mod import to top of test_hooks_cli (E402) by @igorls in #1375
  • fix(searcher): clamp effective_distance to valid cosine range [0, 2] by @eldar702 in #1029
  • fix(mcp): handle null JSON-RPC request payloads safely by @alpiua in #987
  • fix: MCP server JSON output ensure_ascii=False for non-ASCII support by @hzx945627450-eng in #1293
  • fix: clamp similarity scores to [0,1] to prevent negative values by @bobo-xxx in #988
  • fix(searcher): guard against None metadata/doc in search result loops by @cantenesse in #1019
  • fix: reject non-http(s) LLM endpoints + clear ruff bugbear/silent-except findings by @anthonyonazure in #1138
  • fix(mcp): retry _get_collection once on transient failure (#1286) by @igorls in #1377
  • docs: add 30-day expiry callout + ship 4 auto-save tools by @milla-jovovich in #1391
  • fix(repair): add --mode from-sqlite to recover palaces with corrupt HNSW (#1308) by @potterdigital in #1310
  • fix: harden Chroma repair preflight and rollback recovery by @mjc in #1285
  • fix(miner): harden Windows mine against ONNX bad_alloc + silent partial exits (#1296) by @igorls in #1402
  • fix: use configured collection in repair recovery paths by @mjc in #1312
  • fix(migrate): verify write roundtrip before bailout by @fatkobra in #1359
  • fix(storage): quarantine bloated HNSW link payloads by @fatkobra in #1339
  • fix(repair): preflight poisoned max_seq_id before rebuild by @fatkobra in #1357
  • fix(storage): quarantine partial HNSW flush without metadata by @fatkobra in #1342
  • fix(repair): preflight SQLite integrity before rebuild by @fatkobra in #1364
  • fix(repair): run SQLite integrity preflight before chromadb open (follow-up to #1364) by @igorls in #1403
  • fix(closet_llm): retry _call_llm on JSONDecodeError (#1155) by @igorls in #1404
  • fix(exporter): refuse symlinks at export targets (#1156) by @igorls in #1405
  • fix(diary): detect same-size edits via content hash (#925) by @igorls in #1406
  • fix(hooks): detach Popen children so hook exits cleanly on Windows (#1268) by @igorls in #1412
  • fix(mine): identify lock holder + exit non-zero on contention (#1264) by @igorls in #1413
  • fix(hooks): per-target PID guard with atomic claim (#1212, #1206) by @igorls in #1415
  • chore: housekeeping — sync 3.3.4 version + recommend uv for installs by @igorls in #1414
  • chore(deps): bump actions/configure-pages from 5 to 6 by @dependabot[bot] in #1292
  • feat(sync): add gitignore-aware drawer prune (#1252) by @mvalentsev in #1421
  • docs: clarify contributor git identity setup by @sjhddh in #1385
  • fix(miner): use token-boundary matching in detect_room by @coogie in #1004
  • fix(mcp): retry tool_search once on Chroma "Error finding id" transient (#1315) by @JPdeB61 in #1396
  • fix(tests): use spawn instead of fork for lock-test subprocesses by @igorls in #1431
  • chore(tests): wrap sqlite3 connections in contextlib.closing by @igorls in #1430
  • fix(kg): accept ISO datetimes for temporal inputs by @fatkobra in #1417
  • chore(release): 3.3.5 by @igorls in #1432
  • chore(release): 3.3.5 by @igorls in #1434

New Contributors

Full Changelog: v3.3.4...v3.3.5

Don't miss a new mempalace release

NewReleases is sending notifications on new releases.