<?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: Jaskaran</title>
    <description>The latest articles on DEV Community by Jaskaran (@quantapixel).</description>
    <link>https://dev.to/quantapixel</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%2F4191098%2F64aeaa4a-d25d-414d-b75d-fb52f9b0854f.png</url>
      <title>DEV Community: Jaskaran</title>
      <link>https://dev.to/quantapixel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quantapixel"/>
    <language>en</language>
    <item>
      <title>Hivemind: shared memory for coding agents, built on FalkorDB</title>
      <dc:creator>Jaskaran</dc:creator>
      <pubDate>Sat, 17 Oct 2026 20:12:20 +0000</pubDate>
      <link>https://dev.to/quantapixel/hivemind-shared-memory-for-coding-agents-built-on-falkordb-4k0j</link>
      <guid>https://dev.to/quantapixel/hivemind-shared-memory-for-coding-agents-built-on-falkordb-4k0j</guid>
      <description>&lt;p&gt;On many teams, each developer now works with their own coding agent: Claude Code, Kiro, Codex or something else. These agents don't share anything with each other. One agent doesn't know what another is changing, and each session starts without any memory of earlier ones. If one agent found yesterday that an approach breaks the tests, another agent may try the same approach today.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://www.wemakedevs.org/hackathons/falkordb" rel="noopener noreferrer"&gt;Graph Hacks&lt;/a&gt; (WeMakeDevs × FalkorDB) I built &lt;strong&gt;Hivemind&lt;/strong&gt;, a shared memory that all of these agents read from and write to. It is stored as a graph in FalkorDB. This post explains the problem, how the system works, the main Cypher queries, and what I learned while building it.&lt;/p&gt;

&lt;p&gt;There is a live demo that runs in the browser without sign-up. I refer to specific steps of it below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://hivemind-live-demo.vercel.app" rel="noopener noreferrer"&gt;hivemind-live-demo.vercel.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Quantapixel" rel="noopener noreferrer"&gt;
        Quantapixel
      &lt;/a&gt; / &lt;a href="https://github.com/Quantapixel/hivemind" rel="noopener noreferrer"&gt;
        hivemind
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Hivemind: a shared graph brain for every coding agent on a team (Claude Code, Kiro, Codex). Built on FalkorDB for Graph Hacks.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;⬡ Hivemind&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A shared graph brain for every coding agent on a team.&lt;/strong&gt; Built on &lt;a href="https://falkordb.com" rel="nofollow noopener noreferrer"&gt;FalkorDB&lt;/a&gt; for &lt;a href="https://www.wemakedevs.org/hackathons/falkordb" rel="nofollow noopener noreferrer"&gt;Graph Hacks: Context for AI Agents&lt;/a&gt; (WeMakeDevs × FalkorDB, Oct 15–18 2026).&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;▶ Live demo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;a href="https://hivemind-live-demo.vercel.app" rel="nofollow noopener noreferrer"&gt;hivemind-live-demo.vercel.app&lt;/a&gt;&lt;/strong&gt;: press Play, no sign-up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Demo videos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/Quantapixel/hivemind/docs/videos/hivemind-demo.mp4" rel="noopener noreferrer"&gt;Hivemind demo (3:14)&lt;/a&gt; · &lt;a href="https://github.com/Quantapixel/hivemind/docs/videos/hivemind-demo-walkthrough.mp4" rel="noopener noreferrer"&gt;Walkthrough of the live demo (2:49)&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Track&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent Memory and Coordination (Track 02)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built with&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FalkorDB Cloud · Python · MCP · Claude Code, Kiro and Codex hooks · Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Five developers each run their own Claude Code, Codex or Kiro agent on the same repo. None of those agents knows what the others are doing. Agent A rewrites &lt;code&gt;validate_token()&lt;/code&gt; while Agent B builds on its old behaviour. Agent C retries a fix that Agent D found breaks production yesterday. Every session starts from zero.&lt;/p&gt;
&lt;p&gt;Hivemind puts every agent on one graph. It holds the code's call graph, who is changing what, what has been tried…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Quantapixel/hivemind" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;The demo uses a fictional team of five working on a small shop backend: Priya (tech lead, Claude Code), Marco (Kiro), Aisha (Codex), Ken (Claude Code) and Lena (Kiro).&lt;/p&gt;

&lt;p&gt;Marco asks Kiro to add JWT support, and Kiro starts changing &lt;code&gt;validate_token()&lt;/code&gt; in &lt;code&gt;shop/tokens.py&lt;/code&gt;. Shortly after, Aisha asks Codex to add rate limiting to &lt;code&gt;checkout()&lt;/code&gt; in &lt;code&gt;shop/orders.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These are different files, but &lt;code&gt;checkout()&lt;/code&gt; calls &lt;code&gt;current_user()&lt;/code&gt;, which calls &lt;code&gt;validate_token()&lt;/code&gt;. Aisha's change depends on code that Marco is changing at the same time.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Which of these would Git catch?
  &lt;br&gt;
None of them. Marco and Aisha edit different files, so there is no merge conflict. Marco's failed attempt is never committed. Priya's earlier decision that tokens must stay HMAC-signed until the mobile v3 app ships isn't in Git either. Git tracks changes to text, and these problems are about how the code is connected and what the team already knows.&lt;br&gt;


&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Later, Marco's first approach (replacing the HMAC check with a PyJWT decode) fails &lt;code&gt;test_refresh_session&lt;/code&gt;, because users with old tokens get logged out. A second try fails the same way. He stops for the day, and the next morning Lena continues the task with an agent that has none of this information.&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%2Fr7g7kggz24vn2v8b07dd.png" 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%2Fr7g7kggz24vn2v8b07dd.png" alt="Without shared memory vs with Hivemind" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Existing approaches cover parts of this. Git and code review find problems after the work is done. &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt; files are written by hand and don't reflect work in progress. Each agent's own memory stays on one machine and inside one tool. Asking teammates in chat depends on people remembering to ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach: attach memory to the code
&lt;/h2&gt;

&lt;p&gt;Most of the information worth sharing is about a specific function: who is changing &lt;code&gt;validate_token&lt;/code&gt;, what was tried on it, and which decisions apply to it. So Hivemind attaches each piece of memory to the functions it is about.&lt;/p&gt;

&lt;p&gt;Functions are connected by calls. That means the question "what should my agent know before editing &lt;code&gt;checkout&lt;/code&gt;?" becomes "what is connected to &lt;code&gt;checkout&lt;/code&gt;?", which is a graph traversal.&lt;/p&gt;

&lt;p&gt;The other questions an agent needs answered have the same form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is anyone changing code that mine calls? Follow &lt;code&gt;CALLS&lt;/code&gt; edges until reaching a teammate's claim.&lt;/li&gt;
&lt;li&gt;What has been tried near this function? Look one hop out for attempts.&lt;/li&gt;
&lt;li&gt;What could break if I change it? Follow callers backwards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In SQL these would be recursive joins on every agent step. In Cypher each one is a short query, and FalkorDB returns them quickly enough to run before every edit.&lt;/p&gt;

&lt;p&gt;Some other FalkorDB features were useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-graph support.&lt;/strong&gt; Each team gets its own graph (&lt;code&gt;hivemind_shop&lt;/code&gt;, &lt;code&gt;hivemind_payments&lt;/code&gt;) in one database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in algorithms.&lt;/strong&gt; &lt;code&gt;algo.pageRank&lt;/code&gt;, &lt;code&gt;algo.betweenness&lt;/code&gt; and &lt;code&gt;shortestPath&lt;/code&gt; power the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis protocol and a free cloud tier.&lt;/strong&gt; Every agent connects to the database directly, so there is no separate Hivemind server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The data model
&lt;/h2&gt;

&lt;p&gt;The graph has two layers. The code layer is parsed from the repository. The team memory layer records what people and agents did, linked to the relevant functions.&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%2Fgfgnnwikacnyrgk3smsc.png" 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%2Fgfgnnwikacnyrgk3smsc.png" alt="The Hivemind graph data model" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marco ran a Kiro session that works on the task "Add JWT support".&lt;/li&gt;
&lt;li&gt;The task claims &lt;code&gt;validate_token&lt;/code&gt;, which records who is currently changing it.&lt;/li&gt;
&lt;li&gt;The session made an attempt that failed twice and touched &lt;code&gt;validate_token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Later, Lena's passing attempt resolves that failure, and her decision supersedes Priya's earlier one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code layer is built with Python's &lt;code&gt;ast&lt;/code&gt; module. It records every function, the functions it calls, and each file's imports, and writes them in batches using &lt;code&gt;UNWIND&lt;/code&gt; and &lt;code&gt;MERGE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each function also stores a hash of its syntax tree, without line numbers. When an agent edits a file, Hivemind re-indexes only that file and compares the hashes, which shows exactly which functions changed. Formatting changes and comment edits don't change the hash. Logic changes do.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  How function calls are resolved
  &lt;br&gt;
When a function calls &lt;code&gt;validate_token&lt;/code&gt;, the indexer needs to decide which &lt;code&gt;validate_token&lt;/code&gt; is meant. It prefers one defined in the same file, and otherwise uses any function with that name:&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;file:&lt;/span&gt; &lt;span class="n"&gt;$file&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;UNWIND&lt;/span&gt; &lt;span class="n"&gt;a.calls&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;b:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t.file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a.file&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;
&lt;span class="k"&gt;UNWIND&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;MERGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:CALLS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is not a type checker, and dynamic dispatch can mislead it. For typical application code it is accurate enough, and it is cheap to rerun after each edit.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting conflicts before an edit
&lt;/h2&gt;

&lt;p&gt;Before an agent edits a file, a hook checks whether any of the functions being changed are within three calls of a function that a teammate has claimed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t.qname&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;$mine&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:CALLS&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;o:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:CLAIMS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;i:&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;status:&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;i.dev&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;$me&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;t.qname&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o.qname&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i.dev&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i.description&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="nf"&gt;nodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n.qname&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[:CALLS*1..3]&lt;/code&gt; matches paths of one to three call edges. &lt;code&gt;nodes(p)&lt;/code&gt; returns the path itself, so the agent can show the developer how the two pieces of code are connected.&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%2F08x0k4v127di6s9s4466.png" 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%2F08x0k4v127di6s9s4466.png" alt="How Hivemind catches a conflict before anyone writes code" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full check runs three variants:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The same function&lt;/li&gt;
&lt;li&gt;Code that my code calls&lt;/li&gt;
&lt;li&gt;Code that calls my code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For each pair it keeps the shortest path, which is the easiest to read.&lt;/p&gt;

&lt;p&gt;If a conflict is found, the hook pauses the edit once and returns the explanation to the agent, which passes it on to the developer. If the developer continues, the next attempt goes through. The acknowledgement is stored as a node so the same warning isn't shown again. I chose a one-time warning over a hard block, because a hard block would get in the way of normal work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In the demo:&lt;/strong&gt; press &lt;strong&gt;Next step&lt;/strong&gt; until step 4 ("Aisha starts rate limiting in Codex"). A red path appears between &lt;code&gt;checkout&lt;/code&gt; and &lt;code&gt;validate_token&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd8jfwkmlww7hbijcgapy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd8jfwkmlww7hbijcgapy.gif" alt="Live demo: a conflict appears when Aisha starts" width="760" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording memory automatically
&lt;/h2&gt;

&lt;p&gt;The first version gave agents MCP tools such as &lt;code&gt;record_attempt&lt;/code&gt; and relied on them to call these tools. In practice they rarely did, because they focus on the task they were given. The memory stayed mostly empty.&lt;/p&gt;

&lt;p&gt;I moved recording into hooks, which Claude Code, Kiro and Codex all support. The hooks run at fixed points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the agent receives a prompt, it becomes the session's task.&lt;/li&gt;
&lt;li&gt;Before an edit, the functions being edited are claimed and checked for conflicts.&lt;/li&gt;
&lt;li&gt;After an edit, the file is re-indexed and the changed functions are recorded.&lt;/li&gt;
&lt;li&gt;When tests run after code changes, the result is recorded as an attempt on those functions.&lt;/li&gt;
&lt;li&gt;When the agent stops, its summary is saved as the task's progress note.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, Marco's failure is recorded like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The prompt "Add JWT support to auth tokens" becomes a task.&lt;/li&gt;
&lt;li&gt;Kiro edits &lt;code&gt;tokens.py&lt;/code&gt;. Re-indexing shows that the hash of &lt;code&gt;validate_token&lt;/code&gt; changed.&lt;/li&gt;
&lt;li&gt;Kiro runs &lt;code&gt;pytest&lt;/code&gt;. The exit code is 1, and &lt;code&gt;test_refresh_session&lt;/code&gt; is read from the output.&lt;/li&gt;
&lt;li&gt;The changed function and the failing test become an &lt;code&gt;Attempt&lt;/code&gt; linked to &lt;code&gt;validate_token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If Kiro then describes the failure in its own words, that description is added to the same attempt instead of creating a second one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;
  Handling three different hook formats
  &lt;br&gt;
Claude Code sends &lt;code&gt;PascalCase&lt;/code&gt; events (&lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUseFailure&lt;/code&gt;), and its tools are called &lt;code&gt;Edit&lt;/code&gt;, &lt;code&gt;Write&lt;/code&gt; and &lt;code&gt;Bash&lt;/code&gt;.

&lt;p&gt;Kiro sends &lt;code&gt;camelCase&lt;/code&gt; events (&lt;code&gt;preToolUse&lt;/code&gt;, &lt;code&gt;agentSpawn&lt;/code&gt;). Its tools are called &lt;code&gt;fs_write&lt;/code&gt; and &lt;code&gt;execute_bash&lt;/code&gt;, and it puts the exit status in &lt;code&gt;tool_response.result[0]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Codex uses Claude-style event names, but its tool names differ between versions (&lt;code&gt;shell&lt;/code&gt;, &lt;code&gt;exec_command&lt;/code&gt;, &lt;code&gt;apply_patch&lt;/code&gt;). So edits and commands are identified by their inputs instead of by tool name.&lt;/p&gt;

&lt;p&gt;A small normalisation layer converts all of these into one event format. I based it on payloads captured from real sessions, which are now used as test fixtures, because the documentation didn't cover every case.&lt;/p&gt;

&lt;p&gt;Test runs are detected by matching the command (&lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;unittest&lt;/code&gt;, &lt;code&gt;jest&lt;/code&gt;, &lt;code&gt;go test&lt;/code&gt;, &lt;code&gt;cargo test&lt;/code&gt; and others) and checking the exit code. The name of the failing test is then parsed from the output.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Before an agent edits a function, it receives everything that was tried on that function or its direct neighbours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t.qname&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;$q&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:CALLS&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;fn:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:TOUCHED&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Attempt&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;a.summary&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.outcome&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.reason&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.count&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.devs&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.resolved&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Lena's agent opens &lt;code&gt;validate_token&lt;/code&gt;, its context includes the fact that the PyJWT decode failed twice, and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consolidating memory
&lt;/h2&gt;

&lt;p&gt;If every event became a new node, the memory would fill up with near-duplicates. Hivemind merges related entries when it writes them.&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%2Fv1ubr0ztha9uwgmxesc1.png" 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%2Fv1ubr0ztha9uwgmxesc1.png" alt="How memory is consolidated, step by step" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repeated failures merge.&lt;/strong&gt; If a new attempt has the same outcome on the same code, with the same failing tests or very similar wording, the existing attempt's count is increased instead of adding a node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixes resolve failures.&lt;/strong&gt; When tests pass on code with open failures, those failures are marked as fixed and linked to the fix with a &lt;code&gt;RESOLVES&lt;/code&gt; edge. They stay in the graph as history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decisions supersede decisions.&lt;/strong&gt; A new decision on the same code &lt;code&gt;SUPERSEDES&lt;/code&gt; the old one. Agents see only the current decision, while &lt;code&gt;why()&lt;/code&gt; can still show the earlier ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;
  Query: a passing attempt resolves earlier failures
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;f:&lt;/span&gt;&lt;span class="n"&gt;Attempt&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;outcome:&lt;/span&gt; &lt;span class="s1"&gt;'failed'&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:TOUCHED&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;fn:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;fn.qname&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;$q&lt;/span&gt; &lt;span class="ow"&gt;AND&lt;/span&gt; &lt;span class="nf"&gt;coalesce&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f.resolved&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;a:&lt;/span&gt;&lt;span class="n"&gt;Attempt&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;$fix&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:RESOLVES&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;f.resolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f.resolved_dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;DISTINCT&lt;/code&gt; is needed because one failure can touch several of the functions in &lt;code&gt;$q&lt;/code&gt;. Without it, duplicate &lt;code&gt;RESOLVES&lt;/code&gt; edges would be created.&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In the demo:&lt;/strong&gt; step 7 ("Marco tries again, same failure") shows the merged failure with a ×2 badge. Step 10 ("Lena ships the fix") shows it marked as fixed by Lena.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Handoffs
&lt;/h2&gt;

&lt;p&gt;When Marco stops, his agent hands the task off with a note. When Lena resumes it, one query moves the &lt;code&gt;OWNS&lt;/code&gt; edge to her and links her session to the task.&lt;/p&gt;

&lt;p&gt;Everything else stays connected to the task: the claims, Marco's sessions, both failed attempts, his note and Priya's decision. Lena's agent receives the full history in one call.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  The resume query
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;i:&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;$task&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;OPTIONAL&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;:Developer&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="py"&gt;o:&lt;/span&gt;&lt;span class="n"&gt;OWNS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;d:&lt;/span&gt;&lt;span class="n"&gt;Developer&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;$me&lt;/span&gt;&lt;span class="ss"&gt;}),&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;s:&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;$session&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:OWNS&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:WORKS_ON&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;i.status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvcf9s0asleoy9uagxh1o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvcf9s0asleoy9uagxh1o.gif" alt="Live demo: Lena's fix resolves Marco's failure" width="760" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying the memory
&lt;/h2&gt;

&lt;p&gt;Agents also get an MCP server with 14 tools, for things the hooks can't infer: starting and handing off tasks, recording decisions and conventions, and answering questions about the code. Three of these tools explain the code, and each is a single traversal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;blast_radius&lt;/code&gt;&lt;/strong&gt; follows callers backwards:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;  &lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;caller:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:CALLS&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;qname:&lt;/span&gt; &lt;span class="n"&gt;$q&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;caller.qname&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="ss"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;who_knows&lt;/code&gt;&lt;/strong&gt; ranks developers by their claims, attempts and decisions on a function and its neighbours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;why&lt;/code&gt;&lt;/strong&gt; returns the current decision, the decision it replaced, all attempts, and the people involved. At the end of the demo, &lt;code&gt;why("validate_token")&lt;/code&gt; returns the full history of the JWT change.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In the demo:&lt;/strong&gt; at step 12, click &lt;code&gt;validate_token&lt;/code&gt; on the map. The panel on the right shows the same information as &lt;code&gt;why&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The dashboard
&lt;/h2&gt;

&lt;p&gt;The dashboard shows the codebase as a map:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Functions are grouped by file.&lt;/li&gt;
&lt;li&gt;Each developer's work is outlined in their colour.&lt;/li&gt;
&lt;li&gt;Conflict paths are drawn in red.&lt;/li&gt;
&lt;li&gt;Memory is shown as badges on functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It uses FalkorDB's algorithms in a few places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;algo.pageRank&lt;/code&gt; ranks functions that much of the code depends on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;algo.betweenness&lt;/code&gt; finds functions that many call paths pass through, which is where conflicts are more likely.&lt;/li&gt;
&lt;li&gt;The path finder uses &lt;code&gt;shortestPath&lt;/code&gt;. When two functions aren't directly connected, it shows their closest shared caller instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People can also add decisions and conventions from the dashboard.&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%2Fttard3ns7kp8qa2alkrh.png" 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%2Fttard3ns7kp8qa2alkrh.png" alt="The Hivemind live view" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture and setup
&lt;/h2&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%2Fuscybh42d9hdt9wabf31.png" 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%2Fuscybh42d9hdt9wabf31.png" alt="Hivemind architecture" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each agent connects to FalkorDB Cloud directly over Cypher, through both the hooks and the MCP server. The dashboard is hosted on Vercel, reads the same graph, and is protected by an access code. Timestamps come from the database server's clock, since laptop clocks can differ.&lt;/p&gt;

&lt;p&gt;To join a team, you install Hivemind with &lt;code&gt;uv tool install git+https://github.com/Quantapixel/hivemind&lt;/code&gt; and run &lt;code&gt;hivemind join &amp;lt;dashboard-url&amp;gt;&lt;/code&gt; inside the project. The command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Asks for the team's access code&lt;/li&gt;
&lt;li&gt;Detects which agents are installed&lt;/li&gt;
&lt;li&gt;Configures each agent in its own format&lt;/li&gt;
&lt;li&gt;Indexes the code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;hivemind doctor&lt;/code&gt; checks the setup and reports what to fix.&lt;/p&gt;

&lt;p&gt;Source code is not uploaded. The graph stores function names, how functions call each other, and what people did to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with real agents
&lt;/h2&gt;

&lt;p&gt;I tested Hivemind with three separate clones of a project, acting as three machines that shared one cloud graph.&lt;/p&gt;

&lt;p&gt;First, Kira (using Kiro) was asked to add token revocation. The prompt didn't mention Hivemind. The hooks recorded the task, &lt;code&gt;revoke_token&lt;/code&gt; as added, &lt;code&gt;validate_token&lt;/code&gt; as changed, the passing test run, and Kiro's summary.&lt;/p&gt;

&lt;p&gt;Next, Carl (using Claude Code) was asked to add token scopes in the same area. During the task, his agent noted: &lt;em&gt;"The revocation check from kira's recent change is untouched."&lt;/em&gt; It had picked up Kira's change from the shared memory without being asked to check.&lt;/p&gt;

&lt;p&gt;Finally, Cody (using Codex) read the shared task board through MCP and saw both of their tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agents don't reliably record things themselves.&lt;/strong&gt; Relying on agents to call a recording tool left the memory mostly empty. Recording through hooks, outside the agent's decisions, worked much better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first conflict query was too broad.&lt;/strong&gt; It followed &lt;code&gt;CALLS&lt;/code&gt; edges in both directions in one pattern, so two functions were flagged as conflicting just because a third function called both. Splitting it into "code I call" and "code that calls me" removed these false positives and made the explanations clearer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-indexing could delete memory.&lt;/strong&gt; Developers' working copies differ, so a function missing from one copy may still exist on another branch. Early on, re-indexing one copy could delete that function and the memory attached to it. Now a function is only removed if no memory is attached to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation didn't cover all hook payloads.&lt;/strong&gt; The first real Kiro and Claude Code sessions each found cases my hand-written test fixtures had missed. The hook tests now use payloads captured from real sessions, and all tests run against a real FalkorDB instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Region placement matters.&lt;/strong&gt; The dashboard was first deployed in a region far from the database, which made every request slow. Deploying it in the same region as the database fixed this.&lt;/p&gt;

&lt;p&gt;Overall, the graph model kept the code small. Conflict detection, context, consolidation, handoffs, blast radius, &lt;code&gt;who_knows&lt;/code&gt; and &lt;code&gt;why&lt;/code&gt; are each one or two Cypher queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Support for TypeScript, Go and Rust using Tree-sitter (only Python is indexed now)&lt;/li&gt;
&lt;li&gt;Merging similar failures by meaning using FalkorDB's vector index (currently it uses word overlap)&lt;/li&gt;
&lt;li&gt;Linking decisions to pull requests automatically&lt;/li&gt;
&lt;li&gt;Suggesting reviewers using community detection on the call graph&lt;/li&gt;
&lt;li&gt;Expiring old attempts and stale claims&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The &lt;a href="https://hivemind-live-demo.vercel.app" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; plays through the full scenario step by step. There is also a &lt;a href="https://github.com/Quantapixel/hivemind/blob/main/docs/videos/hivemind-demo.mp4" rel="noopener noreferrer"&gt;3-minute video&lt;/a&gt; using real agents.&lt;/p&gt;

&lt;p&gt;To run it locally without any accounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Quantapixel/hivemind &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;hivemind &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv &lt;span class="nb"&gt;sync
&lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;          &lt;span class="c"&gt;# local FalkorDB&lt;/span&gt;
uv run hivemind ui &amp;amp;          &lt;span class="c"&gt;# http://127.0.0.1:8765&lt;/span&gt;
uv run hivemind demo &lt;span class="nt"&gt;--step&lt;/span&gt;   &lt;span class="c"&gt;# press Enter to advance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your team uses several agents on one codebase, I'd be interested to hear how you coordinate them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for Graph Hacks: Context for AI Agents (WeMakeDevs × FalkorDB, October 2026), Track 02: Agent Memory and Coordination. I built it with Claude Code as my coding assistant: the idea and product direction are mine, and much of the code was written by the assistant under that direction and reviewed by me. All example data is synthetic.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>falkordb</category>
      <category>ai</category>
      <category>graphdatabase</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
