feat-memoryDocker-only validation supports the v1 recommendation: use Mem0 behind a CoreSpeed Memory Service, expose a CoreSpeed-owned MCP wrapper, and keep Graphiti as a phase 2 challenger for organization and temporal memory.
filters.user_id and list paths./v1/responses.| Docker | Docker version 29.4.0, build 9d7ad9f |
|---|---|
| Compose | Docker Compose version v5.1.2 |
| Real model key | No real key was used. A Docker-only OpenAI-compatible mock was published on host port 8894. |
| Temp workspace | /private/tmp/sarea-memory-spike |
| Network policy | Sandboxed curl could not reach Docker-published localhost ports; HTTP probes required escalated local network access. |
Mem0 was run from a temp checkout at /private/tmp/sarea-memory-spike/vendors/mem0. The bundled Docker setup needed temp-only fixes: dashboard build moved from node:20-alpine to node:22-alpine, pnpm@9.15.9 was pinned, and explicit Postgres environment values were added so blank example values did not override server defaults.
| Probe | Result | Notes |
|---|---|---|
| Startup | pass | API reached /docs after mock credentials and Postgres env fixes. |
| Add | pass | User A and B writes produced real UUID memory IDs. |
| Search/list | pass | POST /search passed with filters.user_id; top-level user_id is not accepted by current Mem0 search. |
| User isolation | pass | User A search returned only A; User B search returned only B; deleting A did not delete B. |
| Update/delete | pass | Update, get-after-update, delete, and list-after-delete passed against a real user A memory id. |
| Metadata | pass | user_id, agent_id, run_id, session metadata, source, candidate, and update marker were preserved. |
| Extraction quality | mocked | The mock proves extraction wiring and persistence path, not production quality. |
filters.user_id from the authenticated token subject.
A throwaway CoreSpeed-style wrapper ran at /private/tmp/sarea-memory-spike/wrapper, backed by the Docker-only Mem0 and mock OpenAI stack.
| Probe | Result | Notes |
|---|---|---|
| Health | pass | GET /health returned memoryEnabled: true, Mem0 URL, and the fixed spike user id. |
| Initialize | pass | Stateless MCP initialize returned HTTP 200 with Streamable HTTP SSE framing. |
| Tool discovery | pass | Exposed search_memory, remember, update_memory, delete_memory, and list_memory. |
| Remember/search/list | pass | The wrapper wrote and found a memory with provenance metadata. |
| Update/delete | pass | Parsed the returned id, updated the text, deleted it, and verified the list was empty. |
| Disabled mode | pass | MEMORY_ENABLED=false returned HTTP 403 with JSON-RPC error -32000 and message Memory is disabled. |
| JSON-only Accept | fail expected | The MCP SDK rejects Accept: application/json alone with HTTP 406. |
Accept: application/json, text/event-stream and parse data: events. Successful responses are SSE-framed, not bare JSON.
Graphiti was run from /private/tmp/sarea-memory-spike/vendors/graphiti using its FalkorDB Docker compose path. Host ports were patched only because local containers already owned 6379 and 3000.
| Probe | Result | Notes |
|---|---|---|
| Startup | pass | FalkorDB and Graphiti MCP containers reported healthy. |
| Health | pass | GET /health returned {"status":"healthy","service":"graphiti-mcp"}. |
| MCP discovery | pass | Required an mcp-session-id from initialize for subsequent tools/list. |
| Tools | observed | Graph-oriented tools included add_memory, search_nodes, search_memory_facts, get_episodes, clear_graph, and get_status. |
| Write/read | blocked | Background processing failed because current Graphiti uses OpenAI /v1/responses, which the mock did not implement. |
| Scope isolation | wrapper needed | Natural isolation uses group_id / group_ids; Sarea would need sanitized derived groups and ownership checks. |
sarea-user-a triggered a FalkorDB RediSearch syntax error; a non-hyphenated sarea_user_a returned a clean empty result.
Graphiti remains interesting for later org memory because it models episodes, entity nodes, relationship facts, temporal metadata, and invalidated facts. That extra power is also why it is too heavy for the first personal-memory release.
Proceed with Mem0 for v1. Keep Graphiti as a v2 challenger after gateway-backed validation with a Responses-compatible model provider and an explicit Sarea wrapper design.
It passed scoped REST CRUD and search/list paths with real memory ids in Docker.
The PoC exposed the intended tools, preserved provenance, and enforced disabled mode.
Mem0 started from source with temp-only Docker/env fixes; no hosted memory dependency is required for v1.
It is operationally heavier, graph-shaped, sessionful over MCP, and needs more scope-safety work.
filters.user_id for scoped search. Do not rely on top-level user_id for search.
| Report source | docs/superpowers/spikes/2026-05-14-memory-candidate-validation.md |
|---|---|
| Spec source | docs/superpowers/specs/2026-05-13-memory-system-design.md |
| Spike artifacts | /private/tmp/sarea-memory-spike/results |
| Final report commit | 9b8d2b7 docs: finalize memory validation recommendation |
| Cleanup | Spike containers were stopped; artifacts remain under /private/tmp/sarea-memory-spike for inspection. |