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-sqliterecovers palaces stuck onapply_logs. When ChromaDB's HNSW segment writer wedges (the corruption class reported upstream and in #1308),--mode legacyand the inline repair both callCollection.count()as their first read — which is exactly the call that raises. The old failure mode was a repair printingCannot recover — palace may need to be re-mined from source fileseven though the SQLite tables were fully intact. The new mode reads(id, document, metadata)directly fromchroma.sqlite3via 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_fromused to be durably stored but unreachable (the temporal filter matched noas_of);add_triple()now rejects inverted intervals at write time. And date strings forwarded to SQLite are validated up front viasanitize_iso_temporal()(aliassanitize_iso_date()), which acceptsYYYY-MM-DDand 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-atomicPath.write_text()— a power loss, OOM, orkill -9between truncate and full-flush would silently wipe it (the registry'sload()swallowsJSONDecodeError). Save now writes to a sibling temp file,fsyncs, andos.replace()s into place. The previous registry stays intact on any crash before the rename returns. (#1215) -
MCP
tool_searchrides 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 hittingInternal error: Error finding idfor that entire window.tool_searchnow 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 withindex_recovered: trueso callers can observe when it fired. Partial fix for the broader #1315 cluster —tool_check_duplicateand 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 aftershutil.rmtreeno longer fails withSQLITE_READONLY_DBMOVED. And the MCP server's_kgis no longer a module-level singleton: multi-tenant hosts rotatingMEMPALACE_PALACE_PATHbetween 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.txtand piped fact_checker invocations no longer mojibake Cyrillic / CJK drawer text at the process boundary.mcp_server,hooks_cli, andcli/fact_checkerall reconfigure identically. (#1282) -
miner.detect_roomrouting 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.tsxrouted to aninterviewsroom because"views" in "interviews"matched first. Now uses separator-bounded token matching (-,_,.,/). (#1004, closes #1002) -
mempalace compressno longer crashes on palaces above ~32k drawers. Drawer fetch is now paginated, mirroring the #851 fix inminer.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
- @imtylervo made their first contribution in #1162
- @lcatlett made their first contribution in #1305
- @Sathvik-1007 made their first contribution in #1114
- @alonehobo made their first contribution in #1060
- @eldar702 made their first contribution in #1030
- @alpiua made their first contribution in #987
- @hzx945627450-eng made their first contribution in #1293
- @bobo-xxx made their first contribution in #988
- @cantenesse made their first contribution in #1019
- @anthonyonazure made their first contribution in #1138
- @potterdigital made their first contribution in #1310
- @mjc made their first contribution in #1285
- @coogie made their first contribution in #1004
- @JPdeB61 made their first contribution in #1396
Full Changelog: v3.3.4...v3.3.5