From 1fa3300b107a1a64beed9ac3ab318287b057758b Mon Sep 17 00:00:00 2001 From: EdgeMLHacker <126883848+Apageoflove@users.noreply.github.com> Date: Tue, 15 Sep 2026 21:23:36 -0400 Subject: [PATCH] fix(recall): scope filter requires directory boundary match _in_scope used raw startswith(scope), so scope=user also matched username/, user-notes/, user2/, and any domain whose name begins with user. Fix: strip trailing slash, then require exact match or a directory separator after the prefix. --- packages/core/src/agent_memory/core/recall.py | 3 ++- tests/unit/test_storage.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/core/src/agent_memory/core/recall.py b/packages/core/src/agent_memory/core/recall.py index c56eec46..bc368eb5 100644 --- a/packages/core/src/agent_memory/core/recall.py +++ b/packages/core/src/agent_memory/core/recall.py @@ -113,7 +113,8 @@ def _eligible( return eligible def _in_scope(self, path: str, scope: str) -> bool: - return path.startswith(scope.strip("/")) + scope = scope.strip("/") + return path == scope or path.startswith(scope + "/") def _current_at(self, row: sqlite3.Row, moment: dt.datetime) -> bool: if timestamp.parse(str(row["valid_from"])) > moment: diff --git a/tests/unit/test_storage.py b/tests/unit/test_storage.py index 5459efce..d7789946 100644 --- a/tests/unit/test_storage.py +++ b/tests/unit/test_storage.py @@ -163,3 +163,13 @@ def test_slugify_is_idempotent_and_produces_valid_slugs(config): if once: assert is_valid_slug(once) assert slugify(once, config.storage.slug_max_length) == once + + +def test_scope_filter_does_not_match_similar_prefixes(store): + """Regression: scope=user must not match username/, user-notes/, etc. (#17).""" + for name in ("user/preferences/a.md", "username/settings.md", "user-notes/x.md", "user2/p.md"): + seeded.record(abstract=f"Entry in {name}", type="fact", name=name.replace("/", "-")) + + results = seeded.recall(scope="user") + paths = [r["path"] for r in results] + assert all(p == "user" or p.startswith("user/") for p in paths), paths