# Memory Graph View — Handoff Read this first if picking up this work in a new session. It answers: what's done, what's the very next thing to do, what could bite you, and why certain calls were made. For the full checklist see `docs/todos.md`. For the session-by-session history see `docs/progress.md`. For the accepted design see `docs/memory-graph-design.md`. For the repo research behind it see `docs/memory-graph-analysis.md`. ## Where things stand right now - **Branch**: `feature/memory-graph-view`. - **Milestone 1 (backend API)**: done, tested, **committed**. One additional bug found and fixed during M2 verification (see below) — `memory_graph_routes.py`'s owner resolution was inconsistent with the rest of the memory routes, which made the graph always empty and links always 404 in single-user/no-auth mode. Committed separately (`fix(memory-graph): align owner resolution with rest of memory routes`). - **Milestone 2 (frontend)**: done, **committed**, **visually verified** end-to-end against a real seeded dataset (render, node click + neighborhood highlight, detail panel, inline edit, search, category filters, similarity slider, link-mode create, theme switch — see `docs/progress.md` Session 4 for the full pass). Two frontend bugs found and fixed in the same commit: the detail panel's `hidden` class was never removed on node selection (panel was permanently invisible), and `_applySearch()` left stale `mg-dimmed`/`mg-highlighted` classes from a prior node selection that masked search-match nodes at `opacity:0.08`. - **Milestone 3 (polish)**: not started. This is the next work. ## Exact next task Resume the Milestone 3 checklist in `docs/todos.md` (legend refinement, isolate-component affordance, keyboard shortcuts, `MODULE_SUMMARY.md` update, a real automated-test pass for `memoryGraph.js`). Two things need explicit user sign-off before or during M3: 1. **The "feature flag enabled (beta)" open question** (see below) — confirm whether a real togglable flag is wanted, or whether the current privilege-gate + static "beta" label is sufficient as shipped. 2. Whether to also fix the known limitation that graph node colors are only recomputed on next render, not live-reactive to a theme flip while the modal is already open (confirmed still true during verification — switching theme via the Theme modal while Memory Graph was open did not immediately recolor node fills, though all non-canvas chrome — detail panel, chips, buttons — is CSS-variable-driven and did update live). For a repeatable verification loop in a future session (isolated data dir, seeding via the real API, symmetric Chroma cleanup, PID-verified process kill), the exact steps used are preserved in `docs/progress.md` Session 4 — worth reusing as-is rather than re-deriving. ## Open decision needed from the user **The "feature flag enabled (beta)" requirement was interpreted as "ship it visible, label it beta" — not as "build an actual on/off toggle."** Right now the only thing gating visibility of the "Memory Graph" nav item is the existing `can_manage_memory` privilege (the same gate the "Brain" button already uses), plus a static "beta" label next to the title in the modal header. There is no separate settings switch a user or admin could flip to hide the feature independent of that privilege, and no server-side flag either. If a real togglable flag was intended (e.g. a Settings-tab checkbox like the design doc's original Phase 2 proposal in `docs/memory-graph-design.md` §8), that hasn't been built. **Confirm which was actually wanted before Milestone 3.** ## Risks / things to watch 1. **The `src/agent_loop.py` one-line fix is uncommitted and out of this feature's actual scope.** It's a genuinely pre-existing bug (confirmed via `git stash` to reproduce identically on a clean `dev` checkout — `NameError: name 'Any' is not defined` because `dict[str, Any]` was used in a function signature without importing `Any`), and without it the entire app fails to import, which blocks any local verification at all. It's about as safe a fix as they come (adding a missing stdlib import can't break anything that currently works, since nothing downstream of that line currently works). But it wasn't asked for, so **don't fold it silently into a Memory Graph commit** — either commit it separately with a clear message, or ask the user whether they even want it touched at all (they may already have a fix for it elsewhere, or may want to report it upstream themselves). 2. **Do not seed demo/test memory data into the real ChromaDB without cleaning it up.** See step 4 above — this bit us mid-session (server was mid-startup, about to seed, when told to stop; it was killed before anything was written, so no cleanup is currently owed, but the *next* session doing this must not skip the deletion step). 3. **The dev venv (`.venv-test/`) is a throwaway, git-ignored (via `.git/info/exclude`, not `.gitignore`) local artifact**, created because this sandbox had no Python environment with the project's dependencies installed. It is NOT part of the repo and should not be referenced by anything committed. A future session may need to recreate it (`python -m venv .venv-test && .venv-test/Scripts/python.exe -m pip install -r requirements.txt`) if it's not still present. 4. **Pre-existing test failures are numerous (199) in this sandbox and are all environment-driven** (missing Node/`rg`, an `mcp` package version mismatch, Windows path-confinement assumptions) — confirmed byte-for-byte identical between this branch and a clean `dev` baseline. Don't waste time trying to fix them as part of this feature; they were already broken before this work started, and fixing them is a separate, unrelated effort. 5. **Chroma collections are global, not owner-scoped.** This was already true of the existing memory system before this feature (`docs/memory-graph-analysis.md` §10) and the graph feature doesn't change it, but it's exactly why step 4 above matters — any manual testing against a real/shared Chroma instance needs symmetric cleanup. ## Implementation notes worth knowing before touching this code - **Route-ordering is load-bearing.** `routes/memory/memory_graph_routes.py` must be `include_router()`'d in `app.py` *before* `routes/memory/memory_routes.py`'s router. The latter's `GET/PUT/DELETE /api/memory/{memory_id}` is a single-segment wildcard; Starlette matches routes in registration order across the whole app (not by specificity), so `GET /api/memory/graph` would otherwise resolve to the wildcard with `memory_id="graph"` and 404. This is regression-tested in `tests/test_memory_graph_route_ordering.py` using a real `TestClient` — the only place in this feature's tests that a full ASGI app was actually needed, because the repo's usual "look up the route by exact path string, call the endpoint function directly" test convention can't catch this class of bug (it never exercises real Starlette path matching). - **The runtime memory store is a JSON file, not the SQL `memories` table.** `src/memory.py::MemoryManager` persists to `data/memory.json`; there's a `memories` SQLAlchemy table in `core/database.py` too, but it's unused by any live code path (see `docs/memory-graph-analysis.md` §8's "Important discrepancy"). The Memory Graph feature correctly targets the JSON store + the `odysseus_memories` Chroma collection, matching what every other memory route already does. Don't be tempted to "fix" this by wiring the graph to the SQL table — that's explicitly out of scope (see the analysis doc). - **Manual links are stored as a new optional `links: []` field directly on memory JSON entries.** No migration was needed because it's a JSON file, not a SQL schema — old entries without the field are handled via `entry.get("links", [])`/`mem.get("links") or []` throughout, so nothing breaks for pre-existing memories. - **The frontend has zero bundler.** Cytoscape.js is vendored as a single UMD file (`static/lib/cytoscape.min.js`, fetched via `npm pack cytoscape@3` in a scratch dir — not a hand-typed CDN URL) and lazy-loaded via a dynamically-created `