<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AlexMikhalev</title>
    <description>The latest articles on DEV Community by AlexMikhalev (@alexmikhalev).</description>
    <link>https://dev.to/alexmikhalev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F615498%2F1d6840a1-b457-4a78-a93a-1fa431bfc53c.png</url>
      <title>DEV Community: AlexMikhalev</title>
      <link>https://dev.to/alexmikhalev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexmikhalev"/>
    <language>en</language>
    <item>
      <title>Graph Embeddings You Can Watch Being Built</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Tue, 20 Oct 2026 15:20:59 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/graph-embeddings-you-can-watch-being-built-2oi8</link>
      <guid>https://dev.to/alexmikhalev/graph-embeddings-you-can-watch-being-built-2oi8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://terraphim.ai/docs/graph-embeddings-interactive/" rel="noopener noreferrer"&gt;terraphim.ai&lt;/a&gt; — where this article is fully interactive.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Graph Embeddings You Can Watch Being Built
&lt;/h1&gt;

&lt;p&gt;A graph embedding does not have to be a vector. In Terraphim it is a node plus its directed edges and a set of integer counters: the synonyms that map to the concept, and the concepts that appear next to it in real text. That claim is easy to make and hard to feel, so we built a page that proves it — the actual Rust &lt;code&gt;terraphim_rolegraph&lt;/code&gt; crate, compiled to WebAssembly, running the algorithm in your browser. Nothing on screen is staged or reimplemented; the counters you watch increment are the same code paths the crate's own tests exercise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the page does
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live Aho-Corasick matching.&lt;/strong&gt; Type any sentence into the matcher and a real automaton — compiled from a 19-term thesaurus — scans it in a single pass and reports concept ids. &lt;code&gt;raft quorum consensus&lt;/code&gt; fires &lt;code&gt;#1 raft&lt;/code&gt;, &lt;code&gt;#6 quorum&lt;/code&gt;, &lt;code&gt;#2 consensus&lt;/code&gt;. The matching is case-folding and leftmost-longest, exactly as the Rust crate configures it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-step indexing.&lt;/strong&gt; Index the eight-document corpus one click at a time. Each document is matched, adjacent concept matches become directed edges, and node and edge counters increment. The end state — 10 concepts, 23 directed edges — includes a &lt;code&gt;raft → raft&lt;/code&gt; self-loop that falls out of &lt;code&gt;Document::to_string()&lt;/code&gt; matching titles as well as bodies. No special-casing; that is what the Rust code does, so that is what the page shows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A query ledger you can audit.&lt;/strong&gt; Query &lt;code&gt;consensus&lt;/code&gt; and the winner, &lt;code&gt;doc-membership&lt;/code&gt;, earns 19 points decomposed as &lt;code&gt;node 7 + edge 1 + doc 2&lt;/code&gt; through one edge plus &lt;code&gt;node 7 + edge 1 + doc 1&lt;/code&gt; through another. Every point of the sum is a named node, a named edge and an inspectable document count. That is the explainability argument for symbolic retrieval made concrete: there is no answer to "why 0.87" for a dense vector, and a complete answer here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest failure.&lt;/strong&gt; &lt;code&gt;byzantine fault tolerance&lt;/code&gt; matches no thesaurus term and the graph returns an empty set — displayed as such — while the illustrative vector panel next to it still produces fuzzy similarity scores. The contrast is the lesson: dense retrieval always answers, symbolic retrieval tells you when it does not know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real rebuild cost.&lt;/strong&gt; Add a synonym — say &lt;code&gt;leader selection&lt;/code&gt; — and the page calls the crate's &lt;code&gt;thesaurus.insert&lt;/code&gt; plus &lt;code&gt;rebuild_automata&lt;/code&gt;, measuring the result live (single-digit milliseconds on an M-series laptop). Set against the "update the embedding" column for node2vec, GCN or a hosted vector index, this is the cost-of-change argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest comparison
&lt;/h2&gt;

&lt;p&gt;The page includes a side-by-side table across training requirements, update cost, explainability, determinism and memory footprint for learned graph embeddings (node2vec, GCN, TransE, Cleora), dense text embeddings behind an API and vector database, and Terraphim's graph. It does not pretend the symbolic graph wins everywhere: it is blind to out-of-vocabulary terms, its ranks reflect frequency rather than importance, and its reach is one hop of adjacency.&lt;/p&gt;

&lt;p&gt;Those gaps have engineered answers that already exist in the codebase rather than proposals: &lt;code&gt;query_graph_with_trigger_fallback&lt;/code&gt; backs the graph with a TF-IDF trigger index as an opt-in fallback, and &lt;code&gt;search_sessions_hybrid&lt;/code&gt; fuses BM25 full-text scoring with knowledge-graph concept boosts for session search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Everything above is one click away at &lt;a href="https://terraphim.ai/docs/graph-embeddings-interactive/" rel="noopener noreferrer"&gt;terraphim.ai/docs/graph-embeddings-interactive/&lt;/a&gt;. The WASM build, its provenance manifest and a conformance suite that cross-checks the browser output against an independent oracle are all in the site's repository.&lt;/p&gt;

&lt;p&gt;What would you index first on a graph you can watch being built?&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>knowledgegraph</category>
      <category>embeddings</category>
    </item>
    <item>
      <title>Graph Embeddings for Checklists</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:19:36 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/graph-embeddings-for-checklists-lgc</link>
      <guid>https://dev.to/alexmikhalev/graph-embeddings-for-checklists-lgc</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://terraphim.ai/posts/graph-embeddings-checklists/" rel="noopener noreferrer"&gt;terraphim.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Graph Embeddings for Checklists
&lt;/h1&gt;

&lt;p&gt;A checklist is an enumerated set of terms that must be checked. That is the whole problem, and it is the reason vector embeddings are the wrong tool for it: a nearest-neighbour index returns fuzzy neighbours, it cannot enumerate a fixed set and report which members fired. A knowledge graph can. Write the checklist once as synonyms under a normalised term, compile it into an Aho-Corasick automaton, and every check becomes a deterministic match with a named edge you can audit.&lt;/p&gt;

&lt;p&gt;This post shows that machinery applied to the two places a checklist earns its keep around an LLM call: &lt;code&gt;terraphim-grep&lt;/code&gt; with a role thesaurus before the call, and &lt;code&gt;terraphim-agent&lt;/code&gt; hooks plus learning after it. Every command below was run while writing this article; the outputs are real.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Checklist Is an Automaton, Not a Promise
&lt;/h2&gt;

&lt;p&gt;The usual pattern for "checklists" in agent workflows is a paragraph in a system prompt that the model promises to honour. That is a probabilistic check enforced by the thing being checked. Terraphim inverts it: the checklist lives outside the model, in the same structure that powers &lt;a href="https://terraphim.ai/docs/graph-embeddings/" rel="noopener noreferrer"&gt;graph embeddings&lt;/a&gt; elsewhere on this site.&lt;/p&gt;

&lt;p&gt;A checklist item set looks like this in the knowledge graph. This is &lt;code&gt;bun install.md&lt;/code&gt; from the rewrite graph on this machine (committed verbatim at &lt;code&gt;.terraphim/rewrite/bun install.md&lt;/code&gt; in this site's repository):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# bun add&lt;/span&gt;

Install dependencies using Bun package manager.

synonyms:: npm install, yarn install, pnpm install, npm i, yarn add, pnpm add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The filename gives the normalised term the hook reports back (the suggestion in the findings output below is &lt;code&gt;bun install&lt;/code&gt;, matching &lt;code&gt;bun install.md&lt;/code&gt;), while the heading names the approved form inside the file. The &lt;code&gt;synonyms::&lt;/code&gt; line is the checklist itself: every surface form that must be recognised and dealt with. Compile the directory of such files into &lt;code&gt;thesaurus.json&lt;/code&gt; and you get the "Terraphim Engineer" role used below, currently 219 entries. Rebuilding the automaton after an edit takes about 20 ms, a figure we verified in &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt;, so the checklist is cheap to change and free to consult.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before the LLM Call: Retrieval as Checklist Execution
&lt;/h2&gt;

&lt;p&gt;The first job of a checklist is deciding what goes into the prompt. "Gather relevant context" is vague; "which of the enumerated terms in this role's graph appear in the repository, and what do they pull in" is a check.&lt;/p&gt;

&lt;p&gt;Here is the query I ran over this site's own source while preparing this article, using the Terraphim Engineer thesaurus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-grep &lt;span class="s2"&gt;"quality gate"&lt;/span&gt; &lt;span class="nt"&gt;--haystack&lt;/span&gt; all &lt;span class="nt"&gt;--paths&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; ~/.config/terraphim/thesaurus.json &lt;span class="nt"&gt;-n&lt;/span&gt; 8 &lt;span class="nt"&gt;-C&lt;/span&gt; 1 &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result, abridged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"stats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"search_latency_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chunks_returned"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kg_hits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"concepts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accept"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"escalate"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fail"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"improve"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"judge-verdicts"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"needs improvement"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pass"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth pausing on. First, the concepts list is the checklist executing: the graph expanded "quality gate" into its item set (accept, fail, escalate, pass, judge-verdicts and friends) and matched those terms across the tree. Second, the chunks it returned came from &lt;code&gt;.github/workflows/*.yml&lt;/code&gt; and &lt;code&gt;campaigns/skills-2026/*&lt;/code&gt;, files that contain none of the words "quality" or "gate". They matched on &lt;code&gt;fail&lt;/code&gt;, &lt;code&gt;curl --fail&lt;/code&gt;, &lt;code&gt;accepted&lt;/code&gt;: items on the checklist, not words in the query. Fifty-three milliseconds, no embedding model, and every hit can name the edge that produced it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The honest empty case
&lt;/h3&gt;

&lt;p&gt;A checklist that cannot say "not covered" is useless, so here is the control run against the same thesaurus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-grep &lt;span class="s2"&gt;"checklist"&lt;/span&gt; &lt;span class="nt"&gt;--haystack&lt;/span&gt; all &lt;span class="nt"&gt;--paths&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; ~/.config/terraphim/thesaurus.json &lt;span class="nt"&gt;-n&lt;/span&gt; 8 &lt;span class="nt"&gt;-C&lt;/span&gt; 2 &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"kg_hits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"sufficiency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SearchOnly"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"sufficiency_explanation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Found 4 chunks but KG confidence 0.00 &amp;lt; 0.50,
  diversity 1 &amp;lt; 2; returning search results only (pass --answer or
  --force-rlm to synthesise)."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero KG hits, four plain lexical chunks, and an explicit admission that graph confidence was too low to claim concept coverage. The tool does not pretend the checklist applied. If you want "checklist" to be a graph concept, you write it into a &lt;code&gt;kg/*.md&lt;/code&gt; file with a &lt;code&gt;synonyms::&lt;/code&gt; line and rebuild; the gap is a text edit, not a retraining run. The project-local variant of this workflow (a &lt;code&gt;.terraphim/&lt;/code&gt; directory in your repo with its own role thesaurus) is documented in the &lt;a href="https://terraphim.ai/posts/skill-spotlight-terraphim-grep/" rel="noopener noreferrer"&gt;terraphim-grep offline setup&lt;/a&gt; and the concepts can be enriched with code anchors mined by ast-grep.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reproduce it
&lt;/h3&gt;

&lt;p&gt;The artefacts behind these outputs are committed. This site's repository carries a &lt;code&gt;.terraphim/&lt;/code&gt; directory: &lt;code&gt;kg/&lt;/code&gt; holds the concept files used above (&lt;code&gt;validation gate&lt;/code&gt;, &lt;code&gt;judge-verdicts&lt;/code&gt;, &lt;code&gt;hook&lt;/code&gt;, &lt;code&gt;grounding gate&lt;/code&gt; and friends), &lt;code&gt;rewrite/&lt;/code&gt; holds the hook checklist, and &lt;code&gt;thesaurus.json&lt;/code&gt; is the compiled 39-entry public subset of the working role. Run the same command against it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-grep &lt;span class="s2"&gt;"quality gate"&lt;/span&gt; &lt;span class="nt"&gt;--haystack&lt;/span&gt; all &lt;span class="nt"&gt;--paths&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; .terraphim/thesaurus.json &lt;span class="nt"&gt;-n&lt;/span&gt; 8 &lt;span class="nt"&gt;-C&lt;/span&gt; 1 &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same eight concepts, same honest &lt;code&gt;kg_hits: 0&lt;/code&gt; on the "checklist" control. For larger published checklist graphs, &lt;code&gt;docs/src/kg/checklists/&lt;/code&gt; in the terraphim-ai repository carries the code review and security concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  After the LLM Call: The Hook Is a Findings Checklist
&lt;/h2&gt;

&lt;p&gt;The second job of a checklist is gating what comes back. In current releases, &lt;code&gt;terraphim-agent hook&lt;/code&gt; does not silently rewrite the agent's proposed action; it returns a machine-readable findings list. That distinction matters: the hook's job is to &lt;em&gt;check&lt;/em&gt;, and a check that mutates silently is a check you cannot audit.&lt;/p&gt;

&lt;p&gt;Run from the directory holding the rewrite config, against a tool call an agent might propose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_name":"Bash","tool_input":{"command":"npm install react"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | terraphim-agent hook &lt;span class="nt"&gt;--hook-type&lt;/span&gt; pre-tool-use &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tool_input"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"npm install react"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"validations"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"findings"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"matched_term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"npm install"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"suggested_replacement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"bun install"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="nl"&gt;"has_findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that output as a completed checklist line: item "npm install" found, prescribed action "bun install", flag raised. The hook types cover the lifecycle (&lt;code&gt;pre-tool-use&lt;/code&gt;, &lt;code&gt;post-tool-use&lt;/code&gt;, &lt;code&gt;pre-commit&lt;/code&gt;, &lt;code&gt;prepare-commit-msg&lt;/code&gt;), and &lt;code&gt;--with-guard&lt;/code&gt; is on by default for pre-tool-use so destructive commands get intercepted in the same pass. We covered the git-hook end of this in &lt;a href="https://terraphim.ai/posts/teaching-ai-agents-with-knowledge-graphs/" rel="noopener noreferrer"&gt;Teaching AI Coding Agents with Knowledge Graph Hooks&lt;/a&gt; and the end-to-end verified flow in &lt;a href="https://terraphim.ai/posts/native-hook-support/" rel="noopener noreferrer"&gt;Native Hook Support&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the checklist itself fails
&lt;/h3&gt;

&lt;p&gt;Checklists also cover the checking tool. Two honest notes from this session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terraphim-agent learn query "checklist"&lt;/code&gt; returned a list of past failed commands (exit-1 runs captured by the PostToolUse hook); the learning store is doing its job remembering what went wrong before.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraphim-agent replace&lt;/code&gt; in this build reports &lt;code&gt;replacements: 1, "changed": true&lt;/code&gt; while returning the input unchanged. The findings-based &lt;code&gt;hook&lt;/code&gt; path above is the contract to rely on; if &lt;code&gt;replace&lt;/code&gt; is your integration point, verify the output rather than the flag. This is itself a captured learning; see &lt;a href="https://terraphim.ai/posts/teaching-ai-agents-to-learn-from-mistakes/" rel="noopener noreferrer"&gt;Teaching AI Agents to Learn from Their Mistakes&lt;/a&gt; for how such a failure becomes a stored correction and eventually a new synonym.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Loop, Closed
&lt;/h2&gt;

&lt;p&gt;Put the phases together and the checklist lifecycle is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Declare&lt;/strong&gt;: write &lt;code&gt;kg/*.md&lt;/code&gt; files; heading = normalised item, &lt;code&gt;synonyms::&lt;/code&gt; = surface forms to catch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile&lt;/strong&gt;: generate &lt;code&gt;thesaurus.json&lt;/code&gt;; ~20 ms per role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve (pre-LLM)&lt;/strong&gt;: &lt;code&gt;terraphim-grep&lt;/code&gt; with the role thesaurus expands queries into concept terms and pulls grounded chunks — or honestly reports &lt;code&gt;kg_hits: 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft&lt;/strong&gt;: the LLM call runs with checklist-shaped context instead of a vibes-based prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate (post-LLM)&lt;/strong&gt;: &lt;code&gt;terraphim-agent hook&lt;/code&gt; emits findings; guards intercept destructive commands; failed commands are captured by the learning hook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct&lt;/strong&gt;: &lt;code&gt;learn correct&lt;/code&gt; / &lt;code&gt;learn correction add&lt;/code&gt; turn observed failures into reviewed rules; &lt;code&gt;memory capture&lt;/code&gt; stores authored lessons; the synonyms grow and the automaton recompiles.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 6 is what makes this different from a static lint list. Corrections feed back into the graph, so the checklist the next session runs against is the checklist this session finished — a property the &lt;code&gt;learn&lt;/code&gt;/&lt;code&gt;memory&lt;/code&gt; subcommands exist to provide, described in the &lt;a href="https://terraphim.ai/posts/skill-spotlight-learning-capture/" rel="noopener noreferrer"&gt;learning capture spotlight&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Not a Vector Problem
&lt;/h2&gt;

&lt;p&gt;A vector index answers "what is near this?" A checklist asks "which of these exact items occurred, and what was the prescribed response?" Those are different questions with different audit requirements. Graph embeddings give you the second in tens of milliseconds on a laptop, with an explainable edge for every match and no network call. The full performance case, including the 1.4-million-pattern sub-millisecond figure, is in &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt; and &lt;a href="https://terraphim.ai/posts/sub-millisecond-context-knowledge-graphs/" rel="noopener noreferrer"&gt;Sub-Millisecond Context with Knowledge Graphs&lt;/a&gt;; the mechanism is on the &lt;a href="https://terraphim.ai/docs/graph-embeddings/" rel="noopener noreferrer"&gt;Graph Embeddings reference&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;If you want the wiring for your own agent setup, the &lt;a href="https://terraphim.ai/how-tos/command-rewriting-howto/" rel="noopener noreferrer"&gt;command rewriting how-to&lt;/a&gt; walks through where the synonym files live, how the role graph is built, and how host hooks call the matcher. Write the checklist once; let the automaton enforce it on both sides of the call.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>knowledgegraph</category>
      <category>agents</category>
      <category>contextengineering</category>
    </item>
    <item>
      <title>Context Engineering: the book is out</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Mon, 19 Oct 2026 10:58:34 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/context-engineering-the-book-is-out-4mpa</link>
      <guid>https://dev.to/alexmikhalev/context-engineering-the-book-is-out-4mpa</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimg24xtlfdzw3bcrqiw5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimg24xtlfdzw3bcrqiw5.jpg" alt="Context Engineering book cover: Designing AI That Understands, Remembers, Acts and Learns, Dr Alexander Mikhalev" width="800" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most AI systems don't fail because the model is wrong.&lt;/p&gt;

&lt;p&gt;They fail because the context is.&lt;/p&gt;

&lt;p&gt;But here's the uncomfortable part: context engineering isn't a new activity. We've been doing it for decades under different names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration management gave us baselines and change control. Systems engineering gave us decision gates and requirements traceability.&lt;/li&gt;
&lt;li&gt;Domain-driven design gave us the ubiquitous language and bounded contexts. The systems-thinking tradition gave us a method for building explanations rather than narratives.&lt;/li&gt;
&lt;li&gt;Then LLMs arrived, and a lot of that discipline was set aside. Context became "the prompt" — something to tweak, not something to version, validate and govern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the argument in my book, Context Engineering: Designing AI That Understands, Remembers, Acts, and Learns.&lt;/p&gt;

&lt;p&gt;It isn't a new invention. It's an old discipline applied to a new kind of system — one where the "software" is partly prompts, partly knowledge, partly retrieval, partly policy, and partly the model's emergent behaviour.&lt;/p&gt;

&lt;p&gt;Inside, 25 chapters cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why AI software fails differently — context drift, not crashes&lt;/li&gt;
&lt;li&gt;knowledge graphs as context infrastructure, and when deterministic search beats vector embeddings&lt;/li&gt;
&lt;li&gt;token budgets, compression and skill architecture&lt;/li&gt;
&lt;li&gt;judge harnesses, typed decision models and evidence lineage — how to make an AI's decisions inspectable&lt;/li&gt;
&lt;li&gt;validation gates, multi-agent context and enterprise governance&lt;/li&gt;
&lt;li&gt;building a context engineering practice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's written for senior developers, engineering managers and architects who need AI to be dependable in production, not just impressive in a demo.&lt;/p&gt;

&lt;p&gt;The book is out now on Kindle: &lt;a href="https://www.amazon.com/dp/B0HMZBWH5Q" rel="noopener noreferrer"&gt;amazon.com&lt;/a&gt; and &lt;a href="https://www.amazon.co.uk/dp/B0HMZBWH5Q" rel="noopener noreferrer"&gt;amazon.co.uk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Which older discipline do you think AI teams have forgotten most?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>book</category>
      <category>contextengineering</category>
      <category>knowledgegraphs</category>
    </item>
    <item>
      <title>3 Ways to Save Money on Tokens for Your AI Agents</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Mon, 19 Oct 2026 09:25:58 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/3-ways-to-save-money-on-tokens-for-your-ai-agents-ai7</link>
      <guid>https://dev.to/alexmikhalev/3-ways-to-save-money-on-tokens-for-your-ai-agents-ai7</guid>
      <description>&lt;p&gt;&lt;em&gt;Three levers we run in production: let a cheap model summarise the evidence, put expensive reasoning only where it earns its keep, and route every request through a proxy that knows the price of each lane.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your agent bill is mostly input tokens. Not the clever parts: the re-reading.&lt;/p&gt;

&lt;p&gt;Every turn, a coding agent replays its entire transcript to the provider. Add a repository to explore and a handful of tool outputs, and a single session can carry a hundred thousand tokens of context that the model reads over and over at full price. The frontier model you are paying for spends most of its expensive attention budget on text it has already seen, or text a much cheaper model could have filtered first.&lt;/p&gt;

&lt;p&gt;We run a fleet of overnight coding agents on a single machine, so we feel this directly. Over the past few months we have landed on three levers that cut token spend without cutting output quality. Each one is running in production today, and each one comes with a caveat we will not hide: we have measured the mechanisms, not the end-to-end savings. The arithmetic below uses published prices and our own logs; treat it as illustrative, not invoiced.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Search first, let a cheap model summarise
&lt;/h2&gt;

&lt;p&gt;The most expensive way to give a frontier model context is to hand it raw search results. The cheapest way is to let a small model read them first.&lt;/p&gt;

&lt;p&gt;The pattern looks like this. A local search tool retrieves candidate chunks from your codebase or knowledge base. Before those chunks reach your GPT-6 or Fable-class session, a lower-cost model synthesises them into a compact answer, with deterministic references back to the source. The expensive model receives the synthesis and the few references it may need to reopen, not ten near-duplicate excerpts.&lt;/p&gt;

&lt;p&gt;We do this with &lt;code&gt;terraphim-grep&lt;/code&gt; (part of the open-source &lt;a href="https://github.com/terraphim/terraphim-clients" rel="noopener noreferrer"&gt;terraphim-clients&lt;/a&gt; repository), which resolves query terms through a curated knowledge-graph thesaurus before ranking local matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENROUTER_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'deepseek/deepseek-v4-flash-0731'&lt;/span&gt;

terraphim-grep provider &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; docs/src/thesaurus.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--haystack&lt;/span&gt; code &lt;span class="nt"&gt;--paths&lt;/span&gt; crates &lt;span class="nt"&gt;-n&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--answer&lt;/span&gt; &lt;span class="nt"&gt;--force-rlm&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--answer&lt;/code&gt; requests an answer when the sufficiency check calls for one; &lt;code&gt;--force-rlm&lt;/code&gt; forces the synthesis path. Only the selected excerpts leave the machine, through OpenRouter to a DeepSeek-flash-class model. There is also &lt;code&gt;--search-only&lt;/code&gt;, which hard-disables synthesis and skips building the LLM client entirely, so a stray API key in the environment cannot cost a single network call.&lt;/p&gt;

&lt;p&gt;In our verified run, the raw chunk contents measured 505 characters, the evidence with source locations 1,108 characters, and the synthesised answer text 440 characters: the frontier model received about 40% of the context it would otherwise have been fed, with references it could reopen on demand.&lt;/p&gt;

&lt;p&gt;Two honest caveats. First, we measured characters, not tokens: the CLI does not expose token usage, so treat the ratio as directional. Second, the win is conditional on the handoff shape. The full answer object with all ten references measured 1,918 characters; forward that wholesale and the saving evaporates. Pass the synthesis plus the references needed for verification, nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Put expensive reasoning at the edges of the V, not in the middle
&lt;/h2&gt;

&lt;p&gt;The second lever is not a tool but a lifecycle decision: match the model's price to the phase of work.&lt;/p&gt;

&lt;p&gt;We structure agent work as a V-model. The left side asks "what should we build" (research, design, specification); the right side asks "did we build it correctly" (review, validation, judge panels). The long middle (implementation, test-writing, mechanical verification loops) is where most of the tokens burn, and it is precisely the part that needs the least reasoning depth.&lt;/p&gt;

&lt;p&gt;So we split the model placement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edges get the expensive models.&lt;/strong&gt; Research synthesis, architecture decisions, and final review stay on high-reasoning lanes, because a bad plan or a rubber-stamped review costs more than the tokens save.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The middle runs on fast delivery models.&lt;/strong&gt; Implementation and test loops run on grok, MiniMax, or Kimi highspeed lanes over flat-rate subscriptions. Our overnight agents execute on &lt;code&gt;opencode-go&lt;/code&gt; subscription models; our judge panels run on Kimi K2.5, which reaches 90% verdict agreement with human reviewers at around nine seconds per review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The arithmetic is worth doing once. At published API prices, an illustrative workload of 100 million input and 20 million output tokens costs $800 if everything runs on a frontier model. Route 80% of it to a mid-tier model and the same token volume costs $480: 40% less, with the model count unchanged. That is a counterfactual at published prices, not a measured bill, but the shape of the saving is real: you are not paying for fewer tokens, you are paying less per token for the phases that do not need frontier reasoning.&lt;/p&gt;

&lt;p&gt;The catch is discipline. Left to itself, an agent uses whatever model it started on for everything. The phases have to be explicit, which is why this lever only works inside a structured lifecycle rather than a free-running session.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Put a proxy in front of the providers
&lt;/h2&gt;

&lt;p&gt;The third lever makes the cheap route the default: an LLM proxy between the agent and every provider, routing each request by what it contains or how fast each lane currently is.&lt;/p&gt;

&lt;p&gt;Ours is &lt;code&gt;terraphim-llm-proxy&lt;/code&gt;, shared with our &lt;a href="https://github.com/sponsors/terraphim" rel="noopener noreferrer"&gt;GitHub Sponsors&lt;/a&gt; supporters. It sits on localhost, exposes OpenAI and Anthropic compatible endpoints, and decides where each request goes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyword routing.&lt;/strong&gt; The proxy matches prompt content against a markdown taxonomy using Aho-Corasick string matching (microsecond overhead, the same crate our knowledge graph uses). A prompt that says "think step by step about this architecture" routes to the &lt;code&gt;think&lt;/code&gt; chain regardless of the model ID the client requested. In our logs, three consecutive requests landed on three different providers under three different scenarios, transparently to the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed and price routing.&lt;/strong&gt; Eligible candidates can be ranked by time to first token, or by cost class: free lanes first, then subscription, then metered. Per scenario you choose whether latency or price wins. Agent idle time waiting on a rate limit is a real cost, just billed in hours rather than tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback chains make the cheap lanes safe.&lt;/strong&gt; Cheap providers have real reliability gaps: silent API format changes, undocumented capacity errors, higher latency from Europe. Pipe-delimited fallback chains mean a dead provider just moves the request to the next target. We verify this with deliberate failure injection (pointing the primary provider's domain at localhost in &lt;code&gt;/etc/hosts&lt;/code&gt;) rather than trusting the config.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One caveat that took us by surprise: &lt;strong&gt;prompt caches are per model, not per session&lt;/strong&gt;. Measured across real coding sessions, 82% of mid-session model switches collapsed the cache read below 10% of its previous value: a warm 107,000-token prefix became 110,000 tokens of uncached input, roughly ten times the cached price for the same text. The saving pattern is therefore to route the session or the scenario, not each turn at random. Pick a lane for the job and stay on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we do not claim
&lt;/h2&gt;

&lt;p&gt;Token savings are not measured end-to-end. The synthesis example is measured in characters, not tokens; the model-mix example is arithmetic at published prices, not an invoice; the 82% cache-collapse figure comes from one operator's session logs, not a fleet benchmark. The honest metric is quality-adjusted cost per accepted task, and we are still building the instrumentation to report it.&lt;/p&gt;

&lt;p&gt;What we can claim is mechanism: retrieval that filters before the expensive model reads, a lifecycle that spends reasoning budget where judgement lives, and a proxy that prices the lanes before it forwards the request. None of these require trusting a benchmark. All three are running today.&lt;/p&gt;

&lt;p&gt;The underlying pieces have full write-ups: &lt;a href="https://terraphim.ai/posts/skill-spotlight-terraphim-grep/" rel="noopener noreferrer"&gt;terraphim-grep and role-aware search&lt;/a&gt; and &lt;a href="https://terraphim.ai/posts/disciplined-engineering-ai-systems/" rel="noopener noreferrer"&gt;the V-model for AI agents&lt;/a&gt;. The grep tooling is in &lt;a href="https://github.com/terraphim/terraphim-clients" rel="noopener noreferrer"&gt;terraphim-clients&lt;/a&gt;. The routing proxy from section 3 is &lt;code&gt;terraphim-llm-proxy&lt;/code&gt;, shared with &lt;a href="https://github.com/sponsors/terraphim" rel="noopener noreferrer"&gt;GitHub Sponsors&lt;/a&gt; supporters. If you want this running inside your team rather than as homework, we do it as part of our consulting work at &lt;a href="https://zestic.ai" rel="noopener noreferrer"&gt;zestic.ai&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Testing a learning hook from failed command to real execution</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Tue, 13 Oct 2026 08:55:34 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/testing-a-learning-hook-from-failed-command-to-real-execution-4bgn</link>
      <guid>https://dev.to/alexmikhalev/testing-a-learning-hook-from-failed-command-to-real-execution-4bgn</guid>
      <description>&lt;h2&gt;
  
  
  Update: what the model requested, and what actually ran
&lt;/h2&gt;

&lt;p&gt;The hook rewrites the command; OpenCode executes it. I extended the included adapter so one execution record now links the original request, matching compiled rule ID and graph hash, final executed command, tool result and saved output file. The result file carries the same execution ID and its SHA-256 is checked.&lt;/p&gt;

&lt;p&gt;The new 66-second demo uses actual saved outputs and synthetic British narration. All 22 regression tests pass, and the downloadable ZIP was extracted and run again with released terraphim-agent 1.21.19 and OpenCode 2.0.20. The adapter source is version 2.0.0; this covers the public example’s sequential whole-command mapping.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terraphim.ai/how-tos/command-rewriting-howto/" rel="noopener noreferrer"&gt;Watch the new demo&lt;/a&gt; · &lt;a href="https://terraphim.ai/demos/hooks-execution-evidence/terraphim-hooks-execution-evidence-v2.zip" rel="noopener noreferrer"&gt;Download the source, tests and saved evidence&lt;/a&gt; · &lt;a href="https://terraphim.ai/demos/hooks-execution-evidence/execution-receipt.json" rel="noopener noreferrer"&gt;Inspect the execution receipt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original walkthrough below retains its original recorded outputs.&lt;/p&gt;




&lt;p&gt;The adapter normalizes OpenCode V2 after-tool events for learn hook and applies the pre-tool result through mutable event.input. A failure-record correction is explicitly promoted to a tool-preference record, then compiled to the role thesaurus. Returned JSON and actual shell execution are verified separately.&lt;/p&gt;

&lt;p&gt;A coding agent uses the wrong flag. You explain the correction, and the command works. The useful question is what happens when another session makes the same mistake.&lt;/p&gt;

&lt;p&gt;This walkthrough follows one harmless error from failure to saved correction to actual execution. It uses the Harbour project brief from the &lt;a href="https://terraphim.ai/posts/cold-start-tax-agentic-memory/" rel="noopener noreferrer"&gt;memory walkthrough&lt;/a&gt;, a released Terraphim agent, and an OpenCode adapter included in the download.&lt;/p&gt;

&lt;p&gt;Watch the narrated demonstration: &lt;a href="https://terraphim.ai/how-tos/command-rewriting-howto/" rel="noopener noreferrer"&gt;https://terraphim.ai/how-tos/command-rewriting-howto/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terraphim.ai/demos/agent-hooks/harbour-hooks-example.zip" rel="noopener noreferrer"&gt;Download the Harbour hooks example&lt;/a&gt; · &lt;a href="https://terraphim.ai/demos/agent-hooks/transcript.txt" rel="noopener noreferrer"&gt;Read the transcript&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the command that fails
&lt;/h2&gt;

&lt;p&gt;Harbour's validator checks that its brief contains a decision, a risk and an owner. The correct command ends with &lt;code&gt;--check&lt;/code&gt;. In the baseline run, OpenCode actually attempts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 validate-brief.py &lt;span class="nt"&gt;--chek&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The argument is misspelled. Python's argument parser reports that &lt;code&gt;--check&lt;/code&gt; is required and exits with status 2. No &lt;code&gt;validation.json&lt;/code&gt; is created. The downloadable trace contains the host's actual tool result, including that output and exit status.&lt;/p&gt;

&lt;p&gt;That distinction matters: the coding host itself can finish normally after reporting a failed tool. Its overall process exit code is not evidence that the validator succeeded. Here we inspect the shell tool's result and the expected output file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capture the failure that really happened
&lt;/h2&gt;

&lt;p&gt;The included project-local adapter receives OpenCode's after-tool event. It translates the real command, combined output and exit status into Terraphim's learning-hook schema, then calls the released agent.&lt;/p&gt;

&lt;p&gt;The saved Markdown learning contains the command, exit code 2 and the parser's actual error. A new CLI process can list it again. This gives the correction a concrete source: an observed failure, with a saved record that can be inspected.&lt;/p&gt;

&lt;p&gt;The adapter is part of this example. In the tested release, passing a native OpenCode event directly with &lt;code&gt;--format opencode&lt;/code&gt; does not by itself establish that a learning was captured. The script checks the resulting file and later readback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review and promote the correction
&lt;/h2&gt;

&lt;p&gt;We add the intended command to the failure record with &lt;code&gt;learn correct&lt;/code&gt;. That records a proposed correction. It does not automatically make it a replacement rule: compiling the failure record alone produces zero correction entries in this release.&lt;/p&gt;

&lt;p&gt;The next step is explicit human review and promotion to a tool preference. The reproduction script runs this command inside its isolated store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-agent learn correction add &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--correction-type&lt;/span&gt; tool-preference &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--original&lt;/span&gt; &lt;span class="s1"&gt;'python3 validate-brief.py --chek'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--corrected&lt;/span&gt; &lt;span class="s1"&gt;'python3 validate-brief.py --check'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="s1"&gt;'Manually reviewed correction from the captured failure'&lt;/span&gt;
terraphim-agent learn compile &lt;span class="nt"&gt;--output&lt;/span&gt; compiled-corrections.json
terraphim-agent learn export-kg &lt;span class="nt"&gt;--output&lt;/span&gt; kg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compilation now reports one correction. The selected &lt;strong&gt;Harbour Validator&lt;/strong&gt; role loads that compiled JSON. The exported Markdown makes the relationship readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# python3 validate-brief.py --check&lt;/span&gt;
synonyms:: python3 validate-brief.py --chek
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses the same explicit &lt;code&gt;synonyms::&lt;/code&gt; idea as the &lt;a href="https://terraphim.ai/how-tos/synonym-search/" rel="noopener noreferrer"&gt;editor-to-search flow&lt;/a&gt;. The correction says which command this project prefers. In this walkthrough the hook reads the compiled JSON; exporting a Markdown file alone is not the activation step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect the hook before involving execution
&lt;/h2&gt;

&lt;p&gt;First we call the pre-tool hook on a saved input. Without &lt;code&gt;--rewrite&lt;/code&gt;, the returned command keeps &lt;code&gt;--chek&lt;/code&gt; and includes a warning about a replaceable match. With &lt;code&gt;--rewrite&lt;/code&gt;, the returned JSON contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python3 validate-brief.py --check"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a hook result. It has not executed Python. The example retains both results so you can compare the disabled and enabled behavior. An unrelated command and a role without this correction remain unchanged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8uobg8gnbq3m50b56i0e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8uobg8gnbq3m50b56i0e.jpg" alt="The actual hook outputs preserve the typo with rewriting disabled and return the corrected command with rewriting enabled" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify what the coding host actually runs
&lt;/h2&gt;

&lt;p&gt;Now start a fresh OpenCode process and ask it to try the same misspelled command. Its before-tool event reaches the adapter. The adapter calls Terraphim with rewriting enabled, then applies the returned command through OpenCode V2's mutable tool input.&lt;/p&gt;

&lt;p&gt;The after-tool event records &lt;code&gt;python3 validate-brief.py --check&lt;/code&gt;. Its exit status is 0. The validator prints a valid Harbour result and writes this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harbour"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Decision"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Risk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Owner"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The host transcript can still display the model's original attempted input. That is why the download includes adapter events, the actual tool result and &lt;code&gt;validation.json&lt;/code&gt;, as well as the transcript. Together they show the attempted command, the applied correction and the resulting execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ehtidje94i0xfo9tp48.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ehtidje94i0xfo9tp48.jpg" alt="The real after-tool event and saved validation JSON confirm exit zero and the expected Harbour fields" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it in a new folder
&lt;/h2&gt;

&lt;p&gt;The example was verified with &lt;a href="https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.19" rel="noopener noreferrer"&gt;terraphim-agent v1.21.19&lt;/a&gt; and OpenCode 2.0.20. Follow the ZIP's README to check the release archive, choose your already authenticated coding model and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 reproduce.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--agent&lt;/span&gt; /absolute/path/to/terraphim-agent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; kimi-code-plan-global/k3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; /absolute/path/to/a-new-harbour-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use your configured model identifier if it differs. The script uses a new folder for the fixture, settings, learning store and project-local adapter. It does not install global hooks or select a paid fallback. The recorded run used the existing Kimi coding-plan route.&lt;/p&gt;

&lt;p&gt;Read &lt;code&gt;evidence/verification.json&lt;/code&gt;, the host events and &lt;code&gt;validation.json&lt;/code&gt;. The script fails if the host does not perform the expected command or the saved outputs do not match. You can inspect the source before running it; only the two local validator commands are permitted by the fixture's host configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the scope of the example visible
&lt;/h2&gt;

&lt;p&gt;This demonstrates one reviewed correction with one tested host adapter. It does not prove every coding host applies the same JSON, or that failure capture should automatically approve arbitrary substitutions. The earlier memory example and this learning store also remain separate; neither silently copies into the other.&lt;/p&gt;

&lt;p&gt;Use public sample data for this release's learning capture. Tests with deliberately fake secret-shaped values found redaction gaps in saved command and working-directory fields, and in some hook passthrough values. The example makes no blanket secret-redaction claim. The coding host uses its configured online model; Terraphim's demonstrated correction lookup uses the local compiled graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terraphim.ai/demos/agent-hooks/harbour-hooks-example.zip" rel="noopener noreferrer"&gt;Download the example and saved outputs&lt;/a&gt; to follow the whole chain: a failure that happened, a correction someone reviewed, and a later command whose result can be checked.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Updated 13 October 2026. The video uses synthetic British female narration and readable excerpts from actual released-binary and host outputs. The Harbour project is fictional public sample data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>coding</category>
      <category>learning</category>
    </item>
    <item>
      <title>Testing authored memory across fresh CLI processes</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Tue, 13 Oct 2026 08:25:25 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/testing-authored-memory-across-fresh-cli-processes-4fmk</link>
      <guid>https://dev.to/alexmikhalev/testing-authored-memory-across-fresh-cli-processes-4fmk</guid>
      <description>&lt;p&gt;The example separates persistence from retrieval: an evolution-store record survives process exit, while a role thesaurus and co-occurrence graph select relevant memory items. Nine assertions check exact capture/readback/export, useful retrieval and negative controls. Roles are retrieval filters, not access controls.&lt;/p&gt;

&lt;p&gt;You explain the project's validation command. The session ends. Next time, the agent needs the same explanation. Keeping the instruction somewhere is useful only if the next run can find the right instruction again.&lt;/p&gt;

&lt;p&gt;This walkthrough saves one lesson from a fictional project, opens it in a new process, and retrieves it through a small knowledge graph. You can inspect the exact saved text and repeat the same checks with the downloadable example.&lt;/p&gt;

&lt;p&gt;Watch the narrated demonstration: &lt;a href="https://terraphim.ai/posts/cold-start-tax-agentic-memory/" rel="noopener noreferrer"&gt;https://terraphim.ai/posts/cold-start-tax-agentic-memory/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terraphim.ai/demos/agent-memory/harbour-memory-example.zip" rel="noopener noreferrer"&gt;Download the Harbour example&lt;/a&gt; · &lt;a href="https://terraphim.ai/demos/agent-memory/transcript.txt" rel="noopener noreferrer"&gt;Read the transcript&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with an instruction worth keeping
&lt;/h2&gt;

&lt;p&gt;Harbour is a small public project brief. It records a decision, a risk and an owner. Before publication, its validator checks that all three fields are present. The instruction we want the next session to remember is deliberately specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validate the Harbour project brief with python3 validate-brief.py --check.
Keep the source decisions in brief.md intact.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives a future agent an action and a boundary. “Remember to be careful” would be much harder to use. The example also includes the brief and validator, so the instruction has something concrete to refer to.&lt;/p&gt;

&lt;p&gt;The first step is to save this actual text. Download and extract the example, then install &lt;a href="https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.20" rel="noopener noreferrer"&gt;terraphim-agent v1.21.20&lt;/a&gt;. From the example folder, prepare a new, isolated run before entering the manual commands below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 reproduce.py &lt;span class="nt"&gt;--agent&lt;/span&gt; /path/to/terraphim-agent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ./harbour-run &lt;span class="nt"&gt;--prepare-only&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;harbour-run/fixture
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TERRAPHIM_DEFAULT_DATA_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .. &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/data"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TERRAPHIM_SETTINGS_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .. &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/settings"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use your actual binary path in the first command and put that same verified binary on your PATH for the remaining commands. Preparation creates the role configuration, settings and &lt;code&gt;lesson.md&lt;/code&gt;; it has not captured anything yet. To run all checks automatically instead, omit &lt;code&gt;--prepare-only&lt;/code&gt; and choose a fresh output folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-agent &lt;span class="nt"&gt;--format&lt;/span&gt; json memory capture &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--file&lt;/span&gt; lesson.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provenance-tag&lt;/span&gt; harbour-public-brief-v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capture returns a memory identifier. We use that identifier to inspect the saved item, rather than treating a successful exit code as the whole result. The saved content is the lesson itself, and the provenance tag identifies which public example supplied it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--content&lt;/code&gt; accepts a short instruction directly. &lt;code&gt;--file&lt;/code&gt; is convenient for a multiline note. Both preserve the supplied text. Missing, blank, unreadable or conflicting inputs fail without creating a substitute placeholder.&lt;/p&gt;

&lt;h2&gt;
  
  
  End the process, then open the memory
&lt;/h2&gt;

&lt;p&gt;Each command in the demonstration starts a new process. After capture exits, &lt;code&gt;memory show&lt;/code&gt; opens the saved identifier. &lt;code&gt;memory list&lt;/code&gt; finds the item, and &lt;code&gt;memory export&lt;/code&gt; includes the same instruction in JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-agent &lt;span class="nt"&gt;--format&lt;/span&gt; json memory show MEMORY_ID
terraphim-agent &lt;span class="nt"&gt;--format&lt;/span&gt; json memory list
terraphim-agent memory &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--output&lt;/span&gt; memories.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;MEMORY_ID&lt;/code&gt; with the identifier returned by capture. The reproduction script does this automatically and checks that the reopened and exported text exactly matches &lt;code&gt;lesson.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmurz1cq9dzy6kvtbvks.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmurz1cq9dzy6kvtbvks.jpg" alt="A new process displays the exact saved Harbour instruction and its provenance tag" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The store has an explicit location. The example sets &lt;code&gt;TERRAPHIM_DEFAULT_DATA_PATH&lt;/code&gt; to a new folder for that run; the evolution file lives beneath &lt;code&gt;terraphim/evolution/cli-agent.json&lt;/code&gt;. A second empty folder supplies the independent-store check. This keeps the demonstration separate from your existing memory files.&lt;/p&gt;

&lt;p&gt;Without an explicit data root, the agent retains its usual platform configuration-directory location. Changing the root selects another store; it does not silently migrate or merge your previous one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Teach the role the words used by the project
&lt;/h2&gt;

&lt;p&gt;Knowing an identifier is useful for inspection. Day to day, you are more likely to ask a question such as “validate brief”. The &lt;strong&gt;Brief Reviewer&lt;/strong&gt; role supplies the terminology for that question.&lt;/p&gt;

&lt;p&gt;Its small graph has a &lt;strong&gt;Brief validation&lt;/strong&gt; concept:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Brief validation&lt;/span&gt;
synonyms:: validate brief, brief validation, --check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A second concept describes the &lt;strong&gt;Project brief&lt;/strong&gt;, including &lt;code&gt;brief.md&lt;/code&gt;. Both definitions are ordinary Markdown in the download. The saved instruction mentions these concepts, connecting an action with the document it applies to.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;synonyms::&lt;/code&gt; property makes the relationship visible: “validate brief” and &lt;code&gt;--check&lt;/code&gt; are labels for the same concept in this project. You can review or change that relationship. Saving a memory does not invent new terminology or update every other role automatically.&lt;/p&gt;

&lt;p&gt;This is the same explicit-knowledge principle used in the &lt;a href="https://terraphim.ai/how-tos/synonym-search/" rel="noopener noreferrer"&gt;editor-to-search example&lt;/a&gt;: record the relationship, then use the configured graph to find relevant work. Here the searched material is an authored memory item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieve the instruction, then check the empty cases
&lt;/h2&gt;

&lt;p&gt;With the example's role configuration loaded, this query retrieves the stored lesson:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-agent &lt;span class="nt"&gt;--config&lt;/span&gt; ../config.json memory retrieve &lt;span class="s1"&gt;'validate brief'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="s1"&gt;'Brief Reviewer'&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result includes the original text, its memory identifier, the provenance tag and a matched concept. It is a retrieved instruction, not a generated paraphrase. The viewer can compare it directly with the file that was captured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmwf28yixgoi5osqr6f28.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmwf28yixgoi5osqr6f28.jpg" alt="The Brief Reviewer retrieves one saved lesson while unrelated-query, other-role and empty-store controls return zero" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The accompanying controls matter just as much. “Volcanic eruption” returns no memory. The &lt;strong&gt;Finance Reviewer&lt;/strong&gt; role, which has budget and grant concepts, does not match the validation query. The same useful query against a separate empty store also returns nothing.&lt;/p&gt;

&lt;p&gt;Those observations distinguish useful retrieval from a command that always prints the latest note. Roles shape which concepts are recognised. They are not an access-control system: use separate stores and ordinary filesystem permissions where separation is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens inside the CLI
&lt;/h2&gt;

&lt;p&gt;The agent stores the lesson using Terraphim's existing evolution types. During retrieval, the role's thesaurus matches terms, and the role graph ranks memory items by their concept relationships. There is no lexical fallback when a question matches none of the configured concepts.&lt;/p&gt;

&lt;p&gt;This graph uses co-occurrence edges, so the example intentionally connects validation with the project brief. A single isolated concept is not enough to assume the same retrieval behavior. Keep the graph and the note inspectable, and test the questions people will actually ask.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;memory apply&lt;/code&gt; can preview proposed context for a prompt. That preview does not execute a command or prove a coding host consumed it. The separate hooks walkthrough will follow the instruction into actual execution. Captured memories and reviewed learning corrections also use different stores; neither is an automatic copy of the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the next run yourself
&lt;/h2&gt;

&lt;p&gt;The ZIP contains the public brief, validator, lesson, Markdown concepts, reproduction script and saved outputs. Follow its README to verify the released binary, create an isolated run and compare the positive and negative results. The script checks the saved content, rather than relying on expected screenshots.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terraphim.ai/demos/agent-memory/harbour-memory-example.zip" rel="noopener noreferrer"&gt;Download and reproduce the memory example&lt;/a&gt;. You can also inspect the &lt;a href="https://terraphim.ai/demos/agent-memory/manifest.json" rel="noopener noreferrer"&gt;saved outputs and verification manifest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The useful result here is modest and concrete: an instruction survives the process that captured it, and the intended project terminology can find it again. Measuring whether that saves tokens or improves later decisions needs a separate experiment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Updated 13 October 2026 with a reproducible walkthrough. The broader memory-lifecycle vocabulary was inspired by &lt;a href="https://www.memco.ai/field-guide" rel="noopener noreferrer"&gt;memco.ai's field guide&lt;/a&gt;. The video uses synthetic British female narration and readable views of actual CLI outputs. The Harbour fixture is public sample data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>cli</category>
      <category>testing</category>
    </item>
    <item>
      <title>From editor alternatives to synonym-aware search with Terraphim</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Mon, 12 Oct 2026 10:45:02 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/from-editor-alternatives-to-synonym-aware-search-with-terraphim-12g9</link>
      <guid>https://dev.to/alexmikhalev/from-editor-alternatives-to-synonym-aware-search-with-terraphim-12g9</guid>
      <description>&lt;p&gt;&lt;a href="https://x.com/jasonfried/status/2105403067793584590" rel="noopener noreferrer"&gt;Jason Fried shared a video of Write_On&lt;/a&gt;, and it clicked. I had a Terraphim Editor built in Rust and WebAssembly that I’d abandoned two years ago. His idea of “alternative control” gave me a meaningful way to return to it: keep the alternatives we choose while writing connected to a knowledge graph, then use that same graph to find our work. Now I can finish the editor with a clear purpose.&lt;/p&gt;

&lt;p&gt;I’m building Terraphim around a small frustration in knowledge work: we change our words, then lose track of the ideas behind them.&lt;/p&gt;

&lt;p&gt;A project brief calls something a risk. A colleague asks about their concerns. A literal search can miss the useful decision sitting beside the word neither person remembered to type.&lt;/p&gt;

&lt;p&gt;We made an 80-second demonstration of a different flow. It starts with choosing a word in an editor and ends with finding a decision from the command line. The connection is an explicit, exportable knowledge graph.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/zZWjuqqVN4I" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://terraphim.ai/how-tos/synonym-search/" rel="noopener noreferrer"&gt;Try the editor and download the actual files&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the sentence
&lt;/h2&gt;

&lt;p&gt;The example is a public community garden brief. Timber might arrive late, rain might delay planting, and volunteers might cancel. Each risk has a decision beside it: keep a backup supplier, reserve a covered area, or keep a reserve list.&lt;/p&gt;

&lt;p&gt;In Terraphim Editor, I add “Concern” as an alternative to “Risk”. I can swap between the two without losing the other wording. For the final clean document, I keep “Risk”. The word “Concern” is absent.&lt;/p&gt;

&lt;p&gt;That last detail matters. Otherwise a successful search for “concern” could simply be a literal match dressed up as a knowledge-graph demonstration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the relationship inspectable
&lt;/h2&gt;

&lt;p&gt;The editor’s Knowledge graph export shows the authored relationship:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;synonyms:: Risk, Concern&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Those alternatives refer to the same idea in this document. I download the graph as a Terraphim thesaurus and export the clean text. This is an explicit sharing step; creating an alternative does not silently change every role or every other application’s graph.&lt;/p&gt;

&lt;p&gt;The downloads are part of the demonstration. You can inspect the graph, reopen the Markdown with alternatives, and compare it with the clean brief.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search with the meaning you just recorded
&lt;/h2&gt;

&lt;p&gt;I pass that exported graph to terraphim-grep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-grep concern &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; knowledge-graph.json &lt;span class="nt"&gt;--paths&lt;/span&gt; corpus &lt;span class="nt"&gt;-C&lt;/span&gt; 2 &lt;span class="nt"&gt;--search-only&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It finds all three “Risk” passages and the decisions next to them, even though “Concern” does not appear in the clean text.&lt;/p&gt;

&lt;p&gt;The released-binary verification also includes a baseline. With the editor’s default role, which does not contain this authored synonym, the query returns zero passages. Supplying the exported graph produces three. That makes the effect of sharing the synonym visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide whether you need a generated answer
&lt;/h2&gt;

&lt;p&gt;Search alone gives the source passages and their locations. Replace &lt;code&gt;--search-only&lt;/code&gt; with &lt;code&gt;--answer&lt;/code&gt; to request synthesis from your configured model provider.&lt;/p&gt;

&lt;p&gt;The paired results retrieve the same three passages. The generated answer adds a summary with references; it does not replace the evidence. It also should not turn a decision into a completed action. “Keep a backup supplier” is a recorded decision, not proof that someone has already contacted one.&lt;/p&gt;

&lt;p&gt;Editing and local retrieval run locally. Optional synthesis sends the retrieved passages to the configured provider. A fresh answer may use different wording.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open the same example in Obsidian
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://terraphim.ai/how-tos/synonym-search/#try-it-with-an-obsidian-vault" rel="noopener noreferrer"&gt;Obsidian walkthrough&lt;/a&gt; now includes a &lt;a href="https://terraphim.ai/demos/obsidian-synonym-search/obsidian-synonym-search-example.zip" rel="noopener noreferrer"&gt;downloadable sample vault&lt;/a&gt; and an actual screenshot from the installed app.&lt;/p&gt;

&lt;p&gt;The raw &lt;code&gt;synonyms:: Risk, Concern&lt;/code&gt; property lives in &lt;code&gt;Notes/Risk.md&lt;/code&gt;. The separate project brief contains “Risk” and no literal “Concern”. The search-results note contains only saved CLI passages; it does not define the synonyms.&lt;/p&gt;

&lt;p&gt;Using released terraphim-grep 1.21.19 against &lt;code&gt;Notes/Projects&lt;/code&gt;, the baseline returns zero passages and the supplied synonym graph returns three exact passages at lines 7, 10 and 13. The concept note and saved results are outside that search corpus. The ZIP includes both graphs, source notes, actual outputs and a checksum manifest.&lt;/p&gt;

&lt;p&gt;Terraphim’s &lt;code&gt;synonyms::&lt;/code&gt; convention uses Logseq-compatible Markdown property syntax. Obsidian native aliases use YAML &lt;code&gt;aliases&lt;/code&gt;. This example passes the exported graph explicitly; saving the note does not automatically rebuild it. Obsidian displays saved CLI output, and this additional capture demonstrates retrieval only. The main narrated video separately compares synthesis off and on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open the same example in Logseq
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://terraphim.ai/how-tos/synonym-search/#try-it-with-a-logseq-graph" rel="noopener noreferrer"&gt;Logseq walkthrough&lt;/a&gt; includes a &lt;a href="https://terraphim.ai/demos/logseq-synonym-search/logseq-synonym-search-example.zip" rel="noopener noreferrer"&gt;downloadable graph&lt;/a&gt; and a screenshot from Logseq 2.0.2. Import the included Markdown graph through &lt;strong&gt;Import existing notes → File to DB graph&lt;/strong&gt;, then open &lt;strong&gt;Risk&lt;/strong&gt; and the &lt;strong&gt;Concern search&lt;/strong&gt; sidebar.&lt;/p&gt;

&lt;p&gt;The Risk concept owns &lt;code&gt;alias:: Concern&lt;/code&gt; and &lt;code&gt;synonyms:: Risk, Concern&lt;/code&gt;; the project brief holds the decisions. Native Logseq aliases give a page another name. Terraphim uses the explicitly supplied editor-exported graph for the CLI search.&lt;/p&gt;

&lt;p&gt;Run released terraphim-grep 1.21.19 from the extracted &lt;code&gt;graph&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-grep concern &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; baseline-role.json &lt;span class="nt"&gt;--paths&lt;/span&gt; pages/Projects &lt;span class="nt"&gt;-C&lt;/span&gt; 2 &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--search-only&lt;/span&gt;
terraphim-grep concern &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; knowledge-graph.json &lt;span class="nt"&gt;--paths&lt;/span&gt; pages/Projects &lt;span class="nt"&gt;-C&lt;/span&gt; 2 &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="nt"&gt;--search-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The baseline returns zero passages; the shared graph retrieves three decisions about a backup supplier, a covered area and a volunteer reserve list. The searched project notes never contain “concern”; concept definitions and saved results are outside the corpus. Logseq displays imported notes and saved CLI results. Its database import is a copy; the commands read the original Markdown folder.&lt;/p&gt;

&lt;p&gt;The graph-view screenshot shows the project brief connected to Risk, Start here and the saved-results page. Its arrows are native Logseq page links; the Risk note holds the synonym definition shared through the supplied Terraphim graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1oe38kjpj2x3l5ni61qs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1oe38kjpj2x3l5ni61qs.jpg" alt="The actual Logseq graph view beside saved CLI passages" width="799" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the released VS Code extension and the Kilo example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=applied-knowledge-systems.terraphim-it" rel="noopener noreferrer"&gt;Install Terraphim for VS Code&lt;/a&gt;, then create an alternative, save your document and explicitly export its knowledge graph. Keep the graph separate from the clean text you search.&lt;/p&gt;

&lt;p&gt;The tutorial also includes a &lt;a href="https://terraphim.ai/how-tos/synonym-search/#see-the-same-workflow-in-kilo" rel="noopener noreferrer"&gt;44-second captioned replay of a saved Kilo integration run&lt;/a&gt;. Its &lt;a href="https://terraphim.ai/demos/kilo-synonym-search/kilo-synonym-search-example.zip" rel="noopener noreferrer"&gt;example ZIP&lt;/a&gt; contains the actual graph, annotated Markdown, clean brief and results from both search modes. The replay is silent and retains the versions used in that run; the candidate Kilo marketplace skills are still under review.&lt;/p&gt;

&lt;p&gt;Extract that ZIP and run these commands from its folder with released terraphim-grep 1.21.20:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-grep Concern &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; knowledge-graph.json &lt;span class="nt"&gt;--paths&lt;/span&gt; corpus &lt;span class="nt"&gt;-n&lt;/span&gt; 3 &lt;span class="nt"&gt;--search-only&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
terraphim-grep Concern &lt;span class="nt"&gt;--thesaurus&lt;/span&gt; knowledge-graph.json &lt;span class="nt"&gt;--paths&lt;/span&gt; corpus &lt;span class="nt"&gt;-n&lt;/span&gt; 3 &lt;span class="nt"&gt;--answer&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first returns the source passages. The second uses your configured model provider to synthesize an answer from them. A fresh answer may differ from the saved example. I rechecked the downloaded graph and clean corpus with the released binary: an empty-graph baseline returned zero passages, and the exported synonym graph returned all three Risk passages. The corpus contains no literal Concern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the evidence, not just the video
&lt;/h2&gt;

&lt;p&gt;The tutorial includes the graph, clean brief, annotated Markdown, actual results with and without synthesis, captions, transcript, and a ZIP with reproduction instructions. Follow the included README to try the same search on your machine.&lt;/p&gt;

&lt;p&gt;“Grammarly for knowledge work” is the ambition: help with the wording now, and preserve enough meaning to make the work easier to find later.&lt;/p&gt;

&lt;p&gt;Which pairs of words cause missed searches in your project notes?&lt;/p&gt;

&lt;p&gt;Demo and reproducible downloads: &lt;a href="https://terraphim.ai/how-tos/synonym-search/" rel="noopener noreferrer"&gt;https://terraphim.ai/how-tos/synonym-search/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disclosure: I work on Terraphim. The example is public sample input; the graph exports and search results are actual outputs. The video uses synthetic British female narration. This article was prepared with AI assistance and checked against the actual outputs.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Plug Terraphim Search into Claude Code and opencode (CLI First, MCP When You Need It)</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Sat, 18 Apr 2026 08:53:07 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/plug-terraphim-search-into-claude-code-and-opencode-cli-first-mcp-when-you-need-it-4404</link>
      <guid>https://dev.to/alexmikhalev/plug-terraphim-search-into-claude-code-and-opencode-cli-first-mcp-when-you-need-it-4404</guid>
      <description>&lt;p&gt;Your AI coding agent already has a knowledge graph. It is just not yours yet. The model knows GitHub, Stack Overflow, and the public training corpus -- it has no idea that in your project &lt;code&gt;npm&lt;/code&gt; should be &lt;code&gt;bun&lt;/code&gt;, that &lt;code&gt;RFP&lt;/code&gt; is shorthand for &lt;code&gt;acquisition need&lt;/code&gt;, or that the email about the Stripe receipt for the Obsidian licence lives in your Fastmail mailbox. This post shows the smallest path to fixing that for both &lt;a href="https://claude.com/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; and &lt;a href="https://opencode.ai" rel="noopener noreferrer"&gt;opencode&lt;/a&gt;, using &lt;a href="https://terraphim.ai" rel="noopener noreferrer"&gt;Terraphim&lt;/a&gt; and the three roles we have published over the last week (Terraphim Engineer, &lt;a href="https://terraphim.ai/posts/personal-assistant-role-jmap-obsidian/" rel="noopener noreferrer"&gt;Personal Assistant&lt;/a&gt;, &lt;a href="https://terraphim.ai/posts/system-operator-logseq-knowledge-graph/" rel="noopener noreferrer"&gt;System Operator&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Two paths. CLI first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "integrate" means here
&lt;/h2&gt;

&lt;p&gt;The host -- Claude Code or opencode -- needs a way to ask your role-aware Terraphim setup a question and get back ranked, source-attributed results. The model decides when to ask. The role decides which haystacks to search. Terraphim's &lt;code&gt;terraphim-graph&lt;/code&gt; ranker decides which results come back first.&lt;/p&gt;

&lt;p&gt;Concrete example. You are working in opencode and you type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tsearch "System Operator" RFP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The slash command runs against the System Operator role. The role's knowledge graph normalises &lt;code&gt;RFP&lt;/code&gt; to its INCOSE-canonical form &lt;code&gt;acquisition need&lt;/code&gt;. The Aho-Corasick matcher walks the role's haystack (1,347 Logseq pages from the &lt;a href="https://github.com/terraphim/system-operator" rel="noopener noreferrer"&gt;terraphim/system-operator&lt;/a&gt; repository). The top hit comes back ranked 13 -- &lt;code&gt;Acquisition need.md&lt;/code&gt; -- with the &lt;code&gt;synonyms::&lt;/code&gt; line that mapped your query to it visible in the snippet. The model now has the right page in its context window and can answer your follow-up without a hallucinated INCOSE handbook reference.&lt;/p&gt;

&lt;p&gt;This works in both hosts because both speak the same two integration languages: shell-out slash commands and MCP servers. We are going to use both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path A -- CLI via slash command
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why this is the recommended starting point
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;terraphim-agent&lt;/code&gt; already exists, takes &lt;code&gt;--role&lt;/code&gt; and &lt;code&gt;--limit&lt;/code&gt;, and writes ranked results to stdout. There is nothing to build. Both Claude Code and opencode let slash commands shell out via Bash. So a two-line command file is the entire integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  One file, two hosts
&lt;/h3&gt;

&lt;p&gt;Drop this at &lt;code&gt;~/.claude/commands/tsearch.md&lt;/code&gt; (and an identical copy at &lt;code&gt;~/.config/opencode/command/tsearch.md&lt;/code&gt; -- both hosts read the same frontmatter shape):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Terraphim&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;across&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;configured&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;roles.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Usage:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/tsearch&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;[role]&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;query&amp;gt;"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash(terraphim-agent search:*), Bash(terraphim-agent-pa search:*)&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
Run &lt;span class="sb"&gt;`terraphim-agent search --role "&amp;lt;role&amp;gt;" --limit 5 "&amp;lt;query&amp;gt;"`&lt;/span&gt; (or
&lt;span class="sb"&gt;`terraphim-agent-pa search ...`&lt;/span&gt; if the role is "Personal Assistant" and
the query needs the JMAP haystack). Return the top results as a numbered
list with title, source path/URL, and a 120-char snippet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it. The &lt;code&gt;allowed-tools&lt;/code&gt; line auto-approves the two CLI invocations so the model does not have to ask permission per call. Restart the host (or reload commands) and &lt;code&gt;/tsearch&lt;/code&gt; is live.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why fast enough
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;terraphim-agent&lt;/code&gt; reads its persisted role state at start (low milliseconds), runs the query against the role's haystacks, and returns. For a typical knowledge-graph query against the Terraphim Engineer role on a laptop, the round trip from slash command to formatted output is well under a second. The agent already has the typed CLI -- &lt;code&gt;--role&lt;/code&gt;, &lt;code&gt;--limit&lt;/code&gt;, &lt;code&gt;--format json&lt;/code&gt; -- so there is nothing the MCP layer adds for the search-only flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three example queries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tsearch "Terraphim Engineer" rolegraph
/tsearch "System Operator" RFP
/tsearch "Personal Assistant" invoice    # uses terraphim-agent-pa wrapper for JMAP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Personal Assistant case is the most interesting because it crosses surfaces -- Obsidian notes interleave with &lt;code&gt;jmap:///email/&amp;lt;id&amp;gt;&lt;/code&gt; URLs from your Fastmail mailbox, ranked by the same &lt;code&gt;terraphim-graph&lt;/code&gt; scoring. The wrapper script injects &lt;code&gt;JMAP_ACCESS_TOKEN&lt;/code&gt; from 1Password at call time so the secret never lands on disk; the bare &lt;code&gt;terraphim-agent&lt;/code&gt; continues to work for the other five roles without paying the unlock cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Path B -- MCP server (when you want typed tools)
&lt;/h2&gt;

&lt;p&gt;The CLI path is enough for search. If you want the model to call &lt;code&gt;search&lt;/code&gt; as a first-class tool with structured JSON parameters -- alongside &lt;code&gt;autocomplete_terms&lt;/code&gt;, &lt;code&gt;autocomplete_with_snippets&lt;/code&gt;, four flavours of fuzzy autocomplete, &lt;code&gt;build_autocomplete_index&lt;/code&gt;, and &lt;code&gt;update_config_tool&lt;/code&gt; -- that is what &lt;code&gt;terraphim_mcp_server&lt;/code&gt; exposes. It reads the same &lt;code&gt;~/.config/terraphim/embedded_config.json&lt;/code&gt;, so the role list is identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build and install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/terraphim/terraphim-ai
cargo build &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; terraphim_mcp_server &lt;span class="nt"&gt;--features&lt;/span&gt; jmap
&lt;span class="nb"&gt;cp &lt;/span&gt;target/release/terraphim_mcp_server ~/.cargo/bin/terraphim_mcp_server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Personal Assistant role, mirror the existing &lt;code&gt;terraphim-agent-pa&lt;/code&gt; wrapper at &lt;code&gt;~/bin/terraphim_mcp_server-pa&lt;/code&gt; so the JMAP token flows through &lt;code&gt;op run&lt;/code&gt; instead of being baked into config.&lt;/p&gt;

&lt;h3&gt;
  
  
  Register
&lt;/h3&gt;

&lt;p&gt;opencode -- add to &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt; under &lt;code&gt;mcp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="nl"&gt;"terraphim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/Users/alex/.cargo/bin/terraphim_mcp_server"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"terraphim-pa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/Users/alex/bin/terraphim_mcp_server-pa"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code -- one shell command per server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add terraphim    /Users/alex/.cargo/bin/terraphim_mcp_server
claude mcp add terraphim-pa /Users/alex/bin/terraphim_mcp_server-pa
claude mcp list      &lt;span class="c"&gt;# both should show as Connected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model now sees &lt;code&gt;mcp__terraphim__search&lt;/code&gt; and &lt;code&gt;mcp__terraphim_pa__search&lt;/code&gt; (plus the autocomplete tools) in its tool list.&lt;/p&gt;

&lt;h2&gt;
  
  
  SessionStart primer (both paths)
&lt;/h2&gt;

&lt;p&gt;Slash commands and MCP tools are useless if the model does not know the roles exist. Extend the SessionStart hook in &lt;code&gt;~/.claude/settings.json&lt;/code&gt; to print a one-screen role index when each session starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\n--- Terraphim search via /tsearch [role] &amp;lt;query&amp;gt; ---\n'&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  Terraphim Engineer  (Rust/agent KG)\n'&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  Personal Assistant  (Obsidian + Fastmail JMAP, use terraphim-agent-pa for email)\n'&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  System Operator     (INCOSE/MBSE Logseq KG)\n'&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  Context Engineering Author, Rust Engineer, Default\n'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Equivalent hook in opencode. Cost: one screen of context per session. Benefit: the model picks the right role on the first try instead of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to pick which path
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CLI (Path A)&lt;/th&gt;
&lt;th&gt;MCP (Path B)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;New binaries&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;terraphim_mcp_server&lt;/code&gt; plus wrapper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;~50-200 ms per call&lt;/td&gt;
&lt;td&gt;~10-50 ms per call (long-lived process)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools exposed&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;search&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;search&lt;/code&gt; + 4 autocomplete + &lt;code&gt;build_autocomplete_index&lt;/code&gt; + &lt;code&gt;update_config_tool&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works in any host&lt;/td&gt;
&lt;td&gt;Yes -- anything that runs a slash command&lt;/td&gt;
&lt;td&gt;Only hosts that speak MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token handling&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;terraphim-agent-pa&lt;/code&gt; wrapper&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;terraphim_mcp_server-pa&lt;/code&gt; wrapper&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the search-across-roles flow, CLI is enough. Add MCP when the model needs autocomplete-as-you-type, when you want it to manage role configuration without leaving the conversation, or when you are using a host where the typed-tool surface matters more than the cold-start cost.&lt;/p&gt;

&lt;p&gt;You do not have to choose. Wire both. The slash command above defaults to CLI and falls back to MCP if the binary is missing -- the two paths coexist cleanly because they read the same role config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is the right shape
&lt;/h2&gt;

&lt;p&gt;Most "AI assistant + knowledge base" integrations end up tightly coupled to a specific host. Vendor X's plugin marketplace, Vendor Y's tool format. Terraphim takes the opposite stance: the role configuration lives in your filesystem, the haystacks live in your filesystem (or your mailbox), the ranker runs in a process you own, and the integration with the AI host is the thinnest possible shim -- a slash command or an MCP server, both of which are commodity surfaces.&lt;/p&gt;

&lt;p&gt;Yesterday the Personal Assistant role was a private setup on one laptop. Today it is callable from inside two different AI coding hosts via a one-file slash command. Tomorrow you can add Cursor or Aider with the same two-line wrapper because the integration surface is &lt;code&gt;terraphim-agent search&lt;/code&gt;, not &lt;code&gt;vendor-specific-tool-protocol-v3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The expensive part of context engineering is not the ranker. It is the vocabulary in the knowledge graph and the haystacks the role can reach. The integration layer should not be allowed to compete for that budget. CLI-first keeps it small.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build (or install the published crate when JMAP feature lands on crates.io)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/projects/terraphim/terraphim-ai
cargo &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; crates/terraphim_agent &lt;span class="nt"&gt;--features&lt;/span&gt; jmap

&lt;span class="c"&gt;# Configure roles -- copy the snippets from the how-tos linked below&lt;/span&gt;
&lt;span class="nv"&gt;$EDITOR&lt;/span&gt; ~/.config/terraphim/embedded_config.json

&lt;span class="c"&gt;# Install the slash command&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.claude/commands ~/.config/opencode/command
&lt;span class="nb"&gt;cp&lt;/span&gt; ~/projects/terraphim/terraphim-ai/docs/src/howto/mcp-integration-claude-opencode.md &lt;span class="se"&gt;\&lt;/span&gt;
   /tmp/tsearch.md  &lt;span class="c"&gt;# adapt to your slash command file shape&lt;/span&gt;

&lt;span class="c"&gt;# Reload roles&lt;/span&gt;
terraphim-agent config reload

&lt;span class="c"&gt;# Try&lt;/span&gt;
terraphim-agent search &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="s2"&gt;"Terraphim Engineer"&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 3 &lt;span class="s2"&gt;"rolegraph"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step-by-step in the docs: &lt;a href="https://docs.terraphim.ai/howto/mcp-integration-claude-opencode.html" rel="noopener noreferrer"&gt;Plug Terraphim Search into Claude Code and opencode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the underlying engine, start with &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt;. For the two roles this integration most cleanly exposes, see &lt;a href="https://terraphim.ai/posts/personal-assistant-role-jmap-obsidian/" rel="noopener noreferrer"&gt;Personal Assistant&lt;/a&gt; and &lt;a href="https://terraphim.ai/posts/system-operator-logseq-knowledge-graph/" rel="noopener noreferrer"&gt;System Operator&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>terraphim</category>
      <category>claudecode</category>
      <category>opencode</category>
      <category>mcp</category>
    </item>
    <item>
      <title>System Operator Demo: A Logseq Knowledge Graph Drives Enterprise MBSE Search</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Fri, 17 Apr 2026 18:31:00 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/system-operator-demo-a-logseq-knowledge-graph-drives-enterprise-mbse-search-1kb</link>
      <guid>https://dev.to/alexmikhalev/system-operator-demo-a-logseq-knowledge-graph-drives-enterprise-mbse-search-1kb</guid>
      <description>&lt;p&gt;Terraphim's &lt;a href="https://github.com/terraphim/system-operator" rel="noopener noreferrer"&gt;System Operator role&lt;/a&gt; is the demo we point people at when they want to see a real Logseq knowledge graph drive search. 1,347 Logseq pages, 52 of them carrying explicit &lt;code&gt;synonyms::&lt;/code&gt; lines, covering Model-Based Systems Engineering vocabulary -- requirements, architecture, verification, validation, life cycle concepts. This post walks the demo end-to-end and shows the piece people miss: the KG is doing real work, not just re-ranking text matches.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the demo is
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;terraphim/system-operator&lt;/code&gt; repository on GitHub is a &lt;strong&gt;Logseq vault&lt;/strong&gt; -- flat folder of markdown files under &lt;code&gt;pages/&lt;/code&gt;, one page per concept, with Logseq's bullet-tree syntax for structure and Terraphim-format &lt;code&gt;synonyms::&lt;/code&gt; lines for the knowledge-graph layer. Two things make it a useful demo rather than a toy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Real MBSE vocabulary.&lt;/strong&gt; The synonyms are not invented; they track the INCOSE Systems Engineering Handbook v.4, the V-Model, and SEMP conventions. When you type &lt;code&gt;RFP&lt;/code&gt;, the automaton normalises it to &lt;code&gt;acquisition need&lt;/code&gt; because that is what the handbook calls it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real scale.&lt;/strong&gt; 1,347 markdown files is enough to expose cold-start behaviour (~5-10 seconds to index on a laptop) without being so large it obscures the ranking signal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Run it
&lt;/h2&gt;

&lt;p&gt;There is an automated setup script in the repo. As of today it clones to a durable path under &lt;code&gt;~/.config/terraphim/system_operator&lt;/code&gt; instead of &lt;code&gt;/tmp&lt;/code&gt;, so the vault survives a reboot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/setup_system_operator.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then either drive the role via the server --&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run &lt;span class="nt"&gt;--bin&lt;/span&gt; terraphim_server &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; terraphim_server/default/system_operator_config.json
curl &lt;span class="s2"&gt;"http://127.0.0.1:8000/documents/search?q=RFP&amp;amp;role=System%20Operator&amp;amp;limit=5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;-- or via the &lt;code&gt;terraphim-agent&lt;/code&gt; CLI after adding the role entry to &lt;code&gt;~/.config/terraphim/embedded_config.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraphim-agent config reload
terraphim-agent search &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="s2"&gt;"System Operator"&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 5 &lt;span class="s2"&gt;"RFP"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full config snippet and the &lt;code&gt;embedded_config.json&lt;/code&gt; entry are in the &lt;a href="https://github.com/terraphim/terraphim-ai/blob/main/terraphim_server/README_SYSTEM_OPERATOR.md" rel="noopener noreferrer"&gt;&lt;code&gt;README_SYSTEM_OPERATOR.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The piece people miss
&lt;/h2&gt;

&lt;p&gt;Search-over-notes tools usually describe ranking in terms of "it uses a knowledge graph". That sentence hides a lot. Is the graph actually consulted at query time? Is it just a post-hoc re-ranker on top of BM25? Does it expand synonyms? On what vocabulary?&lt;/p&gt;

&lt;p&gt;Terraphim exposes the answer directly. &lt;code&gt;validate --connectivity&lt;/code&gt; prints which words in your query the automaton matched and what canonical terms they normalised to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraphim-agent validate &lt;span class="nt"&gt;--role&lt;/span&gt; &lt;span class="s2"&gt;"System Operator"&lt;/span&gt; &lt;span class="nt"&gt;--connectivity&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"RFP business analysis life cycle model business requirements documentation tree"&lt;/span&gt;

Connectivity Check &lt;span class="k"&gt;for &lt;/span&gt;role &lt;span class="s1"&gt;'System Operator'&lt;/span&gt;:
  Connected: &lt;span class="nb"&gt;false
  &lt;/span&gt;Matched terms: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"acquisition need"&lt;/span&gt;, &lt;span class="s2"&gt;"business or mission analysis"&lt;/span&gt;,
                  &lt;span class="s2"&gt;"business requirements"&lt;/span&gt;, &lt;span class="s2"&gt;"documentation tree"&lt;/span&gt;,
                  &lt;span class="s2"&gt;"life cycle concepts"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five query fragments, five canonical matches. &lt;code&gt;RFP&lt;/code&gt; collapsed to &lt;code&gt;acquisition need&lt;/code&gt; (its synonym, from &lt;code&gt;Acquisition need.md&lt;/code&gt; in the vault). &lt;code&gt;business analysis&lt;/code&gt; collapsed to &lt;code&gt;business or mission analysis&lt;/code&gt; (INCOSE terminology). &lt;code&gt;life cycle model&lt;/code&gt; collapsed to &lt;code&gt;life cycle concepts&lt;/code&gt;. None of this is text matching -- the word &lt;code&gt;RFP&lt;/code&gt; does not appear in the canonical page body; it lives in the &lt;code&gt;synonyms::&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;Once a query is normalised, the ranker walks the graph. A document that mentions &lt;code&gt;acquisition need&lt;/code&gt; directly outranks one that mentions it through three synonym hops, and both outrank a document that mentions none of the canonical terms at all. Ranks come back with concrete integer scores -- &lt;code&gt;[13]&lt;/code&gt; on a top result, not an opaque 0.87 cosine.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it compares to the Personal Assistant role
&lt;/h2&gt;

&lt;p&gt;We &lt;a href="https://terraphim.ai/posts/personal-assistant-role-jmap-obsidian/" rel="noopener noreferrer"&gt;wrote up the Personal Assistant role yesterday&lt;/a&gt;: a private per-user role that indexes a Fastmail mailbox plus an Obsidian vault. Same engine, same ranker, different haystacks. The knowledge graph there is a small &lt;code&gt;kg/&lt;/code&gt; folder inside the user's vault with 14 synonym files covering personal vocabulary (&lt;code&gt;bun&lt;/code&gt; with &lt;code&gt;npm&lt;/code&gt;/&lt;code&gt;yarn&lt;/code&gt;/&lt;code&gt;pnpm&lt;/code&gt; synonyms, &lt;code&gt;odilo&lt;/code&gt;, &lt;code&gt;invoice&lt;/code&gt;, &lt;code&gt;meeting&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The two roles expose the same pattern at two scales:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;System Operator&lt;/th&gt;
&lt;th&gt;Personal Assistant&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;KG size&lt;/td&gt;
&lt;td&gt;52 synonym files, 1,300-concept vocabulary&lt;/td&gt;
&lt;td&gt;14 synonym files, ~30-concept personal vocabulary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haystacks&lt;/td&gt;
&lt;td&gt;1 (Logseq repo)&lt;/td&gt;
&lt;td&gt;2 (Obsidian vault + Fastmail JMAP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;Public GitHub repo&lt;/td&gt;
&lt;td&gt;Private user files and mailbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audience&lt;/td&gt;
&lt;td&gt;Demos, onboarding, public showcase&lt;/td&gt;
&lt;td&gt;One user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifetime&lt;/td&gt;
&lt;td&gt;Frozen per release&lt;/td&gt;
&lt;td&gt;Edited daily, rebuilt in 20 ms per edit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both use &lt;code&gt;terraphim-graph&lt;/code&gt; ranking. Both build an Aho-Corasick automaton once at role-load time. Both run in a 4 GB process on a laptop with no cloud round-trip. The only interesting difference is the vocabulary, which is exactly the separation of concerns a knowledge-graph-first design is supposed to deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for teams evaluating MBSE tooling
&lt;/h2&gt;

&lt;p&gt;If you are evaluating Terraphim for a systems engineering group, the System Operator role is the honest starting point. It runs on a laptop against a public vault; you can check that every synonym mapping traces back to a concrete page; you can diff the &lt;code&gt;pages/&lt;/code&gt; folder against the INCOSE handbook and argue about terminology. When your team's own vocabulary diverges (every organisation's does), you clone the repo, edit &lt;code&gt;synonyms::&lt;/code&gt; lines, and the graph rebuilds in 20 milliseconds without a retraining step.&lt;/p&gt;

&lt;p&gt;The expensive part of enterprise search is not the ranker. It is the vocabulary. A deterministic graph makes the vocabulary an asset you curate, not a black box you tune.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/terraphim/terraphim-ai
&lt;span class="nb"&gt;cd &lt;/span&gt;terraphim-ai
./scripts/setup_system_operator.sh
cargo run &lt;span class="nt"&gt;--bin&lt;/span&gt; terraphim_server &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; terraphim_server/default/system_operator_config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or cut to the CLI if you already have &lt;code&gt;terraphim-agent&lt;/code&gt; installed -- the &lt;code&gt;embedded_config.json&lt;/code&gt; snippet is in the &lt;a href="https://github.com/terraphim/terraphim-ai/blob/main/terraphim_server/README_SYSTEM_OPERATOR.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the underlying engine, start with &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt;. For the personal-productivity analogue, see the &lt;a href="https://terraphim.ai/posts/personal-assistant-role-jmap-obsidian/" rel="noopener noreferrer"&gt;Personal Assistant role post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>terraphim</category>
      <category>logseq</category>
      <category>knowledgegraph</category>
      <category>mbse</category>
    </item>
    <item>
      <title>Personal Assistant Role: One Search Across Email and Notes</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Fri, 17 Apr 2026 13:31:38 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/personal-assistant-role-one-search-across-email-and-notes-5691</link>
      <guid>https://dev.to/alexmikhalev/personal-assistant-role-one-search-across-email-and-notes-5691</guid>
      <description>&lt;p&gt;Most "personal AI" tools split your context across silos: one search box for email, another for notes, a third for your chat history. Terraphim treats every source as a haystack on the same role, so a single query crosses all of them. This post shows how to wire up the two most common personal sources -- email via JMAP and notes in an Obsidian vault -- under a new Personal Assistant role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a unified role matters
&lt;/h2&gt;

&lt;p&gt;The mental tax of personal search is not the typing. It is the &lt;em&gt;deciding&lt;/em&gt;. "Did I read that in an email or write it in a note?" Each silo you skip is a context switch with no useful payload. Once Terraphim is the front door for both surfaces, the question collapses to "where is the thing about X" and the role's &lt;code&gt;terraphim-graph&lt;/code&gt; ranking serves whichever source actually has the strongest signal.&lt;/p&gt;

&lt;p&gt;The Personal Assistant role uses two haystacks under one role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Obsidian vault&lt;/strong&gt; indexed by the &lt;code&gt;Ripgrep&lt;/code&gt; service. Plain markdown, sub-millisecond local search, no daemon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fastmail mailbox&lt;/strong&gt; indexed by the &lt;code&gt;Jmap&lt;/code&gt; service (&lt;a href="https://www.rfc-editor.org/rfc/rfc8620" rel="noopener noreferrer"&gt;RFC 8620/8621&lt;/a&gt;). One HTTPS round trip per query, server-side full-text against your real mailbox, results returned with &lt;code&gt;jmap:///email/&amp;lt;id&amp;gt;&lt;/code&gt; URLs you can paste back into a mail client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ranking is the same &lt;code&gt;terraphim-graph&lt;/code&gt; scoring as every other Terraphim role: an Aho-Corasick automaton built from the Obsidian vault contributes synonyms specific to your project vocabulary, then both haystacks share the same rank ladder. Notes and email interleave by relevance, not by source.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A single command: &lt;code&gt;terraphim-agent-pa search "&amp;lt;query&amp;gt;"&lt;/code&gt; returns mixed hits ordered by rank.&lt;/li&gt;
&lt;li&gt;Determinism. Every match traces back to a concrete edge in your knowledge graph -- no opaque embedding score.&lt;/li&gt;
&lt;li&gt;Privacy. The Obsidian vault never leaves disk; the JMAP query goes directly to your mail provider with your token. No Terraphim cloud component sits in the path.&lt;/li&gt;
&lt;li&gt;Composition. The role is just a JSON entry in &lt;code&gt;~/.config/terraphim/embedded_config.json&lt;/code&gt;. Add another haystack tomorrow (calendar, contacts, browser history) and the same query sweeps it too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 4 GB process on your laptop holds the whole working set; queries return in single-digit milliseconds for the local side and a few hundred for the remote JMAP round trip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring sketch
&lt;/h2&gt;

&lt;p&gt;The role config is roughly thirty lines of JSON: two haystacks, one knowledge-graph pointer, no LLM. The Fastmail token is &lt;em&gt;not&lt;/em&gt; in the config -- it is injected at runtime via &lt;code&gt;op run&lt;/code&gt; from 1Password into the &lt;code&gt;JMAP_ACCESS_TOKEN&lt;/code&gt; environment variable, so the secret never lands on disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exec &lt;/span&gt;op run &lt;span class="nt"&gt;--account&lt;/span&gt; my.1password.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'JMAP_ACCESS_TOKEN=op://VAULT/ITEM/credential'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; /Users/alex/.cargo/bin/terraphim-agent &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrap that in &lt;code&gt;~/bin/terraphim-agent-pa&lt;/code&gt;, &lt;code&gt;chmod +x&lt;/code&gt;, and the JMAP haystack lights up only for queries that ask for it. The other roles keep using the bare &lt;code&gt;terraphim-agent&lt;/code&gt; and never pay for the 1Password unlock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why graph embeddings make this practical
&lt;/h2&gt;

&lt;p&gt;The reason a unified role works at all -- not just for two haystacks but for any reasonable number -- is that Terraphim's graph-embeddings layer is sub-millisecond and deterministic. There is no per-query embedding API call to amortise across sources, no vector database to keep in sync, no opaque ranker that has to be retrained when you add a new haystack. The matching is byte-level Aho-Corasick traversal of an automaton built once at role-load time. We wrote up the engine in detail at &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt;; this Personal Assistant role is one application of that engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The end-to-end how-to is in the docs: install the prerequisites, add the JSON snippet, write the wrapper, run three verification queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the how-to: &lt;a href="https://docs.terraphim.ai/howto/personal-assistant-role.html" rel="noopener noreferrer"&gt;Personal Assistant Role on docs.terraphim.ai&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One caveat worth surfacing up front: the published &lt;code&gt;terraphim-agent&lt;/code&gt; on crates.io does not yet ship with the JMAP haystack (the &lt;code&gt;haystack_jmap&lt;/code&gt; dependency is not published either). For email search you need to build from local source with &lt;code&gt;cargo build --release -p terraphim_agent --features jmap&lt;/code&gt;. The how-to walks through the two Cargo.toml edits required.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;Personal Assistant is the smallest useful instance of "Terraphim as the front door for everything I read." Calendar (CalDAV), contacts (CardDAV), browser bookmarks, RSS, and AI session logs are all natural follow-ups -- each is a single haystack entry on the same role. The pattern composes; the cost stays linear in haystacks, not quadratic in cross-source queries.&lt;/p&gt;

&lt;p&gt;If you want the underlying engine, start with &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt;. If you want to wire knowledge-graph hooks into your AI coding agent on the same machine, &lt;a href="https://terraphim.ai/posts/teaching-ai-agents-with-knowledge-graphs/" rel="noopener noreferrer"&gt;Teaching AI Coding Agents with Knowledge Graph Hooks&lt;/a&gt; covers that side of the same engine.&lt;/p&gt;

</description>
      <category>terraphim</category>
      <category>personalassistant</category>
      <category>jmap</category>
      <category>obsidian</category>
    </item>
    <item>
      <title>Teaching AI Coding Agents with Knowledge Graph Hooks</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Fri, 17 Apr 2026 10:50:07 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/teaching-ai-coding-agents-with-knowledge-graph-hooks-497i</link>
      <guid>https://dev.to/alexmikhalev/teaching-ai-coding-agents-with-knowledge-graph-hooks-497i</guid>
      <description>&lt;p&gt;How we use Aho-Corasick automata and knowledge graphs to automatically enforce coding standards across AI coding agents like Claude Code, Cursor, and Aider.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;New:&lt;/strong&gt; see &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt; for the underlying engine that makes these hooks possible — sub-millisecond, deterministic, fully explainable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Anthropic Bought Bun. Claude Still Outputs &lt;code&gt;npm install&lt;/code&gt;.
&lt;/h2&gt;

&lt;p&gt;On December 3, 2025, &lt;a href="https://www.anthropic.com/news/anthropic-acquires-bun-as-claude-code-reaches-usd1b-milestone" rel="noopener noreferrer"&gt;Anthropic announced its first-ever acquisition&lt;/a&gt;: Bun, the blazing-fast JavaScript runtime. This came alongside Claude Code reaching &lt;a href="https://bun.com/blog/bun-joins-anthropic" rel="noopener noreferrer"&gt;$1 billion in run-rate revenue&lt;/a&gt; just six months after public launch.&lt;/p&gt;

&lt;p&gt;As Mike Krieger, Anthropic's Chief Product Officer, put it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Bun represents exactly the kind of technical excellence we want to bring into Anthropic... bringing the Bun team into Anthropic means we can build the infrastructure to compound that momentum."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude Code &lt;a href="https://simonwillison.net/2025/Dec/2/anthropic-acquires-bun/" rel="noopener noreferrer"&gt;ships as a Bun executable&lt;/a&gt; to millions of developers. Anthropic now owns the runtime their flagship coding tool depends on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And yet...&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask Claude to set up a Node.js project, and what do you get?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;express
yarn add lodash
pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yet Anthropic's own models still default to npm, yarn, and pnpm in their outputs. The training data predates the acquisition, and old habits die hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how do you teach your AI coding tools to consistently use Bun, regardless of what the underlying LLM insists on?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: LLMs Don't Know Your Preferences
&lt;/h2&gt;

&lt;p&gt;AI coding agents are powerful, but they're trained on the internet's collective habits—which means npm everywhere. Your team might have standardized on Bun for its speed (25% monthly growth, &lt;a href="https://devclass.com/2025/12/03/bun-javascript-runtime-acquired-by-anthropic-tying-its-future-to-ai-coding/" rel="noopener noreferrer"&gt;7.2 million downloads&lt;/a&gt; in October 2025), but every AI agent keeps suggesting the old ways.&lt;/p&gt;

&lt;p&gt;Manually fixing these inconsistencies is tedious. What if your knowledge graph could automatically intercept and transform AI outputs?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Knowledge Graph Hooks
&lt;/h2&gt;

&lt;p&gt;Terraphim provides a hook system that intercepts AI agent actions and applies knowledge graph-based transformations. The system uses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Aho-Corasick automata&lt;/strong&gt; for efficient multi-pattern matching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LeftmostLongest strategy&lt;/strong&gt; ensuring specific patterns match before general ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markdown-based knowledge graph&lt;/strong&gt; files that are human-readable and version-controlled&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input Text → Aho-Corasick Automata → Pattern Match → Knowledge Graph Lookup → Transformed Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The knowledge graph is built from simple markdown files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# bun install&lt;/span&gt;

Fast package installation with Bun.

synonyms:: pnpm install, npm install, yarn install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the automata encounter any synonym, they replace it with the canonical term (the heading).&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example: npm → bun
&lt;/h2&gt;

&lt;p&gt;Let's prove it works. Here's a live test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"npm install"&lt;/span&gt; | terraphim-agent replace
bun &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"yarn install lodash"&lt;/span&gt; | terraphim-agent replace
bun &lt;span class="nb"&gt;install &lt;/span&gt;lodash

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"pnpm install --save-dev jest"&lt;/span&gt; | terraphim-agent replace
bun &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LeftmostLongest matching ensures &lt;code&gt;npm install&lt;/code&gt; matches the more specific pattern before standalone &lt;code&gt;npm&lt;/code&gt; could match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hook Integration Points
&lt;/h2&gt;

&lt;p&gt;Terraphim hooks integrate at multiple points in the development workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Claude Code PreToolUse Hooks
&lt;/h3&gt;

&lt;p&gt;Intercept Bash commands before execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"terraphim-agent replace"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Claude Code tries to run &lt;code&gt;npm install express&lt;/code&gt;, the hook transforms it to &lt;code&gt;bun install express&lt;/code&gt; before execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Git prepare-commit-msg Hooks
&lt;/h3&gt;

&lt;p&gt;Enforce attribution standards in commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;COMMIT_MSG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;ORIGINAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COMMIT_MSG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;TRANSFORMED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ORIGINAL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | terraphim-agent replace&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TRANSFORMED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COMMIT_MSG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a knowledge graph entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Terraphim AI&lt;/span&gt;

Attribution for AI-assisted development.

synonyms:: Claude Code, Claude, Anthropic Claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every commit message mentioning "Claude Code" becomes "Terraphim AI".&lt;/p&gt;

&lt;h3&gt;
  
  
  3. MCP Tools
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;replace_matches&lt;/code&gt; MCP tool exposes the same functionality to any MCP-compatible client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"replace_matches"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Run npm install to setup"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The hook system is built on three crates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraphim_automata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aho-Corasick pattern matching, thesaurus building&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraphim_hooks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ReplacementService, HookResult, binary discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terraphim_agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CLI with &lt;code&gt;replace&lt;/code&gt; subcommand&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pattern matching&lt;/strong&gt;: O(n) where n is input length (not pattern count)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup&lt;/strong&gt;: ~50ms to load knowledge graph and build automata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt;: Automata are compact finite state machines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Extending the Knowledge Graph
&lt;/h2&gt;

&lt;p&gt;Adding new patterns is simple. Create a markdown file in the mdBook source tree under &lt;code&gt;docs/src/kg/&lt;/code&gt; (published at &lt;a href="https://docs.terraphim.ai/src/kg/" rel="noopener noreferrer"&gt;https://docs.terraphim.ai/src/kg/&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# pytest&lt;/span&gt;

Python testing framework.

synonyms:: python -m unittest, unittest, nose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system automatically rebuilds the automata on startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern Priority
&lt;/h3&gt;

&lt;p&gt;The LeftmostLongest strategy means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; matches before &lt;code&gt;npm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;python -m pytest&lt;/code&gt; matches before &lt;code&gt;python&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Longer, more specific patterns always win&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install all hooks&lt;/span&gt;
./scripts/install-terraphim-hooks.sh &lt;span class="nt"&gt;--easy-mode&lt;/span&gt;

&lt;span class="c"&gt;# Test the replacement&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"npm install"&lt;/span&gt; | ./target/release/terraphim-agent replace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manual Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Build the agent:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;-p&lt;/span&gt; terraphim_agent &lt;span class="nt"&gt;--features&lt;/span&gt; repl-full &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Configure Claude Code hooks in &lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Git hooks:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;scripts/hooks/prepare-commit-msg .git/hooks/
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .git/hooks/prepare-commit-msg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Package manager standardization&lt;/td&gt;
&lt;td&gt;npm, yarn, pnpm&lt;/td&gt;
&lt;td&gt;bun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI attribution&lt;/td&gt;
&lt;td&gt;Claude Code, Claude&lt;/td&gt;
&lt;td&gt;Terraphim AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework migration&lt;/td&gt;
&lt;td&gt;React.Component&lt;/td&gt;
&lt;td&gt;React functional components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API versioning&lt;/td&gt;
&lt;td&gt;/api/v1&lt;/td&gt;
&lt;td&gt;/api/v2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deprecated function replacement&lt;/td&gt;
&lt;td&gt;moment()&lt;/td&gt;
&lt;td&gt;dayjs()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Claude Code Skills Plugin
&lt;/h2&gt;

&lt;p&gt;For AI agents that support skills, we provide a dedicated plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugin &lt;span class="nb"&gt;install &lt;/span&gt;terraphim-engineering-skills@terraphim-ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;terraphim-hooks&lt;/code&gt; skill teaches agents how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the replace command correctly&lt;/li&gt;
&lt;li&gt;Extend the knowledge graph&lt;/li&gt;
&lt;li&gt;Debug hook issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Knowledge graph hooks provide a powerful, declarative way to enforce coding standards across AI agents. By defining patterns in simple markdown files, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standardize package managers across your team&lt;/li&gt;
&lt;li&gt;Ensure consistent attribution in commits&lt;/li&gt;
&lt;li&gt;Migrate deprecated patterns automatically&lt;/li&gt;
&lt;li&gt;Keep your knowledge graph version-controlled and human-readable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Aho-Corasick automata ensure efficient matching regardless of pattern count, making this approach scale to large knowledge graphs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;To wire knowledge-graph hooks into your own project, the &lt;a href="https://terraphim.ai/how-tos/command-rewriting-howto/" rel="noopener noreferrer"&gt;Command Rewriting How-to&lt;/a&gt; walks through the configuration end to end. To understand &lt;em&gt;why&lt;/em&gt; the matching is sub-millisecond and deterministic — and what that lets you promise to your users — read &lt;a href="https://terraphim.ai/posts/why-graph-embeddings-matter/" rel="noopener noreferrer"&gt;Why Graph Embeddings Matter&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/terraphim/terraphim-ai" rel="noopener noreferrer"&gt;Terraphim AI Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/terraphim/terraphim-claude-skills" rel="noopener noreferrer"&gt;Claude Code Skills Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.terraphim.ai/hooks/" rel="noopener noreferrer"&gt;Hook Installation Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.terraphim.ai/knowledge-graph/" rel="noopener noreferrer"&gt;Knowledge Graph Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraphim</category>
      <category>ai</category>
      <category>hooks</category>
      <category>knowledgegraph</category>
    </item>
    <item>
      <title>Why Graph Embeddings Matter</title>
      <dc:creator>AlexMikhalev</dc:creator>
      <pubDate>Fri, 17 Apr 2026 10:49:56 +0000</pubDate>
      <link>https://dev.to/alexmikhalev/why-graph-embeddings-matter-epp</link>
      <guid>https://dev.to/alexmikhalev/why-graph-embeddings-matter-epp</guid>
      <description>&lt;p&gt;Vector databases are probabilistic and slow. Graph embeddings are deterministic and sub-millisecond. If you are building context for an AI coding agent — or any system where you need to know &lt;em&gt;why&lt;/em&gt; a result came back — the difference is not academic. It changes what your application is allowed to promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pitch in One Paragraph
&lt;/h2&gt;

&lt;p&gt;Terraphim represents concepts as nodes in a knowledge graph and ranks them by how many synonyms and edges connect them. There is no embedding model, no GPU, no per-query distance computation in a 1024-dimensional space. There is an &lt;a href="https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm" rel="noopener noreferrer"&gt;Aho-Corasick&lt;/a&gt; automaton built once, queried in O(n+m+z) time over the input length plus the number of matches. The mechanism is described in detail on the &lt;a href="https://terraphim.ai/docs/graph-embeddings/" rel="noopener noreferrer"&gt;Graph Embeddings reference&lt;/a&gt; page; this post is about why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;Three numbers carry the argument. Each is reproducible on a laptop.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1.4 million patterns matched in under one millisecond, with under 4 GB of RAM.&lt;/strong&gt; That is the working set behind a multi-role knowledge graph — operator, engineer, analyst — held resident in the same process that serves the query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5–10 nanoseconds per knowledge-graph inference step.&lt;/strong&gt; Not microseconds. Nanoseconds. Once the automaton is built, traversal is a tight loop over byte slices and graph edges, and modern CPUs are extremely good at that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20 milliseconds to rebuild the embeddings for a role from scratch.&lt;/strong&gt; Rename a synonym, add a new term, drop an obsolete one — the whole role's graph is reconstituted before your editor has rendered the next frame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For comparison, a typical vector-DB nearest-neighbour query lands in the 5–50 ms range &lt;em&gt;after&lt;/em&gt; you have paid the embedding API call (50–500 ms) and the network round-trip. We are not in the same regime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Consequences
&lt;/h2&gt;

&lt;p&gt;The numbers are interesting on their own. The reason they matter is what they let you build.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Full Explainability
&lt;/h3&gt;

&lt;p&gt;Every match in Terraphim traces back to a specific edge in the knowledge graph and a specific synonym in a specific role. There is no "the model said so." When a search returns a document, you can show the user exactly which terms matched, which role's graph supplied the synonym, and which edges connected them. That is not a debugging nicety — it is a regulatory requirement in any domain where you have to defend a decision after the fact. Healthcare, legal, finance, government. Vector search by construction cannot do this.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. No Training, No Retraining, No Fine-Tuning
&lt;/h3&gt;

&lt;p&gt;Adding a new concept is a text edit. You write the synonym down, you point Terraphim at the file, the graph rebuilds in 20 ms. There is no training run, no GPU bill, no "we need to schedule a retrain on the new corpus." This collapses the loop between &lt;em&gt;noticing a gap&lt;/em&gt; and &lt;em&gt;fixing the gap&lt;/em&gt; from days or weeks to seconds. For an AI coding agent that needs to learn a project's vocabulary as you onboard, this is the difference between a working tool and a stalled rollout.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Language-Agnostic Without Language Detection
&lt;/h3&gt;

&lt;p&gt;Because matching is done on normalised terms — synonyms you supply explicitly — the same node in the graph can carry English, French, Russian, and Mandarin labels at no extra cost. There is no language-detection step, no per-language embedding model, no separate index. The query "consensus" and the query "консенсус" both reach the same node if you have told the graph they are synonyms. Stop-word lists become irrelevant: if a word is not in the graph, it does not match, full stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Lets You Do
&lt;/h2&gt;

&lt;p&gt;The pieces above are infrastructure. The story arc continues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build hooks that transform AI coding agent output deterministically.&lt;/strong&gt; When Claude Code suggests &lt;code&gt;npm install&lt;/code&gt;, intercept it via a graph-embeddings match and replace it with &lt;code&gt;bun install&lt;/code&gt;. We wrote this up at &lt;a href="https://terraphim.ai/posts/teaching-ai-agents-with-knowledge-graphs/" rel="noopener noreferrer"&gt;Teaching AI Coding Agents with Knowledge Graph Hooks&lt;/a&gt; — that post is the &lt;em&gt;demo&lt;/em&gt; of what this engine enables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture and reuse mistakes.&lt;/strong&gt; When an agent gets corrected, store the correction as a new synonym and the next session never repeats it. See &lt;a href="https://terraphim.ai/posts/teaching-ai-agents-to-learn-from-mistakes/" rel="noopener noreferrer"&gt;Teaching AI Agents to Learn from Their Mistakes&lt;/a&gt; and &lt;a href="https://terraphim.ai/posts/learning-via-negativa/" rel="noopener noreferrer"&gt;Learning via Negativa&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the whole thing in a 4 GB process on your laptop with no network calls.&lt;/strong&gt; The compactness is not an accident — it is the engineering brief from the &lt;a href="https://terraphim.ai/capabilities/origin-story/" rel="noopener noreferrer"&gt;Origin Story&lt;/a&gt;, which explains where the design came from and why it has stayed this small.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Apply
&lt;/h2&gt;

&lt;p&gt;If you want to wire this into your own project, the &lt;a href="https://terraphim.ai/how-tos/command-rewriting-howto/" rel="noopener noreferrer"&gt;Command Rewriting How-to&lt;/a&gt; walks through the moving parts: where to put your synonyms, how the role graph is built, how hooks call the matcher.&lt;/p&gt;

&lt;p&gt;The mechanism — automata, ranking formula, ASCII walk-through — is on the &lt;a href="https://terraphim.ai/docs/graph-embeddings/" rel="noopener noreferrer"&gt;Graph Embeddings reference&lt;/a&gt; page. Read that next if you want the data structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bother Saying This Out Loud
&lt;/h2&gt;

&lt;p&gt;The current default in the AI tooling ecosystem is to reach for a vector database the moment anyone mentions "semantic search." It is the path of least resistance because the tools are well-marketed and the API surface is familiar. But for a large class of problems — explainability-first systems, on-device agents, anywhere you need a hard latency budget or a hard explainability guarantee — graph embeddings are the better-engineered answer. Not the only answer; the better one for that class.&lt;/p&gt;

&lt;p&gt;The promotion campaign over the next few weeks goes deeper: a &lt;a href="https://terraphim.ai/posts/sub-millisecond-context-knowledge-graphs/" rel="noopener noreferrer"&gt;sub-millisecond context article&lt;/a&gt; walks through the FST/Aho-Corasick implementation, and the &lt;em&gt;Context Engineering with Knowledge Graphs&lt;/em&gt; book (launching in May) puts it in the wider context of moving from RAG to context graphs.&lt;/p&gt;

&lt;p&gt;Until then: read the reference, try the how-to, and let us know in &lt;a href="https://terraphim.discourse.group" rel="noopener noreferrer"&gt;Discourse&lt;/a&gt; what you build with it.&lt;/p&gt;

</description>
      <category>terraphim</category>
      <category>graphembeddings</category>
      <category>knowledgegraph</category>
      <category>ahocorasick</category>
    </item>
  </channel>
</rss>
