Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
4949aab
fix: guard None metadata/doc in tool_check_duplicate and Layer1/Layer2
eldar702 Apr 19, 2026
35b033d
fix(mcp): force UTF-8 on stdio to fix -32000 on non-ASCII payloads
alonehobo Apr 21, 2026
c2e0531
fix: add total count to tool_list_drawers pagination response
Sathvik-1007 Apr 22, 2026
0b8c2c1
fix(kg): reject inverted intervals in add_triple (valid_to < valid_from)
arnoldwender Apr 26, 2026
f30fdf2
fix: serialize ChromaCollection writes through palace lock
imtylervo Apr 27, 2026
db28bf1
fix: paginate closet_llm col.get (#1073)
sha2fiddy Apr 29, 2026
4d98b05
fix(kg): validate ISO-8601 date formats at MCP boundary
arnoldwender Apr 24, 2026
abe8576
fix(kg): reject partial ISO dates to avoid silent empty result sets
arnoldwender Apr 26, 2026
c3e1104
fix(chroma): harden HNSW startup preflight
mjc Apr 30, 2026
7fa27bd
fix(repair): rebuild collections through temp staging
mjc Apr 30, 2026
2f509b4
fix(cli): restore backup on repair failure
mjc Apr 30, 2026
f57f300
fix(repair): close active backend before rollback restore
mjc Apr 30, 2026
0e32b96
fix: avoid false hnsw divergence fallback
mjc May 1, 2026
8472d55
fix(hooks): treat absent ~/.mempalace as auto-save off
lcatlett May 1, 2026
2d50b21
fix(hooks): use is_dir() for palace root check (review feedback)
lcatlett May 3, 2026
beac5d9
refactor(mcp): replace eager _kg with lazy per-path cache (#1136)
mvalentsev Apr 24, 2026
9e73009
test(mcp): migrate _kg monkeypatches to _get_kg (#1136)
mvalentsev Apr 24, 2026
c69a622
test(mcp): add multi-tenant and lazy-init tests for KG (#1136)
mvalentsev Apr 24, 2026
84f9726
test(mcp): fix Windows subprocess env in KG lazy-init test
mvalentsev Apr 24, 2026
19f8a4f
style(mcp): drop issue-tracker comments from KG cache block
mvalentsev Apr 24, 2026
0a62658
fix(mcp): drain KG cache on tool_reconnect
mvalentsev May 2, 2026
45df1a2
fix(backends/chroma): release SQLite file lock on close_palace/close …
mvalentsev Apr 22, 2026
7cee74c
fix(fact-checker): reconfigure stdio to UTF-8 on Windows
mvalentsev Apr 30, 2026
32f4dfa
fix(cli): reconfigure stdio to UTF-8 on Windows
mvalentsev Apr 30, 2026
03643eb
fix(cli, fact-checker): per-stream stdio errors policy on Windows
mvalentsev May 3, 2026
b8816e0
fix(mcp): retry KG handlers once on concurrent close race
mvalentsev May 3, 2026
75ad8ae
ci: retrigger linux 3.13 (transient onnx download flake)
mvalentsev May 3, 2026
285b3b4
refactor(stdio): extract Windows UTF-8 reconfigure into shared helper
mvalentsev May 3, 2026
f9d939a
fix(storage): quarantine bloated HNSW link payloads
fatkobra May 4, 2026
4f36145
fix(entity_registry): atomic write to prevent partial corruption on c…
arnoldwender Apr 26, 2026
2e441d1
fix(entity_registry): fsync parent dir after rename for ext4 durability
arnoldwender Apr 28, 2026
eff844b
fix(storage): quarantine partial HNSW flush without metadata
fatkobra May 4, 2026
37e7d39
fix(repair): preflight poisoned max_seq_id
fatkobra May 5, 2026
bb40a52
fix(migrate): verify write roundtrip before bailout
fatkobra May 5, 2026
6b04298
fix(repair): preflight SQLite integrity before rebuild
fatkobra May 5, 2026
0cfb4b3
Merge pull request #1214 from arnoldwender/fix/kg-temporal-inversion-…
igorls May 6, 2026
ef0e45a
Merge pull request #1105 from mvalentsev/fix/chroma-backend-close-rel…
igorls May 6, 2026
e18981a
Merge pull request #1215 from arnoldwender/fix/entity-registry-atomic…
igorls May 6, 2026
778f830
Merge pull request #1107 from sha2fiddy/fix/1073-closet-llm-paginate
igorls May 6, 2026
3824ea6
Merge pull request #1282 from mvalentsev/fix/fact-checker-stdio-utf8
igorls May 6, 2026
7ede231
Merge pull request #1167 from arnoldwender/fix/kg-date-validation
igorls May 6, 2026
53675dd
Merge pull request #1160 from mvalentsev/fix/mcp-kg-lazy-per-path-cache
igorls May 6, 2026
2c0ef2c
docs(changelog): document v3.3.5 fixes from #1214 #1105 #1215 #1107 #…
igorls May 6, 2026
5ae83d8
Merge pull request #1370 from MemPalace/docs/changelog-v3.3.5-batch1
igorls May 6, 2026
d1e27b8
style: ruff format new test files (CI lint)
igorls May 6, 2026
ea6f2c0
Merge pull request #1162 from imtylervo/fix/palace-write-lock-queue-p…
igorls May 6, 2026
d9ab5b7
Merge pull request #1305 from lcatlett/upstream/respect-absent-palace…
igorls May 6, 2026
642a073
Merge pull request #1114 from Sathvik-1007/fix/list-drawers-paginatio…
igorls May 6, 2026
0c8314f
Merge pull request #1060 from alonehobo/fix/stdio-utf8
igorls May 6, 2026
67cda9d
Merge pull request #1030 from eldar702/fix/none-metadata-residual-guards
igorls May 6, 2026
f854da7
fix(lint): hoist hooks_cli_mod import to top of test_hooks_cli (E402)
igorls May 6, 2026
46d9eb5
Merge pull request #1375 from MemPalace/fix/lint-e402-test-hooks-cli
igorls May 6, 2026
733e435
fix(searcher): guard against None metadata/doc in search result loops
Apr 18, 2026
5347c2c
fix(searcher): clamp effective_distance to valid cosine range [0, 2]
eldar702 Apr 19, 2026
aac8437
style: ruff format tests/test_searcher.py (CI lint)
igorls May 6, 2026
0fdb480
fix(mcp): handle null JSON-RPC request payloads safely
alpiua Apr 17, 2026
55d79dc
fix: include null id in JSON-RPC invalid request error responses and …
alpiua Apr 18, 2026
a85d432
feat: add validation for missing name parameter in tools/call requests
alpiua Apr 18, 2026
869ab38
style: ruff format mcp_server.py + test_mcp_server.py (CI lint)
igorls May 6, 2026
7b49478
fix: MCP server JSON output ensure_ascii=False for non-ASCII support
May 1, 2026
74288f1
style: ruff format mcp_server.py (CI lint)
igorls May 6, 2026
eef053d
fix(mcp_server): clamp similarity to [0,1] to avoid negative values
bobo-xxx Apr 18, 2026
f2bed92
fix(layers): clamp similarity to [0,1] to avoid negative values
bobo-xxx Apr 18, 2026
f4617b3
Merge pull request #1029 from eldar702/fix/searcher-effective-distanc…
igorls May 6, 2026
9b24cfc
Merge pull request #987 from alpiua/fix-mcp-null-payload
igorls May 6, 2026
f3d9801
Merge pull request #1293 from hzx945627450-eng/fix/mcp-ensure-ascii
igorls May 6, 2026
8a9b2be
Merge pull request #988 from bobo-xxx/clawoss/fix/978-negative-simila…
igorls May 6, 2026
01880f6
Merge pull request #1019 from cantenesse/swe/session-1-bug-mempalace-…
igorls May 6, 2026
b68485d
fix(closet_llm): reject non-http(s) endpoints
anthonyonazure Apr 23, 2026
ca5899e
refactor: fix ruff bugbear and silent-except findings
anthonyonazure Apr 23, 2026
6741b69
Merge pull request #1138 from anthonyonazure/fix/bugbear-cleanup-and-…
igorls May 6, 2026
a7c4ed2
fix(repair): add --mode from-sqlite to recover palaces with corrupt H…
potterdigital May 2, 2026
cb6bfd5
chore: gitignore .envrc for direnv users
potterdigital May 2, 2026
d92c741
fix(repair): address PR #1310 review feedback
potterdigital May 2, 2026
e334e25
fix(mcp): retry _get_collection once on transient failure (#1286)
igorls May 6, 2026
f0d2360
Merge pull request #1377 from MemPalace/fix/get-collection-retry-on-e…
igorls May 6, 2026
bddba59
docs: add 30-day expiry callout + ship 4 auto-save tools
milla-jovovich May 6, 2026
921ff5a
fix(tools/render_jsonl): split chained statements per ruff 0.4.x
milla-jovovich May 6, 2026
7c679ba
fix(tools/render_jsonl): apply ruff format
milla-jovovich May 6, 2026
435f0ad
Merge pull request #1391 from MemPalace/docs/auto-save-tools-on-develop
igorls May 6, 2026
8d8f54a
Merge remote-tracking branch 'origin/develop' into fix/1308-rebuild-f…
igorls May 7, 2026
670aba9
test(repair): close ChromaBackend in _seed_palace to release Windows …
igorls May 7, 2026
be6dc03
merge: develop into hnsw-repair (resolve chroma.py + test_backends.py…
igorls May 7, 2026
be05a2e
Merge pull request #1310 from potterdigital/fix/1308-rebuild-from-sqlite
igorls May 7, 2026
7cf9b17
fix(repair): quote ChromaBackend annotation for Python 3.9 compatibility
igorls May 7, 2026
88493ac
Merge pull request #1285 from mjc/hnsw-repair
igorls May 7, 2026
5488e7b
fix(miner): harden Windows mine against ONNX bad_alloc + silent parti…
igorls May 7, 2026
ec6d2dd
fix: use configured collection in recovery paths
mjc May 2, 2026
e9aee19
fix(tests): apply ruff format after rebase resolution
igorls May 7, 2026
52c70c9
Merge pull request #1402 from MemPalace/fix/1296-windows-mine-resilience
igorls May 7, 2026
72685f3
Merge pull request #1312 from mjc/stale-chroma-reconnect
igorls May 7, 2026
e272ed3
Merge pull request #1359 from fatkobra/fix/1099-migrate-write-roundtrip
igorls May 7, 2026
88a2ebb
Merge pull request #1339 from fatkobra/fix/1218-hnsw-link-payload-health
igorls May 7, 2026
bdaac9d
merge: develop into fix/1295-repair-max-seq-id-preflight
igorls May 7, 2026
a1e1908
merge: develop into fix/1274-missing-hnsw-metadata-gate
igorls May 7, 2026
f2291b0
merge: develop into fix/1362-repair-sqlite-integrity-preflight
igorls May 7, 2026
557b9b1
Merge pull request #1357 from fatkobra/fix/1295-repair-max-seq-id-pre…
igorls May 7, 2026
3893228
Merge pull request #1342 from fatkobra/fix/1274-missing-hnsw-metadata…
igorls May 7, 2026
aecd543
merge: develop into fix/1362-repair-sqlite-integrity-preflight (round 2)
igorls May 7, 2026
f38d9eb
Merge pull request #1364 from fatkobra/fix/1362-repair-sqlite-integri…
igorls May 7, 2026
5134a63
fix(repair): run SQLite integrity preflight before chromadb open
igorls May 7, 2026
7b15103
test(repair): page-align corruption offset in preflight regression test
igorls May 7, 2026
03ed4c4
Merge pull request #1403 from MemPalace/fix/sqlite-preflight-order
igorls May 7, 2026
2a0ed0c
fix(closet_llm): retry _call_llm on JSONDecodeError instead of bailing
igorls May 7, 2026
40e2c8b
fix(exporter): refuse symlinks at export targets
igorls May 7, 2026
0d1c1fb
fix(diary): detect same-size edits via content hash
igorls May 7, 2026
8e21b5a
test(closet_llm): use _ for unused return values per copilot review
igorls May 7, 2026
7545238
fix(exporter): refuse symlinks at file targets and skip tests on Windows
igorls May 7, 2026
2ff6283
fix(diary): rebuild closets on hash change + backfill legacy state
igorls May 7, 2026
83d5364
Merge pull request #1404 from MemPalace/fix/1155-call-llm-retry-on-js…
igorls May 7, 2026
ba30ab6
Merge pull request #1405 from MemPalace/fix/1156-exporter-reject-syml…
igorls May 7, 2026
26bc3d4
test(diary): write fixture with explicit utf-8 to fix Windows hash as…
igorls May 7, 2026
ea36a00
Merge pull request #1406 from MemPalace/fix/925-diary-content-hash
igorls May 7, 2026
1dbffe0
merge: upstream/develop into main (sync 2026-05-07)
jphein May 8, 2026
dfb85cc
docs(claude): update last-sync reference to 2026-05-07
jphein May 8, 2026
1975207
docs(claude): backfill missing commit refs + retire moot row 15 hedge
jphein May 8, 2026
0639b77
fix(lint): remove unused pytest import in test_mcp_server_daemon
jphein May 8, 2026
fe2992b
style: ruff format scripts/chunk_strategy_ablation.py + tests/test_cl…
jphein May 8, 2026
f40224b
fix(tests): make 2 fork-side tests cross-platform on Windows
jphein May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ venv/

# Private / shared artifacts that should never land in the public fork
dashboards-bensig-*/

.envrc
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### Bug Fixes

- **`mempalace_diary_read` silently dropped entries on agent-name case mismatch.** `tool_diary_write` stored the `agent` metadata verbatim after `sanitize_name`, which preserves case, while `tool_diary_read` filtered by exact match. Writing as `"Claude"` and reading as `"claude"` (or vice-versa) returned zero rows. Both endpoints now lowercase `agent_name` immediately after sanitization, so reads are case-insensitive and the default per-agent wing slug is stable across casings. **Behavior change:** entries written prior to this fix under mixed-case agent names will not match the new lowercase filter; run `mempalace repair` if you need to migrate legacy diary metadata. (#1243)
- **Knowledge-graph triples with `valid_to < valid_from` were silently invisible.** `KnowledgeGraph.query_entity()` filters with `valid_from <= as_of AND valid_to >= as_of`, so an inverted interval matches no `as_of` and the row is durably stored but unreachable — a P0 data-integrity foot-gun any caller that mixes up the two date params can hit. `add_triple()` now rejects inverted intervals at write time with a clear `ValueError` naming both bounds. Open intervals (one bound only) and point-in-time facts (`valid_from == valid_to`) remain accepted unchanged. (#1214)
- **`ChromaBackend.close_palace()` / `close()` did not release the SQLite file lock.** Evicted clients sat in `_clients` without `close()`, and chromadb 1.5.x retains the rust-side SQLite lock until GC. Reopening the same palace path after `shutil.rmtree` + recreate within one process failed with `SQLITE_READONLY_DBMOVED` (code 1032). New `_close_client()` helper now calls `PersistentClient.close()` (with a try/except fallback for older chromadb) on `close_palace()`, on whole-backend `close()`, and on the `_client()` invalidation path that detects a missing `chroma.sqlite3`. The mtime/inode auto-invalidation branch is intentionally left alone — callers there may still hold a live `ChromaCollection`. (#1067, #1105)
- **`EntityRegistry.save()` could leave a corrupt or empty `entity_registry.json` on crash.** `Path.write_text()` is not atomic — kernel sees `open('w')` (truncate), `write`, `close`, and any failure between truncate and full-flush (power loss, OOM, FS-full, kill -9) wipes the months-of-mining people/projects map silently (the registry's `load()` swallows `JSONDecodeError`). Save now writes to a sibling `.tmp` in the same directory, `fsync`s, `chmod 0o600`s, then `os.replace()`s into place — atomic on POSIX and Windows. The previous registry stays intact on any crash before the rename returns. (#1215)
- **`mempalace compress` crashed on large palaces.** `regenerate_closets` fetched all closet_llm drawers in a single `col.get()`, which trips `SQLITE_MAX_VARIABLE_NUMBER` on palaces above ~32k drawers. Mirrors the #851 fix in `miner.py`: drawer fetch is now paginated at `batch_size=5000`. Per-source aggregation works across batches, so the LLM regeneration call still groups chunks correctly. (#1073, #1107)
- **CLI and `fact_checker --stdin` mojibaked non-ASCII content on Windows.** Python defaults `sys.stdin`/`stdout`/`stderr` to the system ANSI codepage (cp1252/cp1251/cp950), so `mempalace search > out.txt` and piped fact_checker invocations corrupted Cyrillic / CJK drawer text at the process boundary. New `mempalace/_stdio.py` helper reconfigures all three streams to UTF-8 on `sys.platform == "win32"`, with per-stream `errors` policy: `surrogateescape` on stdin (preserves bad bytes from redirected files for the consumer's parser), `replace` on stdout/stderr (substitutes U+FFFD instead of `UnicodeEncodeError`-ing mid-print). With this, all three user-facing console_scripts (`mcp_server`, `hooks_cli`, `cli`/`fact_checker`) now reconfigure identically on Windows. (#1282)
- **MCP knowledge-graph tools forwarded malformed date strings to SQLite.** `tool_kg_query` (`as_of`), `tool_kg_add` (`valid_from`), and `tool_kg_invalidate` (`ended`) accepted any string and produced empty result sets on natural-language inputs like `"March 2026"` or `"yesterday"` — callers (especially LLM agents) could not distinguish "no fact at this time" from "your date format was unrecognized." New `sanitize_iso_date()` validator in `config.py` accepts `YYYY`, `YYYY-MM`, `YYYY-MM-DD` (and passes through `None`/`""`); all three tools call it before values reach the storage layer. **Behavior change:** previously-silent date typos now raise a clear `ValueError` naming the offending field; full ISO-8601 with time (`YYYY-MM-DDTHH:MM:SS`, timezone offsets) is not yet accepted — file an issue if you have a use case. (#1164, #1167)
- **MCP server's `_kg` was a module-level singleton.** Multi-tenant hosts that rotate `MEMPALACE_PALACE_PATH` between tool calls hit the wrong sqlite file, because the KG was constructed once at import time while the ChromaDB side was already per-call via `_get_client()`. The KG is now resolved per-call through a lazy per-path cache (`_kg_by_path` keyed by `os.path.abspath`, with a double-checked-locking init under `_kg_cache_lock`). `tool_reconnect` drains and `close()`s cached KGs alongside the existing chroma reconnect. A `_call_kg` retry guard catches `sqlite3.ProgrammingError` once after a reconnect race. (#1136, #1160)
- **`mempalace repair` can now recover palaces whose HNSW segment writer is stuck on `apply_logs`.** Both the existing `--mode legacy` rebuild and the inline `cli.cmd_repair` path call `Collection.count()` as their first read — exactly the call that raises `chromadb.errors.InternalError: Failed to apply logs to the hnsw segment writer` on the corruption class introduced upstream and reported in #1308. Repair would print `Cannot recover — palace may need to be re-mined from source files` even though the underlying SQLite tables were fully intact (the corruption lives in the on-disk index files, not the data layer). New `--mode from-sqlite` reads `(id, document, metadata)` rows directly from `chroma.sqlite3` via a `segments` → `embeddings` → `embedding_metadata` join, never opens a chromadb client against the corrupt palace, and re-upserts everything into a fresh palace at `--palace`. `--source PATH` extracts from a corrupt palace already moved aside; `--archive-existing` handles the in-place case by renaming the existing palace to `<palace>.pre-rebuild-<timestamp>` before reading from it. Documents are re-embedded under the user's configured embedding function (the original HNSW vectors live in the corrupt `data_level0.bin` and cannot be recovered, but the embedding model is deterministic so search results remain semantically equivalent). Verified end-to-end on a 52,300-row real-world corrupt palace. (#1308)

---

Expand All @@ -28,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### Bug Fixes

- **MCP server `tool_diary_write` SIGSEGV when default EF provider differs.** `mcp_server._get_collection` bypassed `ChromaBackend.get_collection` and called `client.get_collection` / `client.create_collection` without `embedding_function=`. ChromaDB 1.x persists the EF *identity* (its `name()`) with the collection but not the EF *instance/configuration*, so the MCP server's reopen silently bound chromadb's built-in `DefaultEmbeddingFunction` — its `name()` matches `mempalace.embedding`'s spoofed `"default"` so the identity check passes, but its provider list is chromadb's default rather than the user's resolved device. The miner / Stop hook ingest path routes through the backend helper and binds the configured EF instead. On bleeding-edge interpreters (python 3.14 + chromadb 1.5.x on Apple Silicon) the default provider selection could SIGSEGV the host process on first `col.add()`, killing the MCP stdio server and leaving every subsequent tool call returning `Connection closed` until Claude Code was relaunched. `_get_collection` now reuses `ChromaBackend._resolve_embedding_function()` on the reopen branches that actually open a collection (warm-cache reads stay zero-cost), matching the miner/backend path. (#1299, follow-up to #1262 / #1289)
- **Hooks no longer recreate `~/.mempalace/` after the user removes it.** When `~/.mempalace/` is deleted (a strong "do not auto-capture" signal), the next `Stop`, `PreCompact`, or `SessionStart` hook would silently rebuild the dir hierarchy and ingest existing transcripts: `_log()` called `STATE_DIR.mkdir(parents=True, exist_ok=True)` unconditionally, so the very act of writing `[HH:MM] SESSION START …` recreated `~/.mempalace/hook_state/`; subsequent calls in the save path then materialized `palace/`, `wal/`, `knowledge_graph.sqlite3`, and N drawers from `~/.claude/projects/*.jsonl`. All four entry points (`hook_stop`, `hook_precompact`, `hook_session_start`, and `_log` itself) now check a new module-level `PALACE_ROOT = Path.home() / ".mempalace"` constant first and short-circuit (returning `{}` on stdout, never logging) when the directory is absent. The user-removable directory becomes a kill-switch — `rm -rf ~/.mempalace` is now a stable state. Net: 23 lines added in `mempalace/hooks_cli.py`, 5 unit tests in `tests/test_hooks_cli.py`. (#1305)
- **Cross-wing topic tunnels for hyphenated dir names.** `mempalace init` recorded the `topics_by_wing` registry key under the raw directory name (e.g. `mempalace-public`), while `mempalace.yaml`'s `wing` field used the lower-cased + separator-collapsed slug (`mempalace_public`). At mine time the miner read the slug from the yaml and missed the registry, so `_compute_topic_tunnels_for_wing` returned `0` silently. Real-world: any project whose folder contained a hyphen or space lost every topic tunnel. Now both call sites route through a shared `normalize_wing_name()` in `config.py`. (#1194, follow-up to #1180)
- **CLI `mempalace search` retrieval quality.** The CLI was using pure ChromaDB cosine distance with no BM25 rerank, so drawers containing every query term but embedding as noise (directory listings, diff output, shell logs) scored `Match: 0.0` alongside genuinely irrelevant results with no way to tell them apart. Wired the CLI through the same `_hybrid_rank` the `mempalace_search` MCP tool already used, and surfaced both `cosine=` and `bm25=` scores in the output so users see which component of the match is firing. MCP search was unaffected; this fixes the human-facing CLI parity gap.
- **Legacy-palace distance-metric warning.** CLI search now detects palaces created before `hnsw:space=cosine` was consistently set and prints a one-line notice pointing at `mempalace repair`. Without the warning such palaces silently used L2 distance, under which the similarity display floored every result to `Match: 0.0`. New palaces mined today already set cosine correctly and now have invariant tests pinning that behavior so future refactors can't silently regress it. (#1179)
Expand Down
Loading
Loading