<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>footprintjs | Blog</title><description>The flowchart pattern for backend code. Self-explainable systems that AI can reason about.</description><link>https://footprintjs.github.io/</link><language>en</language><item><title>Backward Causal Chain: Error Stack Traces for Data Quality</title><link>https://footprintjs.github.io/footPrint/blog/backward-causal-chain/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/backward-causal-chain/</guid><description>When your agent produces a bad answer, you need to know which stage caused it — not just that it happened. causalChain() walks the dependency graph backward to find the root cause.

</description><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem-why-is-the-answer-bad&quot;&gt;The problem: “Why is the answer bad?”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Your loan pipeline runs 5 stages. The final decision is &lt;strong&gt;rejected&lt;/strong&gt;, but the quality score is 0.2 — something went wrong. Which stage caused it?&lt;/p&gt;
&lt;p&gt;You could read the narrative top to bottom. But in an agent loop with 15 iterations, 3 tool calls each, that’s 45 stages of output. You need a &lt;strong&gt;stack trace for quality&lt;/strong&gt; — not for crashes, but for data quality degradation.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Quality Trace (score: 0.20 at reject#4):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at reject#4                  score=0.20  — rejection decision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at eval-risk#2               score=0.40 (via riskTier)  — high risk assigned&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;at seed#0                    score=1.00 (via creditScore)  — input data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Root cause: quality dropped at reject#4 (0.40 → 0.20, Δ0.20)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This is &lt;code dir=&quot;auto&quot;&gt;causalChain()&lt;/code&gt; — backward program slicing on the commit log.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-algorithm&quot;&gt;The algorithm&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every stage in footprintjs writes through a transactional buffer. The &lt;strong&gt;commit log&lt;/strong&gt; records what each stage wrote. Every recorder tracks what each stage read. Together, they form an implicit dependency graph:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Seed wrote creditScore, dti&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ (EvalRisk reads creditScore, dti)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;EvalRisk wrote riskTier&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ (Route reads riskTier)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Route → chose reject branch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ (Reject reads riskTier)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Reject wrote decision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;causalChain()&lt;/code&gt; walks this graph &lt;strong&gt;backward&lt;/strong&gt; from any starting point. The algorithm is &lt;strong&gt;BFS backward thin-slicing&lt;/strong&gt; — a simplified form of program slicing (Weiser 1984, Sridharan et al. 2007):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start at the target step (e.g., &lt;code dir=&quot;auto&quot;&gt;reject#4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Get what it read via the &lt;code dir=&quot;auto&quot;&gt;getKeysRead&lt;/code&gt; callback&lt;/li&gt;
&lt;li&gt;For each key read, find who last wrote it (&lt;code dir=&quot;auto&quot;&gt;findLastWriter&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Create a parent node, enqueue it&lt;/li&gt;
&lt;li&gt;Repeat until no more dependencies&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The output is a &lt;strong&gt;DAG&lt;/strong&gt; (not a linked list). If a stage reads &lt;code dir=&quot;auto&quot;&gt;creditScore&lt;/code&gt; AND &lt;code dir=&quot;auto&quot;&gt;dti&lt;/code&gt; from different writers, it has two parents:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Seed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/    \&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;EvalRisk  (also writes dti)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Route&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Reject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;staged-optimization-the-query-optimizer-trick&quot;&gt;Staged optimization: the query optimizer trick&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;For small pipelines (≤ 256 commits), &lt;code dir=&quot;auto&quot;&gt;findLastWriter&lt;/code&gt; scans the log backward — O(N) per lookup, zero setup cost. Fast enough.&lt;/p&gt;
&lt;p&gt;For agent loops with 500+ iterations, that’s expensive. So &lt;code dir=&quot;auto&quot;&gt;causalChain()&lt;/code&gt; automatically switches to a &lt;strong&gt;reverse index&lt;/strong&gt;: a prebuilt &lt;code dir=&quot;auto&quot;&gt;Map&amp;#x3C;key, sortedWriterIndices[]&gt;&lt;/code&gt; that enables O(log N) binary search per lookup.&lt;/p&gt;
&lt;p&gt;The consumer never sees this. It’s chosen internally — like a database query optimizer selecting between sequential scan and index scan based on table size.&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Pipeline Size&lt;/th&gt;&lt;th&gt;Strategy&lt;/th&gt;&lt;th&gt;Per-Lookup Cost&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;≤ 256 commits&lt;/td&gt;&lt;td&gt;Linear scan&lt;/td&gt;&lt;td&gt;O(N) — zero setup&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&gt; 256 commits&lt;/td&gt;&lt;td&gt;Reverse index + binary search&lt;/td&gt;&lt;td&gt;O(log N) — O(N×U) setup amortized&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;built-on-recorder-operations&quot;&gt;Built on recorder operations&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This is where the &lt;a href=&quot;https://footprintjs.github.io/footPrint/blog/recorder-operations/&quot;&gt;recorder operations&lt;/a&gt; become the foundation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { causalChain, flattenCausalDAG, formatCausalChain } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs/trace&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { QualityRecorder, qualityTrace, formatQualityTrace } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs/trace&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 1. Collect quality scores during traversal (Translate pattern)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;QualityRecorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ctx&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if &lt;/span&gt;&lt;span&gt;(ctx&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;keysWritten&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; return { score: &lt;/span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;, factors:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;rejection&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if &lt;/span&gt;&lt;span&gt;(ctx&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;keysWritten&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;riskTier&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; return { score: &lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;, factors:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high risk&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return { score: &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;attachRecorder&lt;/span&gt;&lt;span&gt;(quality);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 2. Find the lowest-scoring step (Aggregate pattern)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;lowest&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getLowest&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 3. Backtrack through the causal chain&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;trace&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;qualityTrace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getSnapshot&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;commitLog&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;lowest&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;runtimeStageId&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;formatQualityTrace&lt;/span&gt;&lt;span&gt;(trace));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;QualityRecorder&lt;/code&gt; uses &lt;strong&gt;Translate&lt;/strong&gt; (per-step scores), &lt;strong&gt;Accumulate&lt;/strong&gt; (progressive quality up to slider), and &lt;strong&gt;Aggregate&lt;/strong&gt; (overall score). The &lt;code dir=&quot;auto&quot;&gt;causalChain()&lt;/code&gt; utility reads from the commit log — which was built during the same traversal, zero post-processing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everything is collected during the single DFS pass.&lt;/strong&gt; The backtracking is a post-execution query over data that was already captured.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-roi&quot;&gt;The ROI&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;time-saved&quot;&gt;Time saved&lt;/h3&gt;&lt;/div&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;/th&gt;&lt;th&gt;Without causal chain&lt;/th&gt;&lt;th&gt;With causal chain&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Debug low-quality agent response&lt;/td&gt;&lt;td&gt;30 min reading logs&lt;/td&gt;&lt;td&gt;10 seconds reading trace&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Root cause analysis&lt;/td&gt;&lt;td&gt;Senior eng required&lt;/td&gt;&lt;td&gt;Any engineer (or the LLM itself)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SLA investigation&lt;/td&gt;&lt;td&gt;”We’ll look into it”&lt;/td&gt;&lt;td&gt;Immediate root cause ID&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;At 5 quality incidents per week, 20 minutes saved each: &lt;strong&gt;~8.7 hours/month&lt;/strong&gt; of senior eng time returned.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;cost-saved&quot;&gt;Cost saved&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The causal chain is &lt;strong&gt;LLM context&lt;/strong&gt; — instead of the LLM reasoning across scattered data, it reads the pre-computed dependency graph:&lt;/p&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Approach&lt;/th&gt;&lt;th&gt;Tokens&lt;/th&gt;&lt;th&gt;Model Required&lt;/th&gt;&lt;th&gt;Cost/1M&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;LLM reasons across logs&lt;/td&gt;&lt;td&gt;~2,500&lt;/td&gt;&lt;td&gt;Reasoning model&lt;/td&gt;&lt;td&gt;$15.00&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LLM reads causal chain&lt;/td&gt;&lt;td&gt;~200&lt;/td&gt;&lt;td&gt;Any model&lt;/td&gt;&lt;td&gt;$0.25&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;At 10,000 queries/day: &lt;strong&gt;$2,190/day savings&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;quality-improvement&quot;&gt;Quality improvement&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;When you can see exactly where quality dropped, you fix the right thing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System prompt too vague? → Fix the prompt, not the model&lt;/li&gt;
&lt;li&gt;Tool returned bad data? → Fix the tool, not the agent loop&lt;/li&gt;
&lt;li&gt;Context window too large? → Trim the right messages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without the causal chain, teams throw money at bigger models hoping it fixes quality. With it, they make targeted fixes.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-dependency-dag&quot;&gt;The dependency DAG&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;memory/backtrack.ts    → causalChain() — pure algorithm, leaf node&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;depends on: memory/types, memory/commitLogUtils&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;recorder/QualityRecorder.ts → scoring during traversal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;depends on: recorder/KeyedRecorder&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;recorder/qualityTrace.ts → decorates causalChain with quality scores&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                           &lt;/span&gt;&lt;/span&gt;&lt;span&gt;depends on: memory/backtrack + recorder/QualityRecorder&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Clean lib-of-libs layering. The backtracking algorithm knows nothing about quality. The quality layer knows nothing about how the DAG is built. They compose.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;try-it&quot;&gt;Try it&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tsx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;examples/post-execution/causal-chain/05-diamond.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tsx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;examples/post-execution/quality-trace/02-root-cause.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or explore in the &lt;a href=&quot;https://footprintjs.github.io/footprint-playground/&quot;&gt;interactive playground&lt;/a&gt;.&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Note &lt;/p&gt; &lt;div&gt; &lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;causalChain()&lt;/code&gt; is exported from &lt;code dir=&quot;auto&quot;&gt;footprintjs/trace&lt;/code&gt;. It works with any recorder that tracks &lt;code dir=&quot;auto&quot;&gt;keysRead&lt;/code&gt; — not just &lt;code dir=&quot;auto&quot;&gt;QualityRecorder&lt;/code&gt;. Build your own: error trackers, cost trackers, latency trackers — all backtrackable.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;</content:encoded></item><item><title>Three Operations on Auto-Collected Data: The Recorder Pattern That Saves You $15/1M Tokens</title><link>https://footprintjs.github.io/footPrint/blog/recorder-operations/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/recorder-operations/</guid><description>Your agent makes 8 tool calls. The LLM spends 2,500 tokens reasoning across scattered logs to answer one question. With recorder operations, it spends 200.

</description><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-15-question&quot;&gt;The $15 question&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A user asks: &lt;strong&gt;“Why was my loan rejected?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Your agent has the data. Credit score, DTI ratio, employment history — all computed across 8 pipeline stages. The LLM needs to answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Without recorder operations:&lt;/strong&gt;
The LLM retrieves scattered logs. Makes 4 tool calls. Spends 4 reasoning steps connecting disconnected data points. Total: ~2,500 tokens, requires an expensive reasoning model ($15/1M tokens).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With recorder operations:&lt;/strong&gt;
The recorder already has the causal chain. One read. The LLM sees the connected trace. Total: ~200 tokens, any lightweight model works ($0.25/1M tokens).&lt;/p&gt;
&lt;p&gt;That’s &lt;strong&gt;60x cost reduction per question&lt;/strong&gt;. At 10,000 questions/day, that’s &lt;strong&gt;$2,190/day&lt;/strong&gt; in savings.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-three-operations&quot;&gt;The three operations&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every recorder in footprintjs collects data during the single DFS traversal — no post-processing, never stale. The consumer chooses how to read that data at query time:&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;1-translate--what-happened-at-this-step&quot;&gt;1. Translate — “What happened at this step?”&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Per-step raw value. O(1) lookup by &lt;code dir=&quot;auto&quot;&gt;runtimeStageId&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { QualityRecorder } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs/trace&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// What was the quality score at step call-llm#5?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getByKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;call-llm#5&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// → { score: 0.3, factors: [&apos;response hallucinated&apos;], keysRead: [&apos;systemPrompt&apos;] }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Time-travel debugger. Click a stage in the UI → see its data. The explainable-ui slider calls &lt;code dir=&quot;auto&quot;&gt;getByKey()&lt;/code&gt; at each position.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ROI:&lt;/strong&gt; Developer debugging time drops from “read 500 log lines” to “click the stage.” At $150/hr senior eng rate, even 10 minutes saved per debug session × 5 sessions/day = &lt;strong&gt;$125/day&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-accumulate--what-happened-up-to-this-point&quot;&gt;2. Accumulate — “What happened up to this point?”&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Progressive running total filtered by a set of visible keys. For time-travel sliders where you scrub forward/backward.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Quality score up to the slider position&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;visibleKeys&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Set&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;seed#0&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;classify#1&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;call-llm#2&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;scoreUpTo&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;accumulate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt; + &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;score&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;visibleKeys&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// → 2.1 (sum of scores for first 3 steps)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Progressive metrics in the UI. As the slider moves, the dashboard updates: “Duration so far: 120ms, Reads: 5, Writes: 3, Quality: 0.7.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ROI:&lt;/strong&gt; Users understand performance characteristics without reading code. Support tickets drop because the UI &lt;em&gt;shows&lt;/em&gt; what happened instead of requiring a human to explain it.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;3-aggregate--whats-the-grand-total&quot;&gt;3. Aggregate — “What’s the grand total?”&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Reduce all entries to a single value. For dashboards, SLA checks, billing.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Overall pipeline quality&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;overallScore&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;aggregate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt; + &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;score&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;quality&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// → 0.65&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Total cost across all LLM calls&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;totalCost&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;costRecorder&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;aggregate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt; + &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cost&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// → $0.0042&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt; SLA monitoring. “Average quality this hour: 0.82. Cost per request: $0.004.” These numbers go to Grafana/Datadog dashboards.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ROI:&lt;/strong&gt; You catch quality degradation before users report it. One prevented outage saves more than a year of infrastructure cost.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-foundation-for-everything&quot;&gt;The foundation for everything&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;These three operations aren’t just a nice API. They’re the &lt;strong&gt;foundation&lt;/strong&gt; for the backward causal chain — footprintjs’s answer to “why did quality drop?”&lt;/p&gt;
&lt;p&gt;When &lt;code dir=&quot;auto&quot;&gt;qualityTrace()&lt;/code&gt; backtracks from a low-scoring step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses &lt;strong&gt;Translate&lt;/strong&gt; to read the quality at each step&lt;/li&gt;
&lt;li&gt;It uses &lt;strong&gt;Accumulate&lt;/strong&gt; to compute progressive quality up to the drop point&lt;/li&gt;
&lt;li&gt;It uses &lt;strong&gt;Aggregate&lt;/strong&gt; to compute the overall pipeline score&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;KeyedRecorder&amp;#x3C;T&gt;&lt;/code&gt; base class provides all three operations. Every built-in recorder (&lt;code dir=&quot;auto&quot;&gt;MetricRecorder&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;QualityRecorder&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;TokenRecorder&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;CostRecorder&lt;/code&gt;) inherits them. Your custom recorders get them for free.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// All three operations on auto-collected data:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;metric&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getByKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;call-llm#5&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);                           &lt;/span&gt;&lt;span&gt;// Translate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;metric&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;accumulate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; sum &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; m&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, keys); &lt;/span&gt;&lt;span&gt;// Accumulate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;metric&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;aggregate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;sum&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; sum &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; m&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);         &lt;/span&gt;&lt;span&gt;// Aggregate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;One interface. Three operations. Collected during traversal. Never stale.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://footprintjs.github.io/footPrint/blog/backward-causal-chain/&quot;&gt;next post&lt;/a&gt;, we’ll show how these operations power the backward causal chain — an algorithm that answers “why did quality drop at step 5?” by walking the dependency graph backward.&lt;/p&gt;
&lt;hr&gt;
&lt;aside aria-label=&quot;Note&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Note &lt;/p&gt; &lt;div&gt; &lt;p&gt;Try the recorder operations in the &lt;a href=&quot;https://footprintjs.github.io/footprint-playground/&quot;&gt;interactive playground&lt;/a&gt;. The “Recorder Operations” sample lets you switch between translate, accumulate, and aggregate views on the same data.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;</content:encoded></item><item><title>Pause/Resume: Human-in-the-Loop for Backend Pipelines</title><link>https://footprintjs.github.io/footPrint/blog/pause-resume/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/pause-resume/</guid><description>Return data from execute to pause. Resume hours later with the human&apos;s answer. No polling, no WebSockets.

</description><pubDate>Fri, 03 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Your pipeline needs a human decision in the middle.&lt;/p&gt;
&lt;p&gt;A refund over $500 requires manager approval. An AI-generated report needs editorial review before publishing. A deployment pipeline pauses for a security sign-off.&lt;/p&gt;
&lt;p&gt;Traditional approaches:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Approach&lt;/th&gt;&lt;th&gt;Problem&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Polling loop&lt;/td&gt;&lt;td&gt;Wastes compute, ties up a process&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebSocket + in-memory state&lt;/td&gt;&lt;td&gt;Loses state on server restart&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Split into two pipelines&lt;/td&gt;&lt;td&gt;Business logic scattered across endpoints&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Background job + callback&lt;/td&gt;&lt;td&gt;Complex coordination, race conditions&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;What if the pipeline could just… pause? Save its state. Shut down. And pick up exactly where it left off — hours later, on a different server — when the human responds?&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-define-a-pausable-stage&quot;&gt;1. Define a pausable stage&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;A pausable stage has two phases: &lt;code dir=&quot;auto&quot;&gt;execute&lt;/code&gt; (first run) and &lt;code dir=&quot;auto&quot;&gt;resume&lt;/code&gt; (when the human responds).&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { flowChart, FlowChartExecutor } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { PausableHandler } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; RefundState {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;orderId&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;riskLevel&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;approved&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;approver&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;approvalGate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PausableHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;RefundState&lt;/span&gt;&lt;span&gt;&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;: async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Classify risk&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskLevel&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt; &gt; &lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt; ? &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;low&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Return data = pause. Return nothing = continue.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;question: &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Approve $&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; refund?&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;riskLevel: &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskLevel&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;resume&lt;/span&gt;&lt;span&gt;: async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;approved&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;approved&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;approver&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;approver&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Tip &lt;/p&gt; &lt;div&gt; &lt;p&gt;&lt;strong&gt;No special imports needed to pause.&lt;/strong&gt; Just return data from &lt;code dir=&quot;auto&quot;&gt;execute&lt;/code&gt; — the library detects the non-void return and pauses automatically. Return &lt;code dir=&quot;auto&quot;&gt;undefined&lt;/code&gt; to skip the pause and continue normally (conditional pause).&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;div&gt;&lt;h3 id=&quot;2-build-the-pipeline&quot;&gt;2. Build the pipeline&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;flowChart&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;RefundState&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ReceiveRequest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;orderId&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ORD-2024-7891&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;299&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;receive&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addPausableFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ManagerApproval&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, approvalGate, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Pause for manager review&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ProcessRefund&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;approved&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Refund approved by &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;approver&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;3-run-pause-checkpoint&quot;&gt;3. Run, pause, checkpoint&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;executor&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlowChartExecutor&lt;/span&gt;&lt;span&gt;(chart);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isPaused&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;checkpoint&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getCheckpoint&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// checkpoint is JSON-safe — store anywhere&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; redis&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;refund:&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;orderId&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(checkpoint));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The checkpoint contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;sharedState&lt;/strong&gt; — full scope at the pause point&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pausedStageId&lt;/strong&gt; — which stage paused&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pauseData&lt;/strong&gt; — the data you returned from &lt;code dir=&quot;auto&quot;&gt;execute&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;executionTree&lt;/strong&gt; — the traversal path for visualization&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;4-resume-hours-later-any-server&quot;&gt;4. Resume (hours later, any server)&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;checkpoint&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;await &lt;/span&gt;&lt;span&gt;redis&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;refund:&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;orderId&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;executor&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FlowChartExecutor&lt;/span&gt;&lt;span&gt;(chart); &lt;/span&gt;&lt;span&gt;// same chart, fresh executor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; executor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;resume&lt;/span&gt;&lt;span&gt;(checkpoint, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;approved: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;approver: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Manager Kim&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Pipeline continues: ProcessRefund → NotifyCustomer → done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;what-makes-this-different&quot;&gt;What makes this different&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;No timeouts.&lt;/strong&gt; The pipeline doesn’t sit in memory waiting. It checkpoints and exits. The process can shut down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No WebSockets.&lt;/strong&gt; The checkpoint is a plain JSON object. Store it in Redis, Postgres, S3, localStorage — anywhere. Resume from any server, any process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous execution tree.&lt;/strong&gt; After resume, &lt;code dir=&quot;auto&quot;&gt;getSnapshot()&lt;/code&gt; returns the full traversal path — pre-pause stages + resume + continuation. The narrative accumulates. Metrics accumulate. No gaps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conditional pause.&lt;/strong&gt; Return data to pause, return nothing to continue. One handler handles both paths:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;execute: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;amount&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; { reason: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;High-value — needs approval&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }; &lt;/span&gt;&lt;span&gt;// pauses&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// under $500 — auto-approved, no pause&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Multi-pause.&lt;/strong&gt; Chain multiple pausable stages. Each resume continues to the next pause point or to completion:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;receive → approve(pause) → resume → review(pause) → resume → process → done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;try-it-live&quot;&gt;Try it live&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Open the &lt;a href=&quot;https://footprintjs.github.io/footprint-playground/samples/pause-resume&quot;&gt;Pause/Resume sample&lt;/a&gt; in the playground. Click Run — the pipeline pauses at ManagerApproval. Edit the resume JSON and click Resume to continue.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-api-surface&quot;&gt;The API surface&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Consumers touch exactly 4 things:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;What&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;PausableHandler&amp;#x3C;TScope, TInput&gt;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Type for &lt;code dir=&quot;auto&quot;&gt;{ execute, resume }&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;.addPausableFunction(name, handler, id)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Builder method&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;executor.isPaused()&lt;/code&gt; / &lt;code dir=&quot;auto&quot;&gt;executor.getCheckpoint()&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Check pause state&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;executor.resume(checkpoint, input)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Continue execution&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Everything else is internal — &lt;code dir=&quot;auto&quot;&gt;PauseSignal&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;isPauseResult&lt;/code&gt;, checkpoint validation, execution tree grafting — the consumer never sees it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This is the foundation for &lt;strong&gt;ask_human&lt;/strong&gt; in &lt;a href=&quot;https://github.com/footprintjs/agentfootprint&quot;&gt;agentfootprint&lt;/a&gt; — a tool that pauses an AI agent loop, sends a question to the frontend, and resumes with the human’s answer. The agent’s full reasoning chain (narrative, decisions, tool calls) is preserved across the pause.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Pause/Resume ships in footprintjs v4.1.0.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Automated Decision Explainability: How Causal Traces Solve GDPR Article 22 and Audit Requirements</title><link>https://footprintjs.github.io/footPrint/blog/explainability-compliance/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/explainability-compliance/</guid><description>GDPR, SOX, and ECOA require you to explain automated decisions. Causal traces from running code solve this structurally.

</description><pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-regulatory-reality&quot;&gt;The regulatory reality&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If your software makes automated decisions about people — loan approvals, insurance claims, hiring screens, fraud flags — regulators increasingly require you to explain &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GDPR Article 22&lt;/strong&gt; gives EU citizens the right to not be subject to decisions based solely on automated processing, and the right to obtain “meaningful information about the logic involved.” &lt;strong&gt;CCPA&lt;/strong&gt; requires disclosure of the “logic involved in such decision-making.” The &lt;strong&gt;EU AI Act&lt;/strong&gt; mandates transparency and explainability for high-risk AI systems. &lt;strong&gt;SOX&lt;/strong&gt; requires auditability of financial decision processes. &lt;strong&gt;ECOA/FCRA&lt;/strong&gt; in the US require specific adverse action notices explaining credit decisions.&lt;/p&gt;
&lt;p&gt;The common thread: &lt;strong&gt;you must be able to explain, after the fact, exactly why your system made a specific decision for a specific person.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most teams solve this with one of two approaches, both flawed:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approach 1: Post-hoc logging.&lt;/strong&gt; Scatter &lt;code dir=&quot;auto&quot;&gt;console.log&lt;/code&gt; or structured log statements throughout the code. After a decision, reconstruct the reasoning from log entries. Problem: logs are incomplete, disconnected, and require manual interpretation. Auditors can’t verify that the logs faithfully represent the actual execution path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approach 2: LLM explanation.&lt;/strong&gt; Feed the code and context to an LLM and ask it to explain the decision. Problem: the LLM hallucinates causal connections. It generates plausible-sounding explanations that may not reflect what actually happened. An auditor can’t trust it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;causal-traces-the-structural-solution&quot;&gt;Causal traces: the structural solution&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;FootPrintJS takes a different approach. Your business logic is structured as a flowchart — a graph of typed functions with transactional state. The runtime automatically captures a &lt;strong&gt;causal trace&lt;/strong&gt; of every read, write, and decision during execution.&lt;/p&gt;
&lt;p&gt;This isn’t logging you add manually. It’s a structural property of the execution model. If the code reads a variable, the trace records it. If the code writes a variable, the trace records it. If the code makes a branching decision, the trace records the operator, the threshold, the actual value, and whether it passed or failed.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { flowChart, decide, narrative } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; UnderwritingState {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;applicantId&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;employmentMonths&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;riskTier&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;adverseReasons&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;underwriting&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;flowChart&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;UnderwritingState&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ReceiveApplication&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;applicantId&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;APP-2026-1847&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;580&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;0.6&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;employmentMonths&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;receive&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;AssessCredit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;620&lt;/span&gt;&lt;span&gt;) scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Below-average credit score&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.43&lt;/span&gt;&lt;span&gt;) scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;DTI exceeds 43% threshold&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;employmentMonths&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;24&lt;/span&gt;&lt;span&gt;) scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Employment tenure under 2 years&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskTier&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;standard&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;assess&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addDeciderFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Route&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decide&lt;/span&gt;&lt;span&gt;(scope, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ when: { riskTier: { eq: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } }, then: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, label: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;High risk applicant&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;route&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Route based on risk assessment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunctionBranch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;GenerateAdverseAction&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;REJECTED&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;adverseReasons&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskFactors&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunctionBranch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;APPROVED&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setDefault&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;underwriting&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The runtime generates this trace automatically:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 1: The process began with ReceiveApplication.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Write applicantId = &quot;APP-2026-1847&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 2: Write creditScore = 580&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 3: Write dtiRatio = 0.6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 4: Write employmentMonths = 12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 2: Next, it moved on to AssessCredit.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Write riskFactors = (0 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 2: Read creditScore = 580&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 3: Write riskFactors = (1 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 4: Read dtiRatio = 0.6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 5: Write riskFactors = (2 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 6: Read employmentMonths = 12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 7: Write riskFactors = (3 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 8: Read riskFactors = (3 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 9: Write riskTier = &quot;high&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 3: Next step: Route based on risk assessment.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Read riskTier = &quot;high&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Condition]: It evaluated &quot;High risk applicant&quot;: riskTier &quot;high&quot; eq &quot;high&quot; ✓, and chose GenerateAdverseAction.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 4: Next, it moved on to GenerateAdverseAction.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Write decision = &quot;REJECTED&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 2: Read riskFactors = (3 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 3: Write adverseReasons = (3 items)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;what-auditors-see&quot;&gt;What auditors see&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;An auditor reviewing this trace can verify:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data provenance.&lt;/strong&gt; What data was used? The trace shows exactly which fields were read at each stage. &lt;code dir=&quot;auto&quot;&gt;creditScore = 580&lt;/code&gt; was read at Stage 2, Step 2. Not inferred, not logged — captured by the runtime.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decision logic.&lt;/strong&gt; Why was this branch chosen? The &lt;code dir=&quot;auto&quot;&gt;[Condition]&lt;/code&gt; entry shows the operator (&lt;code dir=&quot;auto&quot;&gt;eq&lt;/code&gt;), the actual value (&lt;code dir=&quot;auto&quot;&gt;&quot;high&quot;&lt;/code&gt;), the expected value (&lt;code dir=&quot;auto&quot;&gt;&quot;high&quot;&lt;/code&gt;), and the result (&lt;code dir=&quot;auto&quot;&gt;✓&lt;/code&gt;). The auditor can verify the rule without reading source code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Causal chain.&lt;/strong&gt; The auditor backtracks: &lt;code dir=&quot;auto&quot;&gt;decision = &quot;REJECTED&quot;&lt;/code&gt; ← &lt;code dir=&quot;auto&quot;&gt;riskTier = &quot;high&quot;&lt;/code&gt; ← &lt;code dir=&quot;auto&quot;&gt;riskFactors.length &gt;= 2&lt;/code&gt; ← &lt;code dir=&quot;auto&quot;&gt;creditScore &amp;#x3C; 620&lt;/code&gt; AND &lt;code dir=&quot;auto&quot;&gt;dtiRatio &gt; 0.43&lt;/code&gt; AND &lt;code dir=&quot;auto&quot;&gt;employmentMonths &amp;#x3C; 24&lt;/code&gt;. Every link in the chain is in the trace.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Completeness.&lt;/strong&gt; Every read and write is captured. There are no hidden side effects, no unlogged decisions. The trace is a complete record of the execution.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;decision-evidence-with-decide&quot;&gt;Decision evidence with &lt;code dir=&quot;auto&quot;&gt;decide()&lt;/code&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;decide()&lt;/code&gt; function captures structured evidence for every rule evaluation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;decide&lt;/span&gt;&lt;span&gt;(scope&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ when: { creditScore: { gte: &lt;/span&gt;&lt;span&gt;700&lt;/span&gt;&lt;span&gt; }, dtiRatio: { lte: &lt;/span&gt;&lt;span&gt;0.35&lt;/span&gt;&lt;span&gt; } }, then: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;premium&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, label: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Prime borrower&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ when: { creditScore: { gte: &lt;/span&gt;&lt;span&gt;620&lt;/span&gt;&lt;span&gt; } }, then: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;standard&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, label: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Standard borrower&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;subprime&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The evidence object includes:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;chosenBranch&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subprime&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;chosenLabel&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Default&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;rules&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;label&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Prime borrower&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;passed&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;conditions&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;&quot;field&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;operator&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;gte&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;expected&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;700&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;actual&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;580&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;passed&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;&quot;field&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;operator&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lte&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;expected&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.35&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;actual&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.6&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;passed&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;label&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Standard borrower&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;passed&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;conditions&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;&quot;field&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;operator&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;gte&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;expected&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;620&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;actual&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;580&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;passed&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Every rule that was evaluated, whether it passed or failed, with the exact values compared. This is the structured evidence an ECOA adverse action notice needs.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;compliance-properties&quot;&gt;Compliance properties&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;FootPrintJS’s execution model provides several properties that compliance teams care about:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deterministic replay.&lt;/strong&gt; Given the same initial state, the flowchart produces the same trace. You can re-run a historical decision and verify the trace matches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transactional state.&lt;/strong&gt; State changes are atomic — committed per stage with copy-on-write semantics. There are no partial updates, no race conditions, no state corruption. Each stage sees a consistent snapshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tamper-evident.&lt;/strong&gt; The trace is generated by the runtime, not by the application code. Application code cannot modify the trace without changing the execution model itself. The trace is a faithful record of what the runtime observed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PII redaction.&lt;/strong&gt; Built-in &lt;code dir=&quot;auto&quot;&gt;RedactionPolicy&lt;/code&gt; for per-key or declarative redaction with audit trail. Redacted fields appear in the trace as &lt;code dir=&quot;auto&quot;&gt;[REDACTED]&lt;/code&gt; while maintaining the causal structure.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;({ redactionPolicy: { keys: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ssn&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dateOfBirth&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] } }))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Trace shows: Write ssn = [REDACTED], Read dateOfBirth = [REDACTED]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Causal chain is preserved. PII is not exposed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Time-travel debugging.&lt;/strong&gt; Snapshots capture the full state at each stage boundary. Compliance teams can inspect the exact state the system saw when it made a decision — not the state 5 minutes later after other processes modified it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;real-world-application&quot;&gt;Real-world application&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Here’s how teams use this in practice:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Loan underwriting.&lt;/strong&gt; The flowchart evaluates credit, DTI, employment, collateral. The trace generates ECOA-compliant adverse action reasons automatically. When a borrower appeals, the operations team pulls the trace — not a log file, not an LLM explanation — and sees exactly what happened.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Insurance claims.&lt;/strong&gt; The flowchart triages claims by severity, checks policy coverage, applies deductibles. The trace documents every coverage check, every exclusion evaluation. When a regulator audits, the trace is the evidence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fraud detection.&lt;/strong&gt; The flowchart evaluates risk signals: IP geolocation, velocity checks, device fingerprint. The trace documents which signals triggered the flag. When a legitimate transaction is blocked, support can explain exactly which rule fired and why.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { flowChart, decide, narrative } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Define your decision logic as a flowchart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;flowChart&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;YourState&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;FirstStage&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;handler&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ... add stages, decisions, branches&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Run with narrative recording&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// result.narrative — human-readable causal trace&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// result.state — final state with all decisions&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The trace is generated automatically. No logging code to write. No post-hoc reconstruction. No LLM hallucination.&lt;/p&gt;
&lt;aside aria-label=&quot;Caution&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Caution &lt;/p&gt; &lt;div&gt; &lt;p&gt;FootPrintJS provides the technical infrastructure for decision explainability. It does not constitute legal advice. Consult with your legal and compliance teams to determine how causal traces fit into your specific regulatory obligations.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footprint-playground/&quot;&gt;Interactive Playground&lt;/a&gt;&lt;/strong&gt; — try the loan underwriting demo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footPrint/&quot;&gt;Documentation&lt;/a&gt;&lt;/strong&gt; — full API reference&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/footprintjs/footPrint&quot;&gt;GitHub&lt;/a&gt;&lt;/strong&gt; — MIT licensed, zero dependencies, npm provenance&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Auto-Generate MCP Tools from Your Backend Code in One Line</title><link>https://footprintjs.github.io/footPrint/blog/mcp-tool-generation/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/mcp-tool-generation/</guid><description>Stop hand-writing tool descriptions. One line generates the MCP tool name, description, and input schema from your flowchart.

</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-tool-description-problem&quot;&gt;The tool description problem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Every MCP tool needs three things: a &lt;strong&gt;name&lt;/strong&gt;, a &lt;strong&gt;description&lt;/strong&gt;, and an &lt;strong&gt;input schema&lt;/strong&gt;. If you’re building tools by hand, you’re writing and maintaining all three separately from the code that actually runs.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// You write this by hand...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;assess_credit_risk&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Evaluates credit risk by checking credit score, DTI ratio, and employment status. Returns risk tier and decision.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;inputSchema: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;creditScore: { type: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dtiRatio: { type: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;employmentYears: { type: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;required:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;employmentYears&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ...and separately maintain the code that does the work&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assessCreditRisk&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 200 lines of business logic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The description drifts from the code. You add a new stage but forget to update the tool description. You rename a field but the schema still says the old name. The LLM gets confused because the tool it’s calling doesn’t match what it was told.&lt;/p&gt;
&lt;p&gt;FootPrintJS solves this with &lt;strong&gt;self-describing graphs&lt;/strong&gt;. The flowchart &lt;em&gt;is&lt;/em&gt; the tool. The name, description, and schema are generated from the graph structure — always in sync with the code that runs.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;one-line-tomcptool&quot;&gt;One line: &lt;code dir=&quot;auto&quot;&gt;.toMCPTool()&lt;/code&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { flowChart, decide, narrative } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; CreditState {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;employmentYears&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;riskTier&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;reasons&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;flowChart&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;CreditState&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ReceiveApplication&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Input is already on scope from the caller&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;receive&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;CheckCredit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskTier&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;700&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;low&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;620&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;medium&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;check-credit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;CheckDTI&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dtiRatio&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.43&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reasons&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;(scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reasons&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; []), &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;DTI exceeds 43%&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;check-dti&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addDeciderFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Route&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decide&lt;/span&gt;&lt;span&gt;(scope, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ when: { riskTier: { eq: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } }, then: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, label: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;High risk&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;route&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Route based on overall risk assessment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunctionBranch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;REJECTED&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunctionBranch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;APPROVED&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setDefault&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// One line — tool descriptor generated from the graph&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toMCPTool&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;tool&lt;/code&gt; now contains:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;receivecreditapplication&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;description&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;1. ReceiveApplication&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;2. CheckCredit&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;3. CheckDTI&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;4. Route based on overall risk assessment&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;   → Reject&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;   → Approve&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;inputSchema&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;properties&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;creditScore&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;dtiRatio&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;employmentYears&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The description is auto-generated from the graph stages. The input schema comes from the contract (or can be inferred). If you add a new stage to the flowchart, the tool description updates automatically.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;register-with-any-mcp-server&quot;&gt;Register with any MCP server&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The output of &lt;code dir=&quot;auto&quot;&gt;.toMCPTool()&lt;/code&gt; is a standard MCP tool descriptor. Register it with any MCP server implementation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// With the Anthropic SDK directly&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;anthropicTool&lt;/span&gt;&lt;span&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description: &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;input_schema: &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;inputSchema&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Pass to Claude&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;anthropic&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;model: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;claude-sonnet-4-20250514&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tools:&lt;/span&gt;&lt;span&gt; [anthropicTool]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;messages:&lt;/span&gt;&lt;span&gt; [{ role: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Assess credit risk for score 580, DTI 0.6, 1yr employment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When Claude calls the tool, execute the flowchart and return the result &lt;strong&gt;with the causal trace&lt;/strong&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Handle the tool call&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;toolInput&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// { creditScore: 580, dtiRatio: 0.6, employmentYears: 1 }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialState&lt;/span&gt;&lt;span&gt;(toolInput)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Return to Claude: the decision + why&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;toolResult&lt;/span&gt;&lt;span&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decision: &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;,       &lt;/span&gt;&lt;span&gt;// &quot;REJECTED&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;reasons: &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;reasons&lt;/span&gt;&lt;span&gt;,          &lt;/span&gt;&lt;span&gt;// [&quot;DTI exceeds 43%&quot;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;trace: &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,     &lt;/span&gt;&lt;span&gt;// Full causal trace&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Claude now has the structured trace. When the user asks “why was I rejected?”, Claude reads the trace and gives a precise, non-hallucinated explanation.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;also-toopenapi&quot;&gt;Also: &lt;code dir=&quot;auto&quot;&gt;.toOpenAPI()&lt;/code&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Same graph, different output format. Generate an OpenAPI 3.1 spec for REST API documentation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;spec&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toOpenAPI&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Credit Risk Assessment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;version: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;1.0.0&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Automated credit risk evaluation pipeline&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// spec is a full OpenAPI 3.1 document&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Import into Swagger UI, Postman, or any API documentation tool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One flowchart, two output formats — MCP for AI agents, OpenAPI for humans and API consumers.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;with-agentfootprint-full-agent-integration&quot;&gt;With agentfootprint: full agent integration&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you’re using agentfootprint (the agent framework), the integration is even tighter:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Agent, createProvider, anthropic, defineTool } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Turn the flowchart into an agent tool&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;creditTool&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;defineTool&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description: &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;inputSchema: &lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;inputSchema&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;handler&lt;/span&gt;&lt;span&gt;: async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialState&lt;/span&gt;&lt;span&gt;(input)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;decision: &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;trace: &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Build an agent that uses it&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;provider: &lt;/span&gt;&lt;span&gt;createProvider&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;anthropic&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;claude-sonnet-4-20250514&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;You are a loan officer assistant. Use the credit assessment tool to evaluate applications and explain decisions clearly.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;(creditTool)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Evaluate this application: credit score 580, DTI 60%, 1 year employment&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Agent calls the tool, gets the trace, explains the decision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;For AI engineers:&lt;/strong&gt; Your tools stay in sync with your code. Add a stage, the tool description updates. Rename a field, the schema updates. No drift.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For compliance teams:&lt;/strong&gt; The causal trace is auto-generated, not hand-written. It’s a faithful record of what the code actually did — auditable, reproducible, tamper-evident.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For product teams:&lt;/strong&gt; LLMs give better explanations because they have structured traces instead of scattered logs. User trust increases because the explanations are accurate.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;try-the-live-demo&quot;&gt;Try the live demo&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We have a live demo where Claude calls a credit-decision flowchart as an MCP tool. Enter your API key, ask a question, and watch the tool call happen in real time:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footprint-playground/samples/llm-agent-tool&quot;&gt;Live MCP Demo&lt;/a&gt;&lt;/strong&gt; — see &lt;code dir=&quot;auto&quot;&gt;.toMCPTool()&lt;/code&gt; in action&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Note &lt;/p&gt; &lt;div&gt; &lt;p&gt;The demo runs in your browser. Your API key is used client-side only and never stored.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# the engine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# the agent framework (optional)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footprint-playground/&quot;&gt;Interactive Playground&lt;/a&gt;&lt;/strong&gt; — 37+ samples&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footPrint/&quot;&gt;Documentation&lt;/a&gt;&lt;/strong&gt; — full API reference&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/footprintjs/footPrint&quot;&gt;GitHub&lt;/a&gt;&lt;/strong&gt; — MIT licensed, zero dependencies&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Testing AI Agents for $0: The Adapter-Swap Pattern</title><link>https://footprintjs.github.io/footPrint/blog/testing-agents-for-zero/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/testing-agents-for-zero/</guid><description>Write tests with mock(), deploy with anthropic(). Same code, zero API calls, full coverage.

</description><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-47-test-suite&quot;&gt;The $47 test suite&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We ran our agent test suite. Fifty-three tests. Each one called Claude with real prompts, real tools, real multi-turn conversations. Every test passed. The bill: &lt;strong&gt;$47.12&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The next day, two tests failed. Same code, same prompts. Different responses — because LLMs are non-deterministic. We re-ran. They passed. We re-ran again. One failed. Another $14.&lt;/p&gt;
&lt;p&gt;This is the testing problem nobody talks about in the AI agent space: &lt;strong&gt;your tests are expensive, slow, flaky, and non-deterministic.&lt;/strong&gt; Every run costs money. Every assertion is probabilistic. CI pipelines become unreliable. Developers stop writing tests because the feedback loop is broken.&lt;/p&gt;
&lt;p&gt;We fixed this with a pattern we call &lt;strong&gt;adapter swapping&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-pattern-mock--anthropic&quot;&gt;The pattern: mock() → anthropic()&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The idea is simple: your agent code doesn’t know (or care) which LLM provider is behind it. In tests, you use &lt;code dir=&quot;auto&quot;&gt;mock()&lt;/code&gt;. In production, you use &lt;code dir=&quot;auto&quot;&gt;anthropic()&lt;/code&gt; or &lt;code dir=&quot;auto&quot;&gt;openai()&lt;/code&gt;. The agent code is identical.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Agent, defineTool, mock } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createProvider, anthropic } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// The tool — same in tests and production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;calculator&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;defineTool&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;calculator&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Evaluate a math expression&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;inputSchema: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { expression: { type: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;required:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;expression&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;handler&lt;/span&gt;&lt;span&gt;: async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;eval&lt;/span&gt;&lt;span&gt;(input&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;expression&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ── Test code ────────────────────────────────────────&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;testProvider&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;mock&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Let me calculate that.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;toolCalls: [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tc1&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;calculator&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;arguments: { expression: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;42 * 17&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;The answer is 714.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ── Production code ──────────────────────────────────&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;prodProvider&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;createProvider&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;anthropic&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;claude-sonnet-4-20250514&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ── Agent code — IDENTICAL in both cases ─────────────&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildAgent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; Agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;({ provider })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;You are a helpful calculator assistant.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;(calculator)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;maxIterations&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Test: $0, instant, deterministic&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;testAgent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;buildAgent&lt;/span&gt;&lt;span&gt;(testProvider);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;testAgent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;What is 42 times 17?&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt;(result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;714&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Production: real LLM, real cost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;prodAgent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;buildAgent&lt;/span&gt;&lt;span&gt;(prodProvider);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The mock provider returns exactly the responses you specify. Tool calls happen deterministically. The agent’s ReAct loop executes the same way — calling tools, processing results, generating the next turn — but without any API calls.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-you-can-test-for-0&quot;&gt;What you can test for $0&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This isn’t just “mock the HTTP call.” The mock adapter participates in the full agent lifecycle:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tool call orchestration.&lt;/strong&gt; The mock returns a tool call → the agent executes the real tool handler → the result goes back to the mock → the mock returns the next response. Your tool handlers run for real. Your error handling runs for real. Only the LLM is mocked.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;mock&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// Turn 1: LLM decides to search&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Searching for information...&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;toolCalls: [{ id: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tc1&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, name: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, arguments: { query: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;AI trends&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// Turn 2: LLM processes search results and responds&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Based on my research, here are the top AI trends...&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;(searchTool)  &lt;/span&gt;&lt;span&gt;// Real tool — actually executes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;What are the AI trends?&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// searchTool.handler() was called with { query: &apos;AI trends&apos; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// The full ReAct loop ran — mock → tool → mock → response&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Multi-turn conversations.&lt;/strong&gt; Each entry in the mock array is one LLM turn. The agent processes them in sequence, exactly like it would with a real provider.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recorder verification.&lt;/strong&gt; Attach &lt;code dir=&quot;auto&quot;&gt;TokenRecorder&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;CostRecorder&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;TurnRecorder&lt;/code&gt; to mock runs. Verify that your observability pipeline captures the right data.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;tokens&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TokenRecorder&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;turns&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TurnRecorder&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ provider: &lt;/span&gt;&lt;span&gt;mock&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(tokens)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(turns)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Hello&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt;(turns&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getCompletedCount&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// Two LLM turns&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt;(tokens&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getStats&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;totalCalls&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;FlowChart pipelines.&lt;/strong&gt; Mock individual agents within a pipeline. Test the orchestration logic without any API calls.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;pipeline&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;FlowChart&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;research&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Research phase&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, mockResearchAgent)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Writing phase&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, mockWriterAgent)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;pipeline&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Write about AI safety&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Both agents ran with mocks — pipeline orchestration tested for $0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Error handling.&lt;/strong&gt; Mock providers can simulate errors to test your resilience patterns:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { withRetry, withFallback } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Simulate API failure on first call, success on retry&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;flakyProvider&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;mock&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ error: { code: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;rate_limit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, message: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Too many requests&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Success on retry!&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;resilientAgent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;withRetry&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ provider: &lt;/span&gt;&lt;span&gt;flakyProvider&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ maxRetries: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, backoffMs: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;resilientAgent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Hello&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt;(result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Success on retry!&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;the-concept-ladder-makes-this-natural&quot;&gt;The concept ladder makes this natural&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;agentfootprint has five concepts that compose together: &lt;strong&gt;LLMCall → Agent → RAG → FlowChart → Swarm&lt;/strong&gt;. Each one accepts a provider. Every one works with &lt;code dir=&quot;auto&quot;&gt;mock()&lt;/code&gt;.&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Concept&lt;/th&gt;&lt;th&gt;What it adds&lt;/th&gt;&lt;th&gt;Testing pattern&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;LLMCall&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Single invocation&lt;/td&gt;&lt;td&gt;Mock one response&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Tool use loop&lt;/td&gt;&lt;td&gt;Mock response sequence with tool calls&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;RAG&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Retrieval + generation&lt;/td&gt;&lt;td&gt;Mock retriever + LLM response&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;FlowChart&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Sequential pipeline&lt;/td&gt;&lt;td&gt;Mock each agent in the pipeline&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Swarm&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Dynamic routing&lt;/td&gt;&lt;td&gt;Mock router decisions + specialist responses&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;You start simple (test an LLMCall), compose up (test an Agent with tools), and eventually test full Swarm orchestrations — all at $0.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;when-you-still-need-real-llm-tests&quot;&gt;When you still need real LLM tests&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Mock tests verify your orchestration logic, tool integrations, and error handling. They don’t verify prompt quality or response appropriateness. For that, you still need real LLM calls — but far fewer.&lt;/p&gt;
&lt;p&gt;Our recommended split:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;90% mock tests&lt;/strong&gt; — orchestration, tools, error handling, recorders, pipelines&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;10% real LLM tests&lt;/strong&gt; — prompt quality, response format, edge cases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The mock tests run in CI on every commit (fast, free, deterministic). The real LLM tests run nightly or before release (slow, costly, but necessary).&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;try-it&quot;&gt;Try it&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Agent, mock, defineTool } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;Agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;provider: &lt;/span&gt;&lt;span&gt;mock&lt;/span&gt;&lt;span&gt;([{ content: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Hello! How can I help?&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }])&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;You are a helpful assistant.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;agent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Hi there&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(result&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// &quot;Hello! How can I help?&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Zero API calls. Zero cost. Deterministic. Your CI pipeline will thank you.&lt;/p&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Tip &lt;/p&gt; &lt;div&gt; &lt;p&gt;&lt;strong&gt;Try it in the browser&lt;/strong&gt; — the &lt;a href=&quot;https://footprintjs.github.io/agent-playground/&quot;&gt;agent playground&lt;/a&gt; runs all 23 samples with mock adapters by default. No API key needed.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/agent-playground/&quot;&gt;Agent Playground&lt;/a&gt;&lt;/strong&gt; — 23 interactive samples&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/footprintjs/footPrint&quot;&gt;GitHub — agentfootprint&lt;/a&gt;&lt;/strong&gt; — MIT licensed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/footprintjs/footPrint&quot;&gt;GitHub — footprintjs&lt;/a&gt;&lt;/strong&gt; — the engine underneath&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Why We Built FootPrint: The Missing Pattern for Explainable Backend Code</title><link>https://footprintjs.github.io/footPrint/blog/why-we-built-footprint/</link><guid isPermaLink="true">https://footprintjs.github.io/footPrint/blog/why-we-built-footprint/</guid><description>A user asked &apos;Why was my loan rejected?&apos; The LLM&apos;s explanation was wrong. That&apos;s when we realized the problem isn&apos;t the LLM — it&apos;s the code.

</description><pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;April 2026&lt;/strong&gt; · &lt;em&gt;Sanjay Krishna Anbalagan&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;the-moment-that-started-everything&quot;&gt;The moment that started everything&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A user asked: &lt;strong&gt;“Why was my loan rejected?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Our LLM had all the context. It had the code, the logs, the application data. It generated a confident, detailed explanation — citing the user’s credit score, debt-to-income ratio, and employment history.&lt;/p&gt;
&lt;p&gt;The explanation was wrong.&lt;/p&gt;
&lt;p&gt;Not slightly wrong. &lt;em&gt;Structurally&lt;/em&gt; wrong. The LLM had reconstructed a plausible-sounding narrative from scattered signals — a &lt;code dir=&quot;auto&quot;&gt;console.log&lt;/code&gt; here, a database write there — and hallucinated the causal chain that connected them. The user’s actual rejection reason was a completely different risk factor that the LLM never surfaced.&lt;/p&gt;
&lt;p&gt;That’s when we realized: &lt;strong&gt;the problem isn’t the LLM. The problem is the code.&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-structural-problem&quot;&gt;The structural problem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Traditional backend code scatters business logic across controllers, services, and middleware. When something happens — a loan gets rejected, a claim gets denied, an order gets flagged — the &lt;em&gt;why&lt;/em&gt; is implicit. It lives in the execution path the code took, the variables it read, the branches it chose. But none of that is captured in a structured way.&lt;/p&gt;
&lt;p&gt;So when an LLM needs to explain a decision, it does what any intelligent system would: it guesses. It reconstructs reasoning from whatever artifacts it can find — log lines, database states, API responses. And it hallucinates the connections between them.&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Traditional&lt;/th&gt;&lt;th&gt;What we needed&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Decision reasoning&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Implicit in code paths&lt;/td&gt;&lt;td&gt;Explicit causal trace&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;LLM explanation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Reconstructed from logs&lt;/td&gt;&lt;td&gt;Read directly from execution&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;State management&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Global, scattered&lt;/td&gt;&lt;td&gt;Transactional, observable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Tool descriptions&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Written by hand&lt;/td&gt;&lt;td&gt;Generated from the code&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;We didn’t need better prompts. We needed a &lt;strong&gt;different pattern&lt;/strong&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-flowchart-pattern&quot;&gt;The flowchart pattern&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;We looked at how humans explain decisions. When a loan officer explains a rejection, they walk through a flowchart: first we checked this, then we evaluated that, and based on this threshold, we made this decision. Every step is traceable.&lt;/p&gt;
&lt;p&gt;What if backend code worked the same way?&lt;/p&gt;
&lt;p&gt;That’s FootPrint. Your business logic is a &lt;strong&gt;graph of typed functions with transactional state&lt;/strong&gt;. The runtime captures every read, write, and decision as a &lt;strong&gt;causal trace&lt;/strong&gt;. When an LLM needs to explain what happened, it reads the trace — it doesn’t guess.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { flowChart, decide, narrative } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;flowChart&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ReceiveApplication&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;580&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dti&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;0.6&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;receive&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;EvaluateRisk&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;riskTier&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;creditScore&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;620&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;low&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;evaluate&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addDeciderFunction&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Route&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;decide&lt;/span&gt;&lt;span&gt;(scope, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ when: { riskTier: { eq: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } }, then: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, label: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;High risk&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;route&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Route based on risk tier&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunctionBranch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;RejectApplication&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;REJECTED&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addFunctionBranch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ApproveApplication&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scope&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;APPROVED&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setDefault&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;approve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt; = await &lt;/span&gt;&lt;span&gt;chart&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;recorder&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;narrative&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The runtime auto-generates this trace:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 1: The process began with ReceiveApplication.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Write creditScore = 580&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 2: Write dti = 0.6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 2: Next, it moved on to EvaluateRisk.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Read creditScore = 580&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 2: Write riskTier = &quot;high&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 3: Next step: Route based on risk tier.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Read riskTier = &quot;high&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Condition]: It evaluated &quot;High risk&quot;: riskTier &quot;high&quot; eq &quot;high&quot; ✓, and chose RejectApplication.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stage 4: Next, it moved on to RejectApplication.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Step 1: Write decision = &quot;REJECTED&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The LLM backtracks: &lt;code dir=&quot;auto&quot;&gt;decision=REJECTED&lt;/code&gt; ← &lt;code dir=&quot;auto&quot;&gt;riskTier=&quot;high&quot;&lt;/code&gt; ← &lt;code dir=&quot;auto&quot;&gt;creditScore=580&lt;/code&gt;. Every variable links to its cause. No hallucination.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-ecosystem&quot;&gt;The ecosystem&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;FootPrint isn’t one library — it’s an ecosystem of two packages that work together:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.npmjs.com/package/footprintjs&quot;&gt;footprintjs&lt;/a&gt;&lt;/strong&gt; is the engine. The flowchart pattern with typed state, causal traces, decision evidence, and self-describing APIs. Zero runtime dependencies. This is the foundation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.npmjs.com/package/agentfootprint&quot;&gt;agentfootprint&lt;/a&gt;&lt;/strong&gt; is the AI agent framework built on top. Five concepts — LLMCall, Agent, RAG, FlowChart, Swarm — that compose together. Adapter-swap testing (&lt;code dir=&quot;auto&quot;&gt;mock()&lt;/code&gt; in tests, &lt;code dir=&quot;auto&quot;&gt;anthropic()&lt;/code&gt; in production), agent-level tool gating, built-in cost and token tracking.&lt;/p&gt;
&lt;p&gt;Together, they give you explainable backend logic + explainable AI agents. Same causal trace model all the way through.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-we-got-wrong-and-fixed&quot;&gt;What we got wrong (and fixed)&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Building this taught us a few things the hard way:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We started with too many dependencies.&lt;/strong&gt; The first version used lodash. Every enterprise conversation hit the same wall: “how many transitive deps?” By v4.0, we removed everything. &lt;code dir=&quot;auto&quot;&gt;dependencies: {}&lt;/code&gt; in package.json. That single change unlocked more adoption conversations than any feature we shipped.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We built docs before a playground.&lt;/strong&gt; The docs are good — 14 pages, 108+ code examples. But the &lt;a href=&quot;https://footprintjs.github.io/footprint-playground/&quot;&gt;interactive playground&lt;/a&gt; with 37+ runnable samples and a &lt;a href=&quot;https://footprintjs.github.io/footprint-playground/samples/llm-agent-tool&quot;&gt;live LLM demo&lt;/a&gt; converts 10x better. If you’re building a developer tool, build the playground first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We underestimated how important &lt;code dir=&quot;auto&quot;&gt;decide()&lt;/code&gt; would be.&lt;/strong&gt; We originally had decisions as plain &lt;code dir=&quot;auto&quot;&gt;if/else&lt;/code&gt;. But without structured evidence capture, the trace just said “chose branch A” with no &lt;em&gt;why&lt;/em&gt;. Adding &lt;code dir=&quot;auto&quot;&gt;decide()&lt;/code&gt; with declarative rules — &lt;code dir=&quot;auto&quot;&gt;{ when: { score: { gt: 700 } }, then: &apos;approved&apos; }&lt;/code&gt; — made the traces actually useful for explanation.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;try-it&quot;&gt;Try it&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The fastest way to understand FootPrint is to see it run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footprint-playground/&quot;&gt;Interactive Playground&lt;/a&gt;&lt;/strong&gt; — 37+ samples, zero install, runs in your browser&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://footprintjs.github.io/footprint-playground/samples/llm-agent-tool&quot;&gt;Live LLM Demo&lt;/a&gt;&lt;/strong&gt; — watch Claude call a credit-decision flowchart as an MCP tool&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/footprintjs/footPrint&quot;&gt;GitHub&lt;/a&gt;&lt;/strong&gt; — MIT licensed, zero dependencies, npm provenance signed&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;footprintjs&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# the engine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;agentfootprint&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# the agent framework&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We built the pattern we wished existed when our LLM lied to a user. If you’re building systems where AI needs to explain decisions — fintech, healthcare, compliance, insurance — we think this is the right foundation.&lt;/p&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt; &lt;p aria-hidden=&quot;true&quot;&gt; Tip &lt;/p&gt; &lt;div&gt; &lt;p&gt;Questions? Open an issue on &lt;a href=&quot;https://github.com/footprintjs/footPrint/issues&quot;&gt;GitHub&lt;/a&gt; or try the playground and let the code speak for itself.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;</content:encoded></item></channel></rss>