<?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: Reporails</title>
    <description>The latest articles on DEV Community by Reporails (reporails).</description>
    <link>https://dev.to/reporails</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%2Forganization%2Fprofile_image%2F12470%2Feb6c7166-21ab-442d-a75f-7ec3c525f1cd.png</url>
      <title>DEV Community: Reporails</title>
      <link>https://dev.to/reporails</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reporails"/>
    <language>en</language>
    <item>
      <title>Loop Engineering: Fine-Tuning the Guardrail That Fired Wrong</title>
      <dc:creator> Gábor Mészáros</dc:creator>
      <pubDate>Tue, 14 Jul 2026 11:28:12 +0000</pubDate>
      <link>https://dev.to/reporails/loop-engineering-fine-tuning-the-guardrail-that-fired-wrong-3cbc</link>
      <guid>https://dev.to/reporails/loop-engineering-fine-tuning-the-guardrail-that-fired-wrong-3cbc</guid>
      <description>&lt;p&gt;The check had been green for a week. It greps every diff under &lt;code&gt;src/&lt;/code&gt; for &lt;code&gt;import mock&lt;/code&gt;, because production code has no business importing a mock library. Then it went red on &lt;code&gt;src/payments.py&lt;/code&gt;, a file with no mock import anywhere in it. The word &lt;code&gt;mock&lt;/code&gt; was sitting in a docstring, in a sentence telling the next developer not to import one. The grep matched the sentence. The quickest way to clear that red is to delete the check.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://reporails.com/articles/prompt-engineering-context-engineering-loop-engineering-what-actually-changed" rel="noopener noreferrer"&gt;series opener&lt;/a&gt; named the loop that runs generate, check, steer, retry, stop, and put the hard part on the check that decides &lt;em&gt;good enough, stop&lt;/em&gt;. Last week's piece built that check as &lt;code&gt;no-mocks.sh&lt;/code&gt; and set a gate beside it, &lt;a href="https://reporails.com/articles/deterministic-guardrails-prompts-steer-hooks-enforce" rel="noopener noreferrer"&gt;a hook that refuses a bad write instead of catching it after the fact&lt;/a&gt;. This one stays with the check and asks what happens when it is wrong: how to tell a broken instrument from a signal that was never there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a check is here
&lt;/h2&gt;

&lt;p&gt;A check is a function from the agent's output to a verdict: pass or fail. In the loop it is the thing that decides whether an iteration is acceptable and whether to run another. A check comes in two kinds. A deterministic check runs the code or scans the text and returns the same verdict every time: &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;pytest&lt;/code&gt;, an exit status. A model-graded check asks another model &lt;em&gt;is this good?&lt;/em&gt; and reaches criteria the first cannot express, at the cost of the same unreliability the loop was built to contain.&lt;/p&gt;

&lt;p&gt;This piece is about the deterministic kind, because it is the kind you can hold in your hand and debug. When a deterministic check gives you a verdict you disagree with, the verdict is wrong in a way you can inspect line by line. That is exactly the property the argument below leans on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two readings of the same quiet
&lt;/h2&gt;

&lt;p&gt;A check that comes back quiet is telling you one of two things, and it does not tell you which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Absent signal.&lt;/strong&gt; The thing the check guards against genuinely is not there. This is the good news you wanted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken instrument.&lt;/strong&gt; The thing is there, or would be there, but the check cannot see it. The guard is off and the diff looks the same as if it were on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A false alarm, the check firing on a clean file, is the &lt;em&gt;visible&lt;/em&gt; failure. It is annoying, but it announces itself; you know the instrument is off because it is screaming at nothing. The dangerous failure is the opposite one, and it is silent: a check weakened until it stops seeing real violations returns the same green as a check that correctly found nothing. You cannot feel the difference between "safe" and "blind" from the verdict alone.&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%2Fcilrvtcvi8xcykzqr6bf.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%2Fcilrvtcvi8xcykzqr6bf.png" alt="Two identical green pass panels side by side, one labeled absent signal (safe) and one labeled broken instrument (blind), showing the same verdict can mean two different facts." width="799" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the misfire is really about the check, not the file it landed on, and it sets a trap worth naming early. A red mark on a file you know is clean reads as a reason to stop trusting the check, and it tends to show up on the very run where that same check is catching something real elsewhere. Look at what the pattern actually matched before you act on the verdict it returned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The misfire, up close
&lt;/h2&gt;

&lt;p&gt;Make it concrete with the check itself. The version that misfires is the naive one, a bare substring match, before the &lt;code&gt;^&lt;/code&gt; anchor last week's piece put on the pattern:&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="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s1"&gt;'import mock'&lt;/span&gt; src/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a week it did its job. Then you added &lt;code&gt;src/payments.py&lt;/code&gt;, whose docstring happens to document the rule the check enforces:&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Charge processing. Do not import mock here, use the fakes in tests/.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no mock import in that file. Run the check and it fires anyway, alongside a real leftover it correctly caught in &lt;code&gt;src/badcache.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash no-mocks.sh
&lt;span class="go"&gt;src/payments.py:1:"""Charge processing. Do not import mock here, use the fakes in tests/."""
&lt;/span&gt;&lt;span class="gp"&gt;src/badcache.py:1:import mock   #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;leftover from a spike
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;badcache.py&lt;/code&gt; line is the catch, an &lt;code&gt;import mock&lt;/code&gt; that has been sitting in production code. The &lt;code&gt;payments.py&lt;/code&gt; line is the misfire: that file is clean, and the check flagged it because &lt;code&gt;import mock&lt;/code&gt; appears as a substring inside prose documenting the very rule the check enforces.&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%2F1m96n8huk4lnz3x0tl67.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%2F1m96n8huk4lnz3x0tl67.png" alt="A terminal card showing two grep matches for 'import mock', one in a docstring tagged false alarm in red and one a real import tagged real violation in green." width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The tempting fix, and what it costs
&lt;/h2&gt;

&lt;p&gt;You have a red check on a clean file. The path of least resistance has three forms, and all three are the same move:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delete &lt;code&gt;no-mocks.sh&lt;/code&gt;. The red goes away.&lt;/li&gt;
&lt;li&gt;Weaken it: add &lt;code&gt;--exclude=payments.py&lt;/code&gt;, or only run it on Tuesdays, or drop it to a warning nobody reads.&lt;/li&gt;
&lt;li&gt;Conclude the check was never worth much: "grep is dumb, mock imports are rare anyway, this is more trouble than it catches."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of these makes the noise stop, and stopping the noise feels like fixing the problem. Re-read the output. The same run that misfired on &lt;code&gt;payments.py&lt;/code&gt; also, correctly, caught the real &lt;code&gt;import mock&lt;/code&gt; in &lt;code&gt;badcache.py&lt;/code&gt;. Delete or defang the check and that catch disappears with the false alarm. The next real mock import to land in &lt;code&gt;src/&lt;/code&gt; sails through, and the suite stays green, because green is now what this check returns for everything.&lt;/p&gt;

&lt;p&gt;That is the inversion. The misfire tempts you to conclude the signal is not worth watching, at the exact moment the same check proved the signal is real. You would be reasoning about the world from the failure of your instrument to see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix the instrument instead
&lt;/h2&gt;

&lt;p&gt;The misfire told you something specific: the pattern matched prose, not code. So make the pattern match code. That is the one-character change last week's piece already carried, the &lt;code&gt;^&lt;/code&gt; that anchors the pattern to the start of a line, where an &lt;code&gt;import&lt;/code&gt; statement lives and a docstring sentence does not. Add it and &lt;code&gt;payments.py&lt;/code&gt; goes quiet while &lt;code&gt;badcache.py&lt;/code&gt; still fails.&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%2F9ryicpdxbr55cllodrj9.gif" 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%2F9ryicpdxbr55cllodrj9.gif" alt="A terminal running no-mocks.sh: it first catches a real import mock in red, then also flags a clean docstring file in red, and after a one-line caret anchor is added to the pattern it comes back clean on the docstring file while still catching the real import." width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both make the red on &lt;code&gt;payments.py&lt;/code&gt; disappear, and only one keeps catching &lt;code&gt;badcache.py&lt;/code&gt;. Deleting the check clears the false alarm by throwing away the real catch with it; anchoring the pattern clears the false alarm and leaves the real catch standing.&lt;/p&gt;

&lt;p&gt;I run the same shape of check on my own agent's replies. A deterministic scan looks for a short list of banned phrases: a placation opener, or an unbacked &lt;code&gt;the work is verified&lt;/code&gt; when nothing was actually run. One day it fired on a reply that was clean. The banned phrase was sitting inside a quotation of the rule that bans it, and inside a backticked identifier the agent was naming rather than using. The check had matched the words, not their use, the same way the grep matched the docstring. Loosening the phrase list until that alarm stopped would have let the next real unbacked claim through, so I scoped the matcher instead. It blanks the cited spans, the backtick spans and quoted strings and negated clauses, before the scan runs, so an empty &lt;code&gt;the work is verified&lt;/code&gt; still fires while a citation of the rule goes quiet.&lt;/p&gt;

&lt;p&gt;The anchored check still is not a perfect mock detector. An aliased &lt;code&gt;import unittest.mock as m&lt;/code&gt; is a different pattern, and an indented import inside a function slips past a start-of-line anchor. A real linter closes both without any of this hand-rolling: &lt;code&gt;flake8-tidy-imports&lt;/code&gt; carries a banned-modules list, and ruff's &lt;code&gt;F401&lt;/code&gt; flags imports the code never actually uses. If mock-in-production is the exact thing you are guarding, reach for one of those. The grep still earns its place as the first thing you write, because it is the cheapest deterministic check a team reaches for in a hook, one line in a script that already runs. And the lesson holds after you upgrade the instrument, because a linter rule is a pattern too. &lt;code&gt;F401&lt;/code&gt; matches what looks like an unused import, not the fact of use, and it has edge cases where it reads the text wrong. Every one of those is a calibration reading you use to sharpen the rule, never a reason to stop measuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule that generalizes
&lt;/h2&gt;

&lt;p&gt;A misfire is data about the instrument, never a verdict about the world. When a deterministic check fires on something you know is clean, the finding is "my check matches too much," not "the thing I was checking for does not exist."&lt;/p&gt;

&lt;p&gt;The asymmetry is what makes the discipline worth holding. A false alarm is loud and self-correcting: it interrupts you until you deal with it, and it costs you the few minutes the fix takes. A deleted or defanged check fails the other way. It goes quiet and stays quiet: no output, no red, no interruption, just a signal nobody is watching anymore, indistinguishable from a signal that was never there. Every real violation that lands afterward passes clean, and nothing on screen tells you the guard is gone.&lt;/p&gt;

&lt;p&gt;This was one of the questions the &lt;a href="https://reporails.com/articles/prompt-engineering-context-engineering-loop-engineering-what-actually-changed" rel="noopener noreferrer"&gt;series opener&lt;/a&gt; left open: how to tell a broken instrument from an absent one. Its sibling piece took another, &lt;a href="https://reporails.com/articles/deterministic-guardrails-prompts-steer-hooks-enforce" rel="noopener noreferrer"&gt;the gate that refuses a bad write instead of catching it a beat too late&lt;/a&gt;, and the line between a rule that observes and one that refuses. The question underneath both is still open. Every check and every gate is fed by rules the agent carries as loaded text, paid for on every turn whether they apply that turn or not. What an instruction costs to keep loaded, and what changes when you load it only where it applies, is where the series goes next.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on &lt;a href="https://reporails.com" rel="noopener noreferrer"&gt;Reporails&lt;/a&gt;, deterministic diagnostics for the instruction files, rules, and prompts that steer coding agents. It reads the steering surface and tells you, with measured evidence, where it drifts. The checks here are the same idea pointed at a diff: what a rule actually matches, held up against what you meant it to match.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>loopengineering</category>
      <category>ai</category>
      <category>tooling</category>
      <category>claude</category>
    </item>
    <item>
      <title>Deterministic Guardrails: Prompts Steer, Hooks Enforce</title>
      <dc:creator> Gábor Mészáros</dc:creator>
      <pubDate>Thu, 09 Jul 2026 20:35:46 +0000</pubDate>
      <link>https://dev.to/reporails/deterministic-guardrails-prompts-steer-hooks-enforce-14ag</link>
      <guid>https://dev.to/reporails/deterministic-guardrails-prompts-steer-hooks-enforce-14ag</guid>
      <description>&lt;p&gt;A loop that refactors code until a check passes is a few lines of shell. Most guardrails beside such a loop only watch what an agent already wrote; the one worth building refuses a bad edit before it lands, and that is what this piece constructs.&lt;/p&gt;

&lt;p&gt;The rule it enforces: production code under &lt;code&gt;src/&lt;/code&gt; must not import a mock library. That rule can live in two places. Put it in the prompt, &lt;code&gt;do not import mock&lt;/code&gt;, and it is clear and still only a request: text loaded into a model is a lever on probability, not a switch, so it leaves the model free to write the import anyway. Put it in a gate that fires before a write lands, and it refuses the edit outright. Both state the same rule; only one can make it hold every time.&lt;/p&gt;

&lt;p&gt;The ask channel has a ceiling: a prompt pushes the odds up, never to one. How close a prompt gets to that ceiling is measurable work. Reporails reads the asking channel and shows you, with measured evidence, which instructions pull the lever and which are text the model is free to ignore, so you can write the ask against evidence instead of on faith. Even a well-measured prompt tops out below certainty, because the model underneath stays probabilistic. Closing that last gap is what the gate is for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop, and where it can only ask
&lt;/h2&gt;

&lt;p&gt;This is the second component of the loop taken on its own: the gate. The &lt;a href="https://reporails.com/articles/prompt-engineering-context-engineering-loop-engineering-what-actually-changed" rel="noopener noreferrer"&gt;series opener&lt;/a&gt; named the loop (generate, check, steer, retry, stop) and posed three questions about the components it turns on. The first is the check, the arm that decides good enough, stop. This piece takes the second: the rules that can refuse a diff, and the difference between a channel that can ask and a channel that can say no.&lt;/p&gt;

&lt;p&gt;Here is the loop the series is dissecting. It refactors &lt;code&gt;src/&lt;/code&gt; until a guard holds. The agent never decides it is done; the guard does.&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# work-until-checked: refactor src/ until the guard holds.&lt;/span&gt;
&lt;span class="c"&gt;# The steering rule lives in the agent's own instructions (CLAUDE.md),&lt;/span&gt;
&lt;span class="c"&gt;# stated once; the loop feeds back only what changed, the guard's output.&lt;/span&gt;
&lt;span class="nv"&gt;MAX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Remove every mock-library import from production code under src/."&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MAX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;run_agent &lt;span class="nt"&gt;--task&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$prompt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;                      &lt;span class="c"&gt;# GENERATE (rule already in context)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;bash no-mocks.sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;                       &lt;span class="c"&gt;# CHECK&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"stop: guard holds after &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; retries"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nv"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"The last attempt still tripped the guard; fix it:
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;bash no-mocks.sh 2&amp;amp;gt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;amp&lt;span class="p"&gt;;&lt;/span&gt;1&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;                            &lt;span class="c"&gt;# STEER: only the new signal&lt;/span&gt;
    &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;i &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"stop: budget exhausted, guard still red"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the arms one at a time. The steering rule lives once in the agent's own instructions, its &lt;code&gt;CLAUDE.md&lt;/code&gt;, so &lt;code&gt;run_agent --task "$prompt"&lt;/code&gt; never re-ships it. That first &lt;code&gt;$prompt&lt;/code&gt; is the ask, and the rule already in context is text the model reads as a lever on probability, not a switch: &lt;code&gt;Remove every mock-library import&lt;/code&gt; moves the odds that the next diff is clean without setting them to one. The STEER arm rewrites &lt;code&gt;$prompt&lt;/code&gt; to carry only what changed, the guard's latest output, and asks again against that. A tighter, more specific ask, but an ask.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;no-mocks.sh&lt;/code&gt; is the only arm that is not asking. It scans &lt;code&gt;src/&lt;/code&gt; after &lt;code&gt;run_agent&lt;/code&gt; returns, so it is a detector: it reads the diff the agent already wrote and returns a verdict on it. When the verdict is red, the loop retries. Nothing here stops the bad write from landing in the first place. The guard catches after the fact, and the loop re-rolls. Between the write and that catch, a forbidden import can sit in production code for a whole iteration before the detector sees it.&lt;/p&gt;

&lt;p&gt;For a refactor loop that converges, the window is usually harmless, because the next iteration overwrites it. But "usually harmless" is doing a lot of work, and the cases where it is not are exactly the constraints you cared enough to write down twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two channels: one asks, one refuses
&lt;/h2&gt;

&lt;p&gt;A prompt is the channel that can only ask. Everything on the context surface, the task, the agent's instructions, the system prompt, is an input to a probabilistic process. It shifts the distribution of what the model produces, which is why prompt and context engineering are real disciplines. It is also, by construction, unable to force any single outcome. The prompt asks, and any given iteration is free to ignore it.&lt;/p&gt;

&lt;p&gt;A gate is the channel that can refuse. It is a deterministic function that fires at a fixed point and returns a hard verdict the model does not get to route around. It does not shift odds; it either lets the diff through or it does not.&lt;/p&gt;

&lt;p&gt;The base loop has an asker (the task), a steerer (the retry), and a detector (the guard). It has no refuser. Nothing in it can stop a write before the write happens, so a loop that checks for exactly the thing you forbade can still let it land and catch it a beat too late. Adding the refuser is the rest of this piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the missing arm: a hook that refuses
&lt;/h2&gt;

&lt;p&gt;Claude Code fires hooks at named transitions in the harness: &lt;code&gt;SessionStart&lt;/code&gt;, &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;. A &lt;code&gt;PreToolUse&lt;/code&gt; hook runs before a tool call executes and can block it. That is the transition to key on, because it is the one point where you can refuse a write before it happens rather than detect it after. Here is where the gate sits in the lifecycle of a single write:&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%2F4rutlt3aaarblvxl507n.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%2F4rutlt3aaarblvxl507n.png" alt="pretool workflow" width="798" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The gate is the diamond, and everything downstream of the &lt;code&gt;allow&lt;/code&gt; edge is the write reaching disk. Refuse there and the write never reaches the &lt;code&gt;Tool executes&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;Here is a hook that blocks any &lt;code&gt;Write&lt;/code&gt; or &lt;code&gt;Edit&lt;/code&gt; whose content adds a mock import under &lt;code&gt;src/&lt;/code&gt;. Save it as &lt;code&gt;.claude/hooks/block-mock-imports.sh&lt;/code&gt;:&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# block-mock-imports.sh: refuse any Write/Edit that adds a mock import to src/.&lt;/span&gt;
&lt;span class="c"&gt;# Runs as a Claude Code PreToolUse hook. Exit 2 blocks the tool call.&lt;/span&gt;
&lt;span class="nv"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# the tool call arrives as JSON on stdin&lt;/span&gt;
&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.file_path // ""'&lt;/span&gt; &amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.content // .tool_input.new_string // ""'&lt;/span&gt; &amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;src/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;   &lt;span class="c"&gt;# only guard production code&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'^(import mock|from mock|from unittest import mock)\b'&lt;/span&gt; &amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;lt&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"blocked: '&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="s2"&gt;' would add a mock import to production code. &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
Use a real dependency or a constructor-injected fake."&lt;/span&gt; &amp;amp;gt&lt;span class="p"&gt;;&lt;/span&gt;&amp;amp;amp&lt;span class="p"&gt;;&lt;/span&gt;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;2   &lt;span class="c"&gt;# exit 2 == hard block; the message on stderr goes back to the model&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire it up as a &lt;code&gt;PreToolUse&lt;/code&gt; hook matched to the write tools, in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".claude/hooks/block-mock-imports.sh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the agent tries to write &lt;code&gt;import mock&lt;/code&gt; into &lt;code&gt;src/badcache.py&lt;/code&gt;. The transition fires before the write:&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%2Fkn5j4ie842q29nbbjca8.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%2Fkn5j4ie842q29nbbjca8.png" alt="Transition block message" width="800" height="131"&gt;&lt;/a&gt;&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%2Fh465vmb6yjd7g5nztyp4.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%2Fh465vmb6yjd7g5nztyp4.png" alt="A tool-call card for Write(src/badcache.py) stamped 'Blocked by PreToolUse hook' with the hook's refusal message, and the file shown untouched." width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The write never happened. &lt;code&gt;src/badcache.py&lt;/code&gt; is untouched. The hook read the pending tool call, matched &lt;code&gt;^import mock&lt;/code&gt; in the content the agent was about to write, and returned exit code &lt;code&gt;2&lt;/code&gt;, which Claude Code treats as a block with the stderr message handed back to the model. The model sees the refusal in the same turn and has to try something else. There was no window, because there was no write to catch.&lt;/p&gt;

&lt;p&gt;Notice the pattern is anchored: &lt;code&gt;^import mock&lt;/code&gt;, not a bare substring. A docstring that merely names the rule does not start a line with an import, so it does not trip the hook. Anchoring matters more here than in a detector that only reports: a match keyed too broadly refuses clean work, and that carries higher stakes at a gate, which is the next section.&lt;/p&gt;

&lt;p&gt;Compare the hook to &lt;code&gt;no-mocks.sh&lt;/code&gt;. They match nearly the same rule. What differs is where they sit. The guard sits after the write and reports; the hook sits before the write and refuses. Same constraint, two channels, and only one of them closes the window between the write and the catch.&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%2F07nalfpxh6qa9a5ecf8o.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%2F07nalfpxh6qa9a5ecf8o.png" alt="Before/after of one iteration's timeline: top, prompt-only loop with a shaded exposure window where the forbidden import sits in src/ between write and detector catch; bottom, gate added, the write is refused before it lands and the window is gone." width="799" height="409"&gt;&lt;/a&gt;&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%2Fkk6u1l9qfjkw9aybaqgf.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%2Fkk6u1l9qfjkw9aybaqgf.png" alt="workflow explained" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Which rule goes in which channel
&lt;/h2&gt;

&lt;p&gt;Not every rule belongs in the refusing channel, and filing one in the wrong channel fails in a specific, quiet way in each direction.&lt;/p&gt;

&lt;p&gt;A must-hold constraint filed as mere steering gets ignored the one time it matters. The prompt asks, the model is free to decline, and on the iteration where it declines, the diff lands. A must-hold rule in an ask-only channel holds on every iteration except the one where the model declines, and that iteration is the case you filed it for.&lt;/p&gt;

&lt;p&gt;A rule that only needed to steer, but is hard-gated, causes friction and false blocks. This is the over-matching failure pointed at a gate instead of a detector, and it costs more here. A gate keyed too broadly refuses clean work: the developer who names the rule in a docstring, the test file that legitimately imports a mock, the production helper named &lt;code&gt;mock_response&lt;/code&gt; that a bare substring match trips on. A false alarm from a detector is noise you can ignore for one iteration. A false block from a gate is work that cannot proceed until someone fixes the gate. Refusal is load-bearing in the moment, so an over-broad refuser has a larger blast radius than an over-broad detector.&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%2Fpunrzx9yn5jso7uzm9ke.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%2Fpunrzx9yn5jso7uzm9ke.png" alt="A 2x2: must-hold rule as steering (silent miss, the diff landed) vs must-hold rule as gate (held); flexible rule as steering (fine) vs flexible rule as gate (false block on clean work)." width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The heuristic: gate what must hold every time and is cheaply, deterministically checkable; steer what needs judgment or flexibility. &lt;code&gt;no mock import in production code&lt;/code&gt; is a fixed, must-hold, one-line-of-grep constraint, so gate it. &lt;code&gt;prefer constructor injection over a service locator&lt;/code&gt; is a judgment call with real exceptions, so steer it and let a check observe rather than refuse. A criterion you cannot express as a deterministic match at a transition is a criterion you cannot gate; if it needs a model to judge it, it belongs in the asking channel, with all the probability that implies.&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%2Fzaqujsbroi7nsmm7uwno.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%2Fzaqujsbroi7nsmm7uwno.png" alt="Critertion top down workflow" width="736" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The generator's "done" is a claim to re-derive
&lt;/h2&gt;

&lt;p&gt;Underneath both channels is the posture the series keeps returning to. An agent's &lt;code&gt;done&lt;/code&gt; is a claim to re-derive, not a fact to relay. The loop already refuses to trust the generator's report that the work is finished; that is what the check is for. The gate extends the same distrust one step earlier, to the moment of the write. The agent believes its edit is fine. The gate does not accept the belief; it re-derives the verdict from the diff, deterministically, before the edit is allowed to exist. Steering is where you tell the generator what you want. Gating is where you stop taking its word for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What holds, and what it still costs to keep loaded
&lt;/h2&gt;

&lt;p&gt;With the hook wired in, the rule you needed to hold every time does. &lt;code&gt;do not import mock&lt;/code&gt; holds on every iteration now, on the ones you read and the ones you never open, because a &lt;code&gt;PreToolUse&lt;/code&gt; gate does not depend on your attention or the model's cooperation. That is what "hold every time" costs: a channel that can refuse. No amount of rewording the ask gets you there. The prompt still does its job, moving the odds toward a good diff, and the gate makes the one diff you cannot afford impossible. You want both, filed correctly.&lt;/p&gt;

&lt;p&gt;One component the opener flagged sits underneath both channels: the context surface. Every rule you steer with is text loaded into the model, and the whole surface is paid for on every turn, whether the model needs a given rule that turn or not. The loop here states its one rule once, but most agents carry far more. What does an instruction cost to keep loaded, and what changes when you load it only where it applies instead of keeping the whole surface in front of the model every turn? That is the question the next piece takes up.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on &lt;a href="https://reporails.com" rel="noopener noreferrer"&gt;Reporails&lt;/a&gt;, deterministic diagnostics and governance for the instruction files, rules, and prompts that steer coding agents. It reads the steering channel, the text you use to ask, and tells you, with measured evidence, where it drifts. The hooks here are the enforcing channel that sits beside it: what a rule refuses at a transition, not what it asks for on the surface.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>loopengineering</category>
      <category>ai</category>
      <category>claude</category>
      <category>programming</category>
    </item>
    <item>
      <title>Prompt Engineering, Context Engineering, Loop Engineering: What Actually Changed</title>
      <dc:creator> Gábor Mészáros</dc:creator>
      <pubDate>Wed, 08 Jul 2026 11:16:04 +0000</pubDate>
      <link>https://dev.to/reporails/prompt-engineering-context-engineering-loop-engineering-what-actually-changed-2357</link>
      <guid>https://dev.to/reporails/prompt-engineering-context-engineering-loop-engineering-what-actually-changed-2357</guid>
      <description>&lt;p&gt;A few years back the skill had one name: prompt engineering. You rewrote a sentence until the model did the thing. Last year the same corner of the job picked up a new name, context engineering. This year the threads call it loop engineering, and the line repeated under it is &lt;em&gt;the verifier is the bottleneck, not the model anymore.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every rename draws the same reaction from the same developers: is this a real shift, or the same work with fresh paint so someone can sell a course. That reflex is usually right. Most AI-tooling vocabulary turns over faster than the problems it describes.&lt;/p&gt;

&lt;p&gt;So here is the read, one rename at a time. At each step, the thing you were actually engineering changed. What changed was the unit of work, not the marketing around it. The problems the newest name points at have a dated, public record. The dates are on the articles; the arithmetic is yours to do.&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%2Fo01anytlxvvy6qop43ic.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%2Fo01anytlxvvy6qop43ic.png" alt="A horizontal timeline with three stops: 2022 to 2024 the prompt, 2025 the context, and 2026 the loop." width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit was a prompt
&lt;/h2&gt;

&lt;p&gt;Call it 2022 through 2024. The unit of work is a single prompt. You engineer the wording of one request: few-shot examples, a role frame, &lt;em&gt;think step by step&lt;/em&gt;, the ordering of the ask. The entire surface you controlled was the text of one message, and the craft was getting that message right.&lt;/p&gt;

&lt;p&gt;That discipline is not gone. It is no longer the whole job, because the whole job stopped fitting in one message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit became the surface
&lt;/h2&gt;

&lt;p&gt;Around mid-2025 Andrej Karpathy put a name on what people were already doing: context engineering. The unit stopped being a message and became everything in the window: the system prompt, the retrieved documents, the tool definitions, and the &lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;AGENTS.md&lt;/code&gt; instruction files that ride along on every single turn. You are no longer tuning a sentence. You are curating a surface.&lt;/p&gt;

&lt;p&gt;That surface has a property developers kept rediscovering the expensive way: most of what you put on it does not couple to behavior. &lt;a href="https://dev.to/reporails/the-state-of-ai-instruction-quality-35mn"&gt;The State of AI Instruction Quality&lt;/a&gt; pointed a deterministic analyzer at 28,721 repositories and found the median instruction file carries 50 content items and 12 actual directives, and the rest is headings, context, and structure the model is free to ignore. A sharper failure mode got its own writeup. &lt;a href="https://dev.to/cleverhoods/do-not-think-of-a-pink-elephant-383n"&gt;Do NOT Think of a Pink Elephant&lt;/a&gt; shows how a constraint phrased as a negation (&lt;em&gt;do not use mocks&lt;/em&gt;) can raise the odds of the exact thing it forbids, for the same reason the title just did to you.&lt;/p&gt;

&lt;p&gt;Both of those are context-engineering problems, measured and published.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit is the loop
&lt;/h2&gt;

&lt;p&gt;June 2026, the term is loop engineering, popularized by Addy Osmani synthesizing Boris Cherny and Peter Steinberger, and everywhere on the timeline within a few weeks. The unit is now the running loop: generate, check, steer, retry, stop. The prompt is one node in it. The context surface is the state it carries between iterations. The claim repeated across every explainer is that the model is no longer the limiting part. The check that decides &lt;em&gt;good enough, stop&lt;/em&gt; is.&lt;/p&gt;

&lt;p&gt;Take the name generously. It points at something the two earlier names left implicit: the mechanism that decides whether an iteration was any good and whether to run another. That mechanism was always present; every agent that retries has one. Loop engineering's contribution is making it the object you engineer instead of a default you inherit.&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%2F6u8kdydypdb3pw6yu9w6.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%2F6u8kdydypdb3pw6yu9w6.png" alt="simple loop diagramm" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prompt engineering tuned node one. Context engineering curated the state feeding it. Loop engineering points at the diamond, and that is where the interesting question hides.&lt;/p&gt;

&lt;h2&gt;
  
  
  A verifier that checks what, exactly?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The verifier is the bottleneck&lt;/em&gt; is a good slogan and an incomplete one. It names the bottleneck without saying what the verifier is supposed to check, and that gap is the engineering problem the slogan skips.&lt;/p&gt;

&lt;p&gt;There are two kinds of check, and they are not interchangeable. A deterministic check runs the code, asserts the exit status, scans for the forbidden import, and returns the same verdict for the same input every time, with no judgment in the middle. A model-graded check asks another model &lt;em&gt;is this good?&lt;/em&gt; The second reaches criteria the first cannot express, like &lt;em&gt;is this explanation clear&lt;/em&gt; or &lt;em&gt;does this read as rude&lt;/em&gt;, and it pays for that reach by inheriting the exact unreliability the loop was built to contain. You have put a probabilistic judge in charge of deciding when the probabilistic generator is finished.&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%2Fk59xlik6cfe89y3co0ee.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%2Fk59xlik6cfe89y3co0ee.png" alt="A card contrasting a deterministic check (same verdict every time) with a model-graded check that asks another model whether the output is good." width="799" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch where they diverge on a real loop. An agent is told to refactor a module and stop when the work is done. A deterministic verifier can prove the test suite still passes and no banned import crept in. Those are facts, checkable on every iteration, with no argument about them. It cannot decide whether the refactor was &lt;em&gt;worth doing&lt;/em&gt;, so a model-graded check gets bolted on to answer that, and now the stop condition rests on one model grading another's taste. The loop terminates when the judge is satisfied, and the judge is the same class of component the loop exists to supervise. Neither check is wrong. They answer different questions, and confusing which question you asked is how a loop stops confidently on the wrong iteration.&lt;/p&gt;

&lt;p&gt;That trade-off, what the check actually checks, is the content of the verifier, and it is not a new problem. &lt;a href="https://dev.to/reporails/green-tests-dont-mean-better-software-59of"&gt;Green Tests Don't Mean Better Software&lt;/a&gt; works the CI version of it: a green test proves the code conforms to its spec and says nothing about whether the change improved the system. The check answered a question you never asked. Aim the same distinction at an agent loop instead of a test suite and you have the loop-engineering question stated for a case every developer already trusts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the earlier articles measured
&lt;/h2&gt;

&lt;p&gt;Three articles published earlier in 2026 each worked one piece of this problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/cleverhoods/claudemd-lint-score-improve-repeat-2om5"&gt;CLAUDE.md: Check, Score, Improve &amp;amp; Repeat&lt;/a&gt;, 27 January 2026, describes a loop run over an instruction file: score it, change it, score it again. &lt;a href="https://dev.to/cleverhoods/do-not-think-of-a-pink-elephant-383n"&gt;Do NOT Think of a Pink Elephant&lt;/a&gt;, 31 March 2026, shows a single steering instruction that produces the opposite of its intent, and names the mechanism behind it. &lt;a href="https://dev.to/reporails/the-state-of-ai-instruction-quality-35mn"&gt;The State of AI Instruction Quality&lt;/a&gt;, 21 April 2026, analyzed 28,721 repositories and found that most of a typical instruction file is content the model can ignore rather than directives it has to follow.&lt;/p&gt;

&lt;p&gt;Between them they cover the three problems loop engineering now names: what the verifier checks, steering that drifts, and instructions that do not couple to behavior. None of the three used the term. The dates are on the links, and they are the whole of the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the new name solves
&lt;/h2&gt;

&lt;p&gt;Loop engineering gives you two things worth having. It makes the check an explicit object of engineering, something you design and revise on purpose instead of a default you inherit from whatever your agent framework happened to ship. And it gives the verifier question a name, so teams can argue about it directly: what should this check check, and can it be deterministic.&lt;/p&gt;

&lt;p&gt;Both are important, and both have an edge worth stating plainly. The name hands you the object. Building the verifier is still yours to do, and it is most of the work: deciding what &lt;em&gt;correct&lt;/em&gt; means for a task, then turning that into a check you can run.&lt;/p&gt;

&lt;p&gt;Part of that work is choosing the kind of check. A criterion like &lt;em&gt;is this explanation clear&lt;/em&gt; needs a model to judge it. A criterion like &lt;em&gt;no banned import&lt;/em&gt; is a deterministic scan. The cost of picking wrong runs both ways: a model-graded check where a deterministic one would do buys unreliability for nothing, and a deterministic check standing in for a judgment it cannot make gives you a green light that means less than it looks like. The name makes that choice visible and leaves it in your hands.&lt;/p&gt;

&lt;p&gt;Underneath the frame, the generator still guesses. What loop engineering adds is a place to put the check and a reason to take it seriously, which is more than the earlier names offered the same problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop, one part at a time
&lt;/h2&gt;

&lt;p&gt;The vocabulary will change again. Harness engineering was an intermediate name for part of this, and something will come after loop engineering too. Each time the word changes, the useful question is the same: what is the new unit of work, and what is the hard part inside it?&lt;/p&gt;

&lt;p&gt;For loop engineering the unit is the loop, and the hard part is the verifier: what it checks. The verifier is one component of several, and a name that points at the whole loop is easy to nod along to and harder to act on. So the plan from here is to take the loop apart a component at a time and put a measurement under each one. The questions this piece raised are the ones the next few pieces go after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A check that fires on the wrong thing comes back quiet, and so does a signal that was never there. They look identical from the outside. How do you tell a broken instrument from an absent one, before you conclude that the problem you were checking for does not exist?&lt;/li&gt;
&lt;li&gt;Not every rule in the loop is a verifier, and the rules that steer are not the rules that enforce. A prompt asks; a gate refuses. What belongs in the channel that can only ask, what belongs in the channel that can say no, and what breaks when a rule is filed under the wrong one?&lt;/li&gt;
&lt;li&gt;Everything on the context surface is paid for on every turn, whether the model needs it that turn or not. What does an instruction actually cost to keep loaded, and what changes when you load it only where it applies instead of putting the whole surface in front of the model every time?&lt;/li&gt;
&lt;/ul&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%2Fvlhxhp002dzpwrqldxre.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%2Fvlhxhp002dzpwrqldxre.png" alt="A loop motif with three upcoming stops marked along it (the check, the gate, and the context surface), each one a component the rest of the series takes apart." width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of those is one part of the loop, looked at on its own, with the evidence attached. Same discipline as this piece: a claim is worth exactly what the measurement under it is worth, and no more.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on &lt;a href="https://github.com/reporails/cli" rel="noopener noreferrer"&gt;Reporails&lt;/a&gt;, deterministic diagnostics for the instruction files, rules, and prompts that steer coding agents. It reads the steering surface and tells you, with measured evidence, where it drifts. It does not run your loop; it checks the part of the loop you wrote down.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>agents</category>
      <category>loopengineering</category>
    </item>
    <item>
      <title>Green Tests Don't Mean Better Software</title>
      <dc:creator> Gábor Mészáros</dc:creator>
      <pubDate>Tue, 30 Jun 2026 17:15:05 +0000</pubDate>
      <link>https://dev.to/reporails/green-tests-dont-mean-better-software-59of</link>
      <guid>https://dev.to/reporails/green-tests-dont-mean-better-software-59of</guid>
      <description>&lt;p&gt;You spent the week on a refactor because it was supposed to make the next change cheaper. The tests go green, the PR merges, and that's the last anyone thinks about it. Nobody comes back to check whether the next change actually got cheaper. The green bar said the code conforms to its spec, everyone read that as done, and the reason you did the work in the first place went unmeasured.&lt;/p&gt;

&lt;h2&gt;
  
  
  What green actually checks
&lt;/h2&gt;

&lt;p&gt;Your CI is green. The test asserts &lt;code&gt;assert response.status == 200&lt;/code&gt;, and it passes. It does not assert that the caching layer you just shipped cut p99 latency, or that the reworded onboarding step lifted activation, or that the prompt change made the agent follow instructions more often.&lt;/p&gt;

&lt;p&gt;The green bar answered a question you never asked.&lt;/p&gt;

&lt;p&gt;What did that green actually tell you? That the change conforms to the spec — the inputs map to the asserted outputs, the contract holds, nothing you wrote a test for regressed. That is correctness. It is real and it is necessary. It told you nothing about whether the system got better. You shipped because the bar was green, and the bar was never measuring the thing you shipped the change for.&lt;/p&gt;

&lt;p&gt;Those latency, activation, and adherence numbers are effects. CI does not measure effects. It measures conformance, and then we read the green bar as if it answered the other question too.&lt;/p&gt;

&lt;p&gt;The silent assumption is that "passes" and "better" are the same axis — that a change which is correct is, by that fact, an improvement. They are not the same axis. A perfectly correct change can make the system worse, and a perfectly correct change can leave every metric exactly where it was; the suite goes green for both. Green is necessary for "better" and nowhere near sufficient for it, and almost no team instruments the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two disciplines, pitched as rivals
&lt;/h2&gt;

&lt;p&gt;The industry already named both halves of this. It named them as rivals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/github/spec-kit" rel="noopener noreferrer"&gt;Spec-Driven Development&lt;/a&gt; says: write the specification first, then build to it, then prove the build conforms. The test suite is the executable form of the spec. Correctness is the whole game. SDD is disciplined, auditable, and it is what most engineering orgs actually run. As a flow it terminates the moment the bar goes green:&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%2Frr0zsc7dcxa9f98j0ocj.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%2Frr0zsc7dcxa9f98j0ocj.png" alt="Write the spec workflow" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice where it stops. It stops at &lt;em&gt;correct&lt;/em&gt;. Nothing in that loop asks whether the shipped change improved anything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.thoughtworks.com/insights/articles/how-implement-hypothesis-driven-development" rel="noopener noreferrer"&gt;Hypothesis-Driven Development&lt;/a&gt; asks the opposite question. Thoughtworks framed it as a triple: &lt;em&gt;we believe&lt;/em&gt; &lt;code&gt;X&lt;/code&gt; &lt;em&gt;will result in outcome&lt;/em&gt; &lt;code&gt;Y&lt;/code&gt;&lt;em&gt;; we will know we are right when we see measurable signal&lt;/em&gt; &lt;code&gt;Z&lt;/code&gt;. The unit of progress is not a passing build — it is validated learning. You predict an effect, you ship, you measure, and the measurement tells you whether you were right. PMI's expectation-management literature says the same thing from a different room: an expectation is a managed object with an owner and a due date, tracked and surfaced early rather than discovered late. As a flow it begins where SDD ends — at the prediction, and it runs past the ship:&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%2Foewy2uiw1cepnf8jskz1.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%2Foewy2uiw1cepnf8jskz1.png" alt="Predict the effect workflow" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These get pitched as competing philosophies — spec-first versus hypothesis-first, prove-correct versus prove-better, pick a camp. Look at the two flows again. One ends at &lt;em&gt;correct&lt;/em&gt;. The other starts at &lt;em&gt;predicted&lt;/em&gt; and ends at &lt;em&gt;better&lt;/em&gt;. They answer two different questions, and the pick-a-camp framing assumes they answer one. Thoughtworks HDD and PMI expectation-management are not novel claims I am making here; they are prior art, decades of it, and I am citing them as validation. What none of them did was notice the two flows were never aimed at the same question.&lt;/p&gt;

&lt;h2&gt;
  
  
  The orthogonality move
&lt;/h2&gt;

&lt;p&gt;Two different questions means two different axes. You cannot answer one by measuring the other — and a single pass/fail bar is built to answer only the first. Correct sits on one axis; better sits on the one perpendicular to it.&lt;/p&gt;

&lt;p&gt;One axis is spec-conformance: does the change do what it was specified to do? Pass or fail, and &lt;code&gt;pytest&lt;/code&gt; already answers it. The other axis is effect-verdict: did the change move the metric it was supposed to move? Confirmed, refuted, or not yet known — and nothing in your pipeline answers it today.&lt;/p&gt;

&lt;p&gt;Lay them on a 2×2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;
|                         | &lt;span class="gs"&gt;**effect confirmed**&lt;/span&gt;     | &lt;span class="gs"&gt;**effect refuted / unmeasured**&lt;/span&gt; |
|-------------------------|--------------------------|---------------------------------|
| &lt;span class="gs"&gt;**spec passes (green)**&lt;/span&gt; | shipped and works        | &lt;span class="gs"&gt;**green but no better**&lt;/span&gt;         |
| &lt;span class="gs"&gt;**spec fails (red)**&lt;/span&gt;    | blocked (correctly)      | blocked (correctly)             |

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three of those quadrants are familiar. Red blocks the merge, whichever way the effect would have gone. Green-and-confirmed is the win you wanted. The quadrant nobody names is the top-right: &lt;strong&gt;green but no better&lt;/strong&gt;. The change is correct, it conforms to spec, and it shipped — and it did not improve the system, or you never checked, which from the system's point of view is the same thing.&lt;/p&gt;

&lt;p&gt;That quadrant is where most shipped changes actually live, and it is invisible because the only instrument pointed at it is the green bar, which is pointed at the wrong axis. This is the reusable mental model: stop asking "did it pass" as if it were one question. It is two questions on two axes, and you are only instrumenting one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  A worked example: green architecture tests
&lt;/h2&gt;

&lt;p&gt;Make it concrete with a case every Python team recognizes. You adopt the hexagonal layout — Ports and Adapters. A pure core (&lt;code&gt;contract/&lt;/code&gt;, &lt;code&gt;dto/&lt;/code&gt;, &lt;code&gt;policy/&lt;/code&gt;) imports only the standard library. Adapters wrap the I/O. Subsystems compose the adapters. The dependency rule is one sentence: dependencies point inward, and the pure core points at nothing.&lt;/p&gt;

&lt;p&gt;You enforce it with an architecture test that runs on every commit:&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_core_purity&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;pure_core_modules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;no_imports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&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;yaml&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;requests&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;subprocess&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;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is green. Every pure module is import-clean. The dependency graph conforms to the rule — proven correct, mechanically, on every push.&lt;/p&gt;

&lt;p&gt;Now ask the other question. You did not adopt the hexagonal layout for its own sake. You adopted it on a claim: changes would get cheaper, blast radius would shrink, the core would be testable without a single mock. Did that happen? &lt;code&gt;test_core_purity&lt;/code&gt; cannot tell you. It measures the shape of the import graph, not whether the shape paid off. Green here means &lt;em&gt;conformant&lt;/em&gt;. A conformant architecture that made nothing cheaper is the green-but-no-better quadrant with a &lt;code&gt;.py&lt;/code&gt; file pointing straight at it.&lt;/p&gt;

&lt;p&gt;That is the whole thesis in one test file. The architecture test lives on the spec axis. The promise that sold the refactor lives on the effect axis. They do not touch, and only one of them has an instrument.&lt;/p&gt;

&lt;h2&gt;
  
  
  The expectation primitive
&lt;/h2&gt;

&lt;p&gt;If "better" is its own axis, it needs its own instrument. That instrument is an expectation — the HDD flow above, made into a first-class artifact.&lt;/p&gt;

&lt;p&gt;An expectation is a change-scoped, falsifiable, deadline-carrying prediction. It is the HDD triple — believe &lt;code&gt;X&lt;/code&gt;, expect outcome &lt;code&gt;Y&lt;/code&gt;, know it by criterion &lt;code&gt;Z&lt;/code&gt; — turned into an artifact that travels with the change instead of living in a planning doc nobody reopens. It carries a baseline, a bound measurement view — the query, tied to that change, that reads the metric — a threshold, and a due date. Concretely: &lt;em&gt;the caching change should cut p99 latency below 200ms, measured against last week's baseline, re-checked Friday.&lt;/em&gt; That sentence is the whole artifact, and it rides attached to the diff.&lt;/p&gt;

&lt;p&gt;Here is the part that is genuinely new, and the part I want to be precise about: the verdict is &lt;strong&gt;measured by the system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;HDD and PMI both already told you to make a falsifiable prediction with an owner and a deadline. That is not the advance. The advance is that the prediction binds to a real measurement view, and a system reads that view and stamps the verdict itself. The human who made the prediction does not come back and grade it. The human leaves the verification loop.&lt;/p&gt;

&lt;p&gt;This is not a dashboard alert. A dashboard alert fires on a metric crossing a line, untethered to any one change. An expectation binds one prediction to one change and grades &lt;em&gt;that prediction&lt;/em&gt; — the dashboard tells you latency rose; the expectation tells you the change you predicted would cut it did the opposite.&lt;/p&gt;

&lt;p&gt;The verdict vocabulary is three words, and the discipline is in the third:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;confirmed&lt;/code&gt;&lt;/strong&gt; — the measured value met the threshold. Auto-resolved, silently. Announcing it would be filler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;refuted&lt;/code&gt;&lt;/strong&gt; — the measured value missed the threshold. The expectation stays open and surfaces, with the predicted delta and the actual sitting next to each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;inconclusive&lt;/code&gt;&lt;/strong&gt; — the measurement produced no scalar yet. It stays open and surfaces. It is never read as confirmed. "We didn't measure" is not "it worked."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That third word is the one most dashboards quietly drop. A prediction you cannot yet grade is not a pass. Keeping &lt;code&gt;inconclusive&lt;/code&gt; distinct from &lt;code&gt;confirmed&lt;/code&gt; is what stops the green-but-no-better quadrant from refilling under a different name.&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%2F577u922sjt2kwqg7ar26.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%2F577u922sjt2kwqg7ar26.png" alt="Change ready workflow" width="624" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The selector routes by the change's &lt;em&gt;effect&lt;/em&gt;, not its &lt;em&gt;shape&lt;/em&gt;. A one-line diff can carry a large measurable effect and owe an expectation; a thousand-line refactor can be pure conformance and owe none — and you pick the lane when you ship, not after.&lt;/p&gt;

&lt;p&gt;Run the selector over that same hexagonal project. Re-homing a module to clear a &lt;code&gt;test_subsystem_isolation&lt;/code&gt; failure is a shape change. It restores the boundary, its effect resists measurement, it owes nothing past the green test. The refactor that &lt;em&gt;promised&lt;/em&gt; cheaper changes is the other lane — it made a measurable claim, so it owes an expectation: &lt;em&gt;adding the next artifact class touches three files or fewer, re-checked when the next one lands.&lt;/em&gt; Same codebase, two changes, two lanes.&lt;/p&gt;

&lt;p&gt;This is what the spec-versus-hypothesis fight always missed. The selector does not pick a winner between SDD and HDD. It is conditional. It runs SDD's discipline on changes whose effect resists measurement and HDD's discipline on changes whose effect can be measured, and it picks per change. A discipline that reconciles two camps looks like a routing rule that knows when each camp is right.&lt;/p&gt;

&lt;p&gt;There is one trap to name: do not attach an expectation to an unmeasurable change just to satisfy the selector. A prediction with no real threshold is conformance wearing a costume, and it teaches everyone to ignore the verdicts. If the effect resists measurement, declare none. The honesty of the &lt;code&gt;refuted&lt;/code&gt; and &lt;code&gt;inconclusive&lt;/code&gt; verdicts depends on the selector being willing to say "this change owes nothing."&lt;/p&gt;

&lt;p&gt;This is the part that has to survive contact with a real backlog. The tempting first cut is to flag every shipped change as owing a prediction. Try it and it floods on contact: the renames, the prose-clarity passes, the reorganizations — the bulk of any change history — all light up red against a rule they cannot satisfy, because there was never a metric for them to move. The flood is the proof. A selector that cannot say "this change owes nothing" does not enforce the discipline; it discredits it. The conditional design is not a softening of the rule — it is the only version of the rule that does not collapse the first time you run it over changes that already shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of existence, and the cost of skipping it
&lt;/h2&gt;

&lt;p&gt;A coordination system shipped exactly this layer, in working code. A change to a governed surface declares an expectation: predicted delta, bound measurement view, due date. The system re-measures when the due date passes, stamps &lt;code&gt;confirmed&lt;/code&gt; / &lt;code&gt;refuted&lt;/code&gt; / &lt;code&gt;inconclusive&lt;/code&gt; against the threshold, auto-resolves the confirmed ones silently, and surfaces the refuted and inconclusive ones the next session, one line each. The selector decides which changes owe a prediction and which fall back to conformance. The point here is only that the pairing — a governance selector plus an auto-measured verdict — is buildable today, because it has been built. The mechanism is the proof; the product is not the subject.&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%2Fr8xe4jaykyec0u6tz829.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%2Fr8xe4jaykyec0u6tz829.png" alt="Results table" width="800" height="718"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;confirmed&lt;/code&gt; is the minority verdict
&lt;/h3&gt;

&lt;p&gt;What running it actually teaches is the part the diagram cannot. Before the verdicts come back, you assume most predictions will land — you shipped the change believing it would help, so of course the measurement will agree.&lt;/p&gt;

&lt;p&gt;It does not. Once an instrument is actually pointed at the effect axis, &lt;code&gt;confirmed&lt;/code&gt; turns out to be the minority verdict. Most changes come back &lt;code&gt;inconclusive&lt;/code&gt; or &lt;code&gt;refuted&lt;/code&gt;: the metric did not move past the threshold, or there was no scalar to read in the window at all.&lt;/p&gt;

&lt;p&gt;The first time a batch of verdicts surfaces and almost none of them are &lt;code&gt;confirmed&lt;/code&gt;, the 2×2 stops being a diagram. The green-but-no-better quadrant is not a rare corner you occasionally fall into — it is where most changes sit, and you only learn that the moment you can finally see it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;inconclusive&lt;/code&gt; is the common case
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;inconclusive&lt;/code&gt; is the verdict you underestimate most. "We didn't measure" happens far more often than any prediction would lead you to expect — the window was too short, the metric needed traffic that had not arrived, the effect was real but not yet legible.&lt;/p&gt;

&lt;p&gt;The entire value of the third word is that it refuses to round itself up. Without it, every one of those un-graded predictions quietly refiles as a pass, and the quadrant refills under a name that looks like success. The discipline lives in holding &lt;code&gt;inconclusive&lt;/code&gt; open, in plain sight, until there is a scalar — and discovering, run after run, how often that takes longer than you thought.&lt;/p&gt;

&lt;h3&gt;
  
  
  Even &lt;code&gt;confirmed&lt;/code&gt; is not permanent
&lt;/h3&gt;

&lt;p&gt;Even the &lt;code&gt;confirmed&lt;/code&gt; ones are not closed for good. A threshold set too loose can confirm a change that later regresses, and a silent auto-resolve would bury exactly that. A confirmed verdict earns its silence; it does not earn permanent trust.&lt;/p&gt;

&lt;p&gt;One discipline already exists for this. &lt;a href="https://arxiv.org/abs/2605.23904" rel="noopener noreferrer"&gt;SkillOpt&lt;/a&gt; (Yang et al., 2026) accepts a self-authored skill edit only when it improves a &lt;em&gt;held-out&lt;/em&gt; validation split — not the data the edit was tuned against. The held-out split is the guard against exactly that failure: a verdict that reads &lt;code&gt;confirmed&lt;/code&gt; because the threshold was set on the same signal the change was shaped to move. An auto-measured verdict is only as honest as the separation between what tunes the change and what grades it.&lt;/p&gt;

&lt;p&gt;The claim worth taking away is the framework, not any one implementation: a &lt;strong&gt;governance selector&lt;/strong&gt; that routes each change to the right discipline, paired with an &lt;strong&gt;auto-measured verdict&lt;/strong&gt; that proves the effect without a human grading it, is a reconciliation of spec-driven and hypothesis-driven development. It is not a third methodology stacked beside the two you have; it tells you which of the two you already know to apply, and then it does the grading.&lt;/p&gt;

&lt;p&gt;That reconciliation stops being optional the moment the machine ships the change.&lt;/p&gt;

&lt;p&gt;When a human writes every diff, the green-but-no-better quadrant is a slow tax — improvements that were not improvements, paid for in drift you notice eventually. When an AI-assisted or autonomous system writes and ships changes, there is no human in the loop reading the effect at all. The tests still run, so correctness still gets checked; nothing measures the effect, so nothing catches it slipping. Unmeasured-effect is precisely where a self-running system's regressions hide — each change correct, each change green, the system quietly getting worse along the one axis nobody instrumented.&lt;/p&gt;

&lt;p&gt;The practice fits on one line. Before you merge the next change that makes a measurable claim, write down what you expect it to move, where that gets measured, and when you will look — then let the verdict come back to you instead of going to find it.&lt;/p&gt;

&lt;p&gt;Green tells you the machine built the thing right. Only a measured verdict tells you the thing was worth building. If you are handing more of the building to machines, that second instrument is the one you cannot afford to leave off.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://reporails.com" rel="noopener noreferrer"&gt;Reporails&lt;/a&gt; — deterministic instruction diagnostics and governance for the rules, prompts, and agent files that steer AI coding agents.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>testing</category>
      <category>devops</category>
    </item>
    <item>
      <title>The State of AI Instruction Quality</title>
      <dc:creator> Gábor Mészáros</dc:creator>
      <pubDate>Tue, 21 Apr 2026 12:41:52 +0000</pubDate>
      <link>https://dev.to/reporails/the-state-of-ai-instruction-quality-35mn</link>
      <guid>https://dev.to/reporails/the-state-of-ai-instruction-quality-35mn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Everybody has opinions about AGENTS.md/CLAUDE.md files. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Best practices get shared. Templates get copied, and this folk-type knowledge dominates the industry. Last year, &lt;a href="https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/" rel="noopener noreferrer"&gt;GitHub analyzed 2,500 repos&lt;/a&gt; and published best-practice advice. We wanted to go further: measure at scale, publish the data, and let anyone verify.&lt;/p&gt;

&lt;p&gt;When the agent doesn't follow instructions and does something contradictory, the usual suspects are: &lt;em&gt;the model is inconsistent, LLMs are not deterministic, you need better guardrails, you need retries.&lt;/em&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The failures almost always get attributed to the model.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we decided to measure. We built a diagnostic tool &lt;strong&gt;that treats instruction files as structured objects with measurable properties&lt;/strong&gt;. Deterministic. Reproducible. No LLM-as-judge. Then we pointed it at GitHub repositories with instruction files for five agents - &lt;strong&gt;Claude, Codex, Copilot, Cursor, and Gemini&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;28,721 repositories. 165,063 files. 3.3 million instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;... and one question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if the instructions are the problem?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The dataset
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;28,721 projects.&lt;/strong&gt; Sourced from GitHub via API search, cloned, and deterministically analyzed. Each project was scanned for instruction files across five coding agents — then deduplicated to remove false positives from agent detection overlap.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Projects&lt;/th&gt;
&lt;th&gt;% of corpus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;12,356&lt;/td&gt;
&lt;td&gt;43.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;11,206&lt;/td&gt;
&lt;td&gt;39.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;td&gt;7,755&lt;/td&gt;
&lt;td&gt;27.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;7,291&lt;/td&gt;
&lt;td&gt;25.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;5,942&lt;/td&gt;
&lt;td&gt;20.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2F0j1xpnj80ntk84v8g6e3.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.amazonaws.com%2Fuploads%2Farticles%2F0j1xpnj80ntk84v8g6e3.png" alt="Claude leads adoption at 43%, but all five agents have significant presence."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The percentages add up to more than 100% because &lt;strong&gt;37% of projects configure multiple agents&lt;/strong&gt;. More on that later.&lt;/p&gt;

&lt;p&gt;Key distributions stabilized early. A 9,582-repo sub-sample produced identical tier shares (±0.2pp) and the same mean scores as the full 12,076-repo intermediate sample. The final 28,721-repo corpus moved nothing. The patterns reported below are not small-sample artifacts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All classifications are deterministic&lt;/strong&gt; — the same file produces the same result every time. No LLM-as-judge. Sample classifications are published for inspection (methodology below). The tool is &lt;a href="https://github.com/reporails/cli" rel="noopener noreferrer"&gt;source-available&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How we measured
&lt;/h2&gt;

&lt;p&gt;The analyzer parses each instruction file into &lt;strong&gt;atoms&lt;/strong&gt; — the smallest semantically distinct units of content. A heading is one atom. A bullet point is one atom. A paragraph is one atom. Each atom gets classified along a few dimensions, all deterministic, no LLM involved:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charge classification.&lt;/strong&gt; A three-phase pipeline determines whether an atom is a directive ("use X"), a constraint ("do not use Y"), neutral content (context, explanation, structure), or ambiguous (could be read either way). Phase 1 detects negation and prohibition patterns. Phase 2 detects modal auxiliaries and direct commands. Phase 3 uses syntactic dependency parsing to catch imperatives that the first two phases missed. First definitive match wins. Atoms that partially match but don't clear any phase are marked ambiguous. Everything else is neutral.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specificity.&lt;/strong&gt; Binary: does the instruction name a specific construct — a tool, file, command, flag, function, or config key — or does it stay at the category level? "Use consistent formatting" is abstract. "Format with &lt;code&gt;ruff format&lt;/code&gt;" is named. This is a text property, not a judgment call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File categorization.&lt;/strong&gt; Each file is classified as base config (your main CLAUDE.md or .cursorrules), a rule file, a skill definition, or a sub-agent definition — based on file path conventions for each agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content type.&lt;/strong&gt; Charge classification separates behavioral content (directives and constraints) from structural content (headings, context paragraphs, examples). That's how we know what fraction of your file is actually doing work.&lt;/p&gt;

&lt;p&gt;The full tool is source-available (&lt;a href="https://github.com/reporails/cli/blob/main/LICENSE" rel="noopener noreferrer"&gt;BUSL-1.1&lt;/a&gt;). You can run &lt;code&gt;npx @reporails/cli check&lt;/code&gt; on your own project and inspect every finding. More on that at the end.&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 1: Most of your instruction file isn't instructions
&lt;/h2&gt;

&lt;p&gt;Here's what the median instruction file actually contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;50 content items&lt;/strong&gt; total&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;12 of those are actual directives&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The rest is headings, context paragraphs, examples, structure&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fg45f7k3xx4n6naso8gok.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.amazonaws.com%2Fuploads%2Farticles%2Fg45f7k3xx4n6naso8gok.png" alt="Median instruction file: 50 content items, 12 actual directives. The rest is structure."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Only 27% of your instruction file is doing what you think it does.&lt;/strong&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The other 73% is scaffolding. Headings that organize but don't instruct. Explanation paragraphs that compete for the model's attention without adding behavioral weight. Example blocks. Context-setting prose.&lt;/p&gt;

&lt;p&gt;That's not inherently bad. Structure matters. But if you're writing a 200-line CLAUDE.md and only 54 lines are actual instructions, you should probably know that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The average instruction is &lt;strong&gt;8.9 words&lt;/strong&gt; long. That's a sentence fragment.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Finding 2: 90% of instructions don't name what they're talking about
&lt;/h2&gt;

&lt;p&gt;This is the big one.&lt;/p&gt;

&lt;p&gt;We measured whether each instruction references specific tools, files, commands, or constructs by name — or whether it stays at the category level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two-thirds of all instructions are abstract.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Names specific constructs&lt;/th&gt;
&lt;th&gt;Uses category language&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;39.3%&lt;/td&gt;
&lt;td&gt;60.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;38.3%&lt;/td&gt;
&lt;td&gt;61.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;td&gt;33.3%&lt;/td&gt;
&lt;td&gt;66.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;30.8%&lt;/td&gt;
&lt;td&gt;69.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;30.6%&lt;/td&gt;
&lt;td&gt;69.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What does this look like in practice?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abstract&lt;/strong&gt;: "Use consistent code formatting"&lt;br&gt;
&lt;strong&gt;Specific&lt;/strong&gt;: "Format with &lt;code&gt;ruff format&lt;/code&gt; before committing"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abstract&lt;/strong&gt;: "Avoid using mocks in tests"&lt;br&gt;
&lt;strong&gt;Specific&lt;/strong&gt;: "Do not use &lt;code&gt;unittest.mock&lt;/code&gt; — use the real database via &lt;code&gt;test_db&lt;/code&gt; fixture"&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/cleverhoods/instruction-best-practices-precision-beats-clarity-lod"&gt;previous controlled experiments&lt;/a&gt;, specificity produced a 10.9x odds ratio in compliance (N=1000, p&amp;lt;10⁻³⁰). The instruction that names the exact construct gets followed. The one that describes it abstractly... mostly doesn't. This is consistent with independent findings from RuleArena (&lt;a href="https://arxiv.org/abs/2412.08972" rel="noopener noreferrer"&gt;Zhou et al., ACL 2025&lt;/a&gt;), where LLMs struggled systematically with complex rule-following tasks — even strong models fail when the rules themselves are ambiguous or underspecified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;89.9% of all agent configurations&lt;/strong&gt; contain at least one instruction that doesn't name what it means. It's not a few projects. It's nearly everyone.&lt;/p&gt;


&lt;h2&gt;
  
  
  Finding 3: &lt;code&gt;agents.md&lt;/code&gt; is the most common instruction file
&lt;/h2&gt;

&lt;p&gt;Before we get into quality, let's look at what people are actually naming their files:&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;File&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;agents.md&lt;/code&gt; / &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;20,654&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;claude.md&lt;/code&gt; / &lt;code&gt;CLAUDE.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;14,014&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gemini.md&lt;/code&gt; / &lt;code&gt;GEMINI.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;5,703&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5,647&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cursorrules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2,415&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;49,071 unique file paths&lt;/strong&gt; across the corpus. That's not a typo. The format fragmentation is real.&lt;/p&gt;

&lt;p&gt;A few things jumped out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;claude.md&lt;/code&gt; (lowercase, 10,642) is &lt;strong&gt;3x more common&lt;/strong&gt; than &lt;code&gt;CLAUDE.md&lt;/code&gt; (3,372). Both work. The community clearly prefers lowercase.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;agents.md&lt;/code&gt; dominates — the Codex/generic format is the single most popular instruction file name.&lt;/li&gt;
&lt;li&gt;Skills and rules are already showing up in meaningful numbers: &lt;code&gt;.claude/rules/testing.md&lt;/code&gt; (422), &lt;code&gt;.agents/skills/tailwindcss-development/skill.md&lt;/code&gt; (334).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Finding 4: Different agents, completely different config philosophies
&lt;/h2&gt;

&lt;p&gt;Not all agents are configured the same way. Not even close.&lt;/p&gt;

&lt;p&gt;We categorized every file into four types: &lt;strong&gt;base config&lt;/strong&gt; (your main CLAUDE.md, .cursorrules, etc.), &lt;strong&gt;rules&lt;/strong&gt; (scoped rule files), &lt;strong&gt;skills&lt;/strong&gt; (task-specific skill definitions), and &lt;strong&gt;sub-agents&lt;/strong&gt; (role-based agent definitions).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Base&lt;/th&gt;
&lt;th&gt;Rules&lt;/th&gt;
&lt;th&gt;Skills&lt;/th&gt;
&lt;th&gt;Sub-agents&lt;/th&gt;
&lt;th&gt;Total files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;18,733&lt;/td&gt;
&lt;td&gt;4,638&lt;/td&gt;
&lt;td&gt;10,692&lt;/td&gt;
&lt;td&gt;10,538&lt;/td&gt;
&lt;td&gt;44,601&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;5,903&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;19,843&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6,237&lt;/td&gt;
&lt;td&gt;1,716&lt;/td&gt;
&lt;td&gt;33,699&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;td&gt;16,026&lt;/td&gt;
&lt;td&gt;4,486&lt;/td&gt;
&lt;td&gt;10,352&lt;/td&gt;
&lt;td&gt;3,012&lt;/td&gt;
&lt;td&gt;33,876&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;19,001&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;8,911&lt;/td&gt;
&lt;td&gt;165&lt;/td&gt;
&lt;td&gt;28,158&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;10,253&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;3,039&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;13,419&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2Fq3bqyzkqqkzg0cs1vag1.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.amazonaws.com%2Fuploads%2Farticles%2Fq3bqyzkqqkzg0cs1vag1.png" alt="Cursor is 60% rules files. Codex is 68% base config. Same goal, completely different structure."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor is 60% rules files.&lt;/strong&gt; The &lt;code&gt;.cursor/rules/&lt;/code&gt; system dominates its configuration surface. One agent's config looks nothing like another's.&lt;/p&gt;

&lt;p&gt;Claude is the only agent with a roughly balanced architecture across all four config types. Codex and Gemini are almost entirely base config — single-file setups.&lt;/p&gt;

&lt;p&gt;The median Cursor project has &lt;strong&gt;3 instruction files&lt;/strong&gt;. The median Codex project has &lt;strong&gt;1&lt;/strong&gt;. These aren't just different tools. They're different &lt;em&gt;configuration philosophies&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Finding 5: 37% of projects configure multiple agents
&lt;/h2&gt;

&lt;p&gt;10,620 projects in the corpus target two or more agents. That's not a niche pattern — it's over a third of all projects.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agents&lt;/th&gt;
&lt;th&gt;Projects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;18,101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6,776&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2,687&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;949&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;208&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2F4bnski07fneblyvftsat.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.amazonaws.com%2Fuploads%2Farticles%2F4bnski07fneblyvftsat.png" alt="Over a third of projects configure instructions for multiple coding agents."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dominant pair is &lt;strong&gt;Claude + Codex&lt;/strong&gt; (5,038 projects). Makes sense — &lt;code&gt;CLAUDE.md&lt;/code&gt; + &lt;code&gt;AGENTS.md&lt;/code&gt; is the most natural multi-agent starting point.&lt;/p&gt;

&lt;p&gt;Here's what's interesting about multi-agent repos: &lt;strong&gt;the same developer, writing instructions at the same time, for the same project, produces measurably different instruction quality across agents.&lt;/strong&gt; The person didn't change. The project didn't change. The instruction format did.&lt;/p&gt;

&lt;p&gt;Some of that is structural. Cursor's &lt;code&gt;.mdc&lt;/code&gt; rules enforce a different format than Claude's markdown. Codex's &lt;code&gt;AGENTS.md&lt;/code&gt; invites a different writing style than Copilot's &lt;code&gt;copilot-instructions.md&lt;/code&gt;. The format shapes the content.&lt;/p&gt;


&lt;h2&gt;
  
  
  Finding 6: The most-copied skills are the vaguest
&lt;/h2&gt;

&lt;p&gt;This is where it gets interesting.&lt;/p&gt;

&lt;p&gt;13,309 unique skills across the corpus. Some of them appear in hundreds of repos — clearly copied from shared templates or community sources. So we measured them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Named%&lt;/strong&gt; = what fraction of a skill's instructions name a specific tool, file, or command (instead of using category language).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Repos&lt;/th&gt;
&lt;th&gt;Named%&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frontend-design&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;271&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.8%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Almost entirely abstract advice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;web-design-guidelines&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;197&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10.2%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generic design principles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vercel-react-best-practices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;315&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30.7%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mix of specific and vague&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pest-testing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;216&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;55.1%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Names actual test constructs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;livewire-development&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75.5%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Names specific Livewire components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;next-best-practices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92.6%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Names almost everything&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;frontend-design&lt;/code&gt; is in 271 repos with 2.8% specificity. It's a wall of "follow responsive design principles" and "ensure accessibility compliance." That reads well. It sounds professional. It gives the model almost nothing concrete to act on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;next-best-practices&lt;/code&gt; is in 76 repos with 92.6% specificity. It says things like "use &lt;code&gt;next/image&lt;/code&gt; for all images" and "prefer &lt;code&gt;server&lt;/code&gt; components over &lt;code&gt;client&lt;/code&gt;." It reads like a checklist. It tells the model exactly what to do.&lt;/p&gt;

&lt;p&gt;One is shared 3.5x more than the other.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The most popular skills are the most decorative.&lt;/strong&gt; The well-written ones barely spread.&lt;/p&gt;
&lt;/blockquote&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.amazonaws.com%2Fuploads%2Farticles%2Feeksw2bf5spqdgvfba2x.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.amazonaws.com%2Fuploads%2Farticles%2Feeksw2bf5spqdgvfba2x.png" alt="Each bubble is a community skill. The most popular ones cluster in the top-left — widely adopted, almost entirely abstract."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The best and worst skills (&amp;gt;50 repos)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Most specific:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Repos&lt;/th&gt;
&lt;th&gt;Named%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;next-best-practices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;92.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shadcn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;82.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;livewire-development&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;td&gt;75.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pest-testing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;216&lt;/td&gt;
&lt;td&gt;55.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;laravel-best-practices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;td&gt;49.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Most vague:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Repos&lt;/th&gt;
&lt;th&gt;Named%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openspec-explore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;td&gt;2.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frontend-design&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;271&lt;/td&gt;
&lt;td&gt;2.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;web-design-guidelines&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;197&lt;/td&gt;
&lt;td&gt;10.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vercel-composition-patterns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;131&lt;/td&gt;
&lt;td&gt;10.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;find-skills&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;113&lt;/td&gt;
&lt;td&gt;18.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice a pattern? The Laravel/Livewire ecosystem produces specific skills. The generic frontend/design ones stay abstract. &lt;strong&gt;Domain-specific communities write better instructions than cross-cutting ones.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Finding 7: Sub-agents are almost entirely persona prompts
&lt;/h2&gt;

&lt;p&gt;5,526 unique sub-agent roles in the corpus. Developers are building agent teams: code reviewers, architects, debuggers, testers, security auditors.&lt;/p&gt;

&lt;p&gt;The problem? &lt;strong&gt;Sub-agents are the most abstract config type in the entire corpus.&lt;/strong&gt; Only 17% of sub-agent instructions name specific constructs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Repos&lt;/th&gt;
&lt;th&gt;Named%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;code-reviewer.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;236&lt;/td&gt;
&lt;td&gt;14.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;architect.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;td&gt;18.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debugger.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;9.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;security-auditor.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;14.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;test-runner.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;10.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frontend-developer.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;9.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;p&gt;Most of these are persona prompts. "You are a senior code reviewer. You care about code quality, security, and maintainability." That's a role description, not an instruction set. It tells the model &lt;em&gt;who to be&lt;/em&gt;, not &lt;em&gt;what to do&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Compare this to a base config that says "run &lt;code&gt;uv run pytest tests/ -v&lt;/code&gt; before suggesting any commit" — that's 100% named, and the model knows exactly what action to take.&lt;/p&gt;


&lt;h2&gt;
  
  
  The anatomy chart: more directives, worse quality
&lt;/h2&gt;

&lt;p&gt;Here's where it all comes together.&lt;/p&gt;

&lt;p&gt;We measured three things for each config type: how big the files are, how many directives they contain, and what fraction of those directives actually name something specific.&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.amazonaws.com%2Fuploads%2Farticles%2Fk8i6m6ud3s9dwnj3jft3.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.amazonaws.com%2Fuploads%2Farticles%2Fk8i6m6ud3s9dwnj3jft3.png" alt="Sub-agents have the most directives per file — and the least specific ones. More instructions doesn’t mean better instructions."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sub-agents have the &lt;strong&gt;largest&lt;/strong&gt; files (61 items median), the &lt;strong&gt;most&lt;/strong&gt; directives (17), and the &lt;strong&gt;worst&lt;/strong&gt; specificity (17%). They're the wordiest config type in the corpus and the least effective.&lt;/p&gt;

&lt;p&gt;Base configs are the opposite. Fewer directives (11), but 40% of them name specific constructs. The developer writing their own CLAUDE.md by hand, for their own project, produces the most actionable instructions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Config type&lt;/th&gt;
&lt;th&gt;Files&lt;/th&gt;
&lt;th&gt;Median size&lt;/th&gt;
&lt;th&gt;Median directives&lt;/th&gt;
&lt;th&gt;Specificity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base configs&lt;/td&gt;
&lt;td&gt;69,916&lt;/td&gt;
&lt;td&gt;50 items&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;39.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules files&lt;/td&gt;
&lt;td&gt;29,122&lt;/td&gt;
&lt;td&gt;34 items&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31.2%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills&lt;/td&gt;
&lt;td&gt;39,231&lt;/td&gt;
&lt;td&gt;59 items&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-agents&lt;/td&gt;
&lt;td&gt;15,484&lt;/td&gt;
&lt;td&gt;61 items&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17.0%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is clear: &lt;strong&gt;what developers write by hand is the most specific. What gets templated and shared gets progressively vaguer. And what tries hardest to sound authoritative — sub-agent persona prompts — is the most hollow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More instructions is not better instructions.&lt;/p&gt;

&lt;p&gt;Independent research supports the structural angle: FlowBench (&lt;a href="https://arxiv.org/abs/2406.14884" rel="noopener noreferrer"&gt;Xiao et al., 2024&lt;/a&gt;) found that presenting workflow knowledge in structured formats (flowcharts, numbered steps) improved LLM agent planning by 5-6 percentage points over prose — across GPT-4o, GPT-4-Turbo, and GPT-3.5-Turbo. Structure is not decoration. It changes what the model retrieves.&lt;/p&gt;


&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Five things to know about these numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sampling bias.&lt;/strong&gt; GitHub API search, public repos only, English-skewed. Enterprise configurations, private repos, and non-English projects are not represented. This is not a random sample of all instruction files in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classification accuracy.&lt;/strong&gt; The charge classifier is deterministic but not perfect. Edge cases exist: mixed-charge sentences, implicit constructs, domain jargon that looks like a category term but is actually a named tool. Specificity detection (named vs abstract) is simpler and more robust. Sample classifications are &lt;a href="https://github.com/reporails/30k-corpus" rel="noopener noreferrer"&gt;published for inspection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Association, not causation.&lt;/strong&gt; "More directives correlate with lower specificity" is an observed pattern. We do not claim that adding directives &lt;em&gt;causes&lt;/em&gt; quality to drop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snapshot.&lt;/strong&gt; Collected March–April 2026. Instruction practices are changing fast — &lt;code&gt;agents.md&lt;/code&gt; didn't exist six months ago. These numbers describe the ecosystem at collection time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No popularity weighting.&lt;/strong&gt; A 10-star hobby project counts the same as a 50K-star production repo. The distribution of instruction quality in &lt;em&gt;production&lt;/em&gt; agent work may differ.&lt;/p&gt;


&lt;h2&gt;
  
  
  What this means
&lt;/h2&gt;

&lt;p&gt;This isn't an article about AI models being bad at following instructions. The models are fine.&lt;/p&gt;

&lt;p&gt;This is an article about what we actually give them to work with.&lt;/p&gt;

&lt;p&gt;Most instruction files are three-quarters scaffolding. Two-thirds of the actual instructions don't name what they're talking about. The most popular community skills are the most decorative. Sub-agent definitions are the wordiest files in the corpus and the least specific.&lt;/p&gt;

&lt;p&gt;None of that is obvious from reading your own files. It wasn't obvious to us before we measured it. A well-structured CLAUDE.md &lt;em&gt;feels&lt;/em&gt; thorough. A shared skill with 271 repos &lt;em&gt;feels&lt;/em&gt; battle-tested. A sub-agent with 17 directives &lt;em&gt;feels&lt;/em&gt; comprehensive.&lt;/p&gt;

&lt;p&gt;Measurement shows something different.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://medium.com/@cleverhoods/the-undiagnosed-input-problem-03231442219d" rel="noopener noreferrer"&gt;The Undiagnosed Input Problem&lt;/a&gt;, I argued that the industry is great at inspecting outputs and weak at inspecting inputs. This corpus analysis is the evidence for that claim.&lt;/p&gt;

&lt;p&gt;The instruction files are there. The developers wrote them. They just have no way to know which parts are working and which parts are wallpaper.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;The analyzer we used for this corpus analysis is available as a CLI you can run against your own instruction files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/reporails/cli" rel="noopener noreferrer"&gt;Reporails&lt;/a&gt;&lt;/strong&gt; — instruction diagnostics for coding agents. Deterministic. No LLM-as-judge. 97 rules across structure, content, efficiency, maintenance, and governance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @reporails/cli check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That scans your project, detects which agents are configured, and reports findings with specific line numbers and rule IDs. Here's what the output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reporails — Diagnostics

  ┌─ Main (1)
  │ CLAUDE.md
  │   ⚠       Missing directory layout             CORE:C:0035
  │   ⚠ L9    7 of 7 instruction(s) lack reinfor…  CORE:C:0053
  │     ... and 16 more
  │
  └─ 21 findings

  Score: 7.9 / 10  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░

  21 findings · 4 warnings · 1 info
  Compliance: HIGH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corpus analysis used the same classification pipeline at scale. Fix the findings, run again, watch your score improve.&lt;/p&gt;

&lt;h3&gt;
  
  
  The dataset
&lt;/h3&gt;

&lt;p&gt;The full corpus is published at &lt;strong&gt;&lt;a href="https://github.com/reporails/30k-corpus" rel="noopener noreferrer"&gt;reporails/30k-corpus&lt;/a&gt;&lt;/strong&gt;. Three files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Records&lt;/th&gt;
&lt;th&gt;What it contains&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;repos.jsonl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;28,721&lt;/td&gt;
&lt;td&gt;Per-project record: agents configured, stars, language, license, topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stats_public.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Every aggregate statistic in this article&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validation_key.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2,814&lt;/td&gt;
&lt;td&gt;Sample classifications with source text for inspection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Verify any claim:&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;# "28,721 repositories"&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;repos.jsonl | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# "43% Claude"&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;repos.jsonl | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
import sys, json
repos = [json.loads(l) for l in sys.stdin]
claude = sum(1 for r in repos if 'claude' in r['canonical_agents'])
print(f'{claude}/{len(repos)} = {claude/len(repos)*100:.1f}%')
"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every number in every table traces to that dataset. If you disagree with a finding, count the rows.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part of the Instruction Quality series. Previous: &lt;a href="https://medium.com/@cleverhoods/the-undiagnosed-input-problem-03231442219d" rel="noopener noreferrer"&gt;The Undiagnosed Input Problem&lt;/a&gt;. Related: &lt;a href="https://cleverhoods.medium.com/instruction-best-practices-precision-beats-clarity-e1bcae806671" rel="noopener noreferrer"&gt;Precision Beats Clarity&lt;/a&gt; · &lt;a href="https://cleverhoods.medium.com/do-not-think-of-a-pink-elephant-7d40a26cd072" rel="noopener noreferrer"&gt;Do Not Think of a Pink Elephant&lt;/a&gt; · &lt;a href="https://cleverhoods.medium.com/claude-md-best-practices-7-formatting-rules-for-the-machine-a591afc3d9a9" rel="noopener noreferrer"&gt;7 Formatting Rules for the Machine&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>The Undiagnosed Input Problem</title>
      <dc:creator> Gábor Mészáros</dc:creator>
      <pubDate>Wed, 08 Apr 2026 11:51:12 +0000</pubDate>
      <link>https://dev.to/reporails/the-undiagnosed-input-problem-4pmc</link>
      <guid>https://dev.to/reporails/the-undiagnosed-input-problem-4pmc</guid>
      <description>&lt;p&gt;The AI agent ecosystem has built a serious industry around controlling outputs. Guardrails. Safety classifiers. Output validation. Monitoring. Retry systems. Human review.&lt;/p&gt;

&lt;p&gt;All of that matters, but there is simpler upstream question that still goes mostly unmeasured:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are the instructions any good?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounds obvious, &lt;strong&gt;yet it is not how the industry behaves.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an agent fails to follow instructions, the usual explanations come fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Models are probabilistic&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Agents are inconsistent&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;You need stronger guardrails&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;You need better monitoring&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;You need retries&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;You need humans in the loop&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;… and while those explanations are right to a certain degree, they also have a side effect: &lt;strong&gt;they turn instruction quality into a blind spot.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ecosystem has become extremely good at inspecting what comes out of the model, and surprisingly weak at inspecting what goes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptom
&lt;/h2&gt;

&lt;p&gt;Consider &lt;a href="https://sierra.ai/blog/benchmarking-ai-agents" rel="noopener noreferrer"&gt;τ-bench&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It gives agents policy instructions and measures whether they follow them in realistic customer-service tasks. Airline and retail workflows. Real constraints. Real multi-step behavior.&lt;/p&gt;

&lt;p&gt;The benchmark result that gets repeated is the model result: even strong systems still fail a large share of tasks, and consistency across repeated attempts remains weak.&lt;/p&gt;

&lt;p&gt;The conclusion most people draw is straightforward: &lt;strong&gt;we need better models, better agents, better orchestration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My take: &lt;strong&gt;&lt;em&gt;Maybe&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But there is another question sitting underneath the benchmark:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Were the instructions themselves well-formed and well structured?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not just present. Not just long enough. Not just sincere.&lt;/p&gt;

&lt;p&gt;Well-formed. Well-structured. Well-organized.&lt;/p&gt;

&lt;p&gt;Specific enough to anchor behavior. Structured enough to survive context mixing. Non-conflicting across files. Positioned where the model can actually use them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Those questions usually never gets asked.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The industry response
&lt;/h2&gt;

&lt;p&gt;I had a conversation recently where a lead solutions architect put the standard view plainly:&lt;/p&gt;

&lt;p&gt;“&lt;em&gt;The instruction merely influences the probability distribution over outputs. It doesn’t override it.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;That is right about the mechanism but it is wrong about what follows from it.&lt;/p&gt;

&lt;p&gt;Yes, instructions operate probabilistically. &lt;strong&gt;But that does not mean all instructions are weak in the same way.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The shape of the distribution is not fixed. It changes with the properties of the instruction itself. Specificity sharpens it. Structure sharpens it. Conflict flattens it. Vague abstractions flatten it. Bad formatting can suppress it almost entirely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Across my earlier controlled experiments, small changes in wording and placement produced large changes in compliance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cleverhoods.medium.com/do-not-think-of-a-pink-elephant-7d40a26cd072" rel="noopener noreferrer"&gt;Instruction&lt;/a&gt; ordering moved compliance by 25 percentage points with the same model and the same directive.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cleverhoods.medium.com/instruction-best-practices-precision-beats-clarity-e1bcae806671" rel="noopener noreferrer"&gt;Specificity&lt;/a&gt; produced roughly a 10x compliance effect when the instruction named the exact construct instead of describing it abstractly.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cleverhoods.medium.com/claude-md-best-practices-7-formatting-rules-for-the-machine-a591afc3d9a9" rel="noopener noreferrer"&gt;Formatting&lt;/a&gt; changed whether the model reliably registered the instruction at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The problem is that most instruction systems are built without diagnostics.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;That is not an AI limitation. That is an engineering failure.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The folk system
&lt;/h2&gt;

&lt;p&gt;Right now, instruction practice spreads mostly through imitation.&lt;/p&gt;

&lt;p&gt;A popular repository posts “best practices” for Claude Code. Shared Cursor rules circulate as templates. People copy &lt;code&gt;AGENTS.md&lt;/code&gt; files between projects. Teams accumulate &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.cursorrules&lt;/code&gt;, c&lt;code&gt;opilot-instructions.md&lt;/code&gt;, etc and project-specific rule files across multiple tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Copy, paste, hope, repeat.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some of that advice is useful. Almost none of it is tested in any controlled, reproducible way. That would be fine if instruction quality were self-evident. &lt;strong&gt;It is not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A long instruction file can feel thorough while being internally contradictory. A highly opinionated ruleset can feel disciplined while producing almost no behavioral influence on the model.&lt;/p&gt;

&lt;p&gt;A sprawling multi-file setup can look sophisticated while making the system worse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Without diagnostics, developers do not know which instructions are binding, which are noise, and which are actively interfering with each other.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;The tooling split is now pretty clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output tooling&lt;/strong&gt; is mature. Guardrails AI validates structure. Lakera focuses on prompt injection and security. NeMo Guardrails enforces safety and conversational rails. Llama Guard classifies risky content. The output edge is crowded.&lt;/p&gt;

&lt;p&gt;Prompt testing is real. Promptfoo, Braintrust, and LangSmith can all help evaluate behavior. But they are primarily black-box systems: did the prompt produce the output you wanted?&lt;/p&gt;

&lt;p&gt;That is useful.&lt;/p&gt;

&lt;p&gt;It is not the same as measuring the instruction artifact itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instruction-quality tooling&lt;/strong&gt; exists only in fragments. Some tools use LLM-as-judge. Some use deterministic local rules. But the category is still early, inconsistent, and mostly disconnected from measured behavioral outcomes.&lt;/p&gt;

&lt;p&gt;What is still largely missing is a deterministic way to inspect instruction files as engineered objects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how specific they are&lt;/li&gt;
&lt;li&gt;how directly they state intent&lt;/li&gt;
&lt;li&gt;whether they conflict across files&lt;/li&gt;
&lt;li&gt;whether they overuse headings&lt;/li&gt;
&lt;li&gt;whether they provide alternatives instead of bare prohibitions&lt;/li&gt;
&lt;li&gt;whether the system is getting denser while getting weaker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Code gets static analysis.&lt;/p&gt;

&lt;p&gt;Instruction systems usually get &lt;em&gt;vibes&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured
&lt;/h2&gt;

&lt;p&gt;We built an analyzer that treats instruction files as structured objects with measurable properties. Deterministic. Reproducible. No LLM-as-judge.&lt;/p&gt;

&lt;p&gt;I am running it across a large live corpus of real repositories. The full run completes this week; what follows is what the partial sample already shows - stable enough to publish, not yet the full picture.&lt;/p&gt;

&lt;p&gt;Quality is reported on a 0-to-100 scale: &lt;code&gt;0&lt;/code&gt; means the file produces no measurable influence on model behavior, &lt;code&gt;100&lt;/code&gt; is the ceiling the framework can score.&lt;/p&gt;

&lt;p&gt;A fresh aggregation over &lt;strong&gt;12,076&lt;/strong&gt; completed instruction-file scans is virtually identical to an earlier &lt;strong&gt;9,582&lt;/strong&gt;-repo sample:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;bottom tier:&lt;/strong&gt; &lt;code&gt;40.3%&lt;/code&gt; vs &lt;code&gt;40.1%&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;top tier:&lt;/strong&gt; &lt;code&gt;12.1%&lt;/code&gt; vs &lt;code&gt;12.2%&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;mean quality score:&lt;/strong&gt; &lt;code&gt;27&lt;/code&gt; vs &lt;code&gt;27&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;directive content ratio:&lt;/strong&gt; &lt;code&gt;27.9%&lt;/code&gt; vs &lt;code&gt;27.9%&lt;/code&gt; - the share of instruction sentences that directly tell the model what to do&lt;/p&gt;

&lt;p&gt;That matters because it means the pattern is stable.&lt;/p&gt;

&lt;p&gt;This does not look like a small-sample artifact.&lt;/p&gt;

&lt;p&gt;And the strongest finding is not what I expected.&lt;/p&gt;
&lt;h2&gt;
  
  
  More rules, lower quality
&lt;/h2&gt;

&lt;p&gt;The common response to bad agent behavior is to add more rules.&lt;/p&gt;

&lt;p&gt;More files. More guidance. More scoping. More edge-case coverage.&lt;/p&gt;

&lt;p&gt;The corpus says that strategy tends to backfire.&lt;/p&gt;

&lt;p&gt;Across &lt;strong&gt;12,076&lt;/strong&gt; repositories, instruction quality falls as instruction-file count rises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Files per repo     N      Mean score   Bottom tier %   Top tier %
1                  4681   28           46.3%           16.9%
2-5                4796   26           37.3%            9.5%
6-20               1972   26           36.0%            8.8%
21-50               438   25           31.3%            5.7%
51-500              186   25           33.3%            5.4%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key number is the top-tier share.&lt;/p&gt;

&lt;p&gt;It collapses from &lt;code&gt;16.9%&lt;/code&gt; in single-file setups to &lt;code&gt;5.4%&lt;/code&gt; in repositories with &lt;code&gt;51&lt;/code&gt; to &lt;code&gt;500&lt;/code&gt; instruction files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That is a roughly 3x drop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The article version of that finding is simple:&lt;/p&gt;

&lt;p&gt;Developers respond to bad agent behavior by adding more rules. In the corpus, that strategy correlates with a 3x collapse in the probability of landing in the top tier.&lt;/p&gt;

&lt;p&gt;That does not prove file count causes low quality by itself.&lt;/p&gt;

&lt;p&gt;But it does show that rule proliferation is not rescuing these systems. At scale, it is associated with weaker instruction quality, not stronger.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sweet spot
&lt;/h2&gt;

&lt;p&gt;There is also a more subtle result in the partial sample. Instruction quality appears to be non-monotonic in directive density: more directives help at first, then stop helping, and past a point start to hurt.&lt;/p&gt;

&lt;p&gt;The full curve is in next week’s piece. The short version is that there is an optimal density range, after which additional directives stop strengthening the system.&lt;/p&gt;

&lt;p&gt;Enough force to bind behavior. Not so much that the system turns into an overpacked rules document.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real example
&lt;/h2&gt;

&lt;p&gt;Here is the kind of instruction block the corpus is full of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Code should be clear, well documented, clear PHPDocs.

# Code must meet SOLID DRY KISS principles.

# Should be compatible with PSR standards when it need.

# Take care about performance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not malicious. It is not absurd.&lt;/p&gt;

&lt;p&gt;It is just &lt;strong&gt;weak.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything is abstract. Nothing is anchored. Headings are doing the work prose should do. The agent can read it, represent it, and still walk past most of it.&lt;/p&gt;

&lt;p&gt;Now compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Never use &lt;span class="sb"&gt;`&lt;/span&gt;var_dump&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; or &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;committed code. Use &lt;span class="sb"&gt;`&lt;/span&gt;Log::debug&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; instead.
Run &lt;span class="sb"&gt;`&lt;/span&gt;./vendor/bin/phpstan analyse src/&lt;span class="sb"&gt;`&lt;/span&gt; before every commit. Level 6 minimum.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same general intent. Completely different binding strength.&lt;/p&gt;

&lt;p&gt;The second version names the construct, names the alternative, names the command, and names the threshold. &lt;strong&gt;It gives the model something concrete to hold onto.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is what diagnostics should make visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means
&lt;/h2&gt;

&lt;p&gt;Output guardrails still matter.&lt;/p&gt;

&lt;p&gt;Prompt evaluation still matters.&lt;/p&gt;

&lt;p&gt;Safety systems still matter.&lt;/p&gt;

&lt;p&gt;But they do not answer the upstream question: &lt;strong&gt;Are the instructions themselves well-formed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the answer is no, then a large class of downstream failures will keep showing up as mysterious agent unreliability when the real problem is earlier and simpler.&lt;/p&gt;

&lt;p&gt;The agent loaded the instruction and walked past it.&lt;/p&gt;

&lt;p&gt;That is often not a model problem.&lt;/p&gt;

&lt;p&gt;It is an input problem.&lt;/p&gt;

&lt;p&gt;And input quality is measurable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;These are corpus-level findings from a partial sample, not universal laws.&lt;/p&gt;

&lt;p&gt;The sample is still in flight. The strongest claims here are about association, not proof of causality. Specific conflict-count case studies need source verification before publication. Popularity weighting is not yet applied, so “40% of repositories score in the bottom tier” is not the same claim as “40% of production agent work scores in the bottom tier.”&lt;/p&gt;

&lt;p&gt;The full corpus run completes this week. Next week I publish the end-of-run analysis across the full sample — the complete distribution, the cross-cuts the partial sample cannot yet support, and the specific case studies this article deliberately held back. If you want to know where your stack lands, that is the piece to come back for.&lt;/p&gt;

&lt;p&gt;For now, the central pattern is already stable enough to matter:&lt;/p&gt;

&lt;p&gt;The ecosystem keeps responding to weak agent behavior by adding more instructions, while the corpus shows that more instruction files are usually associated with lower measured quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That is the undiagnosed input problem.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Not that instructions do not matter.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;That they matter, measurably, and most teams still have no way to see whether theirs are helping or hurting.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;This is part of the Instruction Best Practices series. Previous: &lt;a href="https://cleverhoods.medium.com/do-not-think-of-a-pink-elephant-7d40a26cd072" rel="noopener noreferrer"&gt;Do NOT Think of a Pink Elephant&lt;/a&gt;, &lt;a href="https://cleverhoods.medium.com/instruction-best-practices-precision-beats-clarity-e1bcae806671" rel="noopener noreferrer"&gt;Precision Beats Clarity&lt;/a&gt;, &lt;a href="https://cleverhoods.medium.com/claude-md-best-practices-7-formatting-rules-for-the-machine-a591afc3d9a9" rel="noopener noreferrer"&gt;7 Formatting Rules for the Machine&lt;/a&gt;. I’m building instruction diagnostics for coding agents. Follow for the full corpus analysis.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>claude</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
