Source-level evaluation of ruflo (formerly claude-flow, roughly 71k stars) as a code-plus-concept knowledge graph. Verdict: rejected for this purpose. One of four source surveys behind knowledge-graph-research-2026-09-07.

Two corrections to this report

1. Ignore its Kuzu recommendation. The report recommends kuzu twice, including in its closing line. Kuzu was archived on 2025-10-10 after the Apple acquisition; the maintained fork is LadybugDB. Inline correction callouts are placed at both points below. The synthesis concludes no embedded graph database is needed at all. 2. Scope. Everything about swarms, agents and orchestration is out of scope here and was not assessed. This is not a verdict on ruflo as an orchestration framework.

For Agents

Investigation date: 2026-09-07. Repo HEAD examined: 277c7bc03ad192eef6d6f57e59ab7bab69a5728d (2026-09-05). Method: shallow clone plus local grep, gh api for tracker metrics. Why it was rejected: the “knowledge graph” is one graph_edges SQLite table with no node table and bare TEXT ids. There is no code parser anywhere; extraction is Claude reading files and writing prose. Every insert mints a new UUID then INSERT OR IGNORE against no unique constraint, so duplicate facts accumulate forever, and last_reinforced is never updated. Default search is a table scan; real HNSW is “not scheduled” (issue #2922). ruflo-knowledge-graph, ruflo-rag-memory, ruflo-ruvector and ruflo-agentdb are markdown prompt packs with zero code. ruvector’s Cypher executor returns an empty vector by design. Install is invasive: npx ruflo init installs hooks on nine events, spawns a daemon, and appends to the global ~/.claude/CLAUDE.md. cleanup never reverses that global edit. Worth borrowing: the roughly 15-line k-hop recursive CTE, if the index ends up on SQLite. Kept verbatim from the source report apart from this header, the two inline correction callouts, and the ## Related footer.

Date of investigation: 2026-09-07 Repo HEAD examined: 277c7bc03ad192eef6d6f57e59ab7bab69a5728d (2026-09-05, dream(memory): #3168 wire embedding-cosine into Sm...) Method: shallow clone + local grep; gh api for tracker metrics; two parallel sub-investigations (issue tracker/community, ruvector).

Evaluated against one narrow goal: a code-level and concept-level knowledge graph for a solo developer’s many repos (Rust, TypeScript, Python), traversable by Claude Code, with good update handling, ideally linked to an Obsidian vault. Everything about swarms, agents and orchestration is out of scope and is not assessed here.


0. Verdict up front

Do not adopt ruflo for this goal, and do not extract a component from it.

The “knowledge graph” is a single SQLite table, graph_edges, with no node table, no schema, no dedup constraint, and no update path. Ingestion has no code parser at all — entity extraction is an LLM reading files with Glob/Read/Grep and deciding what the entities are. Four of the five relevant “plugins” contain zero lines of code; they are markdown prompt packs.

What is real and better than expected: a correct SQLite recursive-CTE k-hop traversal, a correct personalized-PageRank power iteration, and a genuine turn toward honesty in the last six months (release titles like “HNSW status honesty fix”, published negative benchmark results). Credit where due. But the good parts are ~400 lines of ordinary TypeScript you would write yourself in a day, wrapped in a 123 MB, 941-open-issue framework that appends to your global ~/.claude/CLAUDE.md by default and does not remove that on uninstall.


1. Provenance and health

Rename

claude-flow → RuFlo branding landed 2026-03-05 with release v3.5.3 “RuFlo v3.5.3 — Bug Fixes & Branding”. From the release body:

Statusline: Claude Flow V3RuFlo V3 (single-line and multi-line modes) MCP branding (#1280) — identifies as ruflo instead of claude-flow

The GitHub repo rename itself fell between 2026-03-17 and 2026-03-24 (inferred from link forms in issue comments; no rename event retrievable). gh api repos/ruvnet/claude-flow returns "full_name": "ruvnet/ruflo" — the old path redirects. Both npm names are still published at the same version: npm view ruflo version and npm view claude-flow version both return 3.38.21.

Note: on npm, dist-tags latest, alpha and v3alpha all point at 3.38.21. “Alpha” and “stable” are the same build.

Cadence

Very high. Recent releases:

TagDateTitle
v3.38.212026-09-02MCP HTTP bridge memory-persistence fix
v3.38.202026-08-24statusline: stop pinning intelligence to a hardcoded 0%
v3.38.192026-08-22supersedes broken v3.38.17/v3.38.18 (…dead agentdb exports…)
v3.38.72026-08-12HNSW status honesty fix
v3.38.32026-08-12hooks intelligence --train actually trains
v3.12.32026-06MCP no longer emits 128-dim mock embeddings

The release titles are themselves the most damning evidence in the project: they read as a log of admissions that previously-shipped features reported results they did not compute.

Tracker metrics (verified via GitHub search API, 2026-09-07)

MetricValue
Issues total (excl. PRs)1,729
Open / closed648 / 1,081
Opened last 60 days320
Closed last 60 days180
Net change last 60 days+140 open
Open older than 6 months347 (54%)
Open with zero comments291 (45%)
Open issues authored by ruvnet himself254 (39%)
Community-authored open issues with no maintainer reply232 of 394 (59%)

Two readings, both fair. The maintainer has commented on 1,086 issues — genuinely high engagement for one person, and on high-profile technical issues he replies within days against actual source. But the backlog grows, and 39% of the open tracker is his own automation (“Dream Cycle”, “[verification] HIGH:”, “SOTA experiment:”) filing issues at itself, which inflates apparent activity.

Category counts (open issues; full-text search, treat as upper bounds)

TermCount
stub38
mock36
placeholder30
fake16
Math.random10
"not implemented" (all states)67
memory in title99
agentdb (title only)27
hnsw (title only)9
corrupt13
"data loss"12

The memory/vector subsystem is simultaneously the most real part of the project and its single largest problem surface.

Representative issues

#653 — “85% of MCP Tools Are Mock/Stub Implementations” (2025-08-14, closed):

“approximately 85% of claude-flow MCP tools are mock/stub implementations that return success responses without performing actual functionality.” “neural_status - Generic success responses with no real neural data” “performance_report - Generates fake but realistic-looking metrics” “Timestamp-Only Variance: Only timestamps change between identical calls”

#1326 — “Most advertised core features are completely unimplemented” (2026-03-09). Maintainer response 2026-03-17, a partial concession:

“HNSW: Hash-based 128-dim vectors, not transformer-quality embeddings” “Performance claims in docs will be updated to distinguish ‘with WASM’ vs ‘JS fallback’ targets”

#2922 — “Bridge search path not using HNSW” (filed 2026-08-04, still open today). The highest-quality technical issue in the tracker. ruvnet’s reply 2026-08-12:

“Thanks for the thorough tracing here — confirmed all of it against the current source.” “Not scheduled yet. This is a real architectural gap — the bridge path is the default and it’s doing an unindexed full-table scan + cosine loop, which won’t hold up as memory_entries grows.”

#1425 — the maintainer’s own fix table (2026-04-07), quoting his own entries:

“Neural quantize fake — calculated savings but never converted, reported hardcoded 3.92x” “CVE command returned fabricated vulnerability data (CRITICAL 9.8, lodash < 4.17.21) for any CVE ID” “Security validators (@claude-flow/security) existed but were never imported or called by any runtime code” “Intelligence layer dedup (5,706 entries, ~20 unique)”

#530 — “[Bug] Memory is not working at all” — open since 2025-07-30. MCP memory_usage returns "success": true, "stored": true for every write while the bank reads back Total Entries: 0.

#659 — the maintainer’s public stance (22 reactions, 19 of them hearts):

“Alpha is not a label for broken. It’s a signal of active development… It means some parts are stubs waiting to be implemented.” “If You’re Here to Nitpick: 🚪 There’s the door”

Still live in September 2026 (self-reported, days before this evaluation)

  • #3183 (ruvnet, 2026-09-05): “CLI cold-start benchmark measured setTimeout(), not the real CLI”
  • #3179 (ruvnet, 2026-09-04): a package “cites a non-existent ADR-101” — a hallucinated design-doc reference propagated across six files
  • #3175 (2026-09-04): “AgentDB swallows better-sqlite3 ABI mismatch and prints green sql.js success”
  • #3198 (2026-09-05): the “self-learning” sona stat “reports a per-process counter that resets on every MCP server restart”

CI on main is persistently red (#3188, #3171, #3111).

Community

Hacker News never engaged: four submissions of the repo scored 3, 1, 1 and 1 points with zero comments each, for a 71k-star project. Reddit could not be retrieved by the sub-investigator (blocked to the tooling); that is a genuine gap in this report, not evidence of absence.

Independent audit, roman-rr (2026-04-04, v3.5.51):

“We audited 300+ MCP tools… ~10 are real. The rest are JSON state stubs with no execution backend.” “We trained a ‘classifier’ on 5 data points… It reported 93.6% accuracy on 5 samples with 1 epoch… It completely ignores the training data and labels.”

The same audit is explicit that the memory layer is the exception: “REAL — 384-dim embeddings, HNSW index, SQLite persistence.”

Balanced third-party read, HackMD/BASHCAT (2026-05-08): on the memory layer, “relatively mature, the design most worth learning from”; on consensus, “it has the API of consensus protocols but not the implementation”; overall, “a project whose vision outruns its implementation, and whose hype outruns its vision.”

Evidence in ruflo’s favour

Weighted deliberately, because several serious criticisms have been genuinely remediated.

  1. An obfuscated preinstall script (#1261) is gone. Verified directly: npm view ruflo scripts --json at 3.38.21 has no preinstall entry.
  2. The README has been cleaned of the worst claims. No “84.8%”, no SWE-bench, no “150x-12,500x”. What replaced them is calibrated: “measured ~1.9x faster at N=20k, ~3.2x–4.7x at N=5k vs brute force (recall@10 ~0.99); ANN wins above the crossover, ties/loses at small N.” Admitting your own feature loses at small N is the opposite of the earlier posture.
  3. Release notes now publish negative results: v3.10.26 — “BEIR matrix + bootstrap CIs + 2nd dataset (we lose to BM25 on SciFact, ship the truth)“.
  4. ruflo cleanup defaults to dry-run and requires --force (verified at commands/cleanup.ts:130, const dryRun = !force).
  5. Roughly 178k monthly npm downloads across both package names; at least one verifiable named practitioner shipping software with it.

2. The memory and graph subsystem

2.1 What the “plugins” actually contain

Counted by file type across all 40 plugin directories. For the five relevant to this goal:

Plugin.ts.js.md.sh
ruflo-knowledge-graph0061
ruflo-rag-memory0081
ruflo-ruvector0081
ruflo-agentdb0071
ruflo-graph-intelligence34000

Four of the five contain no executable code. They are a README, an agent .md, command .mds, an ADR, SKILL.md files, and a shell smoke test. They are prompt packs that instruct Claude to call MCP tools implemented elsewhere.

The smoke test that each ships as “the contract” is purely structural. From plugins/ruflo-knowledge-graph/scripts/smoke.sh, the ten checks are: the version string is 0.2.1; keywords are present; SKILL files have name:/description:/allowed-tools: lines; the agent and command files exist; a bad tool name is absent; a good tool name is present; five subcommand words appear in a markdown file; a version pin string appears in the README; the ADR says status: Accepted; no skill grants wildcard tools. It never constructs a graph, stores an entity, or traverses anything. “10 passed, 0 failed” means the markdown is well-formed.

2.2 The actual data model

Two tables matter.

memory_entries (v3/@claude-flow/memory/src/sqlite-backend.ts:698) — the memory store:

CREATE TABLE IF NOT EXISTS memory_entries (
  id TEXT PRIMARY KEY, key TEXT NOT NULL, content TEXT NOT NULL,
  type TEXT NOT NULL, namespace TEXT NOT NULL, tags TEXT NOT NULL,
  metadata TEXT NOT NULL, owner_id TEXT, access_level TEXT NOT NULL,
  created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL,
  expires_at INTEGER, version INTEGER NOT NULL, "references" TEXT NOT NULL,
  access_count INTEGER NOT NULL, last_accessed_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS memory_embeddings (
  entry_id TEXT PRIMARY KEY, embedding BLOB,
  FOREIGN KEY (entry_id) REFERENCES memory_entries(id) ON DELETE CASCADE
);

This is free-text content plus an embedding, keyed by key within a namespace. There is no entity type, no symbol, no typed node.

graph_edges (v3/@claude-flow/cli/src/memory/graph-edge-writer.ts:88) — the entire “knowledge graph”:

CREATE TABLE IF NOT EXISTS graph_edges (
  id              TEXT PRIMARY KEY,
  source_id       TEXT NOT NULL,
  target_id       TEXT NOT NULL,
  relation        TEXT NOT NULL,
  weight          REAL DEFAULT 1.0,
  confidence      REAL DEFAULT 1.0,
  decay_rate      REAL DEFAULT 0.0,
  last_reinforced TEXT,
  witness_id      TEXT,
  embedding_ref   TEXT,
  metadata        TEXT,
  created_at      TEXT NOT NULL
);

Assessed honestly:

  • Typed edges: yes. relation TEXT is a real typed edge, with weight, confidence and decay_rate. Better than a bag of vectors.
  • Nodes: no. There is no node or entity table anywhere. source_id and target_id are bare TEXT with no foreign key and no referenced table. A node is whatever string you passed. Nothing constrains, types, or lists nodes. You cannot ask “what entities exist”; you can only ask “what edges exist”.
  • Temporal validity: no. created_at only. No valid_from/valid_to, no supersession, no history.
  • Provenance: nominal. witness_id and metadata columns exist; nothing in the ingestion path populates witness_id with anything meaningful for code ingestion.

2.3 Ingestion — the decisive finding for this goal

There is no code parser in the repository. Searched for tree-sitter, ts-morph, @babel/parser, typescript.createSourceFile, ast-grep across v3/@claude-flow/cli/src, v3/@claude-flow/memory/src, and plugins/, and in the package.json dependency lists. No such dependency exists.

Extraction is done by the language model. From plugins/ruflo-knowledge-graph/skills/kg-extract/SKILL.md:

allowed-tools: Read Glob Grep mcp__…__agentdb_hierarchical-store mcp__…__agentdb_causal-edge …

“1. Scan files — use Glob and Read to enumerate and read source files at the given path”

So building the graph over a Rust/TypeScript/Python repo means Claude reads the source files and decides, in prose, what the entities and relations are, then writes them through MCP calls. Consequences for the stated goal:

  • Non-deterministic. Two runs over identical code produce different graphs.
  • Expensive. Re-indexing is a full LLM read of the codebase, priced in tokens, every time.
  • No incremental update. Nothing watches files, diffs the AST, or maps a changed function to its node. There is no link from a graph row back to a file, line, or symbol.

For a knowledge graph over many repos that must stay correct as code changes, this is the wrong foundation regardless of the quality of everything above it.

2.4 Deduplication and update handling — broken

This is the user’s explicit requirement, so it is worth being precise.

From graph-edge-writer.ts:154, every edge insert mints a fresh primary key and then relies on conflict-ignore:

const id = `edge-${crypto.randomUUID()}`;

db.prepare(`INSERT OR IGNORE INTO graph_edges (id, source_id, …) VALUES (?, ?, …)`)

There is no UNIQUE constraint on (source_id, target_id, relation) — verified by grep; the word UNIQUE does not appear in the file. Because the primary key is a fresh UUID on every call, OR IGNORE can never fire. Asserting the same fact twice creates two rows. Edges accumulate duplicates without bound and nothing ever collapses them.

Updating a fact is worse: there is no update path at all. last_reinforced is declared with the schema comment “set when CONSOLIDATE re-touches edge” (memory-initializer.ts:522), but grep across the whole of v3 finds no UPDATE statement that ever writes it. The single writer is hooks-tools.ts:1636, which sets it once at creation time. So it duplicates created_at; an edge observed a hundred times is indistinguishable from one observed once. There is no supersession, no invalidation, no tombstone. When a decision changes, the old edge stays and a duplicate is added beside it.

A downstream consequence, derived from source and worth flagging: the temporal-centrality algorithm scores by Math.exp(-0.1 * ageDays) where ageDays falls back to the full Unix epoch when last_reinforced is NULL (agentdb-tools.ts:1376-1381). For edges written through agentdb_causal-edge — the path the knowledge-graph plugin uses — last_reinforced is never passed, so it is NULL, so ageDays ≈ 20,700, so exp(-2070) underflows to 0, so every score is 0, so the default threshold: 0.3 filters everything out. temporal-centrality returns an empty result set on any graph built by the knowledge-graph plugin. This is my own inference from reading the code, not a tested claim, but the arithmetic is not subtle.

2.5 Traversal — better than expected, with a caveat

Credit where it is due. agentdb_graph-query in mode: 'k-hop' is backed by a genuine SQLite recursive CTE (agentdb-tools.ts:1157):

WITH RECURSIVE khop(node_id, hop_depth) AS (
  SELECT '<seed>', 0
  UNION
  SELECT e.target_id, k.hop_depth + 1
  FROM graph_edges e
  JOIN khop k ON e.source_id = k.node_id
  WHERE k.hop_depth < <depth>
)
SELECT DISTINCT node_id, MIN(hop_depth) as depth FROM khop
WHERE node_id != '<seed>' GROUP BY node_id ORDER BY depth, node_id LIMIT <maxNodes>

That is real multi-hop traversal in the database, not in the prompt. Note it follows source_id → target_id only, so traversal is strictly directed; there is no reverse or undirected mode.

simplePersonalizedPageRank (agentdb-tools.ts:1204) is likewise a real, correct-looking power iteration with dangling-node restart and per-step normalization. Roughly 60 lines of honest TypeScript.

The caveat. The agentdb_graph-pathfinder tool advertises six algorithms in its MCP schema enum. Three are implemented (personalized-pagerank, temporal-centrality, witness-chain-divergence). The other three share one case body (agentdb-tools.ts:1414):

case 'connected-component-churn':
case 'dynamic-mincut':
case 'spectral-sparsify': {
  // Simplified implementations: return k-hop neighbors with basic score
  const khopResult = await agentdbGraphQuery.handler({ nodeId: seedNodeId, mode: 'k-hop', … });
  paths = khopResult.results.map((r, i) => ({ nodeId: r.nodeId, score: 1.0 / (1 + i), … }))

Asking for a minimum cut runs a k-hop neighbour query and scores results by reciprocal position in an arbitrary ordering. The response then echoes algorithm: 'dynamic-mincut' back to the caller with no flag indicating a substitution. This is the same fabricated-success pattern the maintainer has repeatedly fixed elsewhere, still present on main as of 2026-09-05, in exactly the subsystem relevant here. I found this independently; it is not in the tracker.

#2922 was fixed by renaming, not by implementing. In memory-bridge.ts:1727 the doc comment now reads:

“previously named bridgeGetHNSWStatus and unconditionally returned available: true … but the search this status describes (bridgeSearchBruteForceCosine, below) is a full-table SELECT + brute-force cosine scan, not an HNSW index lookup. Renamed and given an explicit algorithm field so callers can’t mistake ‘vector search works’ for ‘vector search is HNSW-accelerated’.”

The function is now bridgeSearchBruteForceCosine and the status literal is algorithm: 'brute-force-cosine'. That is a real improvement in honesty and a real non-improvement in capability: the default memory search path remains a full table scan. Meanwhile the current README advertises “HNSW-indexed AgentDB — measured ~1.9x–4.7x faster than brute force”, which appears to benchmark a path most users never execute.

2.7 A live bug found in passing

v3/@claude-flow/memory/src/agentdb-backend.ts:649 declares a column named references unquoted:

CREATE TABLE IF NOT EXISTS memory_entries ( … version INTEGER NOT NULL, references TEXT, … )

references is a SQLite reserved word. Verified empirically:

$ sqlite3 :memory: "CREATE TABLE t (id TEXT PRIMARY KEY, references TEXT);"
Error: in prepare, near "references": syntax error

The sibling sqlite-backend.ts:712 quotes it correctly as "references". In agentdb-backend.ts the entire createSchema() body is wrapped in try { … } catch { /* Schema creation failed - using in-memory only */ }, so this backend’s schema creation fails silently on every run and falls back to non-persistent in-memory storage. Scope: AgentDBBackend is consumed by hybrid-backend.ts, the LongMemEval benchmark adapter, and the examples — it is not the CLI’s default path, which uses memory-bridge.ts. So this is a reachable but non-default bug. It plausibly explains a share of the 99 open “memory” issues and the long-standing #530.

2.8 MCP surface and store location

43 relevant tools are exposed, including memory_store, memory_search, memory_search_unified, agentdb_causal-edge, agentdb_graph-query, agentdb_graph-pathfinder, agentdb_hierarchical-store/recall, agentdb_pattern-search, embeddings_generate, embeddings_search.

Store location is per-project. getMemoryRoot() (memory-initializer.ts:106) resolves in order: CLAUDE_FLOW_MEMORY_PATH, then claude-flow.config.json / .claude-flow/config.json, then the default path.resolve(process.cwd(), '.swarm'), with the database at <root>/memory.db. For the goal of one graph spanning many repos, that default is wrong and you would have to pin CLAUDE_FLOW_MEMORY_PATH globally — at which point concurrent Claude Code sessions in different repos share one SQLite file, which is what #2431 and ADR-068 were about.

2.9 ruflo-graph-intelligence — the one plugin with code

34 TypeScript files. It converts a graph into a SparseMatrix and hands it to a sublinear-time-solver for centrality queries. Unlike ruvector’s decapitated Cypher engine, this one is wired to real data: GraphEdgesSource implements KnowledgeGraphSource at src/adapters/knowledge-graph-adapter.ts:116 is documented as reading “live edges from the graph_edges table”.

It is competent code. It is also solving a problem the user does not have — ranking node centrality under a formal complexity budget across federated, cryptographically-signed graphs — and it inherits every weakness of the graph_edges table beneath it.


3. What npx ruflo init writes, and how invasive it is

Read from v3/@claude-flow/cli/src/init/{executor,settings-generator,claudemd-generator}.ts.

Into the project:

  • CLAUDE.md — skipped if one exists. With --force it backs up first (executor.ts:2207, a #2208 fix). Good behaviour.
  • .claude/settings.json — merged if present, created otherwise.
  • .claude/helpers/*.cjs, plus .claude/skills/, .claude/agents/, .claude/commands/.
  • .claude-flow/metrics/{v3-progress,swarm-activity,learning}.json, .claude-flow/security/audit-status.json, .claude-flow/memory-package.json.
  • .swarm/memory.db.
  • A statusline shim at .claude/helpers/statusline.cjs, wired into settings.

Hooks: 9 event types, ~14 commands. PreToolUse (pre-bash, pre-edit), PostToolUse (post-edit, post-bash), UserPromptSubmit (route), SessionStart (session-restore, auto-memory import), SessionEnd (session-end, auto-memory sync), Stop, PreCompact (compact-manual, compact-auto), SubagentStop (post-task), Notification (notify). Several are node -e one-liners that resolve a git root at runtime and dynamically import a script from it.

A daemon exists. commands/daemon.ts spawns a detached background process, background defaulting to true.

Into your home directory — the part that matters most here. executor.ts:2227 appends to your global ~/.claude/CLAUDE.md by default:

// Also write/append global ~/.claude/CLAUDE.md so ruflo tools are used automatically (#1497).
const rufloBlock = [
  '# Ruflo Integration (auto-generated by ruflo init)',
  'When working on multi-file tasks or complex features, use ToolSearch to find and invoke ruflo MCP tools.',
  'Key tools: memory_store, memory_search, hooks_route, swarm_init, agent_spawn.',
  'Check system-reminder tags for [INTELLIGENCE] pattern suggestions before starting work.',
].join('\n');

fs.appendFileSync(globalClaudeMd, rufloBlock);

It is guarded against double-appending and opt-out with --no-global, and the failure is swallowed as best-effort. But the default is that running init inside one repository silently modifies the instruction file that governs every project on the machine, with no backup of that file. For a user with a hand-curated global CLAUDE.md, that is the single most objectionable behaviour in the tool.

Removal. ruflo cleanup is decent, and safer than its reputation: it defaults to dry-run and requires --force (cleanup.ts:130). It removes .claude/helpers, .claude-flow, .swarm, .hive-mind, coordination, data, memory, and claude-flow.config.json, and it surgically strips only the ruflo blocks from .claude/settings.json, preserving the rest.

Two problems remain:

  1. It never touches ~/.claude/CLAUDE.md. Verified by grep: homedir, HOME, USERPROFILE, CLAUDE.md and Ruflo Integration do not appear anywhere in cleanup.ts. Install writes globally; uninstall cleans only locally. You must remove that block by hand, and nothing tells you it exists.
  2. Its removal list includes the generic directory names data/, memory/, and coordination/. Run ruflo cleanup --force in a repository that legitimately has a data/ directory and it is deleted with rmSync(recursive, force). The dry-run default is the only thing standing between a user and that outcome.

4. ruvector as a standalone embedded Rust store

Short version: not a credible alternative to kuzu/cozo/oxigraph for an embedded Rust knowledge graph.

  • No ruvector crate exists. crates.io returns 404. There are ~155 ruvector-* crates. ruvector-core 2.3.0 has 176k downloads; the one you would actually need, ruvector-graph 2.3.1, has 3,085 downloads in 90 days and 11 reverse dependencies, all ruvnet’s own. The npm package is real and busy (349k downloads/month), which tells you where the actual users are.
  • Repo: created 2025-11-19, 4,475 stars, 3,217 commits. Contributors: ruvnet 1436, github-actions[bot] 784, claude 612. One person plus Claude plus bots, on a nightly cron that auto-generates ADRs (now at ADR-346). The root Cargo.toml excludes a growing list of crates that do not compile.
  • Graph model is genuinely good on paper — a labeled property graph with typed edges and hyperedges (crates/ruvector-graph/src/{node,edge}.rs), persisted to redb, a real ACID embedded KV store.
  • But traversal is one hop. The only traversal function in the crate is typed_graph.rs:555 traverse_from(). No BFS, no DFS, no shortest path, no multi-hop, no petgraph.
  • And the Cypher engine is decapitated. cypher/ is 3,488 lines of lexer, parser and optimizer producing a real AST. Nothing calls parse_cypher; there is no GraphDB::query(&str). The executor is a stub — executor/mod.rs:129:
    fn execute_sequential(&self, _plan: &PhysicalPlan) -> Result<Vec<RowBatch>> {
        // Note: In a real implementation, we would need to reconstruct operators
        // For now, return empty results as placeholder
        Ok(Vec::new())
    }
    The only working Cypher lives in the NAPI addon (ruvector-graph-node/src/cypher_exec.rs), reachable from JavaScript only, and it self-reports at runtime that variable-length relationships [*1..n] and chained patterns are unsupported.
  • HNSW is a wrapper over the third-party hnsw_rs crate, which is fine but not novel.
  • TransactionManager is in-memory MVCC over DashMaps and is never used by GraphDB, so the advertised ACID transactions do not cover the persistent path. Opening a graph loads it entirely into RAM.

For the stated goal, kuzu is the right answer: embedded, real Cypher, real multi-hop, real ACID, actual users. cozo if Datalog plus vectors in one engine appeals; oxigraph if RDF/SPARQL fits the concept layer.

Correction added on filing (2026-09-07)

Do not act on the Kuzu recommendation in the line above. Kuzu was archived on 2025-10-10 following the Apple acquisition; the maintained fork is LadybugDB. This report was scoped to ruflo, not to the embedded-graph field, so it did not catch that. If an embedded graph database is genuinely required, use LadybugDB. knowledge-graph-research-2026-09-07 concludes none is required: markdown stays the source of truth and SQLite suffices for the derived indexes.


5. Verdict for this user

Nothing here is worth extracting or running standalone.

Taking the three candidates in turn:

  • The ruflo-knowledge-graph plugin cannot be run standalone because it is not software. It is six markdown files that instruct Claude to call MCP tools living inside the ruflo CLI. Extracting it yields prompts with nothing underneath.
  • AgentDB cannot be usefully extracted either. It is not a separable database; it is memory_entries + graph_edges in SQLite, reached through memory-bridge.ts, entangled with the bridge, the registry, the hooks and the daemon. And the AgentDB backend proper has a schema-creation bug that silently drops it to in-memory.
  • ruflo-graph-intelligence is real code but it solves centrality-ranking under complexity budgets across federated signed graphs, not the user’s problem, and it sits on the same defective edge table.

The honest characterization asked for: ruflo is a heavyweight orchestration framework whose “knowledge graph” is a thin, un-deduplicated edge table over vector memory, fed by an LLM rather than a parser. The traversal layer is a pleasant surprise and is more real than the framing “thin layer over vector memory” would suggest — a correct recursive CTE and a correct PageRank are genuinely there. But the two things this user specifically needs are the two things missing: deterministic ingestion of source code into symbols, and update handling when code or decisions change. On the second, ruflo is not merely weak; asserting a fact twice silently duplicates it forever, and there is no update path at all.

What is worth borrowing, as ideas rather than code:

  1. The edge schema is a decent starting sketch. (source, target, relation, weight, confidence, decay_rate, last_reinforced) is the right shape for a code-plus-concept graph. Add what ruflo lacks: a node table, a UNIQUE(source, target, relation) constraint, and an upsert that bumps an observation count and last_reinforced instead of inserting a duplicate.
  2. The k-hop recursive CTE (§2.5) is ~15 lines of SQL and transfers directly to any SQLite or DuckDB schema. If the build ends up on SQLite rather than kuzu, this is the traversal primitive, and it is worth copying.
  3. The negative lesson, which is the most valuable thing in the repo: an LLM-driven extractor with no AST and no stable node identity cannot keep a code graph correct across changes. Whatever gets built should parse with tree-sitter or rust-analyzer/ts-morph, key nodes on stable symbol identity, and reserve the LLM for the concept layer and for linking concepts to symbols.

Recommended direction instead: kuzu (embedded, Cypher, multi-hop, ACID) for the graph; tree-sitter or per-language tooling for deterministic symbol extraction; the Obsidian vault as the concept layer, linked to symbols by stable ids; and a thin MCP server exposing neighbours, paths and search to Claude Code. That is a smaller build than it sounds, and every part of it is something ruflo either does not do or does wrongly.

Correction added on filing (2026-09-07)

The kuzu recommendation in the closing line above is superseded. Kuzu is archived; use LadybugDB if an embedded graph database is needed at all. The rest of that paragraph survives review and is carried into knowledge-graph-research-2026-09-07 — deterministic symbol extraction, the vault as the concept layer, stable symbol ids, and a thin MCP surface — with two changes: the extractor is rust-analyzer scip rather than tree-sitter, and the build is narrowed to a reconciler rather than a graph database application.


Appendix: verification notes

  • All source quotations are from HEAD 277c7bc0 (2026-09-05), read from a local clone.
  • The SQLite reserved-word failure in §2.7 was verified by executing the statement, not inferred.
  • The temporal-centrality empty-result consequence in §2.4 is arithmetic derived from source, not an executed test, and is flagged as such in place.
  • One earlier suspicion was retracted during the investigation: _resetBridgeDb() initially appeared to have an empty body, which would have contradicted its own doc comment. A second read with grep -A8 showed the display had elided lines 271-272; the function correctly performs wal_checkpoint(TRUNCATE) and close(). It is not a bug.
  • Reddit sentiment could not be retrieved (blocked to the available tooling). This is a stated gap, not a finding of absence.
  • The sub-investigation on the issue tracker tripped an instruction-shaped-content filter on the string --dangerously-skip-permissions. That string is quoted evidence about ruflo — critics document that its hive-mind spawns child_process.spawn('claude', ['--dangerously-skip-permissions', …]). It was treated as a finding, not an instruction, and no action was taken on it. It is arguably a security concern in its own right and is recorded here for that reason.