# Handover — Reasoning Graph Visualizer (`/admin/graph`)

**Branch**: `claude/visualize-reasoning-graph-nzTxj`
**Commit**: `8e4c081 feat(observability): 3D reasoning-graph visualizer at /admin/graph`
**Status**: Pushed to origin, **NOT merged** to main, **NOT yet PR'd**.
**Track**: v0.2 Foundation Hardening — Axis 3 (Observability / Explainability)
**Risk**: Low — read-only over existing data, admin-gated, no domain coupling.

---

## TL;DR for the next Claude Code session

You are picking up a feature that is **fully implemented, tested, pushed, but
not merged**. Local-first verification was blocked because the user's machine
(`C:\Project\James-RAG-Evol-v010`) had not yet pulled this branch when they
hit `/admin/graph` in the browser and got 404.

**Your immediate job**: help the user (a) pull this branch locally,
(b) restart their Uvicorn, (c) verify the page loads. Once that works,
optionally help them fast-forward merge into main and push.

**Do NOT** start a redesign or add features. The implementation is closed.

---

## What was built

A new admin-only page `/admin/graph` that:

1. Renders every JAMES wiki entity as a 3D point inside a soft-ball sphere.
2. Renders every ontology relation as a line between two points.
3. Force-directed layout — link strength ∝ `min(deg(s), deg(t))` so
   densely-connected entities drift together; custom radial spring pulls
   nodes toward a sphere shell. Drag to rotate 360°, scroll to zoom.
4. Page-internal query bar — when the user submits a question, the
   `/query/` response's `graph_paths: List[str]` is parsed client-side
   and traversed edges are pulsed with a moving cyan sprite + 4 s afterglow.

Framing: this is **observability infrastructure**, not a vertical UI.
It addresses `frontend/HANDOVER_WEB_UI.md` priority #3 ("Graph-RAG strength
not visible"). It is admin-gated and reads only data that already exists
(`wiki/entity/**/*.md` frontmatter + the existing `/query/` response).
**No domain code, no retrieval/ranking changes, no ontology changes.**

---

## Files in this commit

```
 CHANGELOG.md                 |  +40   modified — [Unreleased] v0.2 entry
 core/graph_snapshot.py       | +228   NEW backend snapshot builder
 frontend/admin.html          |   +3   modified — sidebar link
 frontend/graph.html          | +348   NEW page
 frontend/static/graph.js     | +593   NEW 3D renderer + path parser + pulse
 frontend/static/i18n.js      |  +50   modified — graph.* keys (en + ko)
 server_llmwiki.py            |  +54   modified — 2 new routes
 tests/test_graph_snapshot.py | +332   NEW (15 tests, all pass)
 8 files changed, 1648 insertions(+)
```

### Key code locations
- Backend snapshot builder: `core/graph_snapshot.py:build_snapshot`
  (8.4 KB; sibling to `core/graph_engine.py` 15.8 KB so the 20 KB module
  gate stays safe)
- Routes: `server_llmwiki.py:195` (HTML) and `server_llmwiki.py:2463` (JSON)
- Path-string parser: `frontend/static/graph.js:parsePath`
  (handles `"A -[REL(w=0.8)]→ B -[REL(w=0.6)]→ C"`)
- Sphere force: `frontend/static/graph.js:radialBallForce`

---

## Merge readiness — verified clean

Run on the sandbox at the time of this handover:

```bash
git merge-base origin/main HEAD                    # 8e875cf9f2... (= tip of main)
git log --oneline HEAD ^origin/main                # 8e4c081 (1 commit ahead)
git log --oneline origin/main ^HEAD                # (empty — main has not moved)
git merge-tree $(git merge-base origin/main HEAD) origin/main HEAD
                                                   # → "merged" (no conflict markers)
```

Conclusion: **fast-forward is safe**. No conflicts.

---

## Verification recipe (give this to the user)

### 0. Pre-flight — confirm local has the branch

In PowerShell at `C:\Project\James-RAG-Evol-v010`:

```powershell
git fetch origin
git checkout claude/visualize-reasoning-graph-nzTxj
git pull origin claude/visualize-reasoning-graph-nzTxj
git log --oneline -3
```

The top line must read `8e4c081 feat(observability): 3D reasoning-graph ...`.
If it doesn't, the local clone never picked up the branch — investigate.

### 1. Smoke-test the new files exist locally

```powershell
Test-Path frontend\graph.html        # → True
Test-Path frontend\static\graph.js   # → True
Test-Path core\graph_snapshot.py     # → True
Select-String -Path server_llmwiki.py -Pattern "/admin/graph"
                                     # → 2 hits (line 195, 2463)
```

### 2. Run unit tests (FastAPI not required — tests read source as text)

```powershell
python -m unittest tests.test_graph_snapshot -v
```
Expected: `Ran 15 tests in 0.0Xs / OK`.

### 3. Restart Uvicorn

The user's earlier log showed Uvicorn was running with `WatchFiles`
reloader. New files should hot-reload, but a **manual Ctrl+C + restart**
is the safe path for new top-level routes:

```powershell
# In the server PowerShell window:
# Ctrl+C
python server_llmwiki.py
```

### 4. Confirm the route over HTTP

In a *second* PowerShell window:

```powershell
curl.exe -i http://127.0.0.1:8000/admin/graph
```

Expected: `HTTP/1.1 200 OK` + HTML body. If 404 here while the route is
present in source, something is wrong with how Uvicorn loaded the file
(usually stale `__pycache__` — `Remove-Item -Recurse __pycache__` and restart).

### 5. Browser test

Open `http://127.0.0.1:8000/admin/graph`. Expected flow:
1. Admin login modal appears (if no `localStorage.james_api_key`).
2. After login, sphere of dots renders within ~2 s (185 entities per
   the user's startup log).
3. Drag → rotate. Scroll → zoom. Hover → tooltip with name + degree.
4. Type any question into the bottom bar → answer card appears top-right
   AND a cyan sprite pulses along edges in `graph_paths`.

---

## After verification — merging to main

If the user wants this on main, fast-forward is safe. Confirm with the
user first (CLAUDE.md self-merge is allowed but the user should approve).

```bash
git checkout main
git pull origin main
git merge --ff-only claude/visualize-reasoning-graph-nzTxj
git push origin main
```

`--ff-only` is the safety net — if main has moved since this handover
was written, the command refuses to run instead of producing a merge
commit. In that case, rebase the feature branch first:

```bash
git checkout claude/visualize-reasoning-graph-nzTxj
git fetch origin
git rebase origin/main
# resolve any conflicts, then:
git push --force-with-lease origin claude/visualize-reasoning-graph-nzTxj
```

CLAUDE.md requires bench numbers in the PR/commit message for any change
touching `core/retrieval`, `core/graph`, or `core/reasoning`. **This commit
does not touch any of those** (snapshot is in `core/graph_snapshot.py`
which is a NEW sibling module), so bench numbers are not required —
but it doesn't hurt to record an unchanged STEP 7 baseline if the user
wants extra assurance.

---

## Known limitations / out of scope

These were intentionally deferred. Do NOT add them in a follow-up unless
the user explicitly asks:

- Editing entities/relations from the graph view (read-only by design).
- LOD / server-side clustering for graphs > 2,000 nodes.
- Custom GLSL shader for the pulse (sprite tween was chosen for
  maintainability).
- Vendoring CDN libs (`three`, `3d-force-graph`, `d3-force-3d`) into
  `frontend/static/vendor/` for air-gapped deploys.
- Mobile / touch tuning beyond OrbitControls defaults.
- Multi-trace simultaneous replay or a time-scrubber.
- Showing sensitive nodes/edges (`include_sensitive=1` is locked off
  server-side at `server_llmwiki.py:2487` until an elevated role is defined).

---

## v0.2 mother-platform compliance checklist

- [x] No domain features added (legal/food/retail/travel) — pure
      observability.
- [x] No file in `core/` exceeds 20 KB. New file
      `core/graph_snapshot.py` = 8.4 KB. `core/graph_engine.py`
      untouched at 15.8 KB.
- [x] Self-evolution gate untouched.
- [x] PolicyEngine untouched.
- [x] No new module / no new trust zone (snapshot is a read sibling
      to existing graph engine).
- [x] No bench numbers required (no `core/retrieval`, `core/graph`,
      `core/reasoning` changes).
- [x] Branch named per convention: `claude/visualize-reasoning-graph-...`.
      (`feat/v0.3-...` not used because v0.3 has not opened yet.)
- [x] Conventional commit prefix: `feat(observability):`.
- [x] Admin-only — `/admin/graph` and `/admin/graph/snapshot` both go
      through `_require_admin(api_key, role)`.
- [x] Sensitive data filter: nodes with `sensitivity == "sensitive"`
      and edges where `RELATION_TYPES[*].sensitive == True` are dropped
      by default.

---

## If the user asks "this is my idea, not a domain feature" challenges

The user's prompt called out vector entities, graph paths, and animated
reasoning traces. None of those are domain-specific — they are properties
of the platform itself. The visualizer renders the **mother platform's
own internal structure**. It will work identically for any future domain
pack because the snapshot reads whatever entities exist.

If a reviewer pushes back on this PR/merge, point them at:
- `frontend/HANDOVER_WEB_UI.md:85-103` (gap explicitly documented)
- `ROADMAP.md` Axis 3 (Observability)
- This handover's compliance checklist above.

---

## Quick reference — actions you may need to take

| Situation | Action |
|---|---|
| User says "404 on /admin/graph" | Verify branch checked out + Uvicorn restarted (recipe above). |
| User says "JS console error: ForceGraph3D undefined" | Check internet — the page loads three/3d-force-graph from unpkg.com CDN. If air-gapped, see "Out of scope" → vendoring follow-up. |
| User says "snapshot is empty {nodes: []}" | They are scoped to `source_type=prod` but their wiki is under `wiki/entity/test/`. Toggle the Source dropdown in the sidebar to `test`. |
| User says "pulse doesn't fire" | Check `/query/` response in browser devtools — if `graph_paths: []` then the reasoner didn't traverse any edges (low-confidence / direct LLM answer). Try a question that hits multiple wiki entities. |
| User says "I want to merge to main" | Use the `--ff-only` recipe above after confirming. |
| User says "let's add feature X" | Re-read CLAUDE.md `## Critical rules`. v0.2 is mother-hardening; new features go to v0.3. Push back. |

---

## End-state contract

On a fully-pulled local clone with Uvicorn running:

- `GET /admin/graph` → 200, serves `frontend/graph.html`
- `GET /admin/graph/snapshot?source_type=prod&api_key=...` (admin role)
  → 200, JSON `{nodes: [...], edges: [...], meta: {...}}`
- `GET /admin/graph/snapshot` without admin role → 403
- `python -m unittest tests.test_graph_snapshot` → 15 / 15 OK
- Sidebar in `/admin` has a new link "Reasoning Graph" / "추론 그래프"
- `core/graph_engine.py` mtime unchanged from before this branch
