Five high-confidence findings from the post-merge reviewer pass:
1. memory.update MCP tool description claimed "Project is upserted if it
doesn't exist", but the handler used resolveProjectId and would error.
Matched the description to the actual behavior (call project.identify
first) — keeps parity with memory.write.
2. updateMemoryAction's UPDATE statement was missing the userId guard.
The preceding scoped SELECT made it not exploitable in practice, but
it diverged from deleteMemoryAction's pattern. Added the guard for
defense in depth.
3. putSnippet's UPDATE statement had the same missing userId guard —
fixed the same way.
4. MemoryUpdateInput's refine for scope='user' accepted both
project=undefined AND project=""; the snippets refine only accepted
undefined. Tightened MemoryUpdateInput to require undefined, matching
the snippets rule. Web actions already coerce "" → undefined before
parsing, so no caller is affected.
5. 0002_snippets_scope.sql created two indexes unconditionally —
replaced with CREATE INDEX IF NOT EXISTS so re-runs after a
drizzle-kit push won't trip.
Also adds .claude/ to .gitignore so worktree directories from
multi-agent builds aren't accidentally committed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Snippets are exact-name-keyed templates (PR formats, checklists, style
rules) — prescriptive artifacts the user wants applied consistently.
Distinct from memories, which are searched descriptive prose.
- Migration 0002 adds scope/project_id/deleted_at to snippets, partial
unique indexes per scope, and a scope/project CHECK constraint
mirroring memories_scope_project_chk.
- New @shared-memory/schemas: SnippetName/Put/Get/List/Delete inputs
with shared scope-project refinement.
- lib/snippets.ts: get/put/list/softDelete helpers used by both the
Web Server Actions and the MCP tool handlers.
- Four new MCP tools (snippet.put/get/list/delete) with directive
descriptions contrasting against memory.* (exact-name lookup vs
search; templates vs facts).
- /snippets pages: list, new, [name] with edit + delete and a
scope-picker when a name lives in multiple scopes.
- Nav: Snippets link between Memories and Projects.
Design note: when a name exists in both user and project scope,
snippet.get without an explicit scope prefers project (if project key
given) then falls back to user. The detail page shows a picker when
the name is ambiguous and no scope query param was supplied.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>