Blog on Dippinhttps://dippin.org/blog.htmlRecent content in Blog on DippinHugoen-usWhat's New in Dippin v0.28https://dippin.org/blog/whats-new-v028.htmlTue, 19 May 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v028.html<p>Issue <a href="https://github.com/2389-research/dippin-lang/issues/39">#39</a> closes a parser gap. Tracker&rsquo;s runtime already supported <code>marker_grep</code>, <code>route_required</code>, and <code>output_limit</code> for stdout-driven routing, but <code>.dip</code> source couldn&rsquo;t reach them — authors following tracker&rsquo;s TRK101 lint recommendation hit <code>unrecognized tool field &quot;marker_grep&quot;</code> from dippin instead of a cleaner pipeline.</p> <h2 id="three-new-tool-node-fields">Three new tool-node fields</h2> <pre><code class="language-dippin">tool RunTests marker_grep: &#34;^(tests_pass|tests_fail)$&#34; route_required: true output_limit: 65536 timeout: 2m command: go test ./... 2&gt;&amp;1</code></pre> <ul> <li><strong><code>marker_grep</code></strong> — regex matched against captured stdout. The last match populates <code>ctx.tool_marker</code>, so routing edges can reference it instead of parsing raw <code>ctx.tool_stdout</code>.</li> <li><strong><code>route_required</code></strong> — when true, the node fails (<code>EventToolRouteMissing</code>) if the command emits no <code>_TRACKER_ROUTE=&lt;value&gt;</code> sentinel line. The matched value populates <code>ctx.tool_route</code>.</li> <li><strong><code>output_limit</code></strong> — per-node override of the engine&rsquo;s captured-stdout byte cap, for tools that need a larger or smaller window than the global default.</li> </ul> <h2 id="why-it-matters">Why it matters</h2> <p>Without these fields, authors working around tracker&rsquo;s TRK101 truncation foot-gun had to enumerate every possible stdout marker as a conditional edge. That made <code>dippin coverage</code> flag false-positive non-exhaustive routing on nodes that were actually fully covered. Typed <code>marker_grep</code> is the canonical fix; now dippin accepts it.</p>What's New in Dippin v0.27https://dippin.org/blog/whats-new-v027.htmlMon, 18 May 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v027.html<p>The LLM market moves. Since the last verification pass roughly a month ago, two providers shipped new flagships, one cut prices fleet-wide, another retired its previous-generation IDs and started silently redirecting calls, and a fifth doubled the price of three older models. <code>v0.27.0</code> is the catalog refresh that catches up.</p> <p>New IDs are now recognized by <code>dippin lint</code>. New prices flow through <code>dippin cost</code>. Stale IDs come out so authors get a DIP108 nudge instead of routing requests at a model that doesn&rsquo;t quite exist anymore.</p>What's New in Dippin v0.26https://dippin.org/blog/whats-new-v026.htmlFri, 15 May 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v026.html<p><code>v0.26.0</code> is a small release. One new keyword — <code>requires:</code> — in the workflow header. The shape is intentionally simple: a comma-separated list of identifiers the workflow needs in order to run. The motivation is less simple, and it shows up most visibly the first time a workflow crashes 20 minutes in because <code>git</code> wasn&rsquo;t on the runtime&rsquo;s PATH.</p> <h2 id="the-problem">The problem</h2> <p>Workflows quietly assume things about the environment they execute in. A pipeline that ends with <code>git commit &amp;&amp; gh pr create</code> assumes <code>git</code> and <code>gh</code> exist. A workflow that calls a <code>jq</code> filter inside a tool node assumes <code>jq</code> is installed. An agent that uses an MCP filesystem server assumes the runtime has that server configured. None of those assumptions live in the <code>.dip</code> file — they live in the author&rsquo;s head, until they don&rsquo;t.</p>What's New in Dippin v0.25https://dippin.org/blog/whats-new-v025.htmlMon, 11 May 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v025.html<p><code>v0.24.0</code> introduced <code>.dipx</code> — a deterministic, hash-verified bundle format for shipping workflows. The shape was right. The implementation was mostly right. But shipping a format means it leaves your machine and starts getting opened, packed, and inspected by <em>other</em> code — Tracker, CI scripts, forensic tools — and that&rsquo;s where the gaps showed up.</p> <p><code>v0.25.0</code> is the follow-up release that closes them. The format itself (v1.1 in the spec) is unchanged in any way that affects bundles you&rsquo;ve already packed. What changes is the contract between the library and its callers: cancellation works end-to-end, <code>inspect</code> actually does what its flags claim, error sentinels match the spec, and the exit codes you script against are the ones the spec promises.</p>What's New in Dippin v0.24https://dippin.org/blog/whats-new-v024.htmlFri, 08 May 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v024.html<p>You&rsquo;ve spent a week stitching together a workflow. It&rsquo;s six <code>.dip</code> files: an entry pipeline, three reusable subgraphs, two interview loops your teammate built and you finally got around to wiring in. It runs locally. Now you need to ship it to Tracker.</p> <p>Today, that means tarring up the directory, scp&rsquo;ing it, and hoping nobody renames a file in transit. There&rsquo;s no way to ask &ldquo;is this the same workflow tree I packaged on Friday?&rdquo; without diffing source files by hand. And if your CI runner unpacks the tarball into a directory that contains a leftover <code>phases/old_review.dip</code> from yesterday&rsquo;s build, your pipeline runs against the wrong subgraph and you don&rsquo;t notice until the bill arrives.</p>What's New in Dippin v0.21–v0.22https://dippin.org/blog/whats-new-v021-v022.htmlWed, 22 Apr 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v021-v022.html<p>Two releases, five days apart. <code>v0.21.0</code> shipped budget caps for workflows that get chatty with the API, plus proper timeout support on human nodes. <code>v0.22.0</code> added a new node kind — <code>manager_loop</code> — for supervising a child sub-pipeline without writing bespoke orchestrator glue.</p> <p>Here&rsquo;s what changed.</p> <h2 id="human-nodes-that-dont-block-forever">Human nodes that don&rsquo;t block forever</h2> <p><strong>v0.21.0</strong> — Human-gate workflows have always had a hole: if nobody ever answers, the pipeline just sits there. You could hack around it by adding a separate timeout branch in your orchestrator, but the <code>human</code> node itself had no concept of a deadline. Now it does:</p>What's New in Dippin v0.23https://dippin.org/blog/whats-new-v023.htmlWed, 22 Apr 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v023.html<p><code>v0.23.0</code> is a small, security-flavored release. Two new <code>defaults:</code> fields give you first-class control over what tool nodes are allowed to shell out — something you previously could only reach by dropping to raw DOT or patching the <code>Workflow.Vars</code> map in Tracker directly.</p> <h2 id="the-problem">The problem</h2> <p>Dippin <code>tool</code> nodes execute real shell commands. Tracker&rsquo;s runtime reads an allowlist and denylist at start time to decide which commands are permitted. Until this release, those lists lived in <code>graph.Attrs</code> — DOT-level attributes. If you wanted to set them from a <code>.dip</code> file, your options were:</p>What's New in Dippin v0.17–v0.20https://dippin.org/blog/whats-new-v020.htmlFri, 17 Apr 2026 00:00:00 +0000https://dippin.org/blog/whats-new-v020.html<p>Four releases shipped in two weeks. Here&rsquo;s what changed and why.</p> <h2 id="conditional-nodes-pure-routing-without-llm-calls">Conditional nodes: pure routing without LLM calls</h2> <p><strong>v0.17.0</strong> — The biggest addition. Before this, routing decisions required an <code>agent</code> node with <code>auto_status: true</code> — which meant burning an LLM API call just to check a condition. Now there&rsquo;s a dedicated node kind:</p> <pre><code class="language-dippin">conditional CheckOutcome label: &#34;Route by Result&#34;</code></pre> <p>Conditional nodes have no prompt, no model, no provider. They evaluate outgoing edge conditions and route accordingly — zero token cost. In DOT export they render as diamond shapes, matching the visual convention for decision points.</p>Conditional Edges: Routing Pipelines with whenhttps://dippin.org/blog/conditional-edges.htmlTue, 31 Mar 2026 00:00:00 +0000https://dippin.org/blog/conditional-edges.html<p>In the <a href="multi-line-prompts.html">last post</a> we showed how Dippin handles prompts. Now let&rsquo;s make pipelines that think. Linear workflows are fine for simple tasks, but real AI pipelines need to branch: retry on failure, escalate when something looks wrong, skip steps that don&rsquo;t apply. Dippin&rsquo;s <code>when</code> keyword is how you express all of that.</p> <h2 id="1-the-linear-baseline"><span class="step-num">1</span> The Linear Baseline</h2> <p>Let&rsquo;s start with a document review pipeline. It drafts, reviews, and publishes &ndash; in that order, every time, with no branching. Here&rsquo;s the whole thing:</p>Cost Estimation: Know Before You Runhttps://dippin.org/blog/cost-estimation.htmlTue, 31 Mar 2026 00:00:00 +0000https://dippin.org/blog/cost-estimation.html<p>You&rsquo;ve got a branching pipeline with conditional edges. Before you run it against real APIs, let&rsquo;s find out what it&rsquo;ll cost. Dippin&rsquo;s <code>cost</code> command gives you a per-run estimate from the <code>.dip</code> file alone &ndash; no API calls required.</p> <h2 id="1-dippin-cost"><span class="step-num">1</span> dippin cost</h2> <p>Let&rsquo;s use <code>complexity_cleanup.dip</code> from the examples directory &ndash; a real pipeline with tool nodes, retry loops, and a mix of models. Run <code>dippin cost</code> on it:</p> <pre> <span class="hl-dim">$</span> <span class="hl-shcmd">dippin cost complexity_cleanup.dip</span> &#x2550;&#x2550;&#x2550; Cost Estimate &#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550;&#x2550; Min Expected Max &#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500; &#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500; &#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500; &#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500; TOTAL $0.65 $0.65 $2.66 &#x2500;&#x2500;&#x2500; By Provider &#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500; anthropic $0.65 $0.65 $2.66 &#x2500;&#x2500;&#x2500; Top Cost Drivers &#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500; Refactor $1.46 (max) anthropic/claude-opus-4-6 Triage $0.50 (max) anthropic/claude-opus-4-6 QualityGate $0.49 (max) anthropic/claude-opus-4-6 Done $0.21 (max) anthropic/claude-sonnet-4-6 RemeasureCognitive $0.00 (max) / </pre> <p>The output has three sections worth understanding. The top section shows three columns: <strong>min</strong>, <strong>expected</strong>, and <strong>max</strong>. Min is the cost if every conditional branch takes the cheapest path. Max is the worst case if every node runs every retry. Expected sits in the middle &ndash; the most likely path through the pipeline.</p>Multi-line Prompts Without Escapinghttps://dippin.org/blog/multi-line-prompts.htmlTue, 31 Mar 2026 00:00:00 +0000https://dippin.org/blog/multi-line-prompts.html<p>If you&rsquo;ve been authoring AI pipelines in Graphviz DOT, you know the pain. Every multi-step workflow lives in a <code>.dot</code> file, and every string &ndash; prompts, commands, instructions &ndash; has to fit on a single quoted line. What that looks like in practice is this, a real <code>tool_command</code> from a sprint management pipeline:</p> <pre> tool_command="set -eu\nmkdir -p .ai .ai/drafts .ai/sprints\nif [ ! -f .ai/ledger.tsv ]; then\n now=$(date -u +%Y-%m-%dT%H:%M:%SZ)\n printf 'sprint_id\\ttitle\\tstatus\\tcreated_at\\tupdated_at\\n001\\tBootstrap sprint\\tplanned\\t%s\\t%s\\n' \"$now\" \"$now\" > .ai/ledger.tsv\nfi\nprintf 'ledger-ready'" </pre> <p>If you&rsquo;ve authored AI pipelines in Graphviz DOT, this looks familiar. And painful. Every newline is <code>\n</code>, every quote is <code>\&quot;</code>, every tab is <code>\\t</code>. Reading this is archaeology. Editing it is anxiety.</p>CI Integration: Lint, Test, Formathttps://dippin.org/blog/ci-integration.htmlFri, 27 Mar 2026 00:00:00 +0000https://dippin.org/blog/ci-integration.html<p>Your Dippin workflows deserve the same CI treatment as your application code. Here&rsquo;s how to set up <a href="https://docs.github.com/en/actions">GitHub Actions</a> to validate, lint, test, and format-check your <code>.dip</code> files on every push. We&rsquo;ll build a workflow YAML from scratch, then look at how dippin-lang dogfoods its own CI.</p> <h2 id="what-to-check-in-ci">What to Check in CI</h2> <p>A Dippin CI pipeline has four stages. Each catches a different class of problem:</p> <div class="pipeline-visual"> <div class="pipeline-box green">validate</div> <span class="pipeline-arrow">&rarr;</span> <div class="pipeline-box lavender">lint</div> <span class="pipeline-arrow">&rarr;</span> <div class="pipeline-box yellow">test</div> <span class="pipeline-arrow">&rarr;</span> <div class="pipeline-box green">fmt --check</div> </div> <table> <thead> <tr> <th>Stage</th> <th>Command</th> <th>What it catches</th> <th>Exit code on failure</th> </tr> </thead> <tbody> <tr> <td>Validate</td> <td><code>dippin validate</code></td> <td>Structural errors (<a href="../validation.html">DIP001-DIP009</a>)</td> <td>1</td> </tr> <tr> <td>Lint</td> <td><code>dippin lint</code></td> <td>Semantic warnings (<a href="../validation.html">DIP101-DIP125</a>)</td> <td>0 (warnings don&rsquo;t fail)</td> </tr> <tr> <td>Test</td> <td><code>dippin test</code></td> <td><a href="scenario-testing.html">Scenario test</a> failures</td> <td>1</td> </tr> <tr> <td>Format</td> <td><code>dippin fmt --check</code></td> <td>Non-canonical formatting</td> <td>1</td> </tr> </tbody> </table> <div class="callout"> <h4>Exit code contract</h4> <p> <code>dippin validate</code> and <code>dippin test</code> exit 1 on failure, making them suitable as CI gates. <code>dippin lint</code> exits 0 even with warnings -- warnings are advisory, not blocking. To fail CI on warnings, use <code>dippin check</code>, which combines validate + lint and can be configured to fail on warnings. </p>Editor Setup: LSP, VS Code, and Tree-sitterhttps://dippin.org/blog/editor-setup.htmlFri, 27 Mar 2026 00:00:00 +0000https://dippin.org/blog/editor-setup.html<p>Dippin ships with a built-in <a href="https://microsoft.github.io/language-server-protocol/">Language Server Protocol</a> (LSP) server and a VS Code extension. Together they give you real-time diagnostics, hover documentation, go-to-definition, autocomplete, and syntax highlighting &ndash; the same checks you&rsquo;d get from <code>dippin lint</code>, but live as you type.</p> <h2 id="the-lsp-server">The LSP Server</h2> <p>The LSP server is built into the <code>dippin</code> binary. No separate install needed:</p> <pre> <span class="hl-dim">$</span> <span class="hl-shcmd">dippin lsp</span> </pre> <p>Starts the server on stdio (stdin/stdout), speaking JSON-RPC 2.0 per the <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/">LSP spec</a>. Any editor with LSP support can connect.<sup>1</sup></p>Getting Started with Dippinhttps://dippin.org/blog/getting-started.htmlFri, 27 Mar 2026 00:00:00 +0000https://dippin.org/blog/getting-started.html<p>Dippin is a domain-specific language for authoring AI pipeline workflows. It replaces <a href="https://graphviz.org/doc/info/lang.html">Graphviz DOT</a> as the format for defining multi-step LLM pipelines, with first-class support for multi-line prompts, conditional routing, parallel execution, and a full <a href="../validation.html">validation toolchain</a>. You&rsquo;ll go from zero to a validated, linted, and visualized workflow in under 10 minutes.</p> <h2 id="1-install-the-toolchain"><span class="step-num">1</span> Install the Toolchain</h2> <p>Dippin is a single <a href="https://go.dev/">Go</a> binary. Install it with <code>go install</code>:</p> <pre> <span class="hl-dim">$</span> <span class="hl-shcmd">go install github.com/2389-research/dippin-lang/cmd/dippin@latest</span> </pre> <p>Or via Homebrew (macOS and Linux):</p>Migrating from DOT to Dippinhttps://dippin.org/blog/migrating-from-dot.htmlFri, 27 Mar 2026 00:00:00 +0000https://dippin.org/blog/migrating-from-dot.html<p>If your team has existing pipelines authored in <a href="https://graphviz.org/doc/info/lang.html">Graphviz DOT</a>, Dippin provides an automated migration path. <code>dippin migrate</code> converts DOT files to Dippin syntax, and <code>dippin validate-migration</code> verifies structural parity between the original and the converted file.<sup>1</sup></p> <h2 id="why-migrate">Why Migrate?</h2> <p>DOT was never designed for AI pipelines. The pain points stack up:</p> <table> <thead> <tr> <th>Problem in DOT</th> <th>How Dippin fixes it</th> </tr> </thead> <tbody> <tr> <td>Prompts need <code>\n</code> escaping and <code>\&quot;</code> quoting</td> <td>Indentation-based multi-line blocks, no escaping</td> </tr> <tr> <td>No validation beyond syntax</td> <td><a href="../validation.html">34 diagnostic codes</a>: structural errors + semantic warnings</td> </tr> <tr> <td>No testing framework</td> <td><a href="scenario-testing.html">Scenario testing</a> with <code>.test.json</code> files</td> </tr> <tr> <td>No formatting standard</td> <td>Idempotent <code>dippin fmt</code> with canonical field ordering</td> </tr> <tr> <td>No cost estimation</td> <td><code>dippin cost</code> and <code>dippin optimize</code></td> </tr> <tr> <td>Node types inferred from shape attribute</td> <td>Explicit <code>agent</code>, <code>tool</code>, <code>human</code> keywords</td> </tr> </tbody> </table> <h2 id="the-migration-process">The Migration Process</h2> <p>We&rsquo;ll migrate <code>consensus_task.dot</code>, a 17-node multi-model consensus workflow with conditional edges and restart loops.</p>Scenario Testing with .test.jsonhttps://dippin.org/blog/scenario-testing.htmlFri, 27 Mar 2026 00:00:00 +0000https://dippin.org/blog/scenario-testing.html<p>AI pipelines are non-deterministic &ndash; LLM outputs vary between runs. But the <em>structure</em> of your pipeline is deterministic: given a particular outcome at each node, the same path should always be followed. Dippin&rsquo;s scenario testing lets you inject context values and assert on execution paths, giving you deterministic tests for non-deterministic systems.</p> <h2 id="the-core-idea">The Core Idea</h2> <p>A <code>.test.json</code> file sits next to your <code>.dip</code> file. It holds an array of test scenarios, each declaring what context values to inject and what execution behavior to expect (visited nodes, path ordering, status).</p>