Skip to content

fix(smart-search): apply the project filter to results, not just lessons#869

Open
jwfaye wants to merge 3 commits into
rohitg00:mainfrom
jwfaye:fix/smart-search-project-scope
Open

fix(smart-search): apply the project filter to results, not just lessons#869
jwfaye wants to merge 3 commits into
rohitg00:mainfrom
jwfaye:fix/smart-search-project-scope

Conversation

@jwfaye

@jwfaye jwfaye commented Jun 9, 2026

Copy link
Copy Markdown

What

mem::smart-search accepts a project argument but never applies it to its observation results — a project-scoped search returns observations from every project (the cross-project leak reported in #787). mem::search already scopes correctly; this brings smart-search in line, which also covers the MCP memory_smart_search / memory_recall tools and /smart-search that route through it.

How

Resolve each hit's project the way mem::search does — the session's project when the session exists, otherwise the memory's own KV.memories record (entries indexed via mem::remember use a synthetic sessionId with no session row). A null result means the project is unknown and the hit passes through (backward-compatible). Applied in the expand-ids branch, the hybrid/compact path, and the over-fetch trigger; the normalized filterProject is also passed to lesson recall. Resolution is cached and runs concurrently.

Verify

  • npm run build clean; test/smart-search.test.ts + test/agent-isolation-search.test.ts green (the tests seed KV.memories and use a synthetic memory sessionId so they exercise the real resolver path, not a synthetic observation that already carries project).
  • Live on a running instance: smart-search {project: "review"} returns the review observation; {project: "builder"} excludes it.

Fixes #787

mem::smart-search accepts a `project` argument but only forwards it to
mem::lesson-recall — the primary observation results are filtered by
agentId alone. A project-scoped search therefore returns observations
from every other project, so callers cannot use `project` to isolate
memory per concern.

Apply the project filter wherever the agentId filter already runs: the
expand-ids branch, the hybrid-results branch, and the over-fetch
trigger (the BM25/vector index carries neither agentId nor project, so
both need post-filter headroom). When `project` is omitted behaviour
is unchanged.

Adds two tests: a project-scoped search returns only the matching
project's observations, and an unscoped search still returns all.

Signed-off-by: Joseph Faye <josephwfaye@gmail.com>
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

@jwfaye is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds optional project-based filtering to mem::smart-search: derives a trimmed filterProject, resolves hit projects from KV session/memory, then applies project constraints in expand-ids, over-fetch/hybrid paths, and lesson recall; tests verify compact-mode scoping behavior.

Changes

Project-scoped observation filtering

Layer / File(s) Summary
Imports, project resolver, and filter extraction
src/functions/smart-search.ts
Adds KV Memory/Session imports, a makeProjectResolver helper that resolves a hit's project via session then memory, and derives filterProject from data.project (trimmed, empty → undefined).
Expand-ids result scoping by project
src/functions/smart-search.ts
Apply filterProject together with filterAgentId in the expandIds branch; resolved null projects are treated as unscoped and allowed through.
Compact search over-fetch and hybrid post-filtering
src/functions/smart-search.ts
Trigger over-fetch when agent or project filters are present, pass filterProject to lesson recall, and post-filter hybrid results by both agent and resolved project before slicing to limit.
Project scoping tests
test/smart-search.test.ts
New test suite seeds observations in two projects and asserts compact-mode smart-search returns only the requested project when project is provided and returns all projects when omitted (includeLessons: false).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SmartSearch
  participant KV_Session
  participant KV_Memory
  participant ResultsFilter
  Client->>SmartSearch: POST /agentmemory/smart-search { query, project?, agentId? }
  SmartSearch->>KV_Session: resolve hit.project (cached lookup)
  KV_Session-->>SmartSearch: project or not found
  SmartSearch->>KV_Memory: resolve hit.project (fallback)
  KV_Memory-->>SmartSearch: project or null
  SmartSearch->>ResultsFilter: apply filterAgentId and filterProject to hits
  ResultsFilter-->>Client: filtered results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • rohitg00

Poem

🐇 I hopped through code with a curious nose,

Trimmed your projects where the memory grows.
Now searches mind each bounded nest,
No cross-talk, no messy guest.
Tests hum softly — queries rest.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: applying project filter to observation results, not just lessons.
Linked Issues check ✅ Passed The PR fully addresses issue #868 by applying project filter to primary observation results (expandIds and filteredHybrid branches) in addition to lesson recall, resolving the cross-project leak.
Out of Scope Changes check ✅ Passed All changes directly support the primary objective of fixing project filtering in smart-search; test additions validate the project scoping behavior without introducing unrelated modifications.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/functions/smart-search.ts`:
- Around line 118-121: The trimmed/normalized variable filterProject is used for
observation filtering but the lesson recall call still passes raw data.project,
causing divergence on whitespace inputs; update the lesson recall invocation(s)
to pass filterProject instead of data.project (and similarly replace any raw
uses of data.project in the nearby recall logic) so both observation filtering
and lesson recall use the same normalized project value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c59ac139-a266-46ba-9cd9-16635e31ffb3

📥 Commits

Reviewing files that changed from the base of the PR and between 749c280 and b8a47a8.

📒 Files selected for processing (2)
  • src/functions/smart-search.ts
  • test/smart-search.test.ts

Comment thread src/functions/smart-search.ts
…cope

The first pass filtered on r.observation.project, but the observation
returned by the hybrid index does not carry the resolved project. Live
testing showed a project-scoped search dropping ALL results — including
those of the requested project. Resolve project the same way mem::search
does: the session's project when the session exists, otherwise the
memory's own KV.memories record (entries indexed via mem::remember use a
synthetic sessionId with no session row). A null result means the project
is unknown and the hit passes through (matches search.ts, backward
compatible). Both lookups are cached.

Tests now seed KV.memories and use the synthetic 'memory' sessionId so
they exercise the real resolver path instead of a synthetic observation
that already carries project.

Addresses the CodeRabbit review: pass the normalized filterProject (not
raw data.project) to recallLessons so observation filtering and lesson
scoping agree on whitespace input.

Signed-off-by: Joseph Faye <josephwfaye@gmail.com>
@jwfaye

jwfaye commented Jun 9, 2026

Copy link
Copy Markdown
Author

Follow-up commit (417fb73) — corrects the approach and addresses the review.

What was wrong with the first commit. It post-filtered on r.observation.project, but the observation returned by the hybrid index doesn't carry the resolved project. Testing live against a running instance, a project-scoped smart-search dropped all results — including those of the requested project (e.g. project: "review" returned nothing).

The fix. Resolve project per hit the same way mem::search already does: the session's project when the session exists, otherwise the memory's own KV.memories record (entries indexed via mem::remember use a synthetic sessionId with no session row). A null result = project unknown → the hit passes through (matches search.ts, backward-compatible). Both lookups are cached.

The tests now seed KV.memories and use the synthetic memory sessionId, so they exercise the real resolver path rather than a synthetic observation that already carries project (which gave a false pass before).

Verified live on a running instance after the fix:

  • smart-search {project: "review"} → returns the review observation ✓
  • smart-search {project: "builder"} → excludes it ✓

@coderabbitai — addressed: recallLessons now receives the normalized filterProject instead of raw data.project, so observation filtering and lesson scoping agree on whitespace input.

npm run build clean; test/smart-search.test.ts + test/agent-isolation-search.test.ts green.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@jwfaye thanks for the detailed follow-up. That correction makes sense: resolving the project per hit from the same sources as mem::search, while allowing unknown project metadata to pass through, avoids the false-negative behavior from filtering on an unset r.observation.project.

Also noted on the original review point: passing normalized filterProject into recallLessons keeps lesson scoping and observation filtering consistent for whitespace input. The updated tests exercising the synthetic memory session path sound like the right coverage.

🐇✨


✏️ Learnings added
Learnt from: jwfaye
URL: https://github.com/rohitg00/agentmemory/pull/869

Timestamp: 2026-06-09T13:26:53.540Z
Learning: In the agentmemory repository, hybrid index observations used by `src/functions/smart-search.ts` may not carry a resolved `observation.project`. Project-scoped filtering in `mem::smart-search` should resolve the project per hit the same way `mem::search` does: use the session's project when a session row exists, otherwise fall back to the memory's own `KV.memories` record for entries indexed via `mem::remember` that use the synthetic `memory` sessionId. A null or unknown project should pass through for backward compatibility.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/functions/smart-search.ts`:
- Around line 198-201: The loop calling resolveProject for each entry in
agentScoped is doing sequential KV reads; change it to run resolveProject in
parallel using Promise.all (e.g., map agentScoped to promises of
resolveProject(sessionId, observation.id)), await the array, then iterate
results to decide whether to push into kept (checking for null or equality with
filterProject); apply the same refactor for the similar block that references
resolveProject at the second occurrence (lines 255-257) so both sets of lookups
execute concurrently.
- Around line 90-92: The session KV read at sessionCache.set(sessionId, (await
kv.get<Session>(KV.sessions, sessionId)) ?? null) can reject and should degrade
to null; update the call that uses kv.get with error handling (e.g., wrap the
await in try/catch or use .catch(() => null)) so any rejection returns null
before the ?? runs, and keep using sessionCache, sessionId, kv.get and
KV.sessions as the referenced symbols so project-scoped search remains
resilient.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1655e671-c159-4fa9-8b33-180adee6fed6

📥 Commits

Reviewing files that changed from the base of the PR and between b8a47a8 and 417fb73.

📒 Files selected for processing (2)
  • src/functions/smart-search.ts
  • test/smart-search.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/smart-search.test.ts

Comment thread src/functions/smart-search.ts
Comment thread src/functions/smart-search.ts Outdated
Address the CodeRabbit review on the project-resolution helper:
- the session KV read now degrades to null on rejection (matching the
  memory lookup), so a transient state::get failure no longer fails the
  whole project-scoped search.
- resolve each hit's project concurrently via Promise.all instead of an
  awaited serial loop, in both the expand-ids and hybrid branches; the
  lookups are independent and the resolver caches both reads.

Signed-off-by: Joseph Faye <josephwfaye@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project scoping not honored by MCP search tools (memory_smart_search / memory_recall) or /smart-search

1 participant