feat: progressive disclosure + <private> tag filter#1033
Open
zackchiutw wants to merge 2 commits intoMemPalace:developfrom
Open
feat: progressive disclosure + <private> tag filter#1033zackchiutw wants to merge 2 commits intoMemPalace:developfrom
zackchiutw wants to merge 2 commits intoMemPalace:developfrom
Conversation
Two privacy-minded additions to the search and write paths: 1. ``<private>...</private>`` tag handling on write. ``tool_add_drawer`` and ``tool_diary_write`` strip the marked blocks before sanitize + embed. Entries whose entire content is inside a private tag are refused outright so nothing leaks through metadata, drawer IDs, or the embedding itself. 2. Progressive disclosure on read. ``tool_search`` returns a ~30-char summary + ``drawer_id`` per hit by default; callers opt in to the full verbatim drawer text via ``full=true`` or fetch on demand via ``tool_get_drawer`` (now accepts a single ID or an array). Stored content stays verbatim — only the preview is truncated — so this does not violate the "verbatim always" principle. Supporting changes: - ``mempalace/privacy.py`` — ``redact_private`` + ``summarize_for_search`` - ``searcher.py`` — hits now carry ``drawer_id`` from chroma ids so the two-step flow can round-trip reliably - Full test coverage in ``tests/test_privacy.py`` and new progressive disclosure test class in ``tests/test_mcp_server.py`` Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document the new search/write semantics so LLM callers discover the two-step fetch naturally instead of re-requesting full results. - search.md: describe the default summary + drawer_id response, the ``full=true`` opt-out, and the recommended search→get_drawer flow. - help.md: add a Privacy section covering <private> tag stripping, the summary-only default, and the expanded ``mempalace_get_drawer`` (single id or array) in the write-side tool list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jphein
added a commit
to jphein/mempalace
that referenced
this pull request
Apr 19, 2026
Scanned all 233 open upstream PRs today against our open PRs and fork-ahead / planned-work items. Findings merged into README: - P2 (decay) and P3 Tier-0 (LLM rerank): both covered by MemPalace#1032 (@zackchiutw, MERGEABLE, 2026-04-19 — Weibull decay + 4-stage rerank pipeline). Older simpler version at MemPalace#337. Dropped as fork work; watching MemPalace#1032. - P7 (alternative storage): formally out of scope. RFC 001 MemPalace#743 (@igorls) defines the plugin contract; four backend PRs already in flight (MemPalace#700, MemPalace#381 Qdrant; MemPalace#574, MemPalace#575 LanceDB). Fork consumes, does not rebuild. - P0 (multi-label tags): still fork/upstream candidate. MemPalace#1033 (@zackchiutw) ships adjacent privacy-tag + progressive disclosure but not the full multi-label scheme. - Merged MemPalace#1023 section acknowledges complementary MemPalace#976 (felipetruman) which adds broader mine_global_lock() + HNSW num_threads pin. Gives future-us a map so we don't re-file MemPalace#1036-style duplicates.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two privacy-minded additions to the MCP search and write paths:
1.
<private>...</private>tag handling on writetool_add_drawerandtool_diary_writenow strip<private>…</private>blocks (case-insensitive, multiline, non-greedy) before sanitize + embed. Entries whose entire content sits inside a private tag are refused, so nothing leaks through metadata, drawer IDs, or the embedding itself.2. Progressive disclosure on read
tool_searchreturns a~30-charsummary +drawer_idper hit by default. Callers that need the raw text opt in viafull=true, or fetch on demand viatool_get_drawer(which now accepts a single ID or an array). Stored content stays verbatim — only the preview is truncated — so this respects the "Verbatim always" design principle inCLAUDE.md. The two-step flow also keeps agent context small when search is exploratory.Why
Private tags let users mark thoughts they want remembered locally but never fed back to the model — useful for rough reasoning, personal notes, and conversation drafts. Progressive disclosure is the read-side complement: long drawer text no longer floods the agent response when it only needed to know which memory matched.
What changes
mempalace/privacy.py(new)redact_private(text) -> (cleaned, is_fully_private)andsummarize_for_search(text, n_chars)mempalace/mcp_server.pytool_searchgainsfull=Falsedefault + schema entry;tool_get_draweraccepts str or list[str];tool_add_drawer/tool_diary_writestrip + reject fully-private contentmempalace/searcher.pydrawer_idpulled from chromaidsso the two-step fetch round-trips reliablytests/test_privacy.py(new)tests/test_mcp_server.pyTestProgressiveDisclosureclass (20+ integration tests)mempalace/instructions/search.mdfull=trueopt-out, and the recommendedsearch → get_drawerflowmempalace/instructions/help.mdCompatibility notes
tool_searchno longer returns full drawer text by default. Callers that depended on full text can passfull=trueto restore the old behavior. LLM callers typically benefit from the new default (smaller responses, clear drawer_id for follow-up).tool_get_drawer(id=...)is backwards compatible (single ID still works); new array form is additive.Example — private tag
Example — progressive disclosure
Test plan
pytest tests/test_privacy.py tests/test_searcher.py tests/test_mcp_server.py— 112 passedruff check— cleanruff format --checkon modified files — clean<private>stripping is case-insensitive (<PRIVATE>,<Private>all work)success=False+ reason fieldtool_get_draweraccepts bothid="abc"andid=["abc", "def"]tool_search(full=true)returns the pre-existing full-text response shapeNotes for reviewers
mempalace/mcp_server.py:tool_searchschema but at different call sites; a trivial merge conflict is expected if both land.~30chars backing up to the last word boundary) is hard-coded inprivacy.py. Happy to make that configurable if reviewers prefer.🤖 Generated with Claude Code