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:
-
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)
-
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)
- Tag synced observations with the same project value used at query time (per-observation, not the worker's cwd).
- Fix the project
where-clause so an exact-match value actually filters.
- 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.
Summary
With
CLAUDE_MEM_SEMANTIC_INJECT=true(runtime=worker), theUserPromptSubmitsemantic injection returns empty context for every real session. The feature only returns results when queried without a project filter.Root cause
Two compounding issues:
Sync writes the wrong
projectmetadata. Every row in the Chroma collection is taggedproject="aaronmellman"— it appears to derive from the worker daemon's cwd (the user's home dir) rather than each observation's originating project. Verified:Injection queries with a different
projectvalue, and the project-filter path returns empty regardless. The hook calls/api/context/semanticwith{q, project, limit}whereprojectis the session slug (e.g.business-os-archreview, derived from cwd/git). The endpoint filterswhere:{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)count{"q":"...","limit":5}(no project){"q":"...","project":"aaronmellman","limit":5}(the only stored value){"q":"...","project":"business-os-archreview","limit":5}(real session slug)Environment
13.6.1, runtimeworker, macOS (Apple Silicon)chroma-mcp==0.2.6persistent, ~16,392 embeddingsSuggested fixes (any one)
where-clause so an exact-match value actually filters.CLAUDE_MEM_SEMANTIC_INJECT_SCOPE=global) to disable project scoping for injection.Workaround in use
Dropping the
projectarg from the inject call inworker-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.