feat(graph): in-app corpus citation graph (/graph) — Phase 1 #113
Reference in New Issue
Block a user
Delete Branch "worktree-corpus-graph"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
מה ולמה
חיים שאל אם אפשר להשתמש ב‑Obsidian Graph View על מאגרי הקורפוס. ההכרעה: להטמיע גרף נייטיב ב‑web‑ui במקום לייצא ל‑vault חיצוני — ייצוא חיצוני יוצר עותק מקביל של הקורפוס שמתיישן (כשל‑השורש ש‑G2 בא לייבש). גרף נייטיב קורא את ה‑DB החי → אפס drift, ומחובר לדפים הקיימים.
התובנה: כל קשתות הגרף כבר קיימות בטבלאות — ה‑PR רק חושף אותן.
דף
/graph(Phase 1)case_law) + נקודות‑נושא (subject_tags) + נקודות‑תחום (practice_area).cites(precedent_internal_citations) ·same_chain(case_law_relations) ·tagged/in_area(שיוך נושא/תחום).GROUP BYעלidx_pic_target(לא N+1)./precedents/[id].Backend (read‑only — G2)
web/graph_api.py— מודלי Pydantic (CorpusGraph/GraphNode/GraphEdge→ OpenAPI מפיק טיפוסים אמיתיים, UI2) + הרכבת‑SQL מעל ה‑pool המשותף. אין כתיבה (8×conn.fetch, 0× INSERT/UPDATE/DELETE).web/app.py—GET /api/graph/corpus,GET /api/graph/node/{id}/neighborhood, שניהם עםresponse_modelמפורש.practice_areaמאומת מול ה‑enum הסגור (G5).Frontend
react-force-graph-2d(canvas/d3‑force), נטען דרךnext/dynamicssr:false./graphpage + כניסה לניווט ·graph.ts(TanStack hooks) · filter/node panels · highlight של שכנים ב‑hover/selection · טיפול שגיאה מפורש (UI4).אימות
ANY(uuid[])+ BFS. הצומת המצוטט ביותר (עע"מ 317/10) — 7 ציטוטים נכנסים = 7 שכנים בעומק 1. ✓npm run lint+npm run buildעוברים;/graphבטבלת ה‑routes. ✓py_compileעלgraph_api.py+app.py. ✓Invariants
db.get_pool()בלבדpractice_area/sourceמסוננים בשרת ב‑WHERECorpusGraphמפורש בשני ה‑endpointsPhase 2 (לא ב‑PR זה)
נקודות‑הלכה + קשתות
corroborates/equivalent, כבר מגודרות מאחוריnode_types(ללא שינוי חוזה). זו הגרנולריות שחיים יחליט עליה דרך ה‑toggle אחרי שיראה.🤖 Generated with Claude Code
Native, Obsidian-graph-view-like network of the precedent corpus, rendered in web-ui from a read-only projection of the live DB. Replaces the idea of exporting to an external Obsidian vault (which would be a parallel, drifting copy of the corpus — the exact root cause G2 forbids). The graph edges already existed in the data model; this only surfaces them: nodes = precedents (case_law) + synthesized topic/practice-area hubs; edges = cites (precedent_internal_citations) + same_chain (case_law_relations) + tagged/in_area (subject_tags / practice_area membership). Node size = incoming-citation count (index-backed GROUP BY on idx_pic_target). Click a node → local-graph neighborhood focus; panel deep-links to /precedents/[id]. Backend (read-only, SELECT only — G2): - web/graph_api.py — Pydantic models (CorpusGraph/GraphNode/GraphEdge, so OpenAPI emits real types — UI2) + SQL assembly over the shared db.get_pool(). - web/app.py — GET /api/graph/corpus, GET /api/graph/node/{id}/neighborhood, both with explicit response_model. practice_area validated against the closed enum (G5); both endpoints write nothing. Frontend: - react-force-graph-2d (canvas/d3-force), loaded via next/dynamic ssr:false. - /graph page + nav entry; graph.ts TanStack hooks; filter panel (practice_area / source / min-citations / search / node-type toggles), node detail panel, hover+selection neighborhood highlight. Explicit error handling (UI4). Not a retrieval path (03-retrieval): returns graph topology, never ranked search results. Halacha nodes + corroboration/equivalence edges are Phase 2, already gated behind the node_types param (no contract change needed). SQL validated read-only against the live DB (142 precedents, 85 resolved citations, JSONB tag expansion, ANY(uuid[]) edge + BFS queries). web-ui lint + build pass; /graph in the route table. Invariants: keeps G2 (single source of truth — live projection, no parallel store), G5 (corpus separation filtered server-side), UI2 (response models), UI4 (no swallowed UI errors). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>