Skip to content

Semantic injection always returns empty: project-filter mismatch between Chroma sync and /api/context/semantic #2979

Description

@amellman

Summary

With CLAUDE_MEM_SEMANTIC_INJECT=true (runtime=worker), the UserPromptSubmit semantic injection returns empty context for every real session. The feature only returns results when queried without a project filter.

Root cause

Two compounding issues:

  1. Sync writes the wrong project metadata. Every row in the Chroma collection is tagged project="aaronmellman" — it appears to derive from the worker daemon's cwd (the user's home dir) rather than each observation's originating project. Verified:

    sqlite3 ~/.claude-mem/chroma/chroma.sqlite3 \
      "SELECT string_value, COUNT(*) FROM embedding_metadata WHERE key='project' GROUP BY 1;"
    -> aaronmellman|16392      (single value, all rows)
    
  2. Injection queries with a different project value, and the project-filter path returns empty regardless. The hook calls /api/context/semantic with {q, project, limit} where project is the session slug (e.g. business-os-archreview, derived from cwd/git). The endpoint filters where:{project: <param>}. Since the stored value never matches the session slug, results are always empty. Critically, even querying with the stored value (aaronmellman) returns empty, while the unscoped query returns correct matches — so the scoped where-clause path itself looks broken, not just the metadata.

Evidence (curl against the worker, 127.0.0.1:37701)

Request body count
{"q":"...","limit":5} (no project) 5 (relevant matches)
{"q":"...","project":"aaronmellman","limit":5} (the only stored value) 0
{"q":"...","project":"business-os-archreview","limit":5} (real session slug) 0

Environment

  • claude-mem 13.6.1, runtime worker, macOS (Apple Silicon)
  • chroma-mcp==0.2.6 persistent, ~16,392 embeddings

Suggested fixes (any one)

  1. Tag synced observations with the same project value used at query time (per-observation, not the worker's cwd).
  2. Fix the project where-clause so an exact-match value actually filters.
  3. Add an env knob (e.g. CLAUDE_MEM_SEMANTIC_INJECT_SCOPE=global) to disable project scoping for injection.

Workaround in use

Dropping the project arg from the inject call in worker-service.cjs ({q:s,project:i,limit:m} -> {q:s,limit:m}) makes injection use the working unscoped path. Reverts on update since it's vendored code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions