<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Siddham Jain</title>
    <description>The latest articles on DEV Community by Siddham Jain (@siddhamjain).</description>
    <link>https://dev.to/siddhamjain</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047889%2Fae40a91b-7528-44e0-a056-c7a74ddf63c6.jpg</url>
      <title>DEV Community: Siddham Jain</title>
      <link>https://dev.to/siddhamjain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siddhamjain"/>
    <language>en</language>
    <item>
      <title>Imagine your ai agent fixing its own fu*ckups, here's how the rewind actually works</title>
      <dc:creator>Siddham Jain</dc:creator>
      <pubDate>Sun, 26 Jul 2026 14:14:54 +0000</pubDate>
      <link>https://dev.to/siddhamjain/imagine-your-ai-agent-fixing-its-own-fuckups-heres-how-the-rewind-actually-works-41b6</link>
      <guid>https://dev.to/siddhamjain/imagine-your-ai-agent-fixing-its-own-fuckups-heres-how-the-rewind-actually-works-41b6</guid>
      <description>&lt;p&gt;When an AI agent fails in production, you get told &lt;em&gt;that&lt;/em&gt; it failed. You almost never get to&lt;br&gt;
answer the only question that matters: &lt;strong&gt;what if the third agent had made a different call?&lt;/strong&gt;&lt;br&gt;
You can't replay the run. You can't isolate the step that broke it. You can't test a fix without&lt;br&gt;
re-running the whole thing and hoping.&lt;/p&gt;

&lt;p&gt;I spent a hackathon building &lt;strong&gt;Rewind&lt;/strong&gt; — a "time machine" for multi-agent runs — to see if&lt;br&gt;
SigNoz's traces plus LangGraph's checkpointer could actually answer that question. They can. This&lt;br&gt;
post is the honest version: how the replay works, and the three things that nearly broke it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8rnnsh4bmkesfpwsq59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8rnnsh4bmkesfpwsq59.png" alt="Rewind forks a failed run and proves the fix — failed vs fixed, side by side" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The setup: a crew that ships a subtly wrong fix
&lt;/h2&gt;

&lt;p&gt;The observed workload is a four-agent coding crew (Planner → Coder → Tester → Reviewer) that&lt;br&gt;
fixes a GitHub-style issue. The issue is deliberately ambiguous:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"An order line for &lt;strong&gt;more than 10 units&lt;/strong&gt; should receive a 10% discount."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tests, however, require the discount at &lt;em&gt;exactly&lt;/em&gt; 10 units. A weak model reads the spec&lt;br&gt;
literally and writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# what the weak model wrote
&lt;/span&gt;    &lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the boundary test fails with a very real assertion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_bulk_discount_boundary():
    assert cart_total([(10.0, 10)]) == 90.0
E   assert 100.0 == 90.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the failure I want to &lt;em&gt;reproduce and fix from a trace&lt;/em&gt;, not by re-reading logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making every agent step a span
&lt;/h2&gt;

&lt;p&gt;I instrument the crew two ways. &lt;code&gt;openinference-instrumentation-langchain&lt;/code&gt; auto-captures the&lt;br&gt;
LangChain/LangGraph internals, and a tiny custom context manager wraps each node so the span&lt;br&gt;
carries exactly the attributes I'll want to query later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;node_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crew.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rewind.node&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rewind.thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rewind.input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;short&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage_metadata&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.request.model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.usage.input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.usage.output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rewind.cost_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used OpenTelemetry's &lt;a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/" rel="noopener noreferrer"&gt;GenAI semantic conventions&lt;/a&gt;&lt;br&gt;
(&lt;code&gt;gen_ai.request.model&lt;/code&gt;, &lt;code&gt;gen_ai.usage.*_tokens&lt;/code&gt;) for the model/token fields so they're not&lt;br&gt;
bespoke, and a &lt;code&gt;rewind.&lt;/code&gt; namespace for the app-specific bits. Spans export over OTLP/HTTP to&lt;br&gt;
SigNoz's ingester on &lt;code&gt;:4318&lt;/code&gt;. The &lt;code&gt;rewind.thread_id&lt;/code&gt; is the thread that stitches an entire run&lt;br&gt;
together — remember it, it's the whole trick.&lt;/p&gt;

&lt;p&gt;Here's what lands in SigNoz — every planner/coder/tester/reviewer step, queryable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffemr53wp09htdjizyhoj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffemr53wp09htdjizyhoj.png" alt="The rewind-crew spans in SigNoz's trace explorer" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Gotcha #1: getting SigNoz up when Docker Desktop is gone
&lt;/h2&gt;

&lt;p&gt;I deployed SigNoz with &lt;strong&gt;Foundry&lt;/strong&gt; (&lt;code&gt;casting.yaml&lt;/code&gt; → &lt;code&gt;casting.yaml.lock&lt;/code&gt; → &lt;code&gt;cast&lt;/code&gt;), which is a&lt;br&gt;
genuinely clean one-command install. But my machine had no Docker Desktop, and the leftovers&lt;br&gt;
fought me: &lt;code&gt;docker&lt;/code&gt; pointed at a dangling symlink, &lt;code&gt;docker compose&lt;/code&gt; was a broken cli-plugin, and&lt;br&gt;
Foundry's &lt;code&gt;cast&lt;/code&gt; died on &lt;code&gt;docker-credential-desktop executable not found&lt;/code&gt;. The fixes, in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;brew install colima docker&lt;/code&gt; and &lt;code&gt;colima start&lt;/code&gt; — a lightweight Docker engine, no Desktop.&lt;/li&gt;
&lt;li&gt;relink the compose plugin into &lt;code&gt;~/.docker/cli-plugins/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;delete the &lt;code&gt;credsStore: "desktop"&lt;/code&gt; line from &lt;code&gt;~/.docker/config.json&lt;/code&gt; so Foundry could
authenticate to the registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is in a tutorial. If you're on a clean Mac without Docker Desktop, Colima + fixing&lt;br&gt;
&lt;code&gt;config.json&lt;/code&gt; is the path.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reading the telemetry &lt;em&gt;back&lt;/em&gt; out of SigNoz
&lt;/h2&gt;

&lt;p&gt;Here's the part I care about most: Rewind doesn't keep its own trace store. To render a run's&lt;br&gt;
timeline, the backend &lt;strong&gt;queries SigNoz&lt;/strong&gt; for its own spans via the v5 query API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v5&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;query_range&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requestType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;compositeQuery&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;queries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;builder_query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expression&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.name = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rewind-crew&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; AND rewind.thread_id = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;run-64c55532&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; AND rewind.node EXISTS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;selectFields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rewind.node&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fieldContext&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attribute&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rewind.cost_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fieldContext&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attribute&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.usage.input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fieldContext&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attribute&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}}]}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;filter.expression&lt;/code&gt; string syntax is the thing to get right in v5 — it's a mini query&lt;br&gt;
language (&lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;EXISTS&lt;/code&gt;), and &lt;code&gt;fieldContext: "attribute"&lt;/code&gt; vs &lt;code&gt;"resource"&lt;/code&gt; matters for&lt;br&gt;
whether SigNoz finds your field. Once that clicked, the same query grouped by &lt;code&gt;rewind.node&lt;/code&gt;&lt;br&gt;
became a &lt;strong&gt;reliability heatmap&lt;/strong&gt; across every run: pass rate, average cost per node, and where&lt;br&gt;
failures surface. Across 24 seeded runs mine sat at a 25% pass rate with the failures localizing&lt;br&gt;
to the Tester and Reviewer nodes — which is exactly right, because that's where a wrong &lt;code&gt;&amp;gt;&lt;/code&gt; vs&lt;br&gt;
&lt;code&gt;&amp;gt;=&lt;/code&gt; gets caught.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbpnhjuguulbfhpbyedo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbpnhjuguulbfhpbyedo.png" alt="Reliability heatmap, sourced from the SigNoz Query Builder — 25% pass rate, Tester/Reviewer are the hot cells" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Gotcha #2: the replay is real because LangGraph makes it real
&lt;/h2&gt;

&lt;p&gt;The reproduce-and-fork isn't a simulation, and that's the honest core of the project. The crew&lt;br&gt;
writes to a LangGraph &lt;code&gt;SqliteSaver&lt;/code&gt; checkpointer, and the backend shares that same file. So to&lt;br&gt;
fork a run I use LangGraph's native &lt;a href="https://langchain-ai.github.io/langgraph/" rel="noopener noreferrer"&gt;time-travel&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_state_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;     &lt;span class="c1"&gt;# every checkpoint of the run
&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pick_snapshot_before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;coder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# rewind to just before the coder
&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# change model / guardrail
&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fork_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                      &lt;span class="c1"&gt;# re-run everything downstream, live
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nodes after the checkpoint actually re-execute with the new state, and the forked run emits&lt;br&gt;
its own spans — so it becomes a &lt;strong&gt;second trace in SigNoz&lt;/strong&gt;, not a diffed copy. In Rewind's UI you&lt;br&gt;
can see the reproduced timeline, the exact code the model wrote, and the real pytest output that&lt;br&gt;
failed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fmosbnl9a0ole4h5elz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fmosbnl9a0ole4h5elz.png" alt="The reproduced timeline: the buggy  raw `&gt; 10` endraw , the failing pytest, and the fork panel" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I fork the failing run with a spec guardrail and a stronger model, the coder rewrites the&lt;br&gt;
line as &lt;code&gt;if quantity &amp;gt;= 10&lt;/code&gt;, and the same pytest that was red goes green. Failed and fixed, side&lt;br&gt;
by side, both backed by real traces — and the fix costs a few more tokens, which the cost deltas&lt;br&gt;
make honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha #3: know when &lt;em&gt;not&lt;/em&gt; to depend on the live query
&lt;/h2&gt;

&lt;p&gt;SigNoz v0.134 moved its auth around (login is under &lt;code&gt;/api/v2/sessions&lt;/code&gt;, not where older guides&lt;br&gt;
say), and I burned time before making a call I'd defend: &lt;strong&gt;the checkpointer is the source of&lt;br&gt;
truth for the timeline; the SigNoz query is enrichment.&lt;/strong&gt; If the query path is configured, the&lt;br&gt;
timeline gets real tokens/cost from SigNoz and every card deep-links to the live trace; if it&lt;br&gt;
isn't, the app still reproduces and forks from the checkpointer. A demo that hard-depends on one&lt;br&gt;
network call to a freshly-booted observability stack is a demo that dies on stage. Degrade&lt;br&gt;
gracefully.&lt;/p&gt;

&lt;p&gt;The same telemetry drives a dashboard and a failure-rate alert, both built programmatically via&lt;br&gt;
the SigNoz API — the alert plots the failure count against a threshold, straight from the Query&lt;br&gt;
Builder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftt20fgjtnhmyusi3ene5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftt20fgjtnhmyusi3ene5.png" alt="A traces-based failure-rate alert, plotted with the SigNoz Query Builder" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell my past self
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pick your span attributes before you write a single query.&lt;/strong&gt; I renamed fields twice because
I hadn't decided that &lt;code&gt;rewind.thread_id&lt;/code&gt; was the join key. Design the schema first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fieldContext&lt;/code&gt; is not optional trivia.&lt;/strong&gt; In the v5 query API, marking a field &lt;code&gt;attribute&lt;/code&gt; vs
&lt;code&gt;resource&lt;/code&gt; is the difference between data and an empty result set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard widgets need a &lt;code&gt;layout&lt;/code&gt;.&lt;/strong&gt; I posted widgets with no &lt;code&gt;id&lt;/code&gt;/&lt;code&gt;layout&lt;/code&gt; and got a dashboard
that rendered the empty "Welcome" state — the panels were stored but never placed on the grid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let the framework do the replay.&lt;/strong&gt; I almost hand-rolled a "re-run from step N" engine.
LangGraph's &lt;code&gt;get_state_history&lt;/code&gt; + &lt;code&gt;update_state&lt;/code&gt; + &lt;code&gt;invoke(None, cfg)&lt;/code&gt; already &lt;em&gt;is&lt;/em&gt; that engine,
and it's the reason the fork is trustworthy instead of a mock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two proofs beat one.&lt;/strong&gt; pytest is the hard gate (it can't be argued with); an LLM-as-judge
score rides along as a soft quality signal on the timeline. Different jobs, both useful.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Rewind reproduces a production agent failure, forks one decision, and proves the fix with a real&lt;br&gt;
passing test — and none of it required a custom trace store or a faked replay. SigNoz's spans&lt;br&gt;
already held the full I/O and cost; LangGraph's checkpointer already held the executable state.&lt;br&gt;
The work was joining them on one thread id. If you're building agents, that pairing —&lt;br&gt;
OpenTelemetry traces for &lt;em&gt;what happened&lt;/em&gt;, a checkpointer for &lt;em&gt;do it again differently&lt;/em&gt; — is worth&lt;br&gt;
stealing.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/siddham-jain/rewind" rel="noopener noreferrer"&gt;github.com/siddham-jain/rewind&lt;/a&gt;. Built for the&lt;br&gt;
Agents of SigNoz hackathon.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>claude</category>
    </item>
  </channel>
</rss>
