<?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: Ishaan Gupta</title>
    <description>The latest articles on DEV Community by Ishaan Gupta (@ishaan_gupta_6c0f9dd08855).</description>
    <link>https://dev.to/ishaan_gupta_6c0f9dd08855</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%2F4164783%2Ffa0181db-ae3b-407c-92fd-3aee2f18a50a.jpg</url>
      <title>DEV Community: Ishaan Gupta</title>
      <link>https://dev.to/ishaan_gupta_6c0f9dd08855</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ishaan_gupta_6c0f9dd08855"/>
    <language>en</language>
    <item>
      <title>Moovi: 700+ commits, 3 days, one FalkorDB graph</title>
      <dc:creator>Ishaan Gupta</dc:creator>
      <pubDate>Mon, 19 Oct 2026 04:29:09 +0000</pubDate>
      <link>https://dev.to/ishaan_gupta_6c0f9dd08855/moovi-700-commits-3-days-one-falkordb-graph-274j</link>
      <guid>https://dev.to/ishaan_gupta_6c0f9dd08855/moovi-700-commits-3-days-one-falkordb-graph-274j</guid>
      <description>&lt;p&gt;&lt;em&gt;Built for Graph Hacks: Context for AI Agents, by WeMakeDevs x FalkorDB. Tracks: Track 01, Agents That Act on Connected Data, and Track 02, Agent Memory and Coordination.&lt;/em&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%2Fx9xlk0jd1t0v4pylcirq.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%2Fx9xlk0jd1t0v4pylcirq.png" alt="Bar chart of commits per hour over 68 hours, split by author, with the bug fixes from this post marked" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;713 commits to main in 68 hours. Every bar is one hour of commits; the yellow dots are the first twelve bugs in this post, placed at the minute the fix landed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Between Thursday 15 and Sunday 18 October, our team of four (Ishaan, Keshav, Srujan and I) built Moovi.&lt;/p&gt;

&lt;p&gt;Moovi is an AI video editor. You give it a clip, it gives you back a finished vertical video: cut, captioned, with sound and effects. The part I care about is under the hood. Every choice it makes is a node in a FalkorDB graph, with an edge back to the reason. So when you ask "why did you cut there?", the answer is a Cypher query, not a guess.&lt;/p&gt;

&lt;p&gt;This post is the honest version of those three days: how the graph is wired, what it is used for, seventeen things that broke and how we fixed them (some with photos of the real errors), and how our graph grew, and the calls we had to make on the last morning. Every commit below links to the real diff.&lt;/p&gt;

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

&lt;p&gt;Moovi is like Lovable, but for video. On the first screen you pick one of three presets: &lt;strong&gt;Edit&lt;/strong&gt; (your clip, cut and captioned), &lt;strong&gt;Launch video&lt;/strong&gt; or &lt;strong&gt;Showreel&lt;/strong&gt;. Then a crew of agents works on it in the studio: Moovi, Editor, Researcher, Writer, Sound, Designer, Lens and Keeper. Ishaan built the studio, the landing page and the product calls. I built the AI side: the graph memory, the edit engine pieces and the Claude Code bridge.&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%2Fi9jidloblfg97wsesffg.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%2Fi9jidloblfg97wsesffg.jpg" alt="The crew of eight on Moovi's landing page, with the eight steps from brief to remember" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The landing page (a Three.js scene). Each crew member owns one step, from BRIEF to REMEMBER.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is one Edit run on our test clip. The studio said this after 29 seconds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's your edit, playing live: 16.2 s from 19.0 s (2.8 s of pauses cut), 10 shots, 48 words captioned.&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%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fny71hxmur1d8elz8d18c.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%2Fny71hxmur1d8elz8d18c.jpg" alt="Raw landscape clip on the left, the 9:16 captioned edit on the right" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real frames from our 18 October end to end test. Left: the raw clip. Right: Moovi's Edit, about one second in.&lt;/em&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%2Fz3oi36r1wvcm7w85raaw.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%2Fz3oi36r1wvcm7w85raaw.jpg" alt="Six frames from the finished Edit" width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Six frames from the same finished Edit: punch ins, word by word captions, the end card.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FalkorDB is the only memory.&lt;/strong&gt; One shared library graph, one graph per user, one graph per project. 248 named Cypher queries. If FalkorDB goes down, the job stops with "Moovi's memory is offline". No silent fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every decision has a &lt;code&gt;BECAUSE&lt;/code&gt; edge.&lt;/strong&gt; "Why this caption?" is a shortest path query that answers in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The task board is in the graph too.&lt;/strong&gt; Agents claim tasks with a lease, so two workers never take the same one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A crew, not one agent.&lt;/strong&gt; Seven specialists plus Moovi, each with its own named queries and its own lessons, handing work over through a task board that lives in the graph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exactly three graph algorithms:&lt;/strong&gt; &lt;code&gt;shortestPath&lt;/code&gt; for "why this?", &lt;code&gt;algo.SPpaths&lt;/code&gt; and &lt;code&gt;algo.pageRank&lt;/code&gt; to plan a film from a written brief. Code below, copied from our GitHub repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moovi is not a closed app.&lt;/strong&gt; Plug it into your own Claude Code over MCP with two commands, and Claude Code becomes the brain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My favourite bug:&lt;/strong&gt; a render queue that waited on itself. No error, no log line, just "working" forever. One line to fix.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The demo
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://moovi-three.vercel.app" rel="noopener noreferrer"&gt;moovi-three.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Ishaan-Gpt/moovi" rel="noopener noreferrer"&gt;github.com/Ishaan-Gpt/moovi&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why a crew and not one agent
&lt;/h2&gt;

&lt;p&gt;We never shipped Moovi as one agent. The engine was a crew from its second commit, one minute into the hackathon (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/c617a55" rel="noopener noreferrer"&gt;&lt;code&gt;c617a55&lt;/code&gt;&lt;/a&gt;, 15 Oct 18:31 IST). Our reasoning: one agent with one prompt has to know captions, sound, fonts, facts and pacing all at once. Every new rule makes the prompt longer and the other rules weaker, and when a film comes out wrong you cannot tell which part of the prompt did it. A real edit room splits that work, so we did too.&lt;/p&gt;

&lt;p&gt;The crew is Moovi (the one you talk to) plus seven specialists: Editor, Researcher, Writer, Sound, Designer, Lens and Keeper (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/033796a" rel="noopener noreferrer"&gt;&lt;code&gt;033796a&lt;/code&gt;&lt;/a&gt;). This is how it is wired, all in the graph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Work is a task board, not a conversation.&lt;/strong&gt; A film becomes &lt;code&gt;Task&lt;/code&gt; nodes with &lt;code&gt;DEPENDS_ON&lt;/code&gt; edges. A task becomes ready only when everything it depends on is done, and each task names its &lt;code&gt;agent&lt;/code&gt;. The worker claims it with the &lt;code&gt;claim&lt;/code&gt; query below, so nothing runs twice. That commit's title says it: "the crew: DAG scheduler on FalkorDB".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each member has its own queries.&lt;/strong&gt; The Designer casts with &lt;code&gt;assetsServing&lt;/code&gt; and &lt;code&gt;assetsForEvent&lt;/code&gt;. The Writer's lines go through &lt;code&gt;truthGuard&lt;/code&gt;. The Director plans with &lt;code&gt;boardPaths&lt;/code&gt; and &lt;code&gt;overUse&lt;/code&gt;. The Keeper writes your reactions with &lt;code&gt;recordFeedback&lt;/code&gt;. No member sees the whole 248.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each member has its own memory.&lt;/strong&gt; &lt;code&gt;lessonsFor&lt;/code&gt; reads "the current lessons one agent must follow", so a lesson for Sound never clutters the Writer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every member signs its work.&lt;/strong&gt; Decisions are written with the agent that made them (&lt;code&gt;decide&lt;/code&gt;), which is what makes "why this?" answerable per choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handoffs are edges.&lt;/strong&gt; When a task finishes, &lt;code&gt;finish&lt;/code&gt; keeps "its output for the tasks that depend on it". Nobody passes a giant chat history along.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The win is boring and real: when a film looks wrong, we can see which task, which agent and which query made it, and fix that one piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the graph is used
&lt;/h2&gt;

&lt;p&gt;This is the part the hackathon is about, so I will go slowly.&lt;/p&gt;

&lt;h3&gt;
  
  
  One FalkorDB, many graphs
&lt;/h3&gt;

&lt;p&gt;FalkorDB can hold thousands of separate graphs in one instance, so we gave each kind of memory its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;craft&lt;/code&gt;&lt;/strong&gt;: the shared library. Caption looks, sounds, camera moves, transitions, effects, fonts and the reference reels they came from. Nothing about any user ever goes in here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sys&lt;/code&gt;&lt;/strong&gt;: accounts, sessions and who owns which project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;user_&amp;lt;uid&amp;gt;&lt;/code&gt;&lt;/strong&gt;: one per user. Their lessons, preferences and what their past videos used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;proj_&amp;lt;pid&amp;gt;&lt;/code&gt;&lt;/strong&gt;: one per project. Chats, videos, runs, the task board, facts, claims and every decision.&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%2Fu965u06risx1nfrrcv06.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%2Fu965u06risx1nfrrcv06.png" alt="Diagram of Moovi's graphs: craft, sys, user and project, with their node labels and relationship types" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every label and edge here is read from the queries in &lt;code&gt;packages/graph/src/queries&lt;/code&gt;. The counts in the craft panel come from our local FalkorDB on 18 October: 1,429 nodes and 5,215 edges.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Agents never write Cypher. They call one of 248 named queries (125 read only, 123 writes), and the read only ones go through &lt;code&gt;GRAPH.RO_QUERY&lt;/code&gt;, so the server itself refuses a write. Every call has a timeout and is timed, and the studio can show the real Cypher behind each step.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Cypher the crew calls
&lt;/h3&gt;

&lt;p&gt;The 248 named queries live in eleven files under &lt;code&gt;packages/graph/src/queries&lt;/code&gt;: craft (39), launch (7), library (44), media (12), project (78), styles (10), sys (13), types (9), user (25), voice (6) and why (5). Each one is a &lt;code&gt;q(name, readOnly, meaning, cypher)&lt;/code&gt; call, so the plain English meaning sits next to the Cypher. Here are three the crew runs on every film, verbatim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The worker claims a task with a lease&lt;/strong&gt; (&lt;code&gt;project.ts&lt;/code&gt;, &lt;code&gt;claim&lt;/code&gt;, called from &lt;code&gt;packages/engine/src/worker.ts&lt;/code&gt;). The &lt;code&gt;WHERE t.status = 'ready'&lt;/code&gt; makes it atomic: two workers can race, only one wins.&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;Task&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;$id&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.status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ready'&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;t.status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'running'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$worker&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.lease_until&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$lease&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.started_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$at&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t.attempts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;t.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.kind&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.agent&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.resource&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.scope&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.title&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;t.input&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.attempts&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.max&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.feedback&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.answer&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.reopened&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;reopened&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Designer casts an asset by what the beat needs&lt;/strong&gt; (&lt;code&gt;library.ts&lt;/code&gt;, &lt;code&gt;assetsServing&lt;/code&gt;, called from &lt;code&gt;edit/cast.ts&lt;/code&gt;, &lt;code&gt;edit/inserts.ts&lt;/code&gt; and &lt;code&gt;edit/effects/auto.ts&lt;/code&gt;). Assets &lt;code&gt;SERVE&lt;/code&gt; needs; the one that serves the most needs of this beat wins.&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;UNWIND&lt;/span&gt; &lt;span class="n"&gt;$needs&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nid&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;Asset&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;category:&lt;/span&gt; &lt;span class="n"&gt;$category&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;s:&lt;/span&gt;&lt;span class="n"&gt;SERVES&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="nc"&gt;:Need&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;nid&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;$runtime&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;$runtime&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;a.runtime&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="n"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;$shipOnly&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;a.ship&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;AND&lt;/span&gt; &lt;span class="ow"&gt;NOT&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;a.retired&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="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;count&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s.w&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;rank&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;a.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.runtime&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.energy&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;energy&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.params&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.impl&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;impl&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a.id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="n"&gt;$n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Writer may not state a fact without a path to a source&lt;/strong&gt; (&lt;code&gt;project.ts&lt;/code&gt;, &lt;code&gt;truthGuard&lt;/code&gt;, called from &lt;code&gt;tasks/script.ts&lt;/code&gt;). No path, no claim.&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;c:&lt;/span&gt;&lt;span class="n"&gt;Claim&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;$id&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;:SUPPORTED_BY&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;f:&lt;/span&gt;&lt;span class="n"&gt;Fact&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;:FROM&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;s:&lt;/span&gt;&lt;span class="n"&gt;Source&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;f.status&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'verified'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'user_stated'&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;f.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f.text&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s.ref&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;whyThis&lt;/code&gt; and &lt;code&gt;boardPaths&lt;/code&gt; below, that is five of the 248, copied as they are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a graph and not a table
&lt;/h3&gt;

&lt;p&gt;The questions Moovi asks on every step are about connections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What can this beat use?&lt;/strong&gt; An insert has to serve what the line needs (a before and after, a list, a number) &lt;em&gt;and&lt;/em&gt; suit the edit style. That is two hops from the asset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is this claim allowed?&lt;/strong&gt; A line in a script ships only if there is a path to a fact we stand behind, and from the fact to its source. That is three hops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why did Moovi do that?&lt;/strong&gt; A decision, because of a rule, which was learned from something you said two videos ago. That is a path of unknown length.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With tables, each of these is a join, then another join, on every step of an agent loop. In FalkorDB each one is a single query. Here is the first one, run for real:&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%2Fsdls4a05wvjqjp5ncl4x.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%2Fsdls4a05wvjqjp5ncl4x.png" alt="Terminal output of a casting query on the craft graph, returning five assets in about 2 ms" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real output from our local FalkorDB. Five inserts that serve "before-after" or "compare" and suit the night-desk style, ranked by how many needs they hit.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The full version is &lt;code&gt;assetsServing&lt;/code&gt; in &lt;code&gt;packages/graph/src/queries/library.ts&lt;/code&gt;. Assets are never deleted, only retired, so an old project can still explain itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The truth guard
&lt;/h3&gt;

&lt;p&gt;When the Writer drafts a script, each claim goes into the project graph linked to the facts it cites. The claim ships only if this query finds a path from it to a fact and from the fact to its source. No path, no claim:&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;c:&lt;/span&gt;&lt;span class="n"&gt;Claim&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;$id&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;:SUPPORTED_BY&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;f:&lt;/span&gt;&lt;span class="n"&gt;Fact&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;:FROM&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;s:&lt;/span&gt;&lt;span class="n"&gt;Source&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;f.status&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'verified'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'user_stated'&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;f.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f.text&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s.ref&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Memory that changes over time
&lt;/h3&gt;

&lt;p&gt;When you give Moovi a note about your taste, Keeper files it as a &lt;code&gt;(:Lesson)&lt;/code&gt; in your own user graph, with the agent roles it binds. A newer lesson on the same topic does not delete the old one. It supersedes it:&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;n:&lt;/span&gt;&lt;span class="n"&gt;Lesson&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;$id&lt;/span&gt;&lt;span class="ss"&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;Lesson&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;$old&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;o.invalid_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$at&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;n&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;:SUPERSEDES&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;o&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before it works, every agent reads only the lessons that are still valid for its role (&lt;code&gt;lessonsFor&lt;/code&gt;). So the memory updates, but the history stays, and &lt;code&gt;lessonTimeline&lt;/code&gt; can show you everything you ever told it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The task board is a graph
&lt;/h3&gt;

&lt;p&gt;Each run is a set of &lt;code&gt;(:Task)&lt;/code&gt; nodes with &lt;code&gt;DEPENDS_ON&lt;/code&gt; edges. When every dependency of a waiting task is done, it becomes ready. A worker then claims it with a lease, and only one worker can win:&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;Task&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;$id&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.status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ready'&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;t.status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'running'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$worker&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.lease_until&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$lease&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t.started_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$at&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t.attempts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;t.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.kind&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t.agent&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a worker dies, its lease runs out and the task is ready again (&lt;code&gt;expired&lt;/code&gt;). If a review sends work back, &lt;code&gt;reopen&lt;/code&gt; puts that task and everything downstream of it back to waiting. The handoff between agents is just state in the graph that every agent can read.&lt;/p&gt;

&lt;h3&gt;
  
  
  The three graph algorithms we really use
&lt;/h3&gt;

&lt;p&gt;People ask "which graph algorithms?" so here is the honest list. There are exactly three, and each one is copied from our GitHub repo as it is on &lt;code&gt;main&lt;/code&gt;. No vector search, no embeddings, no community detection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;shortestPath&lt;/code&gt;: "why did you pick this?"&lt;/strong&gt; Every choice the crew makes (a caption, a voice, a cut) is saved as a &lt;code&gt;Decision&lt;/code&gt; node with &lt;code&gt;BECAUSE&lt;/code&gt; edges to what caused it. When you ask why, this query walks back to the cause, your own words first (&lt;code&gt;packages/graph/src/queries/why.ts&lt;/code&gt;, &lt;code&gt;whyThis&lt;/code&gt;):&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;d:&lt;/span&gt;&lt;span class="n"&gt;Decision&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;d.kind&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;$kinds&lt;/span&gt; &lt;span class="n"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;$run&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;d.run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$run&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;d.at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&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;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;:BECAUSE&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;x&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;:LEARNED_FROM&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;FROM&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;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="n"&gt;c&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Feedback&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Reference&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Asset&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;d&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;shortestPath&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;:BECAUSE&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;LEARNED_FROM&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="o"&gt;*&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;c&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;p&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&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;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Feedback&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Reference&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;3&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;rank&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rank&lt;/code&gt; line is the product decision: if you told us something, that is the reason we show first. A rule comes after, and a library asset last.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;algo.SPpaths&lt;/code&gt;: the cheapest order of looks.&lt;/strong&gt; When Moovi makes a film from a written brief instead of your own clip, the Director builds a small scratch lattice: one column per beat, one node per candidate preset, and &lt;code&gt;NEXT&lt;/code&gt; edges whose weight &lt;code&gt;w&lt;/code&gt; is how badly one look flows into the next. Then FalkorDB returns the k cheapest paths through it (&lt;code&gt;packages/graph/src/queries/project.ts&lt;/code&gt;, &lt;code&gt;boardPaths&lt;/code&gt;):&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;s:&lt;/span&gt;&lt;span class="n"&gt;Lattice&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;$start&lt;/span&gt;&lt;span class="ss"&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;Lattice&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;$end&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;algo.SPpaths&lt;/span&gt;&lt;span class="ss"&gt;({&lt;/span&gt;&lt;span class="py"&gt;sourceNode:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;targetNode:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;relTypes:&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'NEXT'&lt;/span&gt;&lt;span class="ss"&gt;],&lt;/span&gt; &lt;span class="py"&gt;weightProp:&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;pathCount:&lt;/span&gt; &lt;span class="n"&gt;$k&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="py"&gt;maxLen:&lt;/span&gt; &lt;span class="n"&gt;$maxLen&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathWeight&lt;/span&gt;
&lt;span class="k"&gt;RETURN&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;path&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.preset&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;presets&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathWeight&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Director gets these as a tool called &lt;code&gt;suggest_sequence&lt;/code&gt; ("a starting point, not an order", in &lt;code&gt;packages/engine/src/tasks/director.ts&lt;/code&gt;). Once the board is chosen, &lt;code&gt;clearLattice&lt;/code&gt; deletes the scratch nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;algo.pageRank&lt;/code&gt;: what you lean on too much.&lt;/strong&gt; Each user graph keeps the order of presets your past films used, as &lt;code&gt;PresetRef&lt;/code&gt; nodes joined by &lt;code&gt;NEXT_USED&lt;/code&gt;. PageRank over that tells us which looks sit at the centre of your habits, and that score is added to the lattice weights above, so the next film leans away from them (&lt;code&gt;packages/graph/src/queries/user.ts&lt;/code&gt;, &lt;code&gt;overUse&lt;/code&gt;):&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;CALL&lt;/span&gt; &lt;span class="n"&gt;algo.pageRank&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PresetRef'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'NEXT_USED'&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;YIELD&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;node.id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be exact about scope: &lt;code&gt;SPpaths&lt;/code&gt; and &lt;code&gt;pageRank&lt;/code&gt; run only on the written brief path. When you upload your own clip for an Edit, the graph still picks every camera move, sound and transition, but by plain Cypher matching (the &lt;code&gt;assetsServing&lt;/code&gt; query above), not by these two algorithms.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Why this edit?"
&lt;/h3&gt;

&lt;p&gt;Every decision in a run (style, caption look, cuts, cast, effects, voice) is a &lt;code&gt;(:Decision)&lt;/code&gt; with &lt;code&gt;BECAUSE&lt;/code&gt; edges to its causes: your words (&lt;code&gt;:Feedback&lt;/code&gt;), a &lt;code&gt;:Rule&lt;/code&gt;, a &lt;code&gt;:Reference&lt;/code&gt; reel or a library &lt;code&gt;:Asset&lt;/code&gt;. A rule can be &lt;code&gt;LEARNED_FROM&lt;/code&gt; your feedback, and an asset can come &lt;code&gt;FROM&lt;/code&gt; a reference.&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%2Fr8rno2ei7y6knulyhklw.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%2Fr8rno2ei7y6knulyhklw.png" alt="Five decisions from one test run and the causes each points to" width="799" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drawn from a real query on the project graph of one local test run. Every arrow is a BECAUSE edge.&lt;/em&gt;&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;d:&lt;/span&gt;&lt;span class="n"&gt;Decision&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;d.kind&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;$kinds&lt;/span&gt; &lt;span class="n"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;$run&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;d.run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$run&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;d.at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&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;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;:BECAUSE&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;x&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;:LEARNED_FROM&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;FROM&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;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="n"&gt;c&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Feedback&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Reference&lt;/span&gt; &lt;span class="ow"&gt;OR&lt;/span&gt; &lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Asset&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;d&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;shortestPath&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;:BECAUSE&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;LEARNED_FROM&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="o"&gt;*&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;c&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;p&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;d.choice&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;hops&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&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;m.id&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;(Trimmed from &lt;code&gt;whyThis&lt;/code&gt; in &lt;code&gt;packages/graph/src/queries/why.ts&lt;/code&gt;. The real one ranks your own words first.)&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%2F1g7dko0wuv1smrb9vvtw.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%2F1g7dko0wuv1smrb9vvtw.png" alt="Terminal output of the whyThis query for caption and cut decisions" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real output from FalkorDB. The cut came from the brief plus the cut rule. The caption look came from a rule plus one library asset.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pulling the plug
&lt;/h3&gt;

&lt;p&gt;Rule 3 of Graph Hacks says that if the product still works with FalkorDB taken out, the graph is decoration. On Saturday evening we checked our own code and found about 63 places in the engine where a local list quietly stood in for the graph when it was down. Ishaan wrote it down as decision 13 (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/ef950041a1a289a1a82b2f6b584558660e344b19" rel="noopener noreferrer"&gt;&lt;code&gt;ef95004&lt;/code&gt;&lt;/a&gt;), and the same night every one of those fallbacks went behind a test only flag (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/3e1bae94cccfc6f57c9f332c52d34095a84bc5fa" rel="noopener noreferrer"&gt;&lt;code&gt;3e1bae9&lt;/code&gt;&lt;/a&gt;). The server never sets it.&lt;/p&gt;

&lt;p&gt;Then we built the demo for it (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/ca5cafd2c05198df426c43ac17008250e89a2dd6" rel="noopener noreferrer"&gt;&lt;code&gt;ca5cafd&lt;/code&gt;&lt;/a&gt;): run the graph half of an edit, &lt;code&gt;docker stop&lt;/code&gt; FalkorDB, watch the job refuse with "Moovi's memory is offline", &lt;code&gt;docker start&lt;/code&gt;, watch it work again. Measured on Ishaan's PC: the refusal comes in under 0.01 s, Moovi answers again 0.6 to 0.8 s after the restart, and &lt;code&gt;whyThis&lt;/code&gt; takes 4 to 18 ms.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the graph grew: our learning path
&lt;/h3&gt;

&lt;p&gt;We did not plan the whole graph on day one. It grew as each problem showed up. Here is the path, one commit at a time.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Thu 15 Oct, 18:30&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/2bebe4ae67cace3007fd31b34e3d7458df9a2b35" rel="noopener noreferrer"&gt;&lt;code&gt;2bebe4a&lt;/code&gt;&lt;/a&gt;. Day one: the graph comes first. Before any video code, Ishaan built the FalkorDB layer: one client with read only guards, separate graphs for the system, each user, each project and the styles, and 129 named Cypher queries. The crew's task board went into the graph the same minute (&lt;code&gt;c617a55&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thu 15 Oct, 20:05&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/2c6b994902f929aa1ad400f3c84214e6d5d0da5e" rel="noopener noreferrer"&gt;&lt;code&gt;2c6b994&lt;/code&gt;&lt;/a&gt;. Media becomes nodes. I added Clip, Source, Licence, Page and Screenshot nodes, so every piece of footage carries where it came from and what licence it has.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fri 16 Oct, 01:41&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/de8be84c25f832ad9b127355a7c1ddd98d6e925e" rel="noopener noreferrer"&gt;&lt;code&gt;de8be84&lt;/code&gt;&lt;/a&gt;. Memory. Your feedback became Feedback nodes with their own queries. This is where Moovi starts to remember your taste.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fri 16 Oct, 03:32&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/b42f5be62338cbc3f868eb389afc8f8d1ff5b967" rel="noopener noreferrer"&gt;&lt;code&gt;b42f5be&lt;/code&gt;&lt;/a&gt;. The graph starts making choices. The visual library moved into FalkorDB, and what worked before became a prior for casting the next scene, with every pick stored per scene.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fri 16 Oct, 15:08&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/f1c9d17efdaa27617b9de63c31596b093294ec54" rel="noopener noreferrer"&gt;&lt;code&gt;f1c9d17&lt;/code&gt;&lt;/a&gt;. Everything is an asset. The whole library (looks, sounds, kits) became Asset nodes, so styles could borrow pieces from it by query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sat 17 Oct, 21:24&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/3e1bae94cccfc6f57c9f332c52d34095a84bc5fa" rel="noopener noreferrer"&gt;&lt;code&gt;3e1bae9&lt;/code&gt;&lt;/a&gt;. No graph, no Moovi. We removed the quiet fallbacks and made FalkorDB required (bug 12 below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sat 17 Oct, 22:09&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/76fc27026d841cbeb59b6d48839bdc713abe3713" rel="noopener noreferrer"&gt;&lt;code&gt;76fc270&lt;/code&gt;&lt;/a&gt;. Explaining itself. "Why this edit?" became a shortest path from the edit back to the reasons behind it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sun 18 Oct, 05:05&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/26613cf8463c1324de3daf4092a95d6a14a2a78c" rel="noopener noreferrer"&gt;&lt;code&gt;26613cf&lt;/code&gt;&lt;/a&gt;. One edit registry. Every effect, camera move and UI piece became one registry in the graph, read by a single named query, &lt;code&gt;editCast&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sun 18 Oct, 19:31&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/0753733ba6dd614a6680a47cce9c4aff8b301aac" rel="noopener noreferrer"&gt;&lt;code&gt;0753733&lt;/code&gt;&lt;/a&gt;. Watching the crew. Every named graph call each agent makes is now recorded (names, counts and times only) and kept in FalkorDB at the end of a run.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By Sunday evening that was 248 named queries. The lesson for me: put a thing in the graph the moment you need to ask "why" or "what else is like this" about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moovi is not a closed app: plug it into Claude Code
&lt;/h2&gt;

&lt;p&gt;Moovi has two brains: your own API key (Gemini, Groq, OpenAI and others), or your own Claude Code. Either way the model only fills slots and picks from lists the graph gives it. Casting, timing and checks are plain code, so a weaker model makes a plainer film, never a broken one.&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%2Fvepn7udznlcs7s685hpy.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%2Fvepn7udznlcs7s685hpy.png" alt="Diagram of Moovi's parts: studio, worker, FalkorDB, MCP server and Claude Code" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How the parts talk. Claude Code asks for one step at a time and sends back the answer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;packages/mcp&lt;/code&gt; is an MCP server. It gives Claude Code three tools, &lt;code&gt;moovi_next&lt;/code&gt; (give me the next step), &lt;code&gt;moovi_answer&lt;/code&gt; (here is my answer) and &lt;code&gt;moovi_status&lt;/code&gt;, plus our editing skill as read only MCP resources (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/f5a3a20" rel="noopener noreferrer"&gt;&lt;code&gt;f5a3a20&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup is two commands.&lt;/strong&gt; In Moovi's Settings you copy two lines and run them in Claude Code, each starting with &lt;code&gt;!&lt;/code&gt; so Claude Code runs them as your own shell commands (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/d2abfe3" rel="noopener noreferrer"&gt;&lt;code&gt;d2abfe3&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect.&lt;/strong&gt; Claims your setup code and runs &lt;code&gt;claude mcp add&lt;/code&gt; with Moovi's URL and your token. Nothing starts yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start.&lt;/strong&gt; Gets a fresh run ticket and starts a background &lt;code&gt;claude -p&lt;/code&gt; session that can only call Moovi's tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How a job reaches Claude Code.&lt;/strong&gt; Every brain step of a video is a job on the bridge's board. When your Claude Code is online, the job is leased to it first. If it does not pick the job up within 2 minutes, the job falls back to your key or Moovi's server model, so a video never hangs. A video you started before command 2 now waits for your Claude Code too (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/20e4bec" rel="noopener noreferrer"&gt;&lt;code&gt;20e4bec&lt;/code&gt;&lt;/a&gt;). The worker log prints which brain took each decision.&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%2F4z57mrdwv70z1mrajbqf.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%2F4z57mrdwv70z1mrajbqf.png" alt="The Claude Code status pill in three states" width="790" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The studio's Claude Code pill in its three states.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On 18 October I ran each preset end to end through the MCP path on a local box, with a scripted MCP client standing in for Claude Code:&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%2Fwuk8mo4ofpdyngbd08ab.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%2Fwuk8mo4ofpdyngbd08ab.png" alt="Bar chart: Edit live preview 29 s and final render 5.3 min, Launch video done in 49 s, Showreel done in 51 s" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Chart of our own test logs. Edit plays a live preview first, then renders the final film.&lt;/em&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%2F2t0wq7e4o2nxthg74cov.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%2F2t0wq7e4o2nxthg74cov.jpg" alt="Six frames of the Showreel made through the MCP path" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Frames from the Showreel run (28.9 s video, done in 51 s).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke, and how we fixed it
&lt;/h2&gt;

&lt;p&gt;This is the part I would have wanted to read before I started.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The render queue that waited on itself
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 13:28 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/df6c9a8db232cba87b4f9b48a5748d1077c58b33" rel="noopener noreferrer"&gt;&lt;code&gt;df6c9a8&lt;/code&gt;&lt;/a&gt;, merged in &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/63c9eefe376332af0f80bedb21e1db2c39091cc2" rel="noopener noreferrer"&gt;&lt;code&gt;63c9eef&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every Edit did the same thing in our last test round. The live preview played, then the status sat at "working" forever, and Claude Code never heard DONE. The final film renders through a queue, one at a time, because our server has 2 GB of RAM. The queue is a promise chain in &lt;code&gt;router/edit.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;graded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;directed&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;filmQueue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;// read later, when .then runs&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;renderMasteryFilm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;filmQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ...but replaced right now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arrow function reads &lt;code&gt;filmQueue&lt;/code&gt; only when it runs. By then &lt;code&gt;filmQueue&lt;/code&gt; already points at &lt;code&gt;job&lt;/code&gt;. So the job waits for itself. No error, no timeout, no log line. The fix is to read the old queue first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filmQueue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;graded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;directed&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;renderMasteryFilm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It came with a test (&lt;code&gt;film-queue.test.ts&lt;/code&gt;) so it cannot come back quietly. After the fix, the same Edit reached DONE in 352 s.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Press Stop, and Claude Code never hears DONE again
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 13:17 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/1a9493d266a1de4c95e6291a08f80319c11e4af0" rel="noopener noreferrer"&gt;&lt;code&gt;1a9493d&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Stop on the website ended the run in the engine, but nobody told the MCP job board. It kept the job as "running" for 24 hours, so every later video for that user looked busy. Now the board settles a running job from the engine's own &lt;code&gt;Run.status&lt;/code&gt;, read from the graph. The same commit fixed a second bug: for a user with no API key, every model tier was the literal string &lt;code&gt;'server'&lt;/code&gt;, and that string went to the provider as a model id. The Launch writer got a 404 for a model called "server".&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A brand new FalkorDB broke the first edit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 00:08 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/55e6c50acc36d41764da7893f6bc81d971e395da" rel="noopener noreferrer"&gt;&lt;code&gt;55e6c50&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On a fresh database, the first edit failed with &lt;code&gt;ERR Invalid graph operation on empty key&lt;/code&gt;. A read only query on a graph that does not exist yet is an error in FalkorDB, while the same read as a normal query just returns no rows. We now treat that one case as "no rows". A real outage is still "Moovi's memory is offline".&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Hindi captions: 72.92% to 89.41% words right
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fri 16 Oct, 22:40 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/d21fd0f19dac0f7bf260c1a2466238f2a91ded78" rel="noopener noreferrer"&gt;&lt;code&gt;d21fd0f&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our test clip in Hindi kept failing the caption gate. The cause was one helper that made a "bare" key for each word by stripping every combining mark. In Devanagari that removes the vowel signs, so "है", "हैं" and "हूँ" all became the same key, and the aligner anchored captions on the wrong words. The new key keeps the marks and only folds the spellings Hindi writes two ways. We also found our reference subtitle had 192 words for the real 171. Words right went from 72.92% to 89.41%, and the gate passed.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Every scene was big text
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fri 16 Oct, 04:32 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/01fabe522e157ebef16e38fabab4da25a01c09db" rel="noopener noreferrer"&gt;&lt;code&gt;01fabe5&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at a contact sheet of a whole film, almost every scene was a giant word and nothing else. Two regexes in the planner had lost their backslashes: &lt;code&gt;/s+/&lt;/code&gt; instead of &lt;code&gt;/\s+/&lt;/code&gt;. It split sentences on the letter "s", so every line counted as "short", and short lines become big type. We only saw it because we looked at the frames, not the logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The same query, a stricter FalkorDB
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fri 16 Oct, 02:06 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/0ba231a970ab64ae71f65b3f42b741e904cc052f" rel="noopener noreferrer"&gt;&lt;code&gt;0ba231a&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first full test film stopped at the review step with &lt;code&gt;ORDER BY cannot reference variables not projected or grouped in this scope&lt;/code&gt;. The query sorted by &lt;code&gt;c.at&lt;/code&gt; after a &lt;code&gt;collect()&lt;/code&gt;. Newer FalkorDB is stricter about that. We now return the field and sort by its projected name, which runs on FalkorDB 4.x and 6.x alike.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Two Claude Code windows, one video
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 03:35 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/da8dc75d6bf549610af7fcba155ed235eb224664" rel="noopener noreferrer"&gt;&lt;code&gt;da8dc75&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the setup prompt in two Claude Code windows and the second one used to stop. Now it waits, and takes the video over if the first window's lease runs out.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Everything on a 2 GB server
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 03:07 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/89b2da95d2f152ec65ee867bca1985c86624fba2" rel="noopener noreferrer"&gt;&lt;code&gt;89b2da9&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The studio, the worker, FalkorDB, Chromium and ffmpeg all share one small cloud server. The fix was a pile of small ones: swap only when needed, the small speech model, one video at a time under 3.5 GB, a capped build heap, and the worker stopped during builds. That one-film-at-a-time rule is why the queue in bug 1 exists at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Claude Code refused to run our setup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 15:30 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/a66b5c1f481da1d0590298b0477c27edfeaeb591" rel="noopener noreferrer"&gt;&lt;code&gt;a66b5c1&lt;/code&gt;&lt;/a&gt; · &lt;strong&gt;16:10 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/d2abfe364378b1e3c73a3246f2f7e61306faf552" rel="noopener noreferrer"&gt;&lt;code&gt;d2abfe3&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our first setup was one pasted prompt that downloaded a script and ran it (&lt;code&gt;curl ... | sh&lt;/code&gt;, or &lt;code&gt;irm ... | iex&lt;/code&gt; on Windows). Claude Code's permission check blocked it, because it runs code fetched from outside the session. We spelled out plain commands instead. Then Claude Code refused again: a prompt that has Claude Code start a background Claude Code of its own was flagged as "Create Unsafe Agents". Both times it was Claude Code being right. The fix was to stop asking it to do the risky part: setup became the two &lt;code&gt;!&lt;/code&gt; commands above, which you run yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. "Connected", but the video never reached Claude Code
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 17:28 to 17:47 IST (live test) · fixed 18:08 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/20e4bec7d127ea7ab3bb1de2315565ce2df60803" rel="noopener noreferrer"&gt;&lt;code&gt;20e4bec&lt;/code&gt;&lt;/a&gt; · merged &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/4a266835c48ceb2e4e403b9086f4fc9c76d6257f" rel="noopener noreferrer"&gt;&lt;code&gt;4a26683&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one happened to me on Windows, a few hours before the deadline. I ran both commands, uploaded a clip, and the studio sat at "Editor planning the cuts" for over 5 minutes. The setup status said "connected; waiting for a video". The local log had nothing in it after the background session started.&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%2F2ahopw44nu0e2ykehb69.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%2F2ahopw44nu0e2ykehb69.jpg" alt="Claude Code printing the setup status: Moovi is connected and idle, connected; waiting for a video, and the log is empty" width="675" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real photo of my screen, 18 Oct about 17:40 IST: the status says connected, the log is empty, the video is not moving.&lt;/em&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%2F0z6bj2l91k1cew8nwbji.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%2F0z6bj2l91k1cew8nwbji.jpg" alt="Claude Code saying it still has not received the video while the server status says working on your video" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same test about 40 minutes in: the server says it is working on the video, Claude Code says it never got it.&lt;/em&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%2F47yd5hv5j4x0lrjxxlzm.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%2F47yd5hv5j4x0lrjxxlzm.jpg" alt="Claude Code reporting nothing has changed: still connected and waiting, the background session never received a video job" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Claude Code's own summary: still connected and waiting, no video job ever arrived.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three things were wrong at once, and the commit fixes all three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The status lied.&lt;/strong&gt; It said "connected" if the background Claude Code had called Moovi even once. Now it says connected only while it is really calling; otherwise "quiet for N s", then "FAILED: stopped calling Moovi, run command 2 again".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An early video was skipped.&lt;/strong&gt; My clip was uploaded before command 2, and only videos started after the setup were offered to Claude Code. Now a running video, whenever it started, is offered to the starting Claude Code first for up to 2 minutes, then falls back to the server so nothing hangs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The tools were gated behind a slash command.&lt;/strong&gt; Claude Code told me the Moovi tools were "set to run only after you type /mcp_moovi_run", so it never called them. We removed that wording from everything Claude Code reads. We also stopped telling an idle session it could stop after 2 minutes; that is our best guess for why it went quiet, not a proven cause.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;8 new tests cover it (&lt;code&gt;test/pending-jobs.test.ts&lt;/code&gt;), and the worker log now prints one line per brain decision with the brain that took it.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Windows could not find localhost
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 13:26 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/02b5c27195ce8dfc09a4b93bd52fff2141d2f948" rel="noopener noreferrer"&gt;&lt;code&gt;02b5c27&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The bridge printed &lt;code&gt;localhost&lt;/code&gt; but listened only on &lt;code&gt;127.0.0.1&lt;/code&gt;. Windows (and Node 17+) try the IPv6 &lt;code&gt;::1&lt;/code&gt; first, so the connection failed. The bridge now also listens on &lt;code&gt;::1&lt;/code&gt;. The same commit fixed a video that failed in 0.4 s: it started and ended between two looks, so Claude Code never saw it and gave up silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  12. "Moovi's memory is offline" on purpose
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sat 17 Oct, 21:25 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/3e1bae94cccfc6f57c9f332c52d34095a84bc5fa" rel="noopener noreferrer"&gt;&lt;code&gt;3e1bae9&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not a crash, a choice. About 63 places in the engine had a quiet fallback that made a plainer film when FalkorDB was down. That hid real graph bugs. We made FalkorDB required on the product path: if it is down, the job stops and the chat says "Moovi's memory is offline". The fallbacks now run only in tests and local dev, behind &lt;code&gt;MOOVI_GRAPH_OPTIONAL=1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Sign in on a brand new server failed
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Thu 15 Oct, 22:20 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/a20a3daf9a365a40192313a2b1e09e86b493ac5c" rel="noopener noreferrer"&gt;&lt;code&gt;a20a3da&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our first deploy to a clean server could not sign anyone in. Accounts live in the &lt;code&gt;sys&lt;/code&gt; graph, and on a fresh FalkorDB that graph does not exist until the first sign up. Sign in read from it first, and the read failed. The fix is one line, &lt;code&gt;await ensureSys()&lt;/code&gt; before the lookup, plus a one shot &lt;code&gt;seed&lt;/code&gt; service in &lt;code&gt;docker-compose.yml&lt;/code&gt; that builds the shared graphs on every deploy. Bug 3 is the same lesson, met again two days later: in FalkorDB, a graph you never wrote to is not empty, it is missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Captions that skipped words
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fri 16 Oct, 19:51 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/b17d22487563a7ee1addbbd5b65b0b14a9aea286" rel="noopener noreferrer"&gt;&lt;code&gt;b17d224&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A caption read "it will your in 5 minutes". The words "edit" and "videos" were heard right, but the speech step gave them no length: their start and end time were the same. Our edit list dropped every zero length word, so they vanished. Now a run of such words and its two neighbours share the time around them, split by letter count, before anything is filtered. A test (&lt;code&gt;edit-zero-words.test.ts&lt;/code&gt;) keeps it fixed.&lt;/p&gt;

&lt;h3&gt;
  
  
  15. The MP4 lost its punch ins and its end card
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fri 16 Oct, 19:42 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/22b0b5d26866ff81da96f838f4f07161e3f80e3e" rel="noopener noreferrer"&gt;&lt;code&gt;22b0b5d&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three render bugs in one sample. First, ffmpeg's scale filter keeps its first size, so a 1.35x punch in mid shot was drawn at 1x and shifted 408 px up over a black band. We now split each shot into runs of one size. Second, the black logo end card was a fully opaque frame, it came out as rgb24 among rgba PNGs, ffmpeg rebuilt its graph on that change and lost 39 frames: the film stopped 1.3 s before the voice and the end card never showed. &lt;code&gt;-reinit_filter 0&lt;/code&gt; fixed it. Third, every run is now padded to its exact frame count, so the final film has exactly N frames.&lt;/p&gt;

&lt;h3&gt;
  
  
  16. A phone video edited sideways
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 08:02 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/90a8c66ebd5887b3892e5acfeb823f9c1f8c022d" rel="noopener noreferrer"&gt;&lt;code&gt;90a8c66&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Phones often store a video portrait and add a 90 degree rotation flag. ffmpeg plays it the right way, but our probe read the stored size, so the edit framed a landscape clip as portrait. The probe now reads the rotation flag and reports the size you actually see.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. The chat promised a Whip Spin the video did not have
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sun 18 Oct, 00:03 IST&lt;/strong&gt; · &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/85fd0fd927e3f892520d873b4b5981a7f649aba8" rel="noopener noreferrer"&gt;&lt;code&gt;85fd0fd&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a live run the chat announced a Whip Spin transition, and the finished video had none. The picker put the transition in the longest pause, but another effect was on screen at that moment, and the render drops a transition under an effect. Now the transition goes in the longest pause that is clear of every overlay, and a test checks that no transition ever sits under one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Early render vs current render
&lt;/h3&gt;

&lt;p&gt;Same FalkorDB ad prompt, two builds of our agent branch, 16 October. Left: the build at 2:14 AM, before that night's fixes. Right: the build after the second round of fixes.&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%2Flbpqpuegsmixoy4kepxe.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%2Flbpqpuegsmixoy4kepxe.jpg" alt="Contact sheet of the early render, before fixes" width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Early render (16 Oct, 2:14 AM build, before fixes). We scored it 4.5 out of 10 ourselves.&lt;/em&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%2Fygnv8wex7j3xsulinlvt.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%2Fygnv8wex7j3xsulinlvt.jpg" alt="Contact sheet of the later render, after the second round of fixes" width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same prompt after the second round of fixes the same night.&lt;/em&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%2Fhhfi63or9ve4xwblcf9s.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%2Fhhfi63or9ve4xwblcf9s.jpg" alt="QA frames from an early render where the panels came out blank" width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;From our own QA of an early render on 16 October: panels that should hold content came out blank.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://moovi-blog-preview.sites.tab.bot/img/early-vs-later.mp4" rel="noopener noreferrer"&gt;Watch the two renders side by side (8 seconds, early on the left, later on the right)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Smaller ones I still remember: an edit with no speech key came back with 0 words captioned until we added local Whisper in Node (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/9e0b66e3c9021257bb6de23452cbe46ed18f112e" rel="noopener noreferrer"&gt;&lt;code&gt;9e0b66e&lt;/code&gt;&lt;/a&gt;), and 5 of 11 shots in one render started on a black frame until a &lt;code&gt;setpts&lt;/code&gt; fix (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/e806e4d16217f8c73ce4597507a68f2a07b90f42" rel="noopener noreferrer"&gt;&lt;code&gt;e806e4d&lt;/code&gt;&lt;/a&gt;). That same commit moved face detection from Python to Node (its boxes match OpenCV within 0.3 px), so the effects step no longer needs Python, which was one of the things that broke on my Windows laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The calls we made on the last morning
&lt;/h2&gt;

&lt;p&gt;Not everything was a bug. Some of the most useful commits were decisions, written down on our agent board (&lt;code&gt;docs/agents/DECISIONS.md&lt;/code&gt;) before anyone wrote code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decision 13, FalkorDB is required&lt;/strong&gt; (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/ef950041a1a289a1a82b2f6b584558660e344b19" rel="noopener noreferrer"&gt;&lt;code&gt;ef95004&lt;/code&gt;&lt;/a&gt;). Covered above. It cost us a night and made the product honest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision 18, replace, don't merge&lt;/strong&gt; (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/744a4fcc5a414030979f1afd0b3ab8c1f3963370" rel="noopener noreferrer"&gt;&lt;code&gt;744a4fc&lt;/code&gt;&lt;/a&gt;). When a preset gets a better pipeline, the new one replaces the old one. No blending with old looks, and no silent fallback to the old path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decision 19, three presets&lt;/strong&gt; (&lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/492ec6acc8600811b8db4f985f58780b48ffe9db" rel="noopener noreferrer"&gt;&lt;code&gt;492ec6a&lt;/code&gt;&lt;/a&gt;, built in &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/19ccdb107c6472c5bf83c076bbebbb4060d42b6a" rel="noopener noreferrer"&gt;&lt;code&gt;19ccdb1&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/Ishaan-Gpt/moovi/commit/6dcec5e38260a8a8c330630d37485d241b0e8395" rel="noopener noreferrer"&gt;&lt;code&gt;6dcec5e&lt;/code&gt;&lt;/a&gt;). We had many styles and a generic prompt box. On Sunday morning we cut it to Edit, Launch video and Showreel, and the first screen became that choice. Every preset still casts from the whole craft graph.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we worked
&lt;/h2&gt;

&lt;p&gt;We worked with several AI agents on one board: tasks, decisions and notes in &lt;code&gt;docs/agents&lt;/code&gt;, one file per agent. Claude Code co-wrote many of our commits, and each of those commits carries a &lt;code&gt;Co-Authored-By&lt;/code&gt; line.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Put the "why" in the graph on day one.&lt;/strong&gt; Once every decision had &lt;code&gt;BECAUSE&lt;/code&gt; edges, debugging a bad edit became a query instead of a guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail loudly.&lt;/strong&gt; "Moovi's memory is offline" beats a film made from the wrong list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give every agent loop an end state.&lt;/strong&gt; DONE, FAILED or IDLE. A loop that can only say "working" will one day say it forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look at the frames.&lt;/strong&gt; Bug 5 was invisible in the logs and obvious on a contact sheet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test on the machine your users have.&lt;/strong&gt; Half our bugs only lived on Windows, on a fresh database, or with no API key.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Things it does not do yet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The live graph view of a run (every agent's named queries, pulsing as they happen, from an Enter button on the FalkorDB sphere in the studio office) is brand new and only lightly tested. The "why this caption?" answer itself is still read in the studio chat.&lt;/li&gt;
&lt;li&gt;In the Launch video preset, the writing step still runs on the server model even when Claude Code is linked.&lt;/li&gt;
&lt;li&gt;The end to end runs in the MCP section used a scripted MCP client in place of Claude Code. My live test with real Claude Code on Windows is bug 10: it found that bug, and the fix went live on our server the same evening. A full rerun of that live test after the fix is not in this post.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Team
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vignesh B S&lt;/strong&gt; (&lt;a href="https://github.com/vigneshbs33" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;): AI side, FalkorDB memory, the Claude Code bridge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ishaan&lt;/strong&gt; (&lt;a href="https://github.com/Ishaan-Gpt" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;): the studio, the landing page and the product.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keshav Kumar Agarwal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Srujan N Hiremath&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;One graph remembers everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://moovi-three.vercel.app" rel="noopener noreferrer"&gt;moovi-three.vercel.app&lt;/a&gt; · &lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Ishaan-Gpt/moovi" rel="noopener noreferrer"&gt;github.com/Ishaan-Gpt/moovi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to WeMakeDevs and &lt;a href="https://www.falkordb.com" rel="noopener noreferrer"&gt;FalkorDB&lt;/a&gt; for running &lt;a href="https://www.wemakedevs.org/hackathons/falkordb" rel="noopener noreferrer"&gt;Graph Hacks&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>falkordb</category>
      <category>ai</category>
      <category>mcp</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
