mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-05 19:05:28 +00:00
Updates handoff/progress/todos to reflect that M2 is committed and visually verified, and documents the three bugs found (and fixed) along the way: the memory_graph_routes.py owner-scoping mismatch, the detail panel's stuck `hidden` class, and search leaving stale selection-highlight opacity over its own matches.
108 lines
14 KiB
Markdown
108 lines
14 KiB
Markdown
# Memory Graph View — Progress Log
|
||
|
||
Session-by-session record. Newest entry on top. See `docs/todos.md` for the live checklist and `docs/handoff.md` for the exact next action.
|
||
|
||
---
|
||
|
||
## Session 4 — Milestone 2 verification, two bugs found and fixed, M2 committed
|
||
|
||
**Goal**: pick up exactly where Session 3 stopped — get a real, visual confirmation the Memory Graph tab works, then commit M2.
|
||
|
||
### What was done
|
||
|
||
1. Launched an isolated instance (`ODYSSEUS_DATA_DIR`/`DATABASE_URL` in the session scratchpad, `AUTH_ENABLED=false`, `LOCALHOST_BYPASS=true`, `CHROMADB_PORT=8100`, `APP_PORT=7010`). ChromaDB was still reachable at `localhost:8100` as documented. FastEmbed model download completed this time (was cached from the aborted Session 3 attempt).
|
||
2. Seeded the exact 7-memory set from `docs/handoff.md`'s Session 3 plan via `POST /api/memory/add`, pinned one, and attempted the manual link between the two Lighthouse entries via `POST /api/memory/{id}/links`.
|
||
3. **Found bug 1 (backend, Milestone 1 code)**: the link call 404'd and `GET /api/memory/graph` returned zero nodes despite 7 memories existing. Root cause: `memory_graph_routes.py` used `require_user(request)` (returns `""` in single-user/no-auth/localhost-bypass mode) for owner resolution, while every route in `memory_routes.py` uses a local `_owner(request)` = `get_current_user(request)` (returns `None` in that same mode). `MemoryManager.load(owner=...)` and `_verify_memory_owner()` both special-case `None` as "no filter / bypass ownership check" — but `""` is not `None`, so `load(owner="")` filtered strictly against entries that never got `owner=""` stamped (since `add_entry` only sets the `owner` key when it's truthy). Fixed by switching the graph and links routes to the same `_owner()`/`get_current_user()` pattern already used everywhere else in the memory routes (added a local `_owner()` helper to `memory_graph_routes.py`, matching `memory_routes.py`'s). Verified via curl: graph then returned all 7 nodes plus the expected similarity edge (0.875, between the near-duplicate identity pair) and the manual link edge. Committed separately from the M2 frontend work, since it's a fix to already-committed M1 code, not new M2 code.
|
||
4. Restarted the server (data survives — only the process was killed) and re-verified the fix, then moved to browser testing.
|
||
5. Opened `/` in Chrome, clicked "Memory Graph" in the sidebar Tools list. Modal rendered correctly: 7 nodes, similarity edge, manual link edge, category chips, search box, min-match slider, Link mode button, legend.
|
||
6. **Found bug 2 (frontend)**: clicking a node correctly highlighted its neighborhood but no detail panel appeared. Traced to `static/js/memoryGraph.js`: the panel div (`#memory-graph-detail`) is created with a hardcoded `hidden` class in the initial modal template, and `_renderDetailPanel()` only ever set `panel.innerHTML` — nothing removed the `hidden` class on selection. Fixed with a one-line `panel.classList.remove('hidden')` at the top of `_renderDetailPanel()`. Re-verified: panel now shows category tag, text, uses/pinned meta, timestamp, and Unpin/Edit/Start-link/Delete buttons on node click.
|
||
7. **Found bug 3 (frontend)**: typing a search term didn't visually surface matches — matching nodes stayed at `opacity:0.08` because a prior node-selection's `.mg-dimmed` class (added by `_highlightNeighborhood`) was never cleared by `_applySearch()`, which only ever added/removed its own `.mg-search-match` class. Fixed by having `_applySearch()` clear `_selectedId` (resetting the detail panel) and strip `mg-highlighted`/`mg-dimmed` at the top of the function, so a fresh search cleanly supersedes any leftover selection state. Re-verified: searching "lighthouse" now shows all nodes at full opacity with the two matching nodes ringed.
|
||
8. Verified category chip filtering (isolating "identity" correctly showed only the 2 identity nodes), the min-match similarity slider (raising it above 0.875 correctly hid the identity-pair similarity edge), link-mode's two-click create flow end-to-end through the actual UI (new manual edge appeared after the automatic post-mutation reload, proving the bug-1 fix works from the UI path too, not just curl), the inline Edit textarea (Save/Cancel), and a theme switch (light theme via the Theme modal — all non-canvas chrome recolored live via CSS variables as expected; canvas node fill recompute-on-render-only limitation, already documented, was reconfirmed and left as a Milestone 3 item rather than fixed now).
|
||
9. **Cleanup**: deleted all 7 seeded memories via the real `DELETE /api/memory/{id}` endpoint. Queried the shared Chroma `odysseus_memories_fastembed` collection directly (`POST .../collections/{id}/get` with the 7 ids) and confirmed zero vectors remained — symmetric cleanup verified, not just assumed. Killed the server by resolving the actual listening PID via `Get-NetTCPConnection -LocalPort 7010 -State Listen` (not a guessed PID or shell job number — confirmed this matters: the venv's `python.exe` re-execs into a different on-disk interpreter path, so `Get-CimInstance ... -Filter "Name='python.exe'"` alone returns multiple candidates and only the actual socket owner via `Get-NetTCPConnection` disambiguates which one to kill).
|
||
10. Committed as 4 commits: the pre-existing `agent_loop.py` import fix (standalone, per Session 3's flag not to fold it silently into a feature commit), the M1 owner-scoping fix (standalone), the M2 frontend (module + vendored Cytoscape + HTML/app.js/init.js wiring + CSS, one commit), and this docs update.
|
||
|
||
### Files changed this session
|
||
|
||
| File | Change |
|
||
|---|---|
|
||
| `routes/memory/memory_graph_routes.py` | owner-resolution bug fix (bug 1) |
|
||
| `static/js/memoryGraph.js` | detail-panel visibility fix (bug 2) + search-highlight-clearing fix (bug 3) |
|
||
| `docs/handoff.md`, `docs/progress.md`, `docs/todos.md`, `docs/memory-graph-analysis.md`, `docs/memory-graph-design.md` | updated to reflect M2 done/verified/committed |
|
||
|
||
### Test / verification status
|
||
|
||
- `node --check` passes on `static/js/memoryGraph.js` after both fixes.
|
||
- Full manual browser verification pass completed (see step 8 above) — this is the first real visual confirmation the feature has had; Sessions 1–3 never got past syntax-checking the frontend.
|
||
- Backend test suite not re-run this session (only one backend file changed, a two-line owner-variable substitution with no new logic branches; the existing 27 M1 tests don't exercise the no-auth/localhost-bypass code path that the bug lived in, which is exactly how it shipped unnoticed — worth a Milestone 3 follow-up test case for that specific mode).
|
||
|
||
---
|
||
|
||
## Session 3 — Milestone 2 (Frontend), stopped mid-verification
|
||
|
||
**Status when stopped**: all M2 frontend code written and syntax-checked, but **not yet committed** and **not yet visually verified in a browser**. The user interrupted mid-launch to request a documentation-only checkpoint. No further development happened after that instruction — this entry and the other four requested doc files are the only changes made after the stop request.
|
||
|
||
### What was completed this session
|
||
|
||
1. Vendored Cytoscape.js 3.34.0 (MIT license) into `static/lib/cytoscape.min.js` via `npm pack cytoscape@3` in a scratch directory, then copied the UMD `dist/cytoscape.min.js` build — not fetched from a hand-typed/guessed URL, and not installed into the project's own `node_modules` (the app has no bundler; this follows the existing vendoring convention used for `xlsx.full.min.js`, `docx.umd.min.js`, etc.).
|
||
2. Wrote `static/js/memoryGraph.js` (new file, ~/600 lines) — see full breakdown in "Files changed" below.
|
||
3. Added a "Memory Graph" entry to the sidebar Tools section in `static/index.html`, placed directly after the existing "Brain" entry, using the same `.list-item` markup pattern as every other tool.
|
||
4. Wired it up in `static/app.js`:
|
||
- New static import of `memoryGraph.js` next to the existing `memory.js` import.
|
||
- New click handler block for `#tool-memory-graph-btn`, copied from the `#tool-calendar-btn` block's exact structure (`Modals.toggle(...)` check, falling back to the module's own `openMemoryGraph()`/`closeMemoryGraph()`).
|
||
- New entry in the `UI_VIS_MAP` object (Customize UI panel) so the nav item can be hidden/shown like any other tool.
|
||
- New `/memory-graph` entry in the `_routeOpen` deep-link map, mirroring the existing `/memory` entry.
|
||
5. Added one line to `static/js/init.js`: `hideOn('#tool-memory-graph-btn', privs.can_manage_memory)`, directly under the existing identical line for `#tool-memory-btn`, so the privilege gating stays consistent between the two entry points.
|
||
6. Appended a new `/* Memory Graph View (beta) */` CSS block to the end of `static/style.css` (~234 lines) — did not edit any existing rule.
|
||
7. Investigated and fixed a **pre-existing, unrelated** bug blocking `app.py` from importing at all: `src/agent_loop.py` used `dict[str, Any]` in a function annotation without ever importing `Any` from `typing`. Confirmed via `git stash` that this reproduces identically on a clean `dev` checkout — not something introduced by this feature. Patched with a one-line import fix so the app could actually be launched for the live demo. **This fix is currently uncommitted and its disposition needs the user's explicit decision** (see `docs/handoff.md` → Risks).
|
||
8. Started a local server to seed demo data and take a screenshot, using an isolated `ODYSSEUS_DATA_DIR` / `DATABASE_URL` (a scratch directory, not the repo's real `data/`) with `AUTH_ENABLED=false` and `LOCALHOST_BYPASS=true` for frictionless local testing, pointed at the machine's already-running local ChromaDB (`localhost:8100`) since a completely separate Chroma instance wasn't available. The process was still downloading/loading the FastEmbed embedding model (first-run cache warm-up) when the user's stop instruction arrived. **Killed immediately** (`taskkill` on the confirmed PID, verified via `Get-CimInstance Win32_Process` that it was the right process before killing). Nothing was ever seeded — no demo memory entries were created, no port was ever bound, and the isolated data directory (outside the repo, in the session scratchpad) can simply be discarded.
|
||
|
||
### Files changed this session (all uncommitted)
|
||
|
||
| File | Change |
|
||
|---|---|
|
||
| `static/lib/cytoscape.min.js` | new, vendored, 435 KB |
|
||
| `static/js/memoryGraph.js` | new, ~600 lines |
|
||
| `static/index.html` | +17 lines (nav item) |
|
||
| `static/app.js` | +16 lines (import, click handler, UI_VIS_MAP entry, route entry) |
|
||
| `static/js/init.js` | +1 line (privilege gate) |
|
||
| `static/style.css` | +234 lines (appended block only) |
|
||
| `src/agent_loop.py` | +1/-1 line (missing `Any` import — pre-existing bug, see Risks) |
|
||
|
||
### Test / verification status
|
||
|
||
- `node --check` passes on `static/js/memoryGraph.js`, `static/app.js`, `static/js/init.js` (matches the CI `node-syntax` job's check).
|
||
- **No live browser verification performed.** No screenshot exists. This is the single most important open item — see `docs/handoff.md`.
|
||
- No new automated tests written for the frontend module this session (not requested as part of M2's explicit requirement list, but flagged as a Milestone 3 item in `docs/todos.md`).
|
||
- Backend (Milestone 1) test status is unchanged from Session 2 (see below) — nothing in Session 3 touched backend code.
|
||
|
||
---
|
||
|
||
## Session 2 — Milestone 1 (Backend), completed and committed
|
||
|
||
### What was completed
|
||
|
||
- `src/memory_graph.py`: pure `build_graph()` / `build_similarity_edges()` / `build_session_edges()` / `build_manual_edges()` functions. Similarity edges use one nearest-neighbor query per node against `MemoryVectorStore` (never O(n²) pairwise). Manual edges read an additive `links` field on memory entries.
|
||
- `routes/memory/memory_graph_routes.py`: `GET /api/memory/graph` (owner-scoped, filterable by category/min_similarity/max_edges_per_node, with a node `limit` + `truncated` flag), `GET /api/memory/graph/{id}/neighbors` (lazy single-node expansion), `POST /api/memory/{id}/links` and `DELETE /api/memory/{id}/links/{target_id}` (manual relationship editing, gated by the existing `can_manage_memory` privilege).
|
||
- `app.py`: mounted the new router **before** `memory_routes.py`'s router, with an explanatory comment — `memory_routes.py`'s `GET/PUT/DELETE /api/memory/{memory_id}` is a single-segment wildcard that would otherwise swallow `GET /api/memory/graph` (Starlette matches routes in registration order, not by specificity, across the whole app).
|
||
- Three new test files: `tests/test_memory_graph_edges.py` (14 pure-logic unit tests), `tests/test_memory_graph_routes.py` (12 route/owner-isolation tests, following the repo's "call the endpoint function directly" convention), `tests/test_memory_graph_route_ordering.py` (2 tests using a real `TestClient` — the one place a full ASGI app was needed, specifically to prove the route-ordering fix actually works against real Starlette request matching, which the repo's usual direct-call test style can't verify).
|
||
|
||
### Test status
|
||
|
||
- All 27 new tests pass.
|
||
- Ran the full existing suite (`pytest -q --continue-on-collection-errors`) both on this branch and on a `git stash`-clean `dev` checkout in the same throwaway venv. Exact same 199 pre-existing failures/errors on both sides — confirmed via `comm -23`/`comm -13` diff that the sets are byte-for-byte identical. Zero regressions, zero newly-fixed tests (none expected).
|
||
- All pre-existing failures are sandbox/environment gaps (missing Node.js test runner artifacts for a couple of cases, missing `rg` binary, an `mcp` package version newer than the repo pins, Windows-specific path-confinement test assumptions) — none touch memory, graph, or app.py wiring code.
|
||
|
||
### Commits made
|
||
|
||
```
|
||
5d91d1a docs: add Memory Graph View repository analysis and design
|
||
f488a8d feat(memory): add pure edge-derivation logic for Memory Graph View
|
||
366bdbf feat(memory): add Memory Graph API — GET /api/memory/graph + manual links
|
||
```
|
||
|
||
---
|
||
|
||
## Session 1 — Analysis and design (Phase 0)
|
||
|
||
- Produced `docs/memory-graph-analysis.md` (19-section repo inventory) and `docs/memory-graph-design.md` (architecture, API, UI, performance, security, migration/rollback, testing strategy) purely from research — no code touched, no dependencies installed, no database changed, nothing committed at the time (the docs commit itself happened at the start of Session 2, once implementation was approved).
|
||
- User approved the design and requested implementation with: Cytoscape.js, a dedicated Memory Graph module, automatic semantic relationships, manual relationship editing, a beta feature flag, no breaking changes, small commits, Docker compatibility, passing tests, and incremental delivery with a build/test/preview/approval checkpoint after each milestone.
|