<?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: Gabe LG</title>
    <description>The latest articles on DEV Community by Gabe LG (@bucabay).</description>
    <link>https://dev.to/bucabay</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%2F4010551%2Fff81c40c-e957-4d68-bcc6-c6f8430f6c81.jpg</url>
      <title>DEV Community: Gabe LG</title>
      <link>https://dev.to/bucabay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bucabay"/>
    <language>en</language>
    <item>
      <title>Build software that heals itself in the agentic era</title>
      <dc:creator>Gabe LG</dc:creator>
      <pubDate>Wed, 01 Jul 2026 22:16:28 +0000</pubDate>
      <link>https://dev.to/bucabay/build-software-that-heals-itself-in-the-agentic-era-540p</link>
      <guid>https://dev.to/bucabay/build-software-that-heals-itself-in-the-agentic-era-540p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Disclosure: I build &lt;a href="https://mailkite.dev" rel="noopener noreferrer"&gt;MailKite&lt;/a&gt;, and the open-source &lt;code&gt;mail-parse&lt;/code&gt; library I use as the example is ours. But the pattern is the point — it isn't MailKite-specific, and you can apply it to anything that eats messy input.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Self-healing software is a system architected so that, when it hits input the real world throws at it, it doesn't crash and it doesn't stay broken: it records a structured, PII-free &lt;em&gt;failure signature&lt;/em&gt;, and that signature feeds a repair loop — increasingly, an AI agent — that turns the breakage into a permanent fix behind automated gates. In the agentic era the bottleneck is no longer &lt;em&gt;writing&lt;/em&gt; the fix; a capable agent can do that. The bottleneck is architecting your software so an agent's fix is safe, automatic, and cumulative. This post is that pattern. I'll use our open-source MIME parser (&lt;code&gt;mail-parse&lt;/code&gt;) as the running example — messy input is where software goes to die — but the shape applies to almost any system that eats hostile real-world data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two honesty notes before I start, because a post that blurs shipped and planned isn't worth reading. First: this is &lt;strong&gt;part one of a two-part series&lt;/strong&gt; — part one is the architecture and what runs today; part two comes after the fully autonomous loop ships and we've watched it heal real input in the wild. Second: I'll label each piece &lt;strong&gt;shipped&lt;/strong&gt; or &lt;strong&gt;in progress&lt;/strong&gt; as I go, and there's a status table at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop the agentic era changes
&lt;/h2&gt;

&lt;p&gt;The classic repair loop is slow and human-shaped: a bug slips into production → someone eventually files an issue → a human reproduces it, writes a patch, ships a release → weeks later every install benefits. It works, but it's measured in weeks and gated on a human being in the loop for every single fix.&lt;/p&gt;

&lt;p&gt;Agents change what's &lt;em&gt;possible&lt;/em&gt; here, not by being trusted to write perfect code, but by being fast and tireless at the boring middle. The interesting question stops being "can an agent write the fix?" (increasingly, yes) and becomes: &lt;strong&gt;when an agent can propose a fix in seconds, how do you build software so that letting it do so isn't reckless?&lt;/strong&gt; Answer that, and your system stops accumulating breakage — every new way the world is wrong becomes a one-time event.&lt;/p&gt;

&lt;p&gt;Five design moves make it work. I'll state each generally, then ground it in the parser.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Never crash — turn every failure into a structured signal
&lt;/h2&gt;

&lt;p&gt;The foundation of a self-healing system is that failure is a &lt;em&gt;first-class, structured output&lt;/em&gt;, not an exception that unwinds the stack. If your software dies on bad input, there's nothing to heal; if it silently mangles it, there's nothing to detect. The discipline is: always produce the best result you can, and alongside it a machine-readable record of everything you had to paper over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the parser (shipped):&lt;/strong&gt; &lt;code&gt;mail-parse&lt;/code&gt; never throws. An unclosed MIME boundary pops the orphaned context and emits &lt;code&gt;BOUNDARY_NOT_CLOSED&lt;/code&gt;; a &lt;code&gt;charset&lt;/code&gt; that won't decode falls back and emits &lt;code&gt;UNKNOWN_CHARSET&lt;/code&gt;. You always get a message &lt;em&gt;and&lt;/em&gt; a typed list of what was wrong with it. Those diagnostics aren't logging — they're the raw material every downstream loop runs on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mailkite/mail-parse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// parse() never throws — even on a broken message it returns a best-effort&lt;/span&gt;
&lt;span class="c1"&gt;// result *plus* a typed list of everything it had to paper over.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawMime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// decoded as far as it could&lt;/span&gt;
&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// whatever it could recover&lt;/span&gt;
&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// → [&lt;/span&gt;
&lt;span class="c1"&gt;//     { code: "BOUNDARY_NOT_CLOSED", scope: "structure" },&lt;/span&gt;
&lt;span class="c1"&gt;//     { code: "UNKNOWN_CHARSET",     scope: "part", contentType: "text/html" },&lt;/span&gt;
&lt;span class="c1"&gt;//   ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Make fixes additive, not surgery — a plugin seam
&lt;/h2&gt;

&lt;p&gt;If every fix means editing the core, fixes are risky, they collide, and no agent (or human) should be trusted to make them at speed. The move is a &lt;strong&gt;registry&lt;/strong&gt;: a seam where new behavior is a self-contained, narrowly-scoped, &lt;em&gt;contained&lt;/em&gt; unit — it can't take down the whole system, and it's obvious what it touches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the parser (shipped):&lt;/strong&gt; fixups are middleware in a PostCSS-style registry — each declares a phase, a match predicate, and a handler, and a middleware that throws becomes a contained &lt;code&gt;MIDDLEWARE_ERROR&lt;/code&gt; diagnostic while the chain keeps going. A new format quirk is &lt;em&gt;a new middleware with a narrow predicate&lt;/em&gt;, not a patch threaded through the core. That containment is exactly what later lets a &lt;em&gt;generated&lt;/em&gt; fix be admitted without betting the system on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A new format quirk is a self-contained middleware with a narrow predicate —&lt;/span&gt;
&lt;span class="c1"&gt;// not a patch threaded through the core.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tnef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/ms-tnef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;extractWinmailDat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tnef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// If handler throws, the parser records a contained MIDDLEWARE_ERROR&lt;/span&gt;
&lt;span class="c1"&gt;// diagnostic and the rest of the chain keeps running.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Name failures identically everywhere — without leaking data
&lt;/h2&gt;

&lt;p&gt;To fix a class of breakage you first have to &lt;em&gt;name&lt;/em&gt; it, the same way across every install, without ever collecting private data. That's a &lt;strong&gt;failure signature&lt;/strong&gt;: a deterministic hash over &lt;em&gt;structure only&lt;/em&gt;. It does two things at once — it lets a thousand installs hitting the same bug collapse into one prioritized signal, and it gives the repair loop a precise, shareable target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the parser (shipped):&lt;/strong&gt; the signature is an FNV-1a hash over PII-free features — diagnostic codes, content-type, transfer-encoding, a byte-shape fingerprint, mailer family, structure path — and &lt;em&gt;never&lt;/em&gt; bytes, addresses, or subjects. Two installs on opposite sides of the world hitting the same Outlook-TNEF quirk compute the same hash. A multi-granularity rollup lets you cluster loosely or tightly. (It's pinned identical across our TypeScript, Python, and Go ports by a golden-corpus test, so the herd can't drift.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FailureSignature&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                 &lt;span class="c1"&gt;// = fnv1a(canonicalize(features))&lt;/span&gt;
  &lt;span class="nl"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;envelope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;structure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;part&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;diagnosticCodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;  &lt;span class="c1"&gt;// e.g. ["UNKNOWN_CHARSET"]&lt;/span&gt;
    &lt;span class="nl"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// the offending leaf's declared type&lt;/span&gt;
    &lt;span class="nl"&gt;transferEncoding&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;byteSignature&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// hex magic of the first N bytes — never content&lt;/span&gt;
    &lt;span class="nl"&gt;mailerFamily&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// X-Mailer normalized → "Outlook/16"&lt;/span&gt;
    &lt;span class="nl"&gt;structurePath&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// "multipart/mixed&amp;gt;…&amp;gt;application/ms-tnef"&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;Nothing in there is content — no subject, no addresses, no body bytes — so the same broken email produces the same hash in every language:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mailparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;compute_signature&lt;/span&gt;

&lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compute_signature&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scope&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;part&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;diagnosticCodes&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;UNKNOWN_CHARSET&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;contentType&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;text/plain&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;transferEncoding&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;base64&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="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# "13586f32bb2840c6" — byte-identical in Node, Python, and Go
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Two loops: fix the core for everyone, patch the edge safely
&lt;/h2&gt;

&lt;p&gt;Self-healing has two speeds, and you want both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The cold loop — fix the library for everyone.&lt;/strong&gt; &lt;em&gt;(Shipped.)&lt;/em&gt; When the parser degrades it emits a &lt;code&gt;FailureReport&lt;/code&gt;. Where it goes is the deployer's choice — reporting is &lt;strong&gt;opt-in, with no default phone-home&lt;/strong&gt; — but point the built-in reporter at the core repo and it files exactly &lt;em&gt;one deduplicated GitHub issue per signature&lt;/em&gt; (a hidden &lt;code&gt;parse-signature:&lt;/code&gt; marker makes it idempotent; N installs → 1 issue), containing the structural signature and, in writing, &lt;em&gt;no message content&lt;/em&gt;. A responder — a human, or an AI coding routine triggered by the issue — reproduces from the scrubbed signature, fixes the core, and opens a PR that CI won't merge unless a golden corpus and a benign-input regression set both stay green. The fix ships to every install, in every language.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The hot loop — patch one edge now.&lt;/strong&gt; &lt;em&gt;(In progress: designed, next.)&lt;/em&gt; A library release takes time, and some quirks are concentrated in a single tenant's weird upstream system. For those, the design is an agent, handed the sealed failing fixture, that writes a &lt;em&gt;narrowly-scoped&lt;/em&gt; middleware plus a golden test pinning its behavior — a stopgap that heals that edge immediately while the cold loop fixes the root cause for everyone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Trust the gates, not the generator — the security crux
&lt;/h2&gt;

&lt;p&gt;Here's the part the agentic era forces you to get right, because the hot loop means &lt;strong&gt;running code a model wrote against real production data.&lt;/strong&gt; You do not make that safe by trusting the model. You make it safe by building an architecture where a &lt;em&gt;fully compromised or simply wrong&lt;/em&gt; generated fix still can't do harm. Almost the entire hot-loop design &lt;em&gt;(in progress)&lt;/em&gt; is that safety envelope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sandboxed execution.&lt;/strong&gt; Generated fixes run as &lt;strong&gt;Wasm (Extism)&lt;/strong&gt; with &lt;strong&gt;deny-by-default capabilities&lt;/strong&gt; and a hard CPU/fuel budget — no network, no filesystem, no ambient authority. A bad fix can transform its input or burn its fuel and die; it can't reach anything else. (Generation and CI run in a separate sandbox, isolated from production.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adversarial gates the model doesn't author.&lt;/strong&gt; A fix is admitted only if it clears &lt;em&gt;system-owned&lt;/em&gt; tests: it must fire zero times against a benign corpus of well-formed input (no collateral damage), it must satisfy the golden test generated &lt;em&gt;from the failing case&lt;/em&gt; (it actually fixes the thing), and it must clear a &lt;strong&gt;specificity floor&lt;/strong&gt; (its predicate is narrow, not a catch-all). The agent proposes; adversarial tests dispose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary, then commit.&lt;/strong&gt; An admitted fix rolls out at 5% → 25% → 100%, watched against a structural agreement metric — a bad fix is caught on a sliver of traffic, not all of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A kill switch per fix.&lt;/strong&gt; Every generated unit is individually disableable by config, no redeploy — instant, reversible rollback.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What the hot-loop agent generates (designed, next): a narrowly-scoped&lt;/span&gt;
&lt;span class="c1"&gt;// middleware that fires ONLY on the failing signature — plus a golden test.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charset&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-user-defined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// the one quirk, nothing else&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;decodeAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;windows-1252&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Admitted only if it fires zero times on the benign corpus, passes the&lt;/span&gt;
&lt;span class="c1"&gt;// golden test from the failing case, and clears the specificity floor —&lt;/span&gt;
&lt;span class="c1"&gt;// none of which the agent wrote.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's what makes autonomy defensible: a vetted fix can auto-promote &lt;strong&gt;with no human in the loop&lt;/strong&gt; — not because we trust the model, but because what stands between a generated fix and production isn't anyone's judgment, it's a sandbox it can't escape, a battery of adversarial tests it didn't write, a canary that bounds blast radius, and a switch that undoes it. This is the same thesis behind &lt;a href="https://mailkite.dev/blog/agent-inbox-security-by-design/" rel="noopener noreferrer"&gt;how we built our agent inbox&lt;/a&gt;: in the agentic era you stop trying to make the model un-foolable and instead bound what a fooled model is &lt;em&gt;allowed to do&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where else this pattern fits
&lt;/h2&gt;

&lt;p&gt;MIME is a vivid example because email is gloriously broken, but the pattern fits anywhere software meets messy, adversarial, or drifting real-world input. The same five moves — tolerant core, plugin seam, anonymous failure signature, cold/hot loops, gated sandbox — map cleanly onto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ingesting messy formats.&lt;/strong&gt; CSV and bank-statement imports, PDF/OCR extraction, HTML scraping, log parsing, address and phone normalization. Every one is a hostile-input boundary that today either throws or silently corrupts. Signature the failure, let an agent add a scoped normalizer, gate it on a golden corpus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party API and webhook adapters.&lt;/strong&gt; Upstream payloads drift or go malformed and your integration breaks in prod. An adapter that emits a &lt;em&gt;schema-drift signature&lt;/em&gt; instead of a 500 lets an agent write a narrow shim for that provider's quirk — sandboxed, canaried — while a core fix follows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data pipelines / ETL schema drift.&lt;/strong&gt; An upstream column gets renamed or a type changes; the pipeline emits a signature rather than poisoning the warehouse, and an agent proposes the mapping behind tests that must stay green on the historical data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abuse, spam, and fraud rules.&lt;/strong&gt; A new evasion pattern is exactly a new failure signature. An agent generates a candidate rule that must fire zero times against a known-good corpus before it's canaried — the benign-corpus gate is the whole safety story, and it's identical to the parser's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client and device compatibility shims.&lt;/strong&gt; Quirky browsers, email clients, IoT firmware, legacy POS terminals — each non-conforming client is a per-quirk plugin, added on demand, contained, and kill-switchable, instead of a growing tangle of &lt;code&gt;if (userAgent...)&lt;/code&gt; in the core.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In each case the expensive, human-shaped part — noticing, reproducing, scoping, testing — is what the pattern automates, and the sandbox-plus-gates is what makes automating it safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's live today vs. what's next
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tolerant core (never throws, typed diagnostics)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Additive plugin seam (registry, contained fixes)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PII-free failure signatures (deterministic, deduping)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-language parity (golden corpus + signature pinning)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shadow harness (observe-only, structure-only compare)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold loop (opt-in, anonymous, deduplicated GitHub issues)&lt;/td&gt;
&lt;td&gt;✅ Live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot loop (AI-generated fixes)&lt;/td&gt;
&lt;td&gt;🔧 Designed, next&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wasm sandbox + capability/fuel limits&lt;/td&gt;
&lt;td&gt;🔧 Designed, next&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adversarial gates, canary rollout, per-fix kill switch&lt;/td&gt;
&lt;td&gt;🔧 Designed, next&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What does "self-healing software" actually mean here today?&lt;/strong&gt;&lt;br&gt;
Today: the system never dies on bad input, it records a precise PII-free signature of what broke, and identical signatures across all installs collapse into one deduplicated GitHub issue that drives a fix shipped to everyone. The fully autonomous part — an agent generating and shipping a sandboxed fix with no human in the loop — is designed and coming next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isn't letting an AI agent patch production reckless?&lt;/strong&gt;&lt;br&gt;
It would be if you trusted the agent's output. The design doesn't: generated fixes run as capability-denied Wasm with a fuel budget, are admitted only by adversarial tests the agent didn't write (benign-corpus zero-fire, a golden test from the failing case, a specificity floor), are canaried, and are individually kill-switchable. You trust the gates and the isolation, not the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does any of this send my data anywhere?&lt;/strong&gt;&lt;br&gt;
No. Reporting is opt-in with no default phone-home, and the failure signature is structural only — codes, types, byte-shape, mailer family — never bytes, addresses, or subjects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I apply the pattern without a MIME parser?&lt;/strong&gt;&lt;br&gt;
Yes — that's the point. Any boundary where you eat messy real-world input (imports, scrapers, API adapters, ETL, abuse rules, compatibility shims) can adopt the same five moves: tolerant core, plugin seam, anonymous failure signature, cold/hot loops, and a gated sandbox for generated fixes.&lt;/p&gt;




&lt;p&gt;Software will always meet a new way the world is wrong; the agentic era is a chance to make each new way a one-time event instead of a permanent scar. &lt;code&gt;mail-parse&lt;/code&gt; is our open-source instance of the pattern, in TypeScript, Python, and Go — see the &lt;a href="https://mailkite.dev/docs/libraries" rel="noopener noreferrer"&gt;libraries&lt;/a&gt;, and if you'd rather get the parsed message without running any of it, &lt;a href="https://mailkite.dev/docs/quickstart" rel="noopener noreferrer"&gt;point a domain at MailKite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part two comes after the autonomous loop ships.&lt;/strong&gt; Everything labeled &lt;em&gt;in progress&lt;/em&gt; above — the AI hot loop, the Wasm sandbox, the adversarial gates and canary rollout — gets its own post once it's live and we've watched it heal real input. And that feedback is the whole point: it arrives only through the &lt;strong&gt;anonymous, opt-in&lt;/strong&gt; failure signal described above — structural, PII-free, and never sent unless you wire up a reporter — so part two will be written from what actually broke in the wild, not from a single byte of anyone's data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was first published &lt;a href="https://mailkite.dev/blog/self-healing-software-agentic-era/" rel="noopener noreferrer"&gt;on the MailKite blog&lt;/a&gt;. Related: &lt;a href="https://mailkite.dev/blog/agent-inbox-security-by-design/" rel="noopener noreferrer"&gt;You can't prompt your way out of prompt injection&lt;/a&gt; applies the same "trust the architecture, not the model" philosophy to AI agents with email.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>security</category>
    </item>
  </channel>
</rss>
