<?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: Srinivas Nelakuditi</title>
    <description>The latest articles on DEV Community by Srinivas Nelakuditi (@srinivasnelakuditi).</description>
    <link>https://dev.to/srinivasnelakuditi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2237500%2F74c469b1-d351-4367-b44c-c893e999281b.png</url>
      <title>DEV Community: Srinivas Nelakuditi</title>
      <link>https://dev.to/srinivasnelakuditi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srinivasnelakuditi"/>
    <language>en</language>
    <item>
      <title>Your AI agent agrees to the constraint, then violates it anyway. Build the harness that stops it.</title>
      <dc:creator>Srinivas Nelakuditi</dc:creator>
      <pubDate>Sun, 19 Jul 2026 00:55:33 +0000</pubDate>
      <link>https://dev.to/srinivasnelakuditi/your-ai-agent-agrees-to-the-constraint-then-violates-it-anyway-build-the-harness-that-stops-it-4lgg</link>
      <guid>https://dev.to/srinivasnelakuditi/your-ai-agent-agrees-to-the-constraint-then-violates-it-anyway-build-the-harness-that-stops-it-4lgg</guid>
      <description>&lt;p&gt;Here's a failure you can reproduce with any frontier model today.&lt;/p&gt;

&lt;p&gt;You're retiring a data warehouse. &lt;strong&gt;System A&lt;/strong&gt; is going away. A source feeds A now, and you're standing up &lt;strong&gt;System B&lt;/strong&gt;. You give the agent an unambiguous instruction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A is being retired. Do NOT read from A. Go to the source that feeds A and write THAT into B.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent agrees. It even restates your architecture correctly. Then it generates:&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="c1"&gt;# what the agent writes — the exact thing you forbade
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system_a.orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# reads from the retiring system
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveAsTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system_b.orders&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;You correct it. "You're absolutely right." It regenerates the same pipeline. Again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;This isn't a knowledge gap — the model can explain the correct approach perfectly in the abstract. It's &lt;strong&gt;heuristic override&lt;/strong&gt;: when a dominant training pattern (migrate = read A, write B) conflicts with a constraint you just supplied (A is retiring, use its source), the pattern wins. Add sycophancy (it agrees with your correction) and weak self-correction (it can't tell it just repeated itself), and you get confident, cheerful, repeated failure.&lt;/p&gt;

&lt;p&gt;And it's not a small-model problem. Across 14 models — including the latest Claude Opus 4.8, GPT, and Gemini with thinking on — none exceeded &lt;strong&gt;75% strict accuracy&lt;/strong&gt; when a taught constraint had to beat the obvious pattern. Treat it as a standing property of today's models, not a bug the next release fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is a harness, not a bigger model
&lt;/h2&gt;

&lt;p&gt;If the model won't hold the constraint, make the system enforce it. Four moving parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Enumerate preconditions before design.&lt;/strong&gt; Force a separate reasoning step that answers the questions the pattern skips:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;preconditions&lt;/span&gt; &lt;span class="o"&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;is_A_a_source_or_sink&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# -&amp;gt; "sink"
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_A_retiring&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;lifecycle_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;       &lt;span class="c1"&gt;# -&amp;gt; True
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;who_feeds_A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;upstream_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;              &lt;span class="c1"&gt;# -&amp;gt; "source_x"
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Encode real lineage as a graph and plan against it — not the model's habits.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;networkx&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;

&lt;span class="n"&gt;lineage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DiGraph&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;lineage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_x&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;system_a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# SOURCE to A
&lt;/span&gt;&lt;span class="n"&gt;lineage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system_a&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;system_b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# A to B (the wrong path)
&lt;/span&gt;&lt;span class="n"&gt;retiring&lt;/span&gt; &lt;span class="o"&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;system_a&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;&lt;strong&gt;3. A deterministic hard gate the agent cannot bypass.&lt;/strong&gt; This is the load-bearing piece:&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;validate_pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reads_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;writes_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retiring&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reads_from&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;retiring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;PipelineRejected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reads_from&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is retiring. Re-route to its source: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;upstream_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reads_from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# no matter what the model generates, this runs before anything merges
&lt;/span&gt;&lt;span class="nf"&gt;validate_pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system_a&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;system_b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retiring&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# raises, blocks the merge
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent literally cannot ship A to B. The rejection message even hands it the correct route, so the next attempt has the fix in-context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. External feedback loop.&lt;/strong&gt; Compile, run, and diff the output against the true source. Reality is the referee — not the model's self-assessment.&lt;/p&gt;

&lt;p&gt;And keep a hands-on architect owning the constraint set the gate enforces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;Gartner projects more than 40% of agentic projects cancelled by 2027, with only ~11% reaching production. The gap is almost entirely a harness gap. The teams that ship assume the model will violate the constraint and build the system so it can't.&lt;/p&gt;

&lt;p&gt;The harness beats the model. On this class of failure, it's the difference between a demo and a cutover.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pushing agentic AI to production? I'd love to compare notes — the failure modes are more consistent than people expect, and so are the fixes.&lt;/em&gt;&lt;/p&gt;

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