Replace Ruby tag-index CLI with Graphify knowledge graph
Graphify eliminates the need for the Ruby/SQLite tag index (Step 2 on the critical path). It provides entity extraction and relationship inference over vault docs via a local SLM, and deterministic code graphs for projects via AST — both at significantly lower complexity. This commit records the architecture decision and evaluation that justifies the pivot. Vault graphs still require author-maintained summaries (human router hint), but tagging discipline is now optional. Project code graphs are free (no LLM cost). Stale-node drift requires periodic `--force` rebuilds; strategy documented for SessionStart hooks. The three-layer memory system remains unchanged: episodic (memsearch), knowledge (Graphify + vault), integration (custom glue). See 06-graphify-evaluation.md for the open questions and limitations.
This commit is contained in:
parent
54b00364cd
commit
2bc95ed30e
|
|
@ -8,66 +8,149 @@ skills.
|
|||
> This is a build outline, not the implementation plan. The next session should turn this into
|
||||
> a proper implementation plan (writing-plans skill) and execute it.
|
||||
|
||||
**Architecture decision (2026-06-03):** Graphify replaces the Ruby/SQLite tag index.
|
||||
The Ruby CLI (old Step 2) is skipped. Graphify handles both knowledge-graph queries over the
|
||||
vault and free AST-based code graphs per project. The deferred QMD semantic layer (old Step 7)
|
||||
is also skipped — Graphify covers it without vectors. See `06-graphify-evaluation.md` for
|
||||
the full rationale.
|
||||
|
||||
---
|
||||
|
||||
## Part A — Build order (human builder's path)
|
||||
|
||||
Build bottom-up: the vault and CLI first (usable standalone), then the hooks, then the plugin
|
||||
that packages it.
|
||||
Build bottom-up: the vault and Graphify first (usable standalone), then the hooks, then the
|
||||
plugin that packages it.
|
||||
|
||||
### Step 1 — Vault skeleton & conventions
|
||||
- Decide the vault location (default: a synced home dir, e.g. `~/brain`; symlink into
|
||||
`~/.claude/memory` only if a tool insists). **Vault is the single source of truth.**
|
||||
- Write `CONVENTIONS.md` in the vault: the frontmatter contract and the tag namespaces
|
||||
(`tool/`, `client/`, `domain/`, `convention/`, `scope/`). This doubles as the spec the
|
||||
skills teach the AI.
|
||||
- Seed a few real notes (e.g. `tool/semrush` global + a `client/<x>` project note) to test
|
||||
against.
|
||||
- Write `CONVENTIONS.md` in the vault: the frontmatter contract. The required fields are
|
||||
`summary` (one-line, author-written — this is the human-readable router hint Graphify
|
||||
does not generate) and `scope` (`scope/global` or `scope/project`). Tags are now
|
||||
supplementary, not the primary query mechanism.
|
||||
- Seed a few real notes (e.g. `tool/semrush`, `client/<x>`) to use as extraction test cases
|
||||
in Step 2.
|
||||
|
||||
### Step 2 — The Ruby tag-index CLI (the core build)
|
||||
- Ruby project, `sequel` + `sqlite3` gems. SQLite file in a cache dir (e.g.
|
||||
`~/.cache/memory-index/index.sqlite`) — disposable, never synced.
|
||||
- **Schema / migrations**: `files(id, path, mtime, summary, scope)`, `tags(id, name)`,
|
||||
`files_tags(file_id, tag_id)`; `many_to_many` between files and tags.
|
||||
- **Frontmatter parser**: read YAML frontmatter from a `.md` file → `{summary, scope, tags[]}`.
|
||||
Fail loudly (or quarantine) notes missing the required `summary`/tags so the contract is
|
||||
enforced.
|
||||
- **Commands** (a thin CLI dispatch — `thor`/`optparse` or plain):
|
||||
- `index update --since <iso8601|auto>` — find `.md` with `mtime >` last-cache-time, upsert
|
||||
them; **reconcile**: delete rows for paths no longer on disk.
|
||||
- `index update --file <path>` — upsert a single file (used by the write hook); prune if the
|
||||
path is gone.
|
||||
- `index update --rebuild` (default off) — drop & rebuild from a full vault scan.
|
||||
- `index query --client X --tool Y [--domain Z] [--scope global]` — AND the filters; output
|
||||
JSON lines of `{path, summary, tags[]}` (option C).
|
||||
- `index tags [--namespace tool/]` — enumerate the vocabulary (virtual index).
|
||||
- Store `last_cache_time` (a tiny meta table or a stamp file) so `--since auto` works.
|
||||
- **Tests**: create/edit/delete/rename a note, assert the index reflects it; rebuild equals
|
||||
incremental.
|
||||
### Step 2 — Graphify + Ollama setup (the knowledge graph layer)
|
||||
|
||||
This replaces the Ruby tag-index CLI. Graphify builds a knowledge graph over vault docs via a
|
||||
local SLM, and a free code graph over project files via AST.
|
||||
|
||||
#### 2a — Install Graphify
|
||||
```
|
||||
pip install graphify # or follow current install docs
|
||||
```
|
||||
Verify: `graphify --version`
|
||||
|
||||
#### 2b — Configure Ollama for extraction
|
||||
The 2024 approach (Modelfile with `PARAMETER num_ctx`) still works but the preferred
|
||||
programmatic method is now per-request via the API (Graphify supports this via
|
||||
`GRAPHIFY_OLLAMA_NUM_CTX`):
|
||||
|
||||
```bash
|
||||
# Set in your shell profile or service env:
|
||||
OLLAMA_FLASH_ATTENTION=1 # 30-50% VRAM savings on KV cache — always set this
|
||||
GRAPHIFY_OLLAMA_NUM_CTX=8192 # 8K is sufficient for vault notes (200-2000 words)
|
||||
# and leaves comfortable headroom for prompt overhead
|
||||
```
|
||||
|
||||
To verify context is being used: `ollama ps` shows allocated context after first call.
|
||||
|
||||
Available models (as of 2026-06-03, in order of interest):
|
||||
- `gemma4:e2b` — 7.2 GB, ~2B effective params, 128K native window, fast; **start here**
|
||||
- `qwen3.5:2b` — 2.7 GB, smallest, good fallback if VRAM is constrained
|
||||
- `gemma4:e4b` — 9.6 GB, more capable, slower
|
||||
|
||||
#### 2c — Model benchmarking (before committing to a model)
|
||||
|
||||
Run a small extraction test across all local models plus the three Claude API models. The goal
|
||||
is to find the best speed/accuracy tradeoff for entity+relationship extraction from vault notes.
|
||||
|
||||
**Test set:** 5–10 representative vault notes spanning different note types (tool note, client
|
||||
note, convention note, domain note). Include one note that is dense with relationships.
|
||||
|
||||
**What to measure per model:**
|
||||
1. Extraction speed (tokens/sec or wall-clock time per note)
|
||||
2. Entity quality: are the right concepts extracted? Any hallucinated entities?
|
||||
3. Relationship quality: are edges plausible and correctly typed? Missing relationships?
|
||||
4. Confidence tag accuracy: are `INFERRED` vs `AMBIGUOUS` edges appropriately flagged?
|
||||
|
||||
**Models to test:**
|
||||
| Model | Backend | Notes |
|
||||
|---|---|---|
|
||||
| `gemma4:e2b` | Ollama local | Primary candidate — fast, 7.2 GB |
|
||||
| `qwen3.5:2b` | Ollama local | Smallest, fastest |
|
||||
| `gemma4:e4b` | Ollama local | Highest local quality |
|
||||
| `claude-haiku-4-5` | Claude API | Baseline API option |
|
||||
| `claude-sonnet-4-6` | Claude API | Mid-tier reference |
|
||||
| `claude-opus-4-8` | Claude API | **Gold standard** — judge quality against this |
|
||||
|
||||
Run using subagents in Claude Code: dispatch one agent per model, each extracting the same
|
||||
test set, return structured JSON of entities+relationships. Review god-node quality in
|
||||
`GRAPH_REPORT.md` after each run. Use Opus output as the scoring rubric for the local models.
|
||||
|
||||
**Decision rule:** Choose the fastest local model whose entity/relationship quality is
|
||||
"close enough" to Opus (subjective; likely Gemma4:e2b or Gemma4:e4b based on the description).
|
||||
API models are a fallback option for high-stakes notes, not the default.
|
||||
|
||||
#### 2d — Initial vault graph build
|
||||
```bash
|
||||
graphify extract --path ~/brain --backend ollama --model gemma4:e2b \
|
||||
--token-budget 512 --max-concurrency 2
|
||||
```
|
||||
Tune `--token-budget` (semantic chunk size) and `--max-concurrency` based on VRAM headroom.
|
||||
Review `GRAPH_REPORT.md` — check god nodes make sense (they should be your most-connected
|
||||
tools, clients, and domain concepts).
|
||||
|
||||
#### 2e — Per-project code graphs (free, no model needed)
|
||||
For each client project:
|
||||
```bash
|
||||
graphify extract --path ~/projects/<client>/<project> --no-docs
|
||||
```
|
||||
`--no-docs` skips LLM extraction; only AST is run. Zero token cost. Add `--update` on
|
||||
subsequent runs; use `--force` when you've deleted files (to clear stale nodes).
|
||||
|
||||
Store each project's `graphify-out/` alongside the project, or in a configurable cache dir.
|
||||
Keep per-project graphs separate — do not merge client projects into one graph.
|
||||
|
||||
### Step 3 — Hooks (maintenance + retrieval)
|
||||
See Part C for exact mapping. Implement as small shell wrappers that call the Ruby CLI:
|
||||
- `pre-tool-memory` style **PostToolUse** updater (write path → `index update --file`).
|
||||
- **SessionStart** reconcile + inject (index overview + resolved `convention/*` files + journal
|
||||
pointer).
|
||||
- **SessionEnd** journal appender (daily note with pointers).
|
||||
|
||||
Hooks now call Graphify instead of the Ruby CLI.
|
||||
|
||||
- **PostToolUse** (on Write/Edit of vault `.md`): `graphify update --file <path>` to merge
|
||||
the changed note into the vault graph. Note: Graphify's `--update` does not prune deleted
|
||||
nodes — a periodic `--force` rebuild (e.g. weekly, or triggered by SessionStart stale
|
||||
check) is needed to clear ghost nodes.
|
||||
- **SessionStart**: reconcile (check vault graph mtime vs last rebuild; trigger `--force` if
|
||||
stale threshold exceeded) + inject context (query MCP server for god nodes + inject
|
||||
`convention/*` note summaries + journal pointer).
|
||||
- **SessionEnd**: append daily journal note with pointers to vault notes touched.
|
||||
|
||||
**Stale-node mitigation strategy:** track last `--force` rebuild time in a stamp file
|
||||
(e.g. `~/.cache/graphify/vault-rebuild.stamp`). SessionStart hook triggers `--force` if
|
||||
older than N days (7 is a reasonable starting point).
|
||||
|
||||
### Step 4 — Episodic layer (memsearch)
|
||||
- Install memsearch (`/plugin marketplace add zilliztech/memsearch`, then `plugin install
|
||||
memsearch`), local to start. Verify daily memory files appear after a few conversations.
|
||||
- Decide whether memsearch indexes our session-end journal notes or its own capture (likely its
|
||||
own; our journal can point into the knowledge vault).
|
||||
- Graphify does **not** replace memsearch here. Time-anchored semantic queries ("what was I
|
||||
working on last Tuesday?") are better served by vector similarity over session logs.
|
||||
|
||||
### Step 5 — Sync
|
||||
- Pick **git** (versioned, hourly push/pull) or **Syncthing** (continuous, zero-thought) for the
|
||||
vault → VPS. Configure on each machine. **Do not sync the SQLite/Milvus caches.**
|
||||
- Pick **git** (versioned, hourly push/pull) or **Syncthing** (continuous, zero-thought) for
|
||||
the vault → VPS. Configure on each machine.
|
||||
- **Do not sync** the Graphify `graphify-out/` directories, Milvus caches, or the Ollama
|
||||
models. Graphs are rebuilt per machine from the vault (the single source of truth).
|
||||
|
||||
### Step 6 — Package as a global plugin (Part D)
|
||||
- Wrap Steps 2–3 into a Claude Code plugin with skills; install at user level.
|
||||
|
||||
### Step 7 (deferred) — QMD semantic layer
|
||||
- Only when structured-only retrieval misses notes you know exist: `qmd` over the vault,
|
||||
optionally as an MCP server the AI can query.
|
||||
### Step 7 — (SKIPPED) QMD semantic layer
|
||||
Covered by Graphify. The knowledge-graph approach provides structured semantic retrieval
|
||||
without vectors. Only revisit if Graphify's graph queries prove insufficient for a use case
|
||||
where embedding similarity would outperform graph traversal.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -82,74 +165,109 @@ project-ephemeral state (that's the episodic layer / project files). Concretely:
|
|||
|
||||
**What conventions to follow when writing** (enforced by the frontmatter contract):
|
||||
- One concept per note; keep notes small (the L1 "under ~200 lines / one topic" discipline).
|
||||
- **Required frontmatter**: a one-line `summary` (written now, not deferred), `scope/global`
|
||||
or `scope/project`, and namespaced tags (`tool/…`, `client/…`, `domain/…`, optionally
|
||||
`convention/…`).
|
||||
- **Scope rule**: default new tool/domain knowledge to `scope/global`; mark `scope/project` +
|
||||
a `client/<name>` tag when it's specific to how a client uses something.
|
||||
- **Required frontmatter**: a one-line `summary` (written now, not deferred — this is the
|
||||
human-authored router hint; Graphify extracts entities but does not write summaries),
|
||||
and `scope/global` or `scope/project`.
|
||||
- **Scope rule**: default new tool/domain knowledge to `scope/global`; mark `scope/project`
|
||||
when it's specific to how a client uses something.
|
||||
- **Write only to the vault**, never silently into a project repo.
|
||||
- **Promotion to global** (project → global) and **consolidation** are *not* done inline —
|
||||
they happen in the reorganize step (Part C), in plan mode, for human review.
|
||||
- **Promotion to global** and **consolidation** happen in the reorganize step (Part C), in
|
||||
plan mode, for human review.
|
||||
|
||||
### When & how the AI QUERIES
|
||||
- **At session start**: the injected index overview + resolved `convention/*` files tell it
|
||||
what's available without reading everything.
|
||||
- **On demand, during a task**: when the task touches a tool/client/domain, run
|
||||
`index query --tool semrush --client sesame3g` → get `{path, summary, tags}` → open only the
|
||||
files whose summary matches. This is the progressive-disclosure loop that keeps tokens low.
|
||||
- **Cross-client lookups**: query by `--tool` alone (omit client) to surface what was learned
|
||||
with *any* client; tags in the output show provenance.
|
||||
- **"What happened" questions** go to the **episodic layer (memsearch)** in natural language,
|
||||
not the tag index.
|
||||
|
||||
**Knowledge queries (vault graph)** via Graphify CLI (Bash tool):
|
||||
- `graphify query "how do we authenticate with SEMrush?"` — semantic graph search
|
||||
- `graphify path "client/sesame3g" "tool/semrush"` — how these two concepts connect
|
||||
- `graphify explain "convention/seo-workflow"` — node deep-dive with neighbors
|
||||
- Add `--budget N` to cap answer size (default ~2000 tokens); `--dfs --budget N` for bounded traversal
|
||||
- Add `--graph <path>` to query a specific project's `graph.json` instead of the vault graph
|
||||
|
||||
**At session start**: god-node summary (injected by hook from `GRAPH_REPORT.md`) gives the
|
||||
architecture spine — what are the highest-connectivity concepts in the vault. The AI then
|
||||
queries specific nodes on demand rather than reading everything.
|
||||
|
||||
**On demand, during a task**: run `graphify query` when the task touches a tool/client/domain.
|
||||
The graph returns relevant node clusters; the AI opens only the vault notes whose `summary`
|
||||
matches the task. The `summary` frontmatter field remains the human-written progressive-disclosure hint.
|
||||
|
||||
**Cross-client lookups**: `graphify query "tool semrush"` returns all nodes connected to the
|
||||
SEMrush concept, regardless of which client project they came from. Graph edges show provenance.
|
||||
|
||||
**"What happened" questions** go to **memsearch** in natural language — not the graph.
|
||||
|
||||
---
|
||||
|
||||
## Part C — Hooks & CRUD mapping
|
||||
|
||||
CRUD over the knowledge vault, and which hook/command services each operation:
|
||||
|
||||
| Operation | Trigger | Mechanism |
|
||||
|-----------|---------|-----------|
|
||||
| **Create** | AI writes a new note | AI writes `.md` (Write tool) → **PostToolUse** hook → `index update --file` |
|
||||
| **Read / query** | AI needs knowledge | AI calls `index query …` (no hook; on-demand CLI) |
|
||||
| **Update** | AI/user edits a note | AI edit → **PostToolUse** hook → `index update --file`; user edit → **SessionStart** reconcile |
|
||||
| **Delete / rename** | note removed/renamed | covered by `--file` (prune) on AI ops; by **SessionStart** reconcile (prune vanished paths) on manual ops |
|
||||
| **Inject** | new session/sub-agent starts | **SessionStart** hook injects index overview + resolved `convention/*` + journal pointer |
|
||||
| **Create** | AI writes a new vault note | AI writes `.md` (Write tool) → **PostToolUse** hook → `graphify update --file` |
|
||||
| **Read / query** | AI needs knowledge | AI runs `graphify query` / `graphify explain` via Bash (no hook; on-demand) |
|
||||
| **Update** | AI/user edits a note | AI edit → **PostToolUse** → `graphify update --file`; user edit → **SessionStart** reconcile |
|
||||
| **Delete / rename** | note removed/renamed | `graphify update --file` prunes AI-deleted paths; **SessionStart** triggers `--force` rebuild if stale (catches manual deletes) |
|
||||
| **Inject** | new session starts | **SessionStart** hook: god-node summary + `convention/*` summaries + journal pointer |
|
||||
| **Journal** | session ends | **SessionEnd** hook appends a dated journal note with pointers |
|
||||
| **Reorganize** | periodic, user-invoked | `reorganize memory` run in **plan mode**: dedupe, merge, split, re-tag, promote `scope/global`, rebuild index — human approves |
|
||||
| **Reorganize** | periodic, user-invoked | `reorganize memory` in **plan mode**: dedupe, merge, split, re-scope, then `graphify --force` to rebuild clean graph — human approves |
|
||||
| **Stale graph rebuild** | SessionStart stale check | If rebuild stamp > N days old: `graphify --force` on vault; resets stamp |
|
||||
|
||||
Hooks are thin shell wrappers over the Ruby CLI so the logic lives in one place.
|
||||
Hooks are thin shell wrappers; the logic lives in Graphify.
|
||||
|
||||
**Hooks summary:**
|
||||
- **SessionStart** — reconcile (`index update --since auto`) + inject context.
|
||||
- **PostToolUse** (on `Write`/`Edit` of vault `.md`) — `index update --file`.
|
||||
- **SessionStart** — stale check + `--force` if needed, then inject (god nodes + conventions + journal pointer).
|
||||
- **PostToolUse** (on `Write`/`Edit` of vault `.md`) — `graphify update --file`.
|
||||
- **SessionEnd** — append daily journal note.
|
||||
- (memsearch brings its own `UserPromptSubmit` + capture hooks for the episodic layer.)
|
||||
- (memsearch brings its own hooks for the episodic layer.)
|
||||
|
||||
---
|
||||
|
||||
## Part D — Claude Code plugin with skills (global install)
|
||||
|
||||
Goal: one global install so every project/machine knows the vault, conventions, hooks, and CLI.
|
||||
Goal: one global install so every project/machine knows the vault, conventions, hooks, and
|
||||
Graphify config.
|
||||
|
||||
**Plugin contents:**
|
||||
- **Hooks** registered in settings: SessionStart, PostToolUse, SessionEnd (the shell wrappers
|
||||
from Part C), pointed at a configurable vault path + cache dir.
|
||||
- **The Ruby CLI** (the tag index) bundled or installed as a dependency, on `PATH`.
|
||||
- **Hooks** registered in settings: SessionStart, PostToolUse, SessionEnd (shell wrappers
|
||||
from Part C), pointed at a configurable vault path + Graphify output dir.
|
||||
- **Graphify CLI** on `PATH` — the AI uses the Bash tool to run `graphify query`,
|
||||
`graphify path`, `graphify explain` directly. No server process. Project-specific graphs
|
||||
are queried with `--graph <path>` pointing at the project's `graphify-out/graph.json`.
|
||||
- **Skills** (these carry the *know-how* to the model):
|
||||
- `memory-write` — when to record evergreen knowledge, the frontmatter contract, scope rules,
|
||||
"vault not repo." (Part B write section.)
|
||||
- `memory-query` — how/when to query the tag index vs the episodic layer; the progressive-
|
||||
disclosure loop; cross-client lookups. (Part B query section.)
|
||||
- `memory-reorganize` — the plan-mode consolidation/promotion procedure + guardrails.
|
||||
- **Config**: vault path, cache path, sync method — set once at user level.
|
||||
- `memory-write` — when to record evergreen knowledge, the frontmatter contract (summary
|
||||
required, scope rule), "vault not repo."
|
||||
- `memory-query` — how/when to use `graphify query` vs memsearch; god-node discipline;
|
||||
`--budget`/`--dfs` usage; cross-client lookups via `--graph`; progressive-disclosure
|
||||
via summary field.
|
||||
- `memory-reorganize` — the plan-mode consolidation/promotion procedure + guardrails;
|
||||
when to trigger `graphify --force` rebuild.
|
||||
- **Config**: vault path, Graphify output dir, Ollama model name, `num_ctx`, rebuild-stale
|
||||
threshold in days — set once at user level.
|
||||
- **Env vars** set by plugin: `OLLAMA_FLASH_ATTENTION=1`, `GRAPHIFY_OLLAMA_NUM_CTX=8192`,
|
||||
`GRAPHIFY_OLLAMA_KEEP_ALIVE=5`.
|
||||
|
||||
**Why a plugin + skills (not just CLAUDE.md):** hooks must be registered by the harness (the
|
||||
plugin does that), and the conventions are taught as skills so they load on demand without
|
||||
bloating every project's context. A single global install keeps the conventions themselves a
|
||||
single source of truth — edit the skill once, every project follows.
|
||||
**Why a plugin + skills (not just CLAUDE.md):** hooks must be registered by the harness;
|
||||
conventions taught as skills load on demand without bloating every project's context. A single
|
||||
global install keeps conventions a single source of truth.
|
||||
|
||||
**Open question for build time**: do the coding-`convention/*` notes live *in the vault* (data,
|
||||
edited freely, resolved by the SessionStart hook) while the *memory-system skills* live *in the
|
||||
plugin* (behavior, versioned)? Recommended: yes — conventions are data in the vault; the skills
|
||||
are the mechanism.
|
||||
**Convention notes placement:** coding `convention/*` notes live as **data in the vault**
|
||||
(edited freely, resolved by SessionStart hook); memory-system **skills live in the plugin**
|
||||
(behavior, versioned).
|
||||
|
||||
---
|
||||
|
||||
## Open questions (carried from 05-handoff.md + updated)
|
||||
|
||||
1. **Vault location** — `~/brain` (synced home dir)? Symlink into `~/.claude/memory`?
|
||||
2. **Sync mechanism** — git (versioned, hourly) vs Syncthing (continuous)?
|
||||
3. **Stale rebuild threshold** — how many days before SessionStart triggers `--force`?
|
||||
7 days is the starting guess; tune after observing drift in practice.
|
||||
4. **Per-project graph path** — how does the AI know which `graph.json` to pass to
|
||||
`--graph` for the current project? Convention: `<project-root>/graphify-out/graph.json`,
|
||||
injected by SessionStart hook if a project graph exists at that path.
|
||||
5. **Summary field ownership** — Graphify extracts entities but does NOT write summaries.
|
||||
The human (or AI, on vault note creation) must write the `summary` frontmatter. Confirm
|
||||
this discipline holds in practice.
|
||||
6. **Model benchmark results** — defer all model decisions until Step 2c benchmark is done.
|
||||
Don't hardcode `gemma4:e2b` until testing confirms it's the right pick.
|
||||
7. **memsearch + journal** — does memsearch index our SessionEnd journal notes, or only
|
||||
its own auto-capture? See `05-handoff.md:55-56`.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
# Graphify Evaluation: Fit for the Three-Source Memory System
|
||||
|
||||
> Evaluated: 2026-06-03. Source docs: `/docs/graphify/` (00–09 + external-tips).
|
||||
|
||||
---
|
||||
|
||||
## Summary Verdict
|
||||
|
||||
**Graphify is a strong fit for Layer 2 (knowledge) and a natural fit for project-file code analysis. It is NOT the right tool for Layer 1 (episodic/session logs), and it does not "connect" the three sources on its own — that bridge still needs custom glue.**
|
||||
|
||||
---
|
||||
|
||||
## What Graphify Actually Is
|
||||
|
||||
Graphify builds a knowledge graph from code and documents:
|
||||
|
||||
- **Nodes**: entities (functions, classes, concepts, topics)
|
||||
- **Edges**: typed relationships with confidence tags (`EXTRACTED` = deterministic AST, `INFERRED` = LLM, `AMBIGUOUS` = flagged)
|
||||
- **Storage**: `graph.json` + optional exports (Obsidian sidecar, GraphML, Cypher)
|
||||
- **Query**: semantic graph traversal (`query`, `path`, `explain`) — not vector/embedding search
|
||||
- **MCP server**: `graphify.serve` exposes `query_graph`, `get_node`, `shortest_path` as Claude tools
|
||||
|
||||
The core cost split:
|
||||
- **Code** → tree-sitter AST, 33 languages, deterministic, **zero LLM cost**
|
||||
- **Docs** → LLM entity extraction, can use local SLMs via Ollama (`--backend ollama`)
|
||||
|
||||
---
|
||||
|
||||
## Fit by Layer
|
||||
|
||||
### Layer 1: Episodic ("what happened") — memsearch / Milvus Lite
|
||||
|
||||
**Graphify is not the right tool here.**
|
||||
|
||||
Episodic queries are time-anchored semantic lookups: "what was I working on last Tuesday?", "what client did we discuss the Stripe integration for?" Vector similarity over session logs is well-matched to this. A knowledge graph of session entities would add build overhead without improving recall for timeline queries. **Keep memsearch.**
|
||||
|
||||
### Layer 2: Knowledge ("how do we…") — Obsidian vault + tag index
|
||||
|
||||
**Graphify is a strong candidate to replace or complement the tag index.**
|
||||
|
||||
**What it replaces:** The tagging-discipline requirement. Instead of manually adding `#tool/semrush #client/sesame3g` to every note, a local SLM (Ollama + Qwen2.5 7B or Phi-4 14B) extracts entities automatically. You get entity nodes and relationship edges from the vault without frontmatter authoring.
|
||||
|
||||
**What it adds that tags cannot:** Inferred cross-note relationships. Tags only filter ("give me notes tagged tool/semrush"). A graph can say "tool/semrush is connected to client/sesame3g via 3 project notes, and both reference convention/seo-workflow." That's a graph query, not a tag query.
|
||||
|
||||
**What it does NOT replace:** The `summary` column in the tag index schema, which is a human-written first-class router hint. Graphify extracts entities, not prose summaries. If summaries are the primary token-efficiency mechanism (see `02-system-design.md:98-102`), they need to remain author-controlled.
|
||||
|
||||
**Critical limitation:** Stale node drift. Graphify's `--update` does not prune deleted symbols/notes — you must `--force` rebuild to clear ghost nodes. The current design's SQLite is explicitly disposable-and-rebuildable from frontmatter; a Graphify graph is a snapshot with known drift. This is a real tradeoff.
|
||||
|
||||
**Recommendation:** Use Graphify's Obsidian sidecar export (`--obsidian`) to augment vault notes with auto-extracted entity metadata, and the MCP server for graph queries. Keep the summary frontmatter field as author-controlled. Consider Graphify as the **semantic enrichment step** that the deferred QMD layer was meant to fill — but without vectors.
|
||||
|
||||
### Project Files (code) — currently unindexed
|
||||
|
||||
**Graphify is a no-brainer add here.**
|
||||
|
||||
Project-file code analysis via AST is free (no LLM, no API key needed at runtime), deterministic, and produces exactly the call-graph / symbol map that "what does this codebase do?" queries need. Every client project gets a `graph.json` at zero ongoing cost. Query via the MCP server.
|
||||
|
||||
**Limitation from issue #1122:** `graphify extract` demands a backend credential even for pure-code extraction. Workaround: set a dummy key or use `--backend ollama` with a running (or not) Ollama instance — confirm at build time.
|
||||
|
||||
---
|
||||
|
||||
## The "Three Sources Connected" Question
|
||||
|
||||
Graphify does **not** natively connect three separate graphs. It builds one graph per ingestion run. To connect project files, vault, and session logs in one queryable surface, you'd need:
|
||||
|
||||
1. **A combined ingestion run** that points at all three source trees, OR
|
||||
2. **Three separate graphs** queried via three MCP tool instances and synthesized at the Claude layer
|
||||
|
||||
Option 1 risks cross-contamination of unrelated client projects. Option 2 is the safer model: each source has its own graph, and Claude stitches results from three MCP calls.
|
||||
|
||||
The more honest framing: Graphify replaces tags as the **vault entity index**, adds a **free code graph per project**, and leaves episodic (memsearch) alone. That's a meaningful simplification — you no longer need the Ruby/SQLite tag index CLI — but it's a layer replacement, not a unified connector.
|
||||
|
||||
---
|
||||
|
||||
## What Changes in the Current Design
|
||||
|
||||
| Current design | With Graphify |
|
||||
|---|---|
|
||||
| Ruby/Sequel/SQLite tag index CLI | Replaced by `graphify query` + MCP server |
|
||||
| Manual `#tool/ #client/ #domain/` tagging discipline | Auto-extracted by local SLM on vault ingestion |
|
||||
| `summary` frontmatter (author-written, router hint) | Unchanged — Graphify doesn't generate these |
|
||||
| QMD semantic layer (deferred) | Graphify fills this role without vectors |
|
||||
| memsearch (episodic) | Unchanged |
|
||||
| Project files (unindexed) | Free code graph via AST |
|
||||
|
||||
**What's saved:** The entire Ruby CLI build (Step 2 on the critical path in `04-build-plan.md`) could be skipped. That's significant scope reduction.
|
||||
|
||||
**What's added:** A local Ollama SLM running on this machine for vault doc extraction. This is a new infrastructure dependency. On low-RAM machines, a 7B model at 4-bit takes ~5 GB VRAM/RAM.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions This Creates
|
||||
|
||||
1. **Rebuild strategy**: How often do you rebuild the vault graph? On every SessionStart (expensive), on vault git commit (right cadence), or manually? The `--update` flag helps but stale-node drift means periodic `--force` rebuilds are needed.
|
||||
|
||||
2. **Model choice for vault extraction**: No official recommendation. Test Qwen2.5 7B and Phi-4 14B against a sample of your vault notes and review god-node quality before committing.
|
||||
|
||||
3. **Summary field fate**: If Graphify extracts entities but doesn't write summaries, does the human still maintain summary frontmatter? Or does a local SLM generate those too?
|
||||
|
||||
4. **Cross-client isolation**: Project code graphs should be per-client-project, not merged. How do you namespace them? Separate `graphify-out/` per project? Or a single graph with source labels as node properties?
|
||||
|
||||
5. **MCP server management**: Running `graphify.serve` for vault + N project graphs means N+1 MCP server instances. Is that manageable, or do you build a single meta-server?
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
| Topic | Source |
|
||||
|---|---|
|
||||
| Code AST extraction (free, deterministic) | `docs/graphify/03-ingesting-code-ast.md:9–100` |
|
||||
| Local SLM (Ollama) setup | `docs/graphify/05-local-models-and-backends.md:63–156` |
|
||||
| Query verbs + bounded traversal | `docs/graphify/06-querying-and-god-nodes.md:51–93` |
|
||||
| Stale node drift limitation | `docs/graphify/07-token-economics-and-updates.md:136–148` |
|
||||
| Token savings by repo size | `docs/graphify/external-tips.md:31–45` |
|
||||
| Tag index schema and role | `docs/memory-system/02-system-design.md:95–113` |
|
||||
| Graph rejection decisions (LightRAG) | `docs/memory-system/03-architecture-decisions.md:83, 145` |
|
||||
| Critical path (Ruby CLI = Step 2) | `docs/memory-system/04-build-plan.md` |
|
||||
| Open handoff questions | `docs/memory-system/05-handoff.md:47–60` |
|
||||
Loading…
Reference in New Issue