<?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: Nimit Sharma</title>
    <description>The latest articles on DEV Community by Nimit Sharma (@nimit15).</description>
    <link>https://dev.to/nimit15</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%2F4191877%2F5088ce34-6a30-433d-ad20-0660c893c449.png</url>
      <title>DEV Community: Nimit Sharma</title>
      <link>https://dev.to/nimit15</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nimit15"/>
    <language>en</language>
    <item>
      <title>Stop Letting Coding Agents Guess Call Chains: Tracing Blast Radius and Temporal Memory in FalkorDB</title>
      <dc:creator>Nimit Sharma</dc:creator>
      <pubDate>Sun, 18 Oct 2026 15:20:35 +0000</pubDate>
      <link>https://dev.to/nimit15/stop-letting-coding-agents-guess-call-chains-tracing-blast-radius-and-temporal-memory-in-falkordb-20hl</link>
      <guid>https://dev.to/nimit15/stop-letting-coding-agents-guess-call-chains-tracing-blast-radius-and-temporal-memory-in-falkordb-20hl</guid>
      <description>&lt;p&gt;When you change a helper function in a backend service, ask an LLM coding assistant, it will likely do one of two things: retrieve the function name from a vector database, or extract a few top-k chunks from a vector database and guess.&lt;/p&gt;

&lt;p&gt;This is okay if the function is called directly from the test. It fails when your call chain is two or three hops long, or when an agent fails to find a solution to a problem today, saves the fact that it failed and moves on to tomorrow, when a commit alters the logic in the same module.&lt;/p&gt;

&lt;p&gt;I created &lt;strong&gt;GraphTriage&lt;/strong&gt; (&lt;a href="https://github.com/Nimit15/GraphTriage" rel="noopener noreferrer"&gt;github.com/Nimit15/GraphTriage&lt;/a&gt;): an Incident resolution and session memory system supported by &lt;strong&gt;FalkorDB Cloud&lt;/strong&gt; for &lt;strong&gt;WeMakeDevs × FalkorDB Graph Hacks 2026&lt;/strong&gt; (Track 1: Agents That Act on Connected Data, Track 2: Agent Memory and Coordination).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GraphTriage&lt;/strong&gt; is not a visualization supplement to the graph database, but rather it poses four specific questions using FalkorDB before an agent acts: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the function or the test you are asking for exist in the graph? (If not, say NO right away rather than guess!)&lt;/li&gt;
&lt;li&gt;What is the exact call chain between the failed test and the broken function?&lt;/li&gt;
&lt;li&gt;What downstream functions and tests are contained within a blast radius of a change?&lt;/li&gt;
&lt;li&gt;Have we seen this fault signature before—and is that prior finding still &lt;code&gt;ACTIVE&lt;/code&gt;, or has a newer commit marked it &lt;code&gt;SUPERSEDED&lt;/code&gt;?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. The Graph Schema mapping in FalkorDB
&lt;/h2&gt;

&lt;p&gt;FalkorDB contains both repository (Track 1) and three-tier temporal memory (Track 2) data. Each workspace is separated at the database level using &lt;code&gt;db.select_graph(f"graphtriage_ws_{workspace_id}")&lt;/code&gt;, to prevent memory leakage across tenants.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;graph LR
    M[":Module"] &amp;lt;-- DEFINED_IN --- F1[":Function (Caller)"]
    F1 -- CALLS --&amp;gt; F2[":Function (Callee)"]
    T[":Test"] -- COVERS --&amp;gt; F1
    I[":EpisodicIncident"] -- IMPLICATES --&amp;gt; F2
    SF_NEW[":SemanticFact (ACTIVE)] -- APPLIES_TO --&amp;gt; F2
    SF_OLD[":SemanticFact (SUPERSEDED)]]
    PB[":ProceduralPlaybook"] -- REMEDIATES --&amp;gt; SF_NEW&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Nodes and Relationships
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Code Structure Nodes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;(&lt;code&gt;:Module&lt;/code&gt;) — pulled the source files from the repository.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(&lt;code&gt;:Function&lt;/code&gt;) — functions with module paths and line metadata, connected by [&lt;code&gt;:DEFINED_IN&lt;/code&gt;] and directed [&lt;code&gt;:CALLS&lt;/code&gt;] edges.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(&lt;code&gt;:Test&lt;/code&gt;) — test cases exercising the functions they cover via [&lt;code&gt;:COVERS&lt;/code&gt;] edges.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Three-Tier Memory Nodes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;(&lt;code&gt;:EpisodicIncident&lt;/code&gt;) – raw incident data (stack trace, failing test name, session id, timestamp).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(&lt;code&gt;:SemanticFact&lt;/code&gt;) — verified root-cause statements with temporal lifecycle properties (&lt;code&gt;valid_from&lt;/code&gt;, &lt;code&gt;valid_to&lt;/code&gt;, &lt;code&gt;status: ‘ACTIVE’ | ‘SUPERSEDED’&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(&lt;code&gt;:ProceduralPlaybook&lt;/code&gt;) - reusable remediation steps attached to recurrent fault signatures (e.g. &lt;code&gt;KeyError: 'income'&lt;/code&gt;), keeping track of &lt;code&gt;usage_count&lt;/code&gt;over sessions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2F1hikg25c01fgm4en7v4r.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%2F1hikg25c01fgm4en7v4r.png" alt=" " width="788" height="653"&gt;&lt;/a&gt;&lt;em&gt;Figure 1. GraphTriage's local FalkorDB graph showing the code entities and relationships used for incident analysis.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Track 1: Explaining How the Graph Carries Out the Work (A Cypher That Contains No Text Written by a Large Language Model)
&lt;/h2&gt;

&lt;p&gt;A design rule that I adhered to right from the beginning was that the agent should never generate raw Cypher queries at runtime. Allowing an LLM to produce ad-hoc Cypher queries would lead to syntax errors and the invention of hallucinated edge labels. In order to avoid this, the agent interacts with FalkorDB only via the parameterized methods in &lt;code&gt;src/core/incident_resolver.py&lt;/code&gt; and the fixed tools that are available through the official Model Context Protocol (MCP) SDK (&lt;code&gt;src/mcp/mcp_server.py&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;trace_blast_radius&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;find_failure_path&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;run_targeted_tests&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Refusing to Speculate on Unknown Entities&lt;/strong&gt;&lt;br&gt;
Should you inquire of a typical RAG agent regarding a function that has been renamed or deleted, it will readily retrieve chunks of code that look similar and then fabricate an impact analysis.&lt;/p&gt;

&lt;p&gt;In the function &lt;code&gt;IncidentResolver._validate_entity_existence()&lt;/code&gt;, each analysis begins with a parameterized check for existence in FalkorDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_validate_entity_existence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    MATCH (n)
    WHERE (n:Function OR n:Test OR n:Module)
      AND n.name = $entity_name
    RETURN count(n) AS count
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entity_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entity_name&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result_set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&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;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result_set&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If FalkorDB returns &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;analyze_impact()&lt;/code&gt; halts immediately and returns &lt;code&gt;status: "UNVERIFIED_ENTITY"&lt;/code&gt; with &lt;code&gt;hops: 0&lt;/code&gt; and an explicit refusal reason:&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;"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;"UNVERIFIED_ENTITY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hops"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"function_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;"non_existent_helper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Function 'non_existent_helper' not found in graph; refusing to speculate without graph evidence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"affected_functions"&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;"impact_path"&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;Every decision the agent makes is traceable back to graph state: if the node isn't in FalkorDB, the agent refuses to guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Tracing Real Bugs in &lt;code&gt;sandbox_service&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
To test GraphTriage end-to-end without mocks, &lt;code&gt;sandbox_service/app.py&lt;/code&gt; models a multi-function service (7 functions and 12 tests) containing three intentional regressions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;process_user_data&lt;/code&gt;: raises &lt;code&gt;KeyError: 'income'&lt;/code&gt; when optional dictionary keys are missing.
2.
&lt;code&gt;calculate_growth_rate&lt;/code&gt;: uses bitwise XOR (&lt;code&gt;^&lt;/code&gt;) instead of exponentiation (&lt;code&gt;**&lt;/code&gt;).
3.
&lt;code&gt;calculate_compound_interest&lt;/code&gt;: drops the exponent term in the compound formula.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When &lt;code&gt;test_incident_process_user_data_missing_income&lt;/code&gt; fails, GraphTriage queries the call chain (&lt;code&gt;[:COVERS]&lt;/code&gt;$\rightarrow$ &lt;code&gt;[:CALLS]&lt;/code&gt;) in FalkorDB to trace the path from the failing test to the underlying function, then computes the blast radius (&lt;code&gt;calculate_blast_radius&lt;/code&gt;) using PageRank (&lt;code&gt;CALL algo.pageRank('Function', 'CALLS'&lt;/code&gt;) with a &lt;code&gt;&amp;lt; 2&lt;/code&gt; node guard that falls back cleanly to direct relationship degree scoring).&lt;/p&gt;

&lt;p&gt;Instead of re-running all 12 tests in the sandbox on every edit, the agent queries FalkorDB for only the &lt;code&gt;:Test&lt;/code&gt; nodes connected to the affected subgraph and executes &lt;code&gt;pytest&lt;/code&gt; on that targeted subset.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Track 2: Multi-Agent Handoffs &amp;amp; Replacing Old Information
&lt;/h2&gt;

&lt;p&gt;When we store debugging history in a vector index that only adds new entries, old conclusions can stick around forever without anyone noticing.&lt;/p&gt;

&lt;p&gt;In the files &lt;code&gt;src/core/multi_agent_pipeline.py&lt;/code&gt; and &lt;code&gt;src/core/temporal_memory.py&lt;/code&gt;, the GraphTriage system manages three specific agents. These agents pass information between each other using a structured process called &lt;code&gt;AgentHandoff&lt;/code&gt;, with FalkorDB storing the data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
The &lt;code&gt;TriageAgent&lt;/code&gt; looks at the stack trace, pulls out a standard signature for the error (like &lt;code&gt;KeyError: 'income'&lt;/code&gt;), records it as an EpisodicIncident, and then checks FalkorDB to see if there are any active SemanticFacts.
2.
The &lt;code&gt;BlastRadiusAgent&lt;/code&gt; takes the information from the TriageAgent, uses FalkorDB to find out how the failure spread and which functions were affected, and adds that graph evidence.
3.
The MemoryCuratorAgent saves confirmed findings about the root cause as SemanticFact nodes and either finds or creates a ProceduralPlaybook.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Marking Old Facts as Replaced with &lt;code&gt;[:SUPERSEDED]&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a new code change affects how &lt;code&gt;process_user_data&lt;/code&gt; handles default values. Instead of deleting the old &lt;code&gt;SemanticFact&lt;/code&gt; (which would lose the debugging history) or just adding a new, conflicting fact, we use a function called &lt;code&gt;supersede_fact()&lt;/code&gt;. This function sets an expiration date for the old fact, marks it as &lt;code&gt;SUPERSEDED&lt;/code&gt;, and connects the new fact to the old one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Initial architectural finding saved in Session 1
&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_semantic_fact&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process_user_data should use user.get(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;income&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, 0)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;valid_from&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-10-17T10:00:00Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACTIVE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Superseded after schema update in Session 2
&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;supersede_fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_fact_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process_user_data validates schema via default dict mapping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;valid_from&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-10-18T14:00:00Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACTIVE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Cypher, closing the old fact while preserving the lineage looks like this:&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;old:&lt;/span&gt;&lt;span class="n"&gt;SemanticFact&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;fact_id:&lt;/span&gt; &lt;span class="n"&gt;$old_fact_id&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;old.valid_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$timestamp&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;old.status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SUPERSEDED'&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="py"&gt;new:&lt;/span&gt;&lt;span class="n"&gt;SemanticFact&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;fact_id:&lt;/span&gt; &lt;span class="n"&gt;$new_fact_id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
    &lt;span class="py"&gt;statement:&lt;/span&gt; &lt;span class="n"&gt;$statement&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
    &lt;span class="py"&gt;valid_from:&lt;/span&gt; &lt;span class="n"&gt;$timestamp&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
    &lt;span class="py"&gt;valid_to:&lt;/span&gt; &lt;span class="k"&gt;null&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="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:SUPERSEDED&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;old&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a future session queries memory, the fast retrieval path filters strictly on &lt;code&gt;status = 'ACTIVE' AND valid_to IS NULL&lt;/code&gt;. The agent gets only current truth in its context window, while the &lt;code&gt;[:SUPERSEDED]&lt;/code&gt;chain preserves the full audit trail.&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%2Fa492twu6h12n2asc6s7g.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%2Fa492twu6h12n2asc6s7g.png" alt=" " width="800" height="385"&gt;&lt;/a&gt;&lt;em&gt;Figure 2. GraphTriage's incident-resolver and temporal-memory tests running against a local FalkorDB instance (19 passed).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Engineering Lessons From Setting Up FalkorDB Cloud
&lt;/h2&gt;

&lt;p&gt;When I built and tested this on Windows with a live &lt;strong&gt;FalkorDB Cloud&lt;/strong&gt;, I learned some practical things:&lt;/p&gt;

&lt;p&gt;1.&lt;br&gt;
&lt;strong&gt;Centralizing Cloud Connection and Default Parameter Issues&lt;/strong&gt; &lt;strong&gt;(&lt;/strong&gt;&lt;code&gt;src/core/db_connection.py&lt;/code&gt;&lt;strong&gt;)&lt;/strong&gt;:&lt;br&gt;
At first, having default &lt;code&gt;host="localhost"&lt;/code&gt; in our class constructors would quietly ignore the .env variables when setting up test data. We fixed this by putting all connection code into one place (&lt;code&gt;db_connection.py&lt;/code&gt;). This setup loads &lt;code&gt;.env&lt;/code&gt; variables and allows overriding them, splits the host and port, and has a fallback for SSL. Now, switching between our local Docker setup (&lt;code&gt;localhost:6379&lt;/code&gt;) and FalkorDB Cloud is easy.&lt;/p&gt;

&lt;p&gt;2.&lt;br&gt;
&lt;strong&gt;MCP Server Startup and Module Name Conflicts:&lt;/strong&gt;&lt;br&gt;
We named our local folder &lt;code&gt;src/mcp/&lt;/code&gt;, but this conflicted with the official mcp package we installed through pip. This happened when we set &lt;code&gt;PYTHONPATH=/app/src&lt;/code&gt; in Docker. By changing to import from the project root (using &lt;code&gt;PYTHONPATH=/app&lt;/code&gt; and running commands like &lt;code&gt;python -m src.mcp.mcp_server&lt;/code&gt;) and giving &lt;code&gt;InitializationOptions()&lt;/code&gt; to &lt;code&gt;mcp_server.run()&lt;/code&gt;, we fixed the startup problems.&lt;/p&gt;

&lt;p&gt;2.&lt;br&gt;
&lt;strong&gt;Comparing Step-by-Step Path Finding vs. Wildcard Shortest Path:&lt;/strong&gt;&lt;br&gt;
In OpenCypher, how patterns with unbounded or undirected paths work can be different on various graph databases compared to Neo4j. We used specific directed relationships (like &lt;code&gt;[:COVERS]&lt;/code&gt; and &lt;code&gt;[:CALLS]&lt;/code&gt;) along with checking the call chain step-by-step and using a fallback based on how connected a node is. This made our path tracing completely predictable in all &lt;code&gt;8&lt;/code&gt; integration tests. All tests &lt;code&gt;passed&lt;/code&gt;, with &lt;code&gt;one warning&lt;/code&gt;, and it took &lt;code&gt;10.49 seconds&lt;/code&gt; against the live FalkorDB Cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Current Limitations &amp;amp; Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Language Parser Scope:&lt;/strong&gt; The system currently takes in Python modules and follows &lt;code&gt;pytest&lt;/code&gt; conventions; the next logical step is to expand the AST loader to support TypeScript and Go using Tree-sitter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Containerized Test Runner:&lt;/strong&gt; The current test execution runs in a subprocess with a time limit; putting test runs inside containers would make it more reliable for use in multi-user CI pipelines.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository &amp;amp; Disclosure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Nimit15/GraphTriage" rel="noopener noreferrer"&gt;https://github.com/Nimit15/GraphTriage&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hackathon Tracks:&lt;/strong&gt; Track 1 (Agents That Act on Connected Data) and Track 2 (Agent Memory and Coordination)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Development &amp;amp; AI Disclosure:&lt;/strong&gt; This project was built for &lt;strong&gt;WeMakeDevs × FalkorDB Graph Hacks 2026&lt;/strong&gt;. The graph schema design, FalkorDB Cloud connection layer, entity-existence refusal guard, call-chain and blast-radius logic, 3-stage agent handoff flow and temporal &lt;code&gt;SUPERSEDED&lt;/code&gt; memory lifecycle (~75% of the engineering work) were. Checked by me. I used Claude Code (~25%) as an IDE coding assistant, for FastAPI/MCP scaffolding, synthetic sandbox bugs and initial test fixtures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>falkordb</category>
      <category>wemakedevs</category>
      <category>ai</category>
      <category>python</category>
    </item>
  </channel>
</rss>
