feat(entities): persist false-positive entity↔document unlink (#35)#37
Merged
Conversation
Entity↔document links are heuristic and occasionally wrong (a bare first name matching the wrong person, a title substring that isn't really about the entity). Mentions are re-derived on every index and Granola sync regenerates meeting files, so a correction can't live in the DB row or in frontmatter — it would be wiped on the next sync. Suppressions now live in a sidecar (memory/.kbx/entity-suppressions.json), keyed by document path → entity names. The indexer loads it and skips those entities for that document across all matching tiers, so the fix survives a full re-index (names, not ids, are the key). Exposed as `kbx entity unlink` / `kbx entity relink` and the kb_entity_unlink / kb_entity_relink MCP tools; both write through the auto-commit path.
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.
What
Adds a way to suppress false-positive entity↔document links that survives re-indexing and Granola sync.
memory/.kbx/entity-suppressions.json(src/kb/suppressions.py) — keyed by document path → entity names, case-insensitive, atomic writes.find_entity_mentions(..., suppressed_ids=...)skips suppressed entities across all matching tiers.suppressed_idsinto mention derivation — so the fix persists through a full re-index (names, not ids, are the key).KnowledgeBase.unlink_entity()/relink_entity()API methods (drop the live mention now + record/remove the suppression).kbx entity unlink "Name" <doc>/kbx entity relink "Name" <doc>(auto-commit aware).kb_entity_unlink/kb_entity_relink(both idempotent).Why
Entity linking is heuristic and occasionally wrong (a bare first name matching the wrong person, a title substring that isn't really about the entity). Mentions are re-derived on every index and Granola sync regenerates meeting files, so a correction couldn't live in the DB row or in frontmatter — it would be wiped on the next sync. A sidecar keyed by entity name is the only place a suppression survives id churn + file regeneration.
Tests
test_suppressions.py— store unit tests (load/add/remove, idempotency, corrupt-file tolerance).test_unlink.py— full integration throughindex_all: unlink drops the mention and stays dropped across a full reindex; relink restores it; CLI happy-path + error paths.test_entities.py— matcher honourssuppressed_ids.test_mcp.py—kb_entity_unlink/kb_entity_relinkhandlers (success + error shapes).Docs updated:
docs/entities.md(new §Suppressions),docs/cli.md,CLAUDE.md(tool count 31→33 + MCP section), CLI_AGENT_PLAYBOOK.Closes #35.