<?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: Generalist Dev</title>
    <description>The latest articles on DEV Community by Generalist Dev (@generalist_dev).</description>
    <link>https://dev.to/generalist_dev</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%2F4070173%2F6e4dacec-07fd-4021-aee6-9dd7f3820de0.png</url>
      <title>DEV Community: Generalist Dev</title>
      <link>https://dev.to/generalist_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/generalist_dev"/>
    <language>en</language>
    <item>
      <title>The best harness is the one you can't see</title>
      <dc:creator>Generalist Dev</dc:creator>
      <pubDate>Sun, 23 Aug 2026 07:56:45 +0000</pubDate>
      <link>https://dev.to/generalist_dev/the-best-harness-is-the-one-you-cant-see-4g3c</link>
      <guid>https://dev.to/generalist_dev/the-best-harness-is-the-one-you-cant-see-4g3c</guid>
      <description>&lt;p&gt;Agent tooling keeps trying to give me a better seat in front of the factory. I don't want a better seat. I want no seat.&lt;/p&gt;

&lt;p&gt;Six tools keep showing up in my feed, in three shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;firstmate (&lt;a href="https://github.com/kunchenguid/firstmate" rel="noopener noreferrer"&gt;https://github.com/kunchenguid/firstmate&lt;/a&gt;) and HumanLayer (&lt;a href="https://humanlayer.dev" rel="noopener noreferrer"&gt;https://humanlayer.dev&lt;/a&gt;) put a model in the coordinator seat. You talk to one surface; it fans work out to agents.&lt;/li&gt;
&lt;li&gt;herdr (&lt;a href="https://herdr.dev" rel="noopener noreferrer"&gt;https://herdr.dev&lt;/a&gt;) and cmux (&lt;a href="https://cmux.com" rel="noopener noreferrer"&gt;https://cmux.com&lt;/a&gt;) orchestrate nothing. They own the terminals and tell you which pane is working, blocked, idle, or done.&lt;/li&gt;
&lt;li&gt;OpenHands (&lt;a href="https://openhands.dev" rel="noopener noreferrer"&gt;https://openhands.dev&lt;/a&gt;) and Factory (&lt;a href="https://factory.ai" rel="noopener noreferrer"&gt;https://factory.ai&lt;/a&gt;) are closest to ticket in, PR out. Work can start from GitHub, Linear, Slack, or a headless command instead of an open terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different products, same tell: each explains what the human should look at while the agents run. The orchestrator gives you a pane to talk to. The multiplexer gives you better panes to watch. The factory gives you a ticket, then a run history or control center.&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%2Fwww.generalist.dev%2Fimages%2Finvisible-harness%2Fthree-shapes.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%2Fwww.generalist.dev%2Fimages%2Finvisible-harness%2Fthree-shapes.png" title="fig 1 · three shapes, one attention budget" alt="Three shapes of agent tooling, all fed by the same human attention: orchestrators (firstmate, HumanLayer) give you a pane to talk to; multiplexers (herdr, cmux) give you better panes to watch; ticket-in-PR-out tools (OpenHands, Factory) give you the ticket, then a web UI" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interface keeps changing. The human attention budget does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Orchestration is a commodity
&lt;/h2&gt;

&lt;p&gt;Part of my skepticism about orchestrators is how little machinery it takes to get one working. A coding agent already knows how to delegate. Add a stop condition and isolated worktrees and you have the interesting half:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After every turn, evaluate the goal against the transcript.&lt;/span&gt;
/goal every issue &lt;span class="k"&gt;in &lt;/span&gt;docs/issues/ has a merged PR, or stop after 40 turns

&lt;span class="c"&gt;# Fan each issue into an isolated worker, then review the result separately.&lt;/span&gt;
ultracode: &lt;span class="k"&gt;for &lt;/span&gt;each file &lt;span class="k"&gt;in &lt;/span&gt;docs/issues/, implement &lt;span class="k"&gt;in &lt;/span&gt;a worktree, run
make verify, open a PR, and assign a separate reviewer agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've shipped MVPs with roughly this shape: an orchestrator grinding until the output was legible enough for me to inspect. People build cleverer versions: two sessions pointed at each other, one running Matt Pocock's grill-with-docs (&lt;a href="https://github.com/mattpocock/skills" rel="noopener noreferrer"&gt;https://github.com/mattpocock/skills&lt;/a&gt;) as the interviewer and the other playing founder until the ADRs are written.&lt;/p&gt;

&lt;p&gt;Fun to watch. Also the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things wrong with a model in the driver's seat
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It is prompt-shaped.&lt;/strong&gt; An LLM orchestrator's behavior depends on the initial prompt, the context it found, and whichever instructions happened to load. Hand one feature to two agents and you can get two codebases, not two implementations of one design. Adding a model that normalizes the requirement first moves the prompt one layer up. It does not turn the prompt into policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has gravity.&lt;/strong&gt; The orchestrator lives in a terminal, so the terminal stays open, so you look at it. Blue rings, unread badges, and blocked/idle tags make the looking more efficient. They do not make it comprehension.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.generalist.dev/blog/after-the-loop" rel="noopener noreferrer"&gt;part 1&lt;/a&gt; I called comprehension a budget: stop at a checkpoint, trace a critical path, and explain the change. Scrollback is the opposite. It spends attention continuously and builds no reliable model of the system. The factory that burned me had a green suite and a terminal I watched for 18 epics. The watching caught nothing.&lt;/p&gt;

&lt;p&gt;My bet is that model capability has flattened enough that the harness is now the important variable. Once a model can run for hours, trust has to come from the structure around the run, not from your ability to supervise its transcript.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the SDLC already knew
&lt;/h2&gt;

&lt;p&gt;Strip the AI out and software teams already look like a harness for non-deterministic workers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A requirement is written and split into PR-sized issues.&lt;/li&gt;
&lt;li&gt;Dependencies decide what can start.&lt;/li&gt;
&lt;li&gt;Developers implement without anyone watching them type.&lt;/li&gt;
&lt;li&gt;CI, review rules, and a definition of done decide what can land.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The useful property is not that every step is deterministic. People are not deterministic either. The useful property is that authority is explicit: the ticket owns intent, the dependency graph owns order, the worktree isolates implementation, and the gates own admission.&lt;/p&gt;

&lt;p&gt;As a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S : requirement → [issue]         split into PR-sized units
G : [issue]     → dag(issue)      encode dependencies
I : issue       → diff            implement in isolation
V : diff        → diff | reject   verify with independent evidence

H(x) = fold(merge, map(V ∘ I, G(S(x))))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt; carry the highest-leverage human judgment. A vague requirement or bad split poisons the graph before a model writes a line. &lt;code&gt;G&lt;/code&gt;, &lt;code&gt;I&lt;/code&gt;, and the shape of &lt;code&gt;V&lt;/code&gt; can be policy even when the workers inside them are probabilistic. &lt;code&gt;V&lt;/code&gt; is where the oracle, mutation gate, and scheduled comprehension brake from part 1 belong.&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%2Fwww.generalist.dev%2Fimages%2Finvisible-harness%2Fharness-fn.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%2Fwww.generalist.dev%2Fimages%2Finvisible-harness%2Fharness-fn.png" title="fig 2 · H(x): humans at both ends, no window in between" alt="The harness as a pipeline: a human writes the requirement and splits it; inside a dashed no-window region, G builds a DAG of issues, I implements each in a worktree, V gates it with a scheduled comprehension brake and a reject-retry loop, fold merges; out the other side comes a PR that passed V or an escalation with a reason" width="799" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The harness should have a contract, not a cockpit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requirement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/requirements/refunds.md&lt;/span&gt;
  &lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/issues/&lt;/span&gt;
&lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make verify&lt;/span&gt;
  &lt;span class="na"&gt;oracle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;testdata/refunds.json&lt;/span&gt;
  &lt;span class="na"&gt;mutation_threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;
&lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;verified_pr&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;escalation_with_reason&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Vanishing as the feature
&lt;/h2&gt;

&lt;p&gt;CI is the obvious comparison. Nobody tails a runner because it might be lonely. You see a check, or a failure with a link. CI became infrastructure when it stopped asking for ambient attention.&lt;/p&gt;

&lt;p&gt;Graded on that axis:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;S, G&lt;/th&gt;
&lt;th&gt;I&lt;/th&gt;
&lt;th&gt;V&lt;/th&gt;
&lt;th&gt;what you look at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;firstmate&lt;/td&gt;
&lt;td&gt;coordinator, per prompt&lt;/td&gt;
&lt;td&gt;agents in worktrees&lt;/td&gt;
&lt;td&gt;crew policy&lt;/td&gt;
&lt;td&gt;tmux or another visible backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HumanLayer&lt;/td&gt;
&lt;td&gt;human, at plan altitude&lt;/td&gt;
&lt;td&gt;sessions under a daemon&lt;/td&gt;
&lt;td&gt;workflow-defined checks&lt;/td&gt;
&lt;td&gt;tasks, artifacts, session UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;herdr&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;whatever you run&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;panes with state tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cmux&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;whatever you run&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;panes with notification rings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenHands&lt;/td&gt;
&lt;td&gt;external trigger, usually one task&lt;/td&gt;
&lt;td&gt;one agent run&lt;/td&gt;
&lt;td&gt;workflow-defined checks&lt;/td&gt;
&lt;td&gt;conversation or control surface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factory&lt;/td&gt;
&lt;td&gt;external trigger or mission&lt;/td&gt;
&lt;td&gt;Droid sessions&lt;/td&gt;
&lt;td&gt;workflow-defined checks&lt;/td&gt;
&lt;td&gt;ticket, app, or run history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;firstmate's watcher and herdr's detachable daemon reduce the babysitting, then both preserve the pane. HumanLayer detaches further, since agents keep running away from the laptop, while making it easy to jump back into sessions. Those are useful tools. They still treat the run as the product surface.&lt;/p&gt;

&lt;p&gt;OpenHands and Factory get closer to the interface I want: work starts where the requirement already lives, and execution can be headless. But hiding &lt;code&gt;I&lt;/code&gt; is only one third of the problem. If I still split every requirement by hand, encode no dependency graph, and let the implementation grade its own tests, then &lt;code&gt;S&lt;/code&gt;, &lt;code&gt;G&lt;/code&gt;, and &lt;code&gt;V&lt;/code&gt; are still mine. The agent run vanished; the harness did not.&lt;/p&gt;

&lt;p&gt;Invisible must not mean opaque. CI leaves logs, artifacts, exact SHAs, and failed checks. An invisible agent harness should leave the same kind of evidence: which requirement produced which issues, which SHA each gate examined, which oracle disagreed, which mutant survived, and why the run escalated. I should be able to audit it without having to watch it happen.&lt;/p&gt;

&lt;p&gt;The harness I want has no pane in its steady state. Input is a requirement and an accepted split. Output is a PR that passed &lt;code&gt;V&lt;/code&gt;, or an escalation with a reason and the evidence needed to act. Between those events, it should consume compute, not attention.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>It's not loops, graphs, or DAGs. It's what comes after</title>
      <dc:creator>Generalist Dev</dc:creator>
      <pubDate>Sun, 09 Aug 2026 21:42:27 +0000</pubDate>
      <link>https://dev.to/generalist_dev/its-not-loops-graphs-or-dags-its-what-comes-after-4elj</link>
      <guid>https://dev.to/generalist_dev/its-not-loops-graphs-or-dags-its-what-comes-after-4elj</guid>
      <description>&lt;p&gt;&lt;a href="https://x.com/steipete" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Floops.webp" title="@steipete · Jun 7, 2026" alt="Steinberger: you shouldn't be prompting coding agents anymore, you should be designing loops that prompt your agents" width="800" height="349"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://x.com/steipete" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Floops-graphs.webp" title="@steipete · Jul 18, 2026" alt="Steinberger: are we still talking loops or did we shift to graphs yet?" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the discourse in two screenshots, six weeks apart. First we prompted agents. Then we designed loops that prompt agents. Then loops were apparently over and it was graphs. Somewhere in that quarter, "loop engineering" got its own explainers, its own taxonomy, and its own funeral.&lt;/p&gt;

&lt;p&gt;And it's not one guy's timeline. Boris Cherny, who leads Claude Code, says the same thing: "I don't prompt Claude anymore. I have loops running that prompt Claude." Addy Osmani wrote the field guide. The taxonomy posts landed right on schedule — a prompt controls one model response, a loop controls one agent, a graph controls an organization of agents. And the funeral was fast: Steinberger's "did we shift to graphs yet?" went up at 00:34 UTC on July 18; about four and a half hours later an X Article titled "Loop Engineering Is Dead. Enter Graph Engineering." was live, and same-day graph-engineering guides were already reading like settled practice. Receipts, clickable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://addyosmani.com/blog/loop-engineering/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiscourse-osmani.png" title="addyosmani.com · Loop Engineering · Jun 7, 2026" alt="Addy Osmani's " width="800" height="470"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.marktechpost.com/2026/07/29/prompt-engineering-vs-loop-engineering-vs-graph-engineering/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiscourse-taxonomy.png" title="marktechpost.com · Jul 29, 2026" alt="MarkTechPost: " width="680" height="265"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://smartscope.blog/en/blog/graph-engineering-loop-engineering-logic-review/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiscourse-obituary.png" title="smartscope.blog · the obituary, timestamped" alt="SmartScope's post-mortem: from a one-line question to an obituary in four and a half hours" width="799" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.explainx.ai/blog/graph-engineering-ai-agents-multi-agent-organizations-2026" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiscourse-graphs.png" title="explainx.ai · Jul 18, 2026 — same day" alt="explainx.ai: " width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ladder keeps extending: prompt, context, harness, loop, graph. Whatever rung you're standing on, someone is already selling the next one — and every rung lives on the same side of the diagram:&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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-ladder.png%3Fv%3D1" 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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-ladder.png%3Fv%3D1" title="fig 1 · the discourse ladder, all of it on the write path" alt="The discourse ladder: prompt, context, loop, graph engineering — all on the write path, ending at " width="1568" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been on this treadmill too, and this isn't a takedown. Specs did their job — a fine primitive. Loops and graphs are fine primitives too. But look at what every rung of the ladder has in common: it's all machinery for &lt;em&gt;producing&lt;/em&gt; code. Then I ran a real verification pass over the output of my own factory, and came out believing the execution topology is the least interesting part of the problem. Almost nobody is working on what happens after the code exists.&lt;/p&gt;
&lt;h2&gt;
  
  
  The factory worked
&lt;/h2&gt;

&lt;p&gt;I ran a lights-off factory on a real project: 18 epics, 99 merged PRs, 365 commits, ~30k lines of Go — 17k of them tests. The domain was a financial calculation engine, the unforgiving kind where a number wrong in one direction quietly accumulates risk. The specifics don't matter here; every claim below generalizes.&lt;/p&gt;

&lt;p&gt;The output looked like the work of a diligent team. Clean packages, conventional commits, documented invariants. Suite green, &lt;code&gt;go vet&lt;/code&gt; clean, race detector clean, every PR agent-reviewed before merge. By every signal the loop could see, this was done. The code even &lt;em&gt;reads&lt;/em&gt; well.&lt;/p&gt;

&lt;p&gt;That last part matters. This was not slop that escaped because the factory was obviously careless. It was coherent, idiomatic, reviewed software. The failure was more interesting: all of the quality signals were downstream of the same process. The agents produced the implementation, produced most of the tests, reviewed the relationship between the two, and then reported agreement. The factory was very good at proving that its outputs agreed with one another.&lt;/p&gt;
&lt;h2&gt;
  
  
  Then I tested it for real
&lt;/h2&gt;

&lt;p&gt;After the factory finished, I built a separate verification pass: an oracle transcribed by hand from the primary spec, a ~2,300-case corpus derived from that oracle, and a mutation campaign to test the tests.&lt;/p&gt;

&lt;p&gt;It found five classes of bugs, all pointing in the same dangerous direction — the engine confidently outputting numbers that were too small. Shipped as-is, that's the kind of wrong you discover at scale, in production, with money attached. My favorite fits in one character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-   mult := math.Max(optA.Leg.Mult, optB.Leg.Mult)
&lt;/span&gt;&lt;span class="gi"&gt;+   mult := math.Min(optA.Leg.Mult, optB.Leg.Mult)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrong side of a max/min decision. The catch: on the common inputs both sides give the same answer — and those were the only inputs the agent wrote tests for. The suite was green over the bug. Review saw plausible code. Every gate passed.&lt;/p&gt;

&lt;p&gt;This is not a domain-specific anecdote. Every codebase has a pair of paths that agree on the common case and diverge on the one that matters — rounding directions, timezone edges, inclusive/exclusive boundaries. An agent optimizing for green will find the test that passes, not the case that breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Green is only a local fact
&lt;/h2&gt;

&lt;p&gt;A passing test proves a narrow proposition: for the examples somebody chose, actual output matched expected output. It does not prove that the examples came from the spec, that the expected values were independently derived, or that the important branch was exercised. “The suite is green” sounds like a statement about the product. Usually it is only a statement about agreement inside the test harness.&lt;/p&gt;

&lt;p&gt;If implementation output is copied into expectations, the implementation and the suite can be perfectly consistent and jointly wrong. Add an AI review that asks whether the code and tests look plausible, and you do not have three independent signals. You have one assumption echoed three times.&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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-false-green.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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-false-green.png" title="fig 2 · four green signals can still be one source of truth" alt="Correlated evidence: implementation, implementation-derived tests, AI review, and a green suite all repeat one assumption. Independent evidence starts from the primary spec, builds an oracle without reading implementation output, and diffs the two paths." width="799" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The missing ingredient is not another reviewer in the same chain. It is a second path to the answer. A useful oracle can be wrong — my hand transcription certainly could have been — but disagreement between independently derived paths creates somewhere concrete to investigate. Agreement inside one path creates confidence without information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually scared me
&lt;/h2&gt;

&lt;p&gt;Not the bugs. I went to fix them and realized I couldn't — not quickly. I did not know my own codebase.&lt;/p&gt;

&lt;p&gt;Lights-off means lights off: I never read those 99 PRs. Agents wrote them, agents reviewed them, the loop merged them, and I read dashboards. That was the entire appeal — and it worked, right up until the first time something needed a human who could hold the design in their head. Debugging needs recall: where does this value get computed, what invariants does it assume, who else depends on it. I had none of it. The codebase isn't bad — it needs a handful of fixes — but the person who was supposed to make them was never in the room when it was written.&lt;/p&gt;

&lt;p&gt;That's comprehension debt. It compounds silently, and no linter measures 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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-comprehension-debt.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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-comprehension-debt.png" title="fig 3 · build → summary → incident: where comprehension debt appears" alt="Agents build a connected system, but the human receives only “99 PRs merged” and “suite green.” When a wrong output appears, the missing causal map must be reconstructed before debugging can begin." width="799" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comprehension debt is not missing documentation. The factory produced documentation. It is the gap between the system's causal structure and the operator's working model of it. A generated architecture page can tell me that package A imports package B; it cannot give me the practiced ability to predict which invariant breaks when a value changes, or which tests ought to fail. That model is built by tracing, explaining, and occasionally being wrong in public before production forces the lesson.&lt;/p&gt;

&lt;p&gt;The usual automation calculus counts the time no longer spent typing or reviewing. It rarely books the liability created when nobody acquires that model. Lights-off development did not remove the cost of understanding the system. It deferred it until the worst possible moment: when the system was already wrong and I was under pressure to change it safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the outer loop is missing
&lt;/h2&gt;

&lt;p&gt;Three things I'm now convinced belong in any lights-off setup, whatever its topology:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comprehension brakes.&lt;/strong&gt; The loop halts at checkpoints until the human can explain what changed — not approve it, &lt;em&gt;explain&lt;/em&gt; it. Pick a critical path and ask: where does the value enter, where is the authoritative decision made, what invariant must hold, who consumes the result, and which test would fail if the decision flipped? This does not mean reading every generated line. It means sampling until the operator can reconstruct the path without outsourcing the answer back to the factory. Comprehension is a budget you spend deliberately, and if you spend none, you've pre-committed to owning a codebase you can't debug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutation testing.&lt;/strong&gt; An old pattern — the papers are from the 70s — that answers the question agents made urgent: who tests the tests? Seed single-site bugs into the code and rerun the suite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// your code&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;positionLimit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ErrLimitExceeded&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// the mutant: one operator flipped&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;positionLimit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ErrLimitExceeded&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no test fails, the boundary was never really tested — the suite is green decoration. My campaign ran 220 mutants: 213 killed, 7 provably equivalent, a 100% kill rate on the ones that mattered. But that number only existed &lt;em&gt;after&lt;/em&gt; the oracle work. The factory's own suite had an unmeasured kill rate, which is exactly how a green suite shipped &lt;code&gt;max&lt;/code&gt; for &lt;code&gt;min&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mutation score is not a correctness proof. A campaign can only test the faults it knows how to seed, and equivalent mutants need human judgment. But it measures something ordinary coverage does not: whether the suite is sensitive to plausible mistakes in the decisions that matter. Line coverage tells you a test visited the &lt;code&gt;max&lt;/code&gt;. Mutation testing asks whether the test would notice if it became &lt;code&gt;min&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oracles.&lt;/strong&gt; Expected outputs must come from ground truth that is independent of the implementation — a hand transcription of the spec, a reference implementation, an invariant that must hold. Never from the code under test. The rule I now keep at the top of the corpus package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Every expected value in testdata is derived from an independent oracle —&lt;/span&gt;
&lt;span class="c"&gt;// a transcription of the primary spec — never from engine output.&lt;/span&gt;
&lt;span class="c"&gt;// Copying the engine's output into an expectation makes the case&lt;/span&gt;
&lt;span class="c"&gt;// self-grading, and a self-grading test is worthless.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an agent writes the code and its tests in the same loop iteration, the tests &lt;em&gt;are&lt;/em&gt; engine output. Self-grading, worthless. Independence is about derivation, not merely which model or person typed the file. Two agents reading the same implementation and copying the same behavior are not independent. One agent transcribing a normative table from the spec without seeing engine output is.&lt;/p&gt;

&lt;p&gt;Side by side, the two shapes:&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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-selfgrading.png%3Fv%3D1" 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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-selfgrading.png%3Fv%3D1" title="fig 4 · self-grading tests vs an independent oracle" alt="Self-grading: agent writes code, writes tests from that code, suite goes green, merge. Oracle-derived: spec transcribed independently, corpus of expected outputs, diffed against the implementation" width="1568" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These three controls are not interchangeable:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;Failure it exposes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Oracle&lt;/td&gt;
&lt;td&gt;Does the implementation agree with independently derived ground truth?&lt;/td&gt;
&lt;td&gt;Wrong product behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutation gate&lt;/td&gt;
&lt;td&gt;Would the suite notice a plausible logic error?&lt;/td&gt;
&lt;td&gt;Weak or decorative tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comprehension brake&lt;/td&gt;
&lt;td&gt;Can a human trace the decision and repair it safely?&lt;/td&gt;
&lt;td&gt;Operator knowledge debt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An oracle without mutation testing can be guarded by a suite too weak to localize regressions. Mutation testing without an oracle can make a self-grading suite exquisitely sensitive to the wrong behavior. Both without comprehension leave a verified system that its owner still cannot repair. The controls work because they fail differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape I'd bet on
&lt;/h2&gt;

&lt;p&gt;MVP → derive oracles from the primary source → development gated by mutation score → CI review that diffs against the oracle, with comprehension brakes between the gates:&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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-outerloop.png%3Fv%3D1" 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%2Fwww.generalist.dev%2Fimages%2Fafter-the-loop%2Fdiagram-outerloop.png%3Fv%3D1" title="fig 5 · the outer loop with verification gates" alt="The outer loop: MVP, derive oracles, agents develop, mutation gate loops survivors back, comprehension brake halts until a human can explain the change, CI review diffs against the oracle, merge" width="1568" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not every package needs a 2,300-case oracle or a perfect mutation score. Start where being wrong is expensive: calculations, permissions, state transitions, money movement, destructive operations. Freeze a small independent corpus. Mutate the decisions on those paths. At each milestone, make the operator trace one of them end to end. Expand the verified surface when production risk justifies it.&lt;/p&gt;

&lt;p&gt;That is a less glamorous pitch than “design a graph of agents.” It also gives the graph something objective to optimize against. The topology can change every quarter. The authority boundary should not: agents may propose and produce; independently derived evidence decides whether the result lands.&lt;/p&gt;

&lt;p&gt;And note the last step honestly: an AI reviewer without an oracle is just a second opinion with the same blind spot — mine reviewed the &lt;code&gt;max&lt;/code&gt;/&lt;code&gt;min&lt;/code&gt; bug and saw plausible code.&lt;/p&gt;

&lt;p&gt;My draft of this post ended "there are no exact metrics AI-driven development can base itself on." Half wrong. One useful metric already exists and is fifty years old: mutation kill rate. An agent can make it go up, but only by adding tests that notice more seeded faults. That is a much harder number to flatter than lines changed, PRs merged, test count, or coverage.&lt;/p&gt;

&lt;p&gt;Comprehension debt still has no gauge. For now the brake is stubbornly human: stop, point at a critical path, and explain it. Until that becomes part of the factory, loops versus graphs is arguing about the delivery truck while nobody checks the cargo — or knows how to open it when it starts leaking.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
