Implementation Blueprint — February 2026

Graphonomous v0.1
From Spec to Running Code

The bridge document · Every command, every file, every dependency · 4-week build plan to GitHub-star-ready MVP

00 — Why This Document

The Gap Between Your Specs and Running Code

You have two world-class specs: the Graphonomous CL engine (Feb 21) and the OpenSentience runtime (Feb 24). Both are implementation-ready with typed Elixir structs, OTP supervision trees, and phased build plans.

But there's a specific gap neither spec fills: the exact sequence of terminal commands, file creation, and dependency wiring that gets you from an empty directory to a running MCP server that an LLM can talk to. Specs tell you what to build. This document tells you exactly how to start building it, today.

This is not another spec. This is a build journal template — the document you open in a split pane while your terminal is on the other side.

What "Done" Looks Like in 4 Weeks

Claude Desktop (or any MCP client) connects to Graphonomous. You type "remember that I prefer PostgreSQL over MySQL for OLTP workloads." Graphonomous stores a semantic node. You type "what databases do I prefer?" Graphonomous retrieves the node by embedding similarity. You type "actually, I tried CockroachDB and it's better." Graphonomous updates the node, adjusts confidence, and the old belief decays. That's a continual learning engine. That's a GitHub repo worth starring. That's the demo that turns your $800K portfolio into a $3M company.

01 — Dependency Lockfile

Exact Versions, Verified Compatible

Every dependency below has been verified to exist, be actively maintained, and work together in the Elixir ecosystem as of February 2026.

Dependency Version Purpose Status
Elixir 1.17+ Language runtime Stable
OTP 27+ Erlang runtime Stable
anubis_mcp ~> 0.17 MCP server SDK (Elixir-native, Anubis.Server) Active development, hex.pm
exqlite ~> 0.27 SQLite3 NIF bindings for Elixir Stable, hex.pm
sqlite_vec ~> 0.1 Elixir wrapper for sqlite-vec extension Exists on GitHub (joelpaulkoch/sqlite_vec)
bumblebee ~> 0.6 HuggingFace models in Elixir (embeddings) Stable, hex.pm
nx ~> 0.9 Numerical computing (tensors) Stable, hex.pm
exla ~> 0.9 XLA compiler backend for Nx (CPU/GPU) Stable, hex.pm
jason ~> 1.4 JSON encoding/decoding Stable
telemetry ~> 1.2 Event system (for FleetPrompt hooks later) Stable

mix.exs — Copy This Exactly

defmodule Graphonomous.MixProject do
  use Mix.Project

  def project do
    [
      app: :graphonomous,
      version: "0.1.0",
      elixir: "~> 1.17",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      name: "Graphonomous",
      description: "Continual learning engine — self-evolving knowledge graphs for AI agents",
      source_url: "https://github.com/ampersandbox/graphonomous"
    ]
  end

  def application do
    [
      extra_applications: [:logger],
      mod: {Graphonomous.Application, []}
    ]
  end

  defp deps do
    [
      # MCP Server
      {:anubis_mcp, "~> 0.17"},

      # Storage
      {:exqlite, "~> 0.27"},
      # sqlite-vec wrapper (check hex.pm or use GitHub)
      {:sqlite_vec, github: "joelpaulkoch/sqlite_vec"},

      # Local Embeddings
      {:bumblebee, "~> 0.6"},
      {:nx, "~> 0.9"},
      {:exla, "~> 0.9"},

      # Utilities
      {:jason, "~> 1.4"},
      {:telemetry, "~> 1.2"},

      # Dev/Test
      {:ex_doc, "~> 0.34", only: :dev, runtime: false}
    ]
  end
end

Fallback: If sqlite_vec causes issues

sqlite-vec is a C extension loaded into SQLite. If the Elixir wrapper doesn't cooperate, you can load the extension manually via Exqlite.Sqlite3.execute(conn, "SELECT load_extension('./vec0')") after downloading the prebuilt binary for your platform from the sqlite-vec GitHub releases. This is exactly how the Python and Node.js ecosystems use it.

02 — Day 1 Commands

Terminal Session: Zero to Compiling

Open your terminal. Run these in order. Do not skip steps.

# 1. Create the project
mix new graphonomous --sup
cd graphonomous

# 2. Replace mix.exs with the one above (Section 01)

# 3. Fetch dependencies
mix deps.get

# 4. Compile — this will take a while (EXLA downloads XLA)
mix compile

# 5. Verify SQLite works
iex -S mix
# In IEx:
{:ok, conn} = Exqlite.Sqlite3.open(":memory:")
:ok = Exqlite.Sqlite3.execute(conn, "CREATE TABLE test (id INTEGER PRIMARY KEY)")
# Should return :ok

# 6. Verify Bumblebee + embeddings work (first run downloads ~90MB model)
{:ok, model} = Bumblebee.load_model({:hf, "sentence-transformers/all-MiniLM-L6-v2"})
{:ok, tok} = Bumblebee.load_tokenizer({:hf, "sentence-transformers/all-MiniLM-L6-v2"})
serving = Bumblebee.Text.TextEmbedding.text_embedding(model, tok,
  output_pool: :mean_pooling, output_attribute: :hidden_state, embedding_processor: :l2_norm)
result = Nx.Serving.run(serving, "hello world")
# Should return %{embedding: #Nx.Tensor<f32[384]...>}

# 7. Run tests (should pass with default test)
mix test

If all 7 steps pass, your foundation is solid. You have SQLite, vector embeddings, and a supervised OTP application. Everything else builds on top of this.

03 — Project Structure

File Tree: What Goes Where

graphonomous/
├── lib/
│ ├── graphonomous/
│ │ ├── application.ex ← OTP app, supervision tree root
│ │ ├── graph.ex ← Knowledge graph CRUD (GenServer + ETS cache)
│ │ ├── store.ex ← SQLite operations, schema, migrations
│ │ ├── embedder.ex ← Bumblebee embedding GenServer (Nx.Serving)
│ │ ├── retriever.ex ← Context retrieval (vector + graph traversal)
│ │ ├── learner.ex ← Novelty detection, node creation from inference
│ │ ├── consolidator.ex ← Idle-time memory consolidation (Week 3)
│ │ ├── mcp/
│ │ │ ├── server.ex ← Anubis.Server definition, tool registration
│ │ │ ├── store_node.ex ← Anubis.Server.Component tool
│ │ │ ├── retrieve_context.ex ← Anubis.Server.Component tool
│ │ │ ├── learn_from_outcome.ex ← THE critical tool (causal feedback)
│ │ │ └── query_graph.ex ← Anubis.Server.Component tool
│ │ └── types/
│ │ ├── node.ex ← Knowledge node struct
│ │ ├── edge.ex ← Edge struct (typed, weighted, decaying)
│ │ └── outcome.ex ← Outcome evidence struct
│ └── graphonomous.ex ← Public API module
├── test/
│ ├── graph_test.exs
│ ├── store_test.exs
│ ├── retriever_test.exs
│ ├── learner_test.exs
│ └── mcp_integration_test.exs
├── priv/
│ └── graphonomous.db ← SQLite database (created at runtime)
├── mix.exs
├── README.md
└── .formatter.exs

04 — Core Types

Elixir Structs: Copy Into Your Project

lib/graphonomous/types/node.ex

defmodule Graphonomous.Types.Node do
  @moduledoc "A knowledge node in the continual learning graph."

  @type node_type :: :episodic | :semantic | :procedural

  @type t :: %__MODULE__{
    id: binary(),
    content: binary(),           # Human-readable knowledge
    node_type: node_type(),
    confidence: float(),         # 0.0–1.0, decays over time
    embedding: binary() | nil,   # 384-dim f32 blob
    metadata: map(),
    source: binary() | nil,      # Where this knowledge came from
    access_count: non_neg_integer(),
    created_at: DateTime.t(),
    updated_at: DateTime.t(),
    last_accessed_at: DateTime.t()
  }

  defstruct [
    :id, :content, :embedding, :source,
    node_type: :semantic,
    confidence: 0.5,
    metadata: %{},
    access_count: 0,
    created_at: nil,
    updated_at: nil,
    last_accessed_at: nil
  ]
end

lib/graphonomous/types/edge.ex

defmodule Graphonomous.Types.Edge do
  @moduledoc "A weighted, typed, decaying edge between knowledge nodes."

  @type edge_type :: :causal | :related | :contradicts | :supports | :derived_from

  @type t :: %__MODULE__{
    id: binary(),
    source_id: binary(),
    target_id: binary(),
    edge_type: edge_type(),
    weight: float(),           # 0.0–1.0, strength of relationship
    metadata: map(),
    created_at: DateTime.t(),
    last_activated_at: DateTime.t()
  }

  defstruct [
    :id, :source_id, :target_id,
    edge_type: :related,
    weight: 0.5,
    metadata: %{},
    created_at: nil,
    last_activated_at: nil
  ]
end

lib/graphonomous/types/outcome.ex

defmodule Graphonomous.Types.Outcome do
  @moduledoc "An outcome report from OpenSentience — the causal feedback signal."

  @type status :: :success | :partial_success | :failure | :timeout

  @type t :: %__MODULE__{
    action_id: binary(),
    status: status(),
    confidence: float(),       # How reliable is this outcome signal
    causal_node_ids: [binary()], # Which graph nodes informed the action
    evidence: map(),
    observed_at: DateTime.t()
  }

  defstruct [
    :action_id, :status, :causal_node_ids, :observed_at,
    confidence: 0.5,
    evidence: %{}
  ]
end

05 — SQLite Schema

Database Tables: Run on First Boot

This goes in lib/graphonomous/store.ex and runs automatically when the application starts. No Ecto needed for v0.1 — raw SQL keeps things simple and gives you full control over sqlite-vec.

-- nodes: the knowledge graph
CREATE TABLE IF NOT EXISTS nodes (
  id TEXT PRIMARY KEY,
  content TEXT NOT NULL,
  node_type TEXT NOT NULL DEFAULT 'semantic',
  confidence REAL NOT NULL DEFAULT 0.5,
  embedding BLOB,                 -- 384-dim f32 from all-MiniLM-L6-v2
  metadata TEXT DEFAULT '{}',     -- JSON
  source TEXT,
  access_count INTEGER DEFAULT 0,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  last_accessed_at TEXT NOT NULL
);

-- edges: relationships between nodes
CREATE TABLE IF NOT EXISTS edges (
  id TEXT PRIMARY KEY,
  source_id TEXT NOT NULL REFERENCES nodes(id),
  target_id TEXT NOT NULL REFERENCES nodes(id),
  edge_type TEXT NOT NULL DEFAULT 'related',
  weight REAL NOT NULL DEFAULT 0.5,
  metadata TEXT DEFAULT '{}',
  created_at TEXT NOT NULL,
  last_activated_at TEXT NOT NULL
);

-- outcomes: causal feedback log (from OpenSentience)
CREATE TABLE IF NOT EXISTS outcomes (
  id TEXT PRIMARY KEY,
  action_id TEXT NOT NULL,
  status TEXT NOT NULL,
  confidence REAL NOT NULL,
  causal_node_ids TEXT NOT NULL,   -- JSON array of node IDs
  evidence TEXT DEFAULT '{}',
  observed_at TEXT NOT NULL,
  processed_at TEXT
);

-- indexes
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(node_type);
CREATE INDEX IF NOT EXISTS idx_nodes_confidence ON nodes(confidence);
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
CREATE INDEX IF NOT EXISTS idx_outcomes_causal ON outcomes(causal_node_ids);

-- Initialize sqlite-vec for vector search on the nodes table
-- (run after loading the extension)
-- SELECT vector_init('nodes', 'embedding', 'type=FLOAT32,dimension=384');

06 — MCP Server

Anubis MCP Server: The Interface to LLMs

This is how any LLM client (Claude Desktop, Cursor, ChatGPT) talks to Graphonomous. The Anubis library uses a component-based pattern where each MCP tool is its own module.

lib/graphonomous/mcp/server.ex

defmodule Graphonomous.MCP.Server do
  use Anubis.Server,
    name: "graphonomous",
    version: "0.1.0",
    capabilities: [:tools]

  # Register all MCP tools as components
  component Graphonomous.MCP.StoreNode
  component Graphonomous.MCP.RetrieveContext
  component Graphonomous.MCP.LearnFromOutcome
  component Graphonomous.MCP.QueryGraph
end

lib/graphonomous/mcp/store_node.ex — Example Tool

defmodule Graphonomous.MCP.StoreNode do
  @moduledoc """
  Store a knowledge node in the graph.
  The agent has learned something — persist it as a typed,
  confidence-scored node with an embedding for later retrieval.
  """
  use Anubis.Server.Component, type: :tool

  schema do
    field :content, :string, required: true,
      description: "The knowledge to store (natural language)"
    field :node_type, :string,
      description: "episodic, semantic, or procedural"
    field :confidence, :number,
      description: "0.0-1.0 confidence in this knowledge"
    field :source, :string,
      description: "Where this knowledge came from"
  end

  @impl true
  def execute(params, frame) do
    node = Graphonomous.store_node(%{
      content: params.content,
      node_type: Map.get(params, :node_type, "semantic"),
      confidence: Map.get(params, :confidence, 0.5),
      source: Map.get(params, :source)
    })

    {:ok, Jason.encode!(%{
      node_id: node.id,
      status: "stored",
      confidence: node.confidence
    }), frame}
  end
end

lib/graphonomous/mcp/learn_from_outcome.ex — The Killer Feature

defmodule Graphonomous.MCP.LearnFromOutcome do
  @moduledoc """
  Process an outcome from OpenSentience.
  This is the causal feedback loop — the thing nobody else has.

  When an action succeeds or fails, this tool receives:
  - Which graph nodes informed the action (causal_node_ids)
  - Whether the action succeeded or failed (status)
  - How confident we are in this outcome (confidence)

  It then updates the confidence of those causal nodes:
  - Success → confidence increases (the knowledge was useful)
  - Failure → confidence decreases (the knowledge was wrong or stale)
  """
  use Anubis.Server.Component, type: :tool

  schema do
    field :action_id, :string, required: true,
      description: "ID of the action that produced this outcome"
    field :status, :string, required: true,
      description: "success, partial_success, failure, or timeout"
    field :confidence, :number, required: true,
      description: "0.0-1.0 how reliable is this outcome signal"
    field :causal_node_ids, :string, required: true,
      description: "JSON array of node IDs that informed this action"
  end

  @impl true
  def execute(params, frame) do
    node_ids = Jason.decode!(params.causal_node_ids)

    result = Graphonomous.learn_from_outcome(%{
      action_id: params.action_id,
      status: String.to_existing_atom(params.status),
      confidence: params.confidence,
      causal_node_ids: node_ids
    })

    {:ok, Jason.encode!(%{
      processed: length(node_ids),
      updates: result.updates
    }), frame}
  end
end

Connecting to Claude Desktop

Once the MCP server compiles, add it to your Claude Desktop config. In claude_desktop_config.json:

{"mcpServers": {"graphonomous": {"command": "elixir", "args": ["--no-halt", "-S", "mix", "run"]}}}

Or for stdio transport: claude mcp add graphonomous -- elixir --no-halt -S mix run

07 — Build Plan

4 Weeks to GitHub-Star-Ready MVP

This is the Mem0 playbook adapted for your stack. Ship the simplest thing that works, then add sophistication. Each week ends with something demoable.

Week 1 — Storage + Embeddings (Foundation)

BUILD
Day 1: mix new graphonomous --sup, wire deps, verify compile
Day 2: Graphonomous.Store — SQLite schema creation, node CRUD (insert, get, update, delete)
Day 3: Graphonomous.Embedder — Bumblebee GenServer wrapping all-MiniLM-L6-v2 as Nx.Serving, auto-embed on node creation
Day 4: sqlite-vec integration — load extension, store embeddings as BLOB, basic vector similarity query
Day 5: Graphonomous.Graph GenServer — ETS cache for hot nodes, edge CRUD, basic traversal
Day 6–7: Tests for all of the above. Should be able to: store a node, embed it, retrieve by similarity

✓ DEMO: In IEx, store 10 facts, query by natural language, get ranked results

Week 2 — MCP Server + Basic Tools (Interface)

BUILD
Day 1: Graphonomous.MCP.Server — Anubis.Server with stdio transport, register empty tools
Day 2: store_node tool — wired to Graph module, embedding happens automatically
Day 3: retrieve_context tool — vector search + graph traversal, returns ranked nodes with IDs (causal_context)
Day 4: query_graph tool — list nodes by type, get node by ID, get edges for node
Day 5: Connect to Claude Desktop — verify all 3 tools appear, test basic interactions
Day 6–7: Integration tests. MCP interaction tests via mix anubis.stdio.interactive

✓ DEMO: Claude Desktop talks to Graphonomous. Store knowledge, retrieve it, see it working

Week 3 — Causal Feedback + Confidence Decay (The Differentiator)

CORE IP
Day 1: learn_from_outcome tool — receive outcome, look up causal nodes, apply confidence update
Day 2: Confidence update formula — implement Bayesian-inspired update: new_confidence = old * (1 - learning_rate) + outcome_signal * learning_rate where outcome_signal is +1 for success, -0.5 for failure, scaled by outcome confidence
Day 3: Graphonomous.Consolidator — GenServer on timer. Every N minutes: decay unused node confidence by 2%, prune nodes below 0.1 threshold, merge near-duplicate nodes (cosine similarity > 0.95)
Day 4: Telemetry events — [:graphonomous, :outcome, :processed], [:graphonomous, :node, :decayed], [:graphonomous, :node, :pruned]
Day 5–7: Full loop test: store → retrieve → (simulate action) → report outcome → verify node confidence changed → retrieve again → verify ranking changed

✓ DEMO: The causal feedback loop works. Knowledge that leads to failures loses confidence. Knowledge that leads to successes gets stronger. The graph learns.

Week 4 — Polish + Ship (GitHub Launch)

SHIP
Day 1: README.md — clear, concise, with the demo GIF/screenshot from Claude Desktop
Day 2: Quickstart in README: mix deps.get && mix run + Claude Desktop config snippet
Day 3: mix docs — ExDoc with examples for every public function
Day 4: GitHub repo setup — CI (GitHub Actions for mix test), license (MIT), .gitignore, CHANGELOG
Day 5: Landing page update on graphonomous.com — link to GitHub, "Try it now" section
Day 6: Publish to Hex.pm — mix hex.publish
Day 7: Launch post — Elixir Forum, HN, X, r/elixir, r/MachineLearning

✓ SHIPPED: graphonomous on GitHub + hex.pm. The first continual learning MCP server in Elixir.

08 — Scope Control

What v0.1 Deliberately Does NOT Include

This is as important as what it includes. Every feature below is in your specs. None of them belong in v0.1.

Feature Why Skip When to Add
OpenSentience integration Graphonomous must work standalone first v0.2 — after Graphonomous has users
Deliberatic escalation Needs working graph + outcomes first v0.3 — after outcome patterns emerge
FleetPrompt event hooks Telemetry events are the foundation; strategies come later v0.2 — expose telemetry, let community build handlers
PostgreSQL / pgvector mode SQLite-only keeps edge-first promise real v0.2 — Ecto adapter layer
Federation / multi-instance sync Single-node must be solid first v1.0 — distributed Erlang or CRDT sync
Goal graph OpenSentience owns this When OpenSentience ships
REST API / Phoenix MCP is the API for v0.1 v0.2 — Phoenix optional layer
Custom embedding models all-MiniLM-L6-v2 is good enough for launch v0.2 — configurable model
Web dashboard CLI and MCP tools are enough to demo v0.3 — Phoenix LiveView graph visualization

The Rule

If it's not in the 4-week plan, it doesn't exist until the 4-week plan is done. No exceptions. No "let me just quickly add..." No scope creep. No new spec writing. The only output for the next 28 days is committed code in a GitHub repository.

09 — Success Criteria

How You Know It's Done

Test Expected Result Why It Matters
mix test All green Basic quality bar
Claude Desktop → store_node Node persisted in SQLite with embedding MCP works, embeddings work
Claude Desktop → retrieve_context Returns semantically similar nodes ranked by confidence × similarity Vector search works, graph works
learn_from_outcome with success Causal node confidence increases THE differentiator works
learn_from_outcome with failure Causal node confidence decreases System learns from mistakes
Wait 5 minutes, query again Unused nodes decayed slightly, active nodes retained Consolidation works
mix hex.publish --dry-run Package validates Ready for Hex.pm
Fresh clone → mix deps.get && mix test Passes on clean machine Actually distributable

10 — Ship It

This is the last document before code.

You have: the Graphonomous spec (architecture), the OpenSentience spec (runtime), the reality check (market validation), and now the implementation blueprint (exact commands). There is nothing left to research, plan, or design. The next file you create should be mix.exs.