<?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: Azan Hyder</title>
    <description>The latest articles on DEV Community by Azan Hyder (@azank1).</description>
    <link>https://dev.to/azank1</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%2F4048140%2F50858050-e55e-4de9-b324-10688545e390.jpg</url>
      <title>DEV Community: Azan Hyder</title>
      <link>https://dev.to/azank1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/azank1"/>
    <language>en</language>
    <item>
      <title>Vibe coding gets you nowhere. BMAD works if you use a little brain.</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Thu, 13 Aug 2026 15:38:56 +0000</pubDate>
      <link>https://dev.to/azank1/vibe-coding-gets-you-nowhere-bmad-works-if-you-use-a-little-brain-58ee</link>
      <guid>https://dev.to/azank1/vibe-coding-gets-you-nowhere-bmad-works-if-you-use-a-little-brain-58ee</guid>
      <description>&lt;p&gt;Everybody vibe codes now. In case the term passed you by the way it almost passed me by: you describe what you want, the model writes it, you paste the error back, and you repeat until it runs. The vibe is the spec. For a prototype or a weekend script this is genuinely great. For anything that has to survive contact with a real codebase, it falls apart in a way that is hard to notice until it is expensive.&lt;/p&gt;

&lt;p&gt;I hit that wall enough times that I went looking for structure, and I ended up on the BMAD Method. This post is the explainer I wish someone had handed me: what it actually is, why it works, and when it is overkill.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is not the model
&lt;/h2&gt;

&lt;p&gt;Vibe coding fails in a predictable way. The first prompt produces something impressive. The tenth prompt contradicts the third. By the thirtieth, the model has forgotten decisions it made, invented new ones, and your architecture is whatever the last conversation happened to imply. There is no plan outside the chat, so there is nothing to check the work against.&lt;/p&gt;

&lt;p&gt;Notice that none of this is a model capability problem. A smarter model drifts more politely, but it still drifts, because the missing piece is not intelligence. It is shared, persistent structure: a spec, an architecture, and a unit of work small enough to hold in context at once.&lt;/p&gt;

&lt;p&gt;Which is, not coincidentally, what software teams figured out decades ago.&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%2Fbbnjlza8ljqdatnn1sz9.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%2Fbbnjlza8ljqdatnn1sz9.png" alt="This is fine meme. The repository is fully on fire after thirty prompts, and the model calmly reports that everything is fine." width="617" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What BMAD actually is
&lt;/h2&gt;

&lt;p&gt;BMAD (Breakthrough Method for Agile AI Driven Development) is a free, MIT licensed framework that puts a team of specialized agent personas between you and the code. It is one of the most starred agentic development projects on GitHub, and the core idea is almost boringly simple: instead of one generalist AI you prompt for output, you get a workflow of roles, each with a defined job and a written deliverable.&lt;/p&gt;

&lt;p&gt;The cast will be familiar to anyone who has worked on a real team. There is an Analyst who helps you figure out what you are even building. A Product Manager who turns that into a PRD. An Architect who fights the PRD and produces a technical design. A UX specialist, a Scrum Master, a Developer, and a QA role that tries to break what the Developer built.&lt;/p&gt;

&lt;p&gt;The point is not roleplay. The point is that each handoff produces a document, and the documents are the memory that a chat window cannot be.&lt;/p&gt;

&lt;p&gt;The BMAD agent roster inside a coding CLI: analyst, product manager, architect, UX designer, scrum master, developer and QA, each listed with the single job it owns. Screenshot from the install running on my machine.&lt;br&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%2Fcco956jus8rwal592vfx.jpeg" 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%2Fcco956jus8rwal592vfx.jpeg" alt=" " width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The two phases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Phase one is planning.&lt;/strong&gt; You work with the Analyst, PM, and Architect agents to produce a PRD and an architecture document. This is human in the loop the whole way. The agents ask uncomfortable questions, you make actual decisions, and the decisions get written down. This phase is where most of the value is, because it forces the thinking that vibe coding lets you skip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase two is development, and it has one genuinely clever trick.&lt;/strong&gt; The Scrum Master agent takes the PRD and architecture and shards them into story files. Each story is a small, self contained file with everything the Developer agent needs: acceptance criteria, the relevant schema snippets, the API definitions, the design notes. The Dev agent then implements one story at a time.&lt;/p&gt;

&lt;p&gt;Why this matters: the Dev agent loads a few KB of exactly relevant context instead of your entire project documentation. Context is the scarcest resource in AI assisted development, and sharding is context engineering disguised as agile process. The agent always knows what to build, how to build it, and why, because the story file tells it.&lt;/p&gt;

&lt;p&gt;Then QA reviews, findings get fixed, and you move to the next story. It is a loop a real engineering org would recognize, which is the entire point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The little brain part: which skill when
&lt;/h2&gt;

&lt;p&gt;Here is the part the install page does not spell out. BMAD drops a full menu of agents and workflows into your editor, and the first instinct is to close the menu and go back to prompting. Resist. You only ever need one entry at a time, and the whole method reduces to picking the right one for the moment you are in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you cannot yet say what you are building, you start with brainstorming or a product brief, and you let the Analyst interrogate the idea until it survives or dies cheaply.&lt;/li&gt;
&lt;li&gt;If you know what you are building but not where it ends, you write the PRD, because scope you cannot read is scope you cannot check.&lt;/li&gt;
&lt;li&gt;If the requirements exist, you produce the architecture, which is where the Architect picks fights with the PRD before the code has to.&lt;/li&gt;
&lt;li&gt;If the thing has a user facing surface, UX planning happens now, not after the Developer agent invents a layout at two in the morning.&lt;/li&gt;
&lt;li&gt;Once the documents exist, you shard them into stories and hand them to the Developer one at a time, then let QA try to break each result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the complete trick. The menu is long, but the moments are few, and matching one to the other is the entire little brain requirement.&lt;/p&gt;

&lt;p&gt;The BMAD brainstorming composer open in a browser tab: the full technique catalog laid out as a dashboard, with a copy prompt button that hands the composed session back to the chat. This is what the install drops into your project.&lt;br&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%2Fehaejc84wgflieqrg93y.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%2Fehaejc84wgflieqrg93y.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it takes one command
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx bmad-method &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it inside a project and it wires the agents into your editor of choice (Claude Code, Cursor, and friends). From there you can start at the very beginning with brainstorming and a product brief, or drop straight into a PRD if you already know what you are building. It handles brownfield projects too: there is a workflow that documents an existing codebase first, so the agents plan against what you actually have instead of what you wish you had.&lt;/p&gt;

&lt;p&gt;The composer closing the loop: the composed session prompt is copied, and the banner tells you to paste it into the chat, which is where the agent takes over and the session begins&lt;br&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%2F5n6pb6jgfcs2oegu8223.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%2F5n6pb6jgfcs2oegu8223.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest tradeoffs
&lt;/h2&gt;

&lt;p&gt;This is a real workflow, which means real costs. Planning takes time you could have spent typing prompts. For a throwaway script or a quick spike, the full method is absolutely overkill, and the framework itself will tell you to use its lighter flows for small work.&lt;/p&gt;

&lt;p&gt;You also do not get to stop thinking. The agents produce artifacts, but you own the decisions, and a PRD you rubber stamped is just vibe coding with extra steps. The structure only pays off if you actually engage with it.&lt;/p&gt;

&lt;p&gt;What you get back is consistency. The code in story fourteen still respects the decisions made in the architecture doc, because the Dev agent never had a chance to forget them.&lt;/p&gt;

&lt;p&gt;Full disclosure, in the interest of practicing what I post: this article itself went through BMAD style editorial and adversarial review passes before it reached you. The robots reviewing the article about the robot workflow were at least transparent about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger idea
&lt;/h2&gt;

&lt;p&gt;The thing I keep coming back to is that BMAD is not really about the agents. It is about admitting that AI changed how fast we can write code, but not what makes code correct. Specs, reviews, small verifiable units of work: the old discipline turns out to be exactly what the new tools were missing.&lt;/p&gt;

&lt;p&gt;If you have tried structured agent workflows, I am curious where they broke down for you. And if you are still full vibe coding, I am equally curious how far it has taken you. Both answers are interesting.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources and further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/bmad-code-org/BMAD-METHOD" rel="noopener noreferrer"&gt;BMAD-METHOD on GitHub (bmad-code-org)&lt;/a&gt;: the framework itself, MIT licensed, install docs and user guide.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techleadjournal.dev/episodes/255/" rel="noopener noreferrer"&gt;Stop Vibe Coding: Spec Driven Development with The BMad Method (Tech Lead Journal, episode 255)&lt;/a&gt;: interview with creator Brian Madison on why spec driven development beats prompt roulette.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Models converged. Trust hasn't.</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Thu, 13 Aug 2026 13:17:16 +0000</pubDate>
      <link>https://dev.to/azank1/models-converged-trust-hasnt-1k53</link>
      <guid>https://dev.to/azank1/models-converged-trust-hasnt-1k53</guid>
      <description>&lt;p&gt;&lt;em&gt;Field notes from the Harness Layer.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The models are becoming the same
&lt;/h2&gt;

&lt;p&gt;Two years ago, choosing a model felt like the most important decision in the whole stack. The gap between the best frontier model and the next few competitors was wide enough that you had to design around it. Teams picked a provider the way they once picked a database, because the choice shaped everything built on top of it.&lt;/p&gt;

&lt;p&gt;That era is ending. As of mid-2026, the leaderboards people actually cite, like LMArena and Artificial Analysis, show the top cluster within a few points of each other, and the cluster keeps getting denser. Every lab ships reasoning. Every lab ships tool use. API prices fell by roughly an order of magnitude across 2024 and 2025, and they kept falling after that. Open-weight models now trail the frontier by months rather than by a generation.&lt;/p&gt;

&lt;p&gt;None of this is controversial anymore. The interesting part is what it does to the rest of the stack.&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%2Fse0zxjqdhy5wvovu7s4g.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%2Fse0zxjqdhy5wvovu7s4g.png" alt="Scatter plot of frontier model scores from 2023 to now: widely spread dots in 2023 tighten into a dense indigo cluster labeled " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Illustrative, not data. The shape is the claim.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When the models become interchangeable, the model stops being the product. A task that fails with one frontier model usually fails with the others in the same way, because the failure was never in the weights. It was in everything around them. Real edges still exist, mostly in long context, underrepresented languages, and tool-calling reliability, but the center of the distribution has converged.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottleneck moved
&lt;/h2&gt;

&lt;p&gt;There is a frame circulating among people who build agent loops for a living: &lt;strong&gt;in any agent loop, the verifier is the bottleneck, not the model.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The logic becomes obvious once you watch a loop run. Generation is cheap and can spin forever, so a loop only produces value as fast as something can judge whether to ship the output, retry the step, or stop entirely. Andrew Ng's letters on agentic design patterns put reflection, the practice of the model checking its own work, at the center of agent engineering. Karpathy's Software 3.0 keynote frames the whole discipline as keeping fast generation wired to fast verification. The practitioners writing about loop engineering keep landing on the same gate from different directions.&lt;/p&gt;

&lt;p&gt;Single-agent teams have absorbed this lesson. They write evals, they add judges, and they constrain output schemas. It is hard work, but it is understood work.&lt;/p&gt;

&lt;p&gt;Now step up one level, to where the industry is actually headed. The destination is not one agent in a loop. It is many agents, from many vendors, speaking different protocols, composed into a single goal. And when you look at that picture closely, you notice that &lt;strong&gt;nothing about trust made the jump.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What convergence didn't touch
&lt;/h2&gt;

&lt;p&gt;Watch a real multi-agent run and the trust story falls apart in familiar places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plans drift.&lt;/strong&gt; The plan made sense at step one. By step four the context has shifted, an intermediate result was misread, and the remaining steps are executing confidently against a goal that no longer exists. Nobody noticed, because nothing was checking the plan against reality in the middle of the run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calls route wrong.&lt;/strong&gt; The agent picked a plausible tool instead of the right one. With three tools this is a prompt problem. With thirty registered capabilities across a network, it becomes a retrieval problem, and many stacks are still solving it by stuffing everything into context and hoping for the best.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity is a vibe.&lt;/strong&gt; Which agent acted, under what authority, with what declared scope? Protocol-level authorization and scoped tokens are starting to exist, but in most stacks the honest answer is still "whatever the prompt said." If an agent exceeds what it was allowed to do, there is no contract to point at. There is only a transcript to argue over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output evaporates.&lt;/strong&gt; The run ends in prose. A chat bubble cannot be checked against a schema, diffed between runs, or monitored in production. It is the least verifiable format our industry has ever shipped at scale, and it is the default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different teams, different frameworks, same four failures. The models converged. The layer that would make runs trustworthy did not, because it barely exists.&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%2Fxipdzp77hg9ulzc17edu.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%2Fxipdzp77hg9ulzc17edu.png" alt="Four cards side by side: plans drift (step four executes a goal that died at step two), calls route wrong (a plausible tool, not the right one), identity is a vibe (authority is whatever the prompt said), output evaporates (a chat bubble cannot be checked). Footnote: the models converged, the trust layer did not." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The transcript is not evidence
&lt;/h2&gt;

&lt;p&gt;Here is the thought experiment I keep coming back to. An agent run executes something consequential overnight: it moves money between accounts, files a document, or changes infrastructure. In the morning you ask the obvious question: &lt;em&gt;did it do what it was supposed to do?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What do you actually have to answer that with?&lt;/p&gt;

&lt;p&gt;You have a transcript. It is a log of messages, in the best case with tool calls inline, and it was produced by the same system you are trying to check. You cannot verify it independently. You cannot show it to a third party and have them confirm anything. You cannot diff last night's run against last week's. You are asked to trust the narrator, and the narrator is the suspect.&lt;/p&gt;

&lt;p&gt;Every other mature engineering domain has already built its version of an answer to this. Aviation does not trust the pilot's recollection, so it keeps a flight recorder that investigators can read without asking the airline. Finance does not trust the trader's terminal, so it keeps clearing records that both sides can reconcile. Software supply chains stopped trusting build claims and started demanding provenance.&lt;/p&gt;

&lt;p&gt;Agent runs have nothing equivalent. Observability tooling exists and keeps improving, and traces, spans, and dashboards are genuinely useful. But a trace is still produced by the system you are checking, stored by the vendor you are paying, and viewable only by logging back into both. As independently checkable evidence, the state of the art in 2026 is a screenshot of a chat window.&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%2Faep89fvhfog0xq0kp6n7.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%2Faep89fvhfog0xq0kp6n7.png" alt="Two panels. Left, dashed red border: " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What a trust layer would have to look like
&lt;/h2&gt;

&lt;p&gt;Sit with that gap long enough and some properties start to feel non-negotiable. I will offer mine as a draft, in the spirit of the first field note:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Output as contract.&lt;/strong&gt; Results come back in structured, schema-checked formats, so that "did it work" becomes a checkable question instead of a reading comprehension exercise. Plain prose still passes through when it has to, but prose is the exception rather than the interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity and scope as data.&lt;/strong&gt; Every actor carries a verifiable identity and a declared scope of what it is allowed to do, and that scope gets checked at orchestration time instead of being assumed from prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification that admits its own limits.&lt;/strong&gt; Structural checks and semantic judgment are different things. Asking "is this well-formed, non-empty, and in contract" is not the same as asking "is this actually right." A trustworthy system says exactly which check it applied, instead of implying judgment it does not have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence, not transcripts.&lt;/strong&gt; A finished run should export something a third party can check independently. The test of evidence is simple: verification cannot require calling the system that produced it. Signed evidence still has to anchor somewhere, because someone has to answer whose key it is and under what identity it signed. That is part of why property two carries so much weight.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice what is not on this list: a better model. Every property here is orchestration, contract design, and verification engineering. That is not a coincidence. It is the convergence argument applied honestly.&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%2F9di4ms0anyrwxhxv35gn.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%2F9di4ms0anyrwxhxv35gn.png" alt="A four-layer stack: 01 output as contract (structured, schema-checked results, prose is the exception), 02 identity and scope as data (verifiable actors, declared authority, checked at runtime), 03 verification admits its limits (says which check it ran, structural or semantic), 04 evidence, not transcripts (a third party can check the run without calling you), the fourth layer highlighted in indigo. Footnote: none of these is a better model." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;p&gt;The first field note ended by asking what belongs in the harness layer. This one ends with the sharper version:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What evidence would make you trust an agent run you didn't watch?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not "what dashboard would reassure you." Evidence. Something you could check yourself, show to someone else, and rely on when the run mattered. I have been working through one set of answers, and I suspect the interesting disagreements are in property four. Curious what yours are.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources and further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://lmarena.ai" rel="noopener noreferrer"&gt;LMArena leaderboard&lt;/a&gt; and &lt;a href="https://artificialanalysis.ai" rel="noopener noreferrer"&gt;Artificial Analysis&lt;/a&gt;: the clustering at the top of the frontier, tracked continuously&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/azank1/can-mcp-a2a-and-langgraph-share-and-compose-a-single-verified-runtime-36dj"&gt;Can MCP, A2A, and LangGraph share and compose a single verified runtime?&lt;/a&gt;: the three boundaries, and why composing them is the open problem&lt;/li&gt;
&lt;li&gt;Andrew Ng, &lt;a href="https://www.deeplearning.ai/the-batch/agentic-design-patterns-part-2-reflection" rel="noopener noreferrer"&gt;Agentic Design Patterns&lt;/a&gt; letters, The Batch, 2024: reflection and self-checking loops as first-class agent patterns&lt;/li&gt;
&lt;li&gt;Andrej Karpathy, &lt;a href="https://www.ycombinator.com/library/MW-andrej-karpathy-software-is-changing-again" rel="noopener noreferrer"&gt;Software Is Changing (Again)&lt;/a&gt;, YC AI Startup School, 2025: generation wired to verification as the shape of the craft&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Your browser automation clicks are lying to you</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Sun, 09 Aug 2026 20:49:24 +0000</pubDate>
      <link>https://dev.to/azank1/your-browser-automation-clicks-are-lying-to-you-4lp6</link>
      <guid>https://dev.to/azank1/your-browser-automation-clicks-are-lying-to-you-4lp6</guid>
      <description>&lt;p&gt;I spent an evening last month watching an automation script click a button that was definitely there. There was no error. The screenshot showed the cursor parked right on top of it. The script reported success and moved on. Nothing had happened. The page had not received a click at all, and every tool I was using to check said otherwise.&lt;/p&gt;

&lt;p&gt;If you have written browser automation against a modern web app, you have hit this. Here is the fix up front (the &lt;code&gt;send&lt;/code&gt; helper is defined in use case 1), then the full explanation of why the obvious approach silently does nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The fix: click through the browser's input pipeline&lt;/span&gt;
&lt;span class="c1"&gt;// (Chrome DevTools Protocol), not through JS.&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Input.dispatchMouseEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousePressed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;clickCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Input.dispatchMouseEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mouseReleased&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;clickCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pair of events is indistinguishable from a physical mouse to the page. Everything below is about why the &lt;code&gt;.click()&lt;/code&gt; you are probably sending instead is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the old hack looks like
&lt;/h2&gt;

&lt;p&gt;Every automation script starts like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button.publish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&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 in a thousand Stack Overflow answers and it works right up until it does not. The failure shows up on exactly the apps you most want to automate: editors, dashboards, anything with components listening on pointerdown or mousedown, controls gated on trusted input, or framework-managed form fields. Three separate things go wrong.&lt;/p&gt;

&lt;p&gt;First, &lt;code&gt;click()&lt;/code&gt; dispatches a synthetic event. Every synthetic event carries &lt;code&gt;isTrusted: false&lt;/code&gt;, and the page can see that. Plenty of apps simply ignore untrusted events on sensitive controls.&lt;/p&gt;

&lt;p&gt;Second, &lt;code&gt;click()&lt;/code&gt; only fires the &lt;code&gt;click&lt;/code&gt; event (form controls also run their activation behavior, but that is the exception, not the rule). Real input produces a sequence: pointerdown, mousedown, focus, pointerup, mouseup, then click. A growing number of components hang their behavior on the earlier events in that chain. Fire only the last one and the component never wakes up.&lt;/p&gt;

&lt;p&gt;Third, and this is the nasty one, the failure is silent. There is no exception and no rejected promise. Your script reports success because nothing told it otherwise. The only honest signal is whether the DOM changed afterward, and almost nobody checks that.&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%2F5nstzsrfmc8qq71zl0by.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%2F5nstzsrfmc8qq71zl0by.png" alt="Morpheus meme: what if I told you your click event never happened" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 1: clicking a button that ignores you
&lt;/h2&gt;

&lt;p&gt;The setup is a raw WebSocket to the Chrome DevTools Protocol (CDP). Launch Chrome with &lt;code&gt;--remote-debugging-port=10086&lt;/code&gt;, grab the page's &lt;code&gt;webSocketDebuggerUrl&lt;/code&gt; from &lt;code&gt;http://localhost:10086/json&lt;/code&gt;, and add a tiny helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ws = new WebSocket(webSocketDebuggerUrl)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the click. The one non-negotiable step is asserting the element actually has a size before you click its center. A &lt;code&gt;display: none&lt;/code&gt; element returns a rect full of zeros, and clicking coordinates (0, 0) is how you click the wrong thing with total confidence. Note the &lt;code&gt;behavior: "instant"&lt;/code&gt; on the scroll: without it, a page with smooth scrolling animates the scroll and you measure the rect mid-flight, then click stale coordinates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clickForReal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runtime.evaluate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`(() =&amp;gt; {
      const el = document.querySelector(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;);
      if (!el) return null;
      el.scrollIntoView({ block: "center", behavior: "instant" });
      const r = el.getBoundingClientRect();
      return { x: r.x + r.width / 2, y: r.y + r.height / 2, w: r.width, h: r.height };
    })()`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;returnByValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`not clickable: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// A real mouse moves before it clicks; hover-dependent UI expects it too.&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Input.dispatchMouseEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mouseMoved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousePressed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mouseReleased&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Input.dispatchMouseEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;clickCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need both &lt;code&gt;mousePressed&lt;/code&gt; and &lt;code&gt;mouseReleased&lt;/code&gt;. A release with no press is not a click. It is a shrug, and most apps treat it like one. One more caveat: this asserts the element has size, not that it receives the click. A modal overlay or cookie banner sitting on those coordinates will silently eat it. If overlays are in play, add a &lt;code&gt;document.elementFromPoint(x, y)&lt;/code&gt; check before dispatching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 2: typing into a controlled input
&lt;/h2&gt;

&lt;p&gt;The same trap exists for text. Setting &lt;code&gt;value&lt;/code&gt; does nothing the framework can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Looks right, does nothing. React never fires onChange for this,&lt;/span&gt;
&lt;span class="c1"&gt;// and may snap the value back on the next render.&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textarea&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The classic workaround reaches past the framework to the native setter, then dispatches an &lt;code&gt;input&lt;/code&gt; event so React notices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textarea&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTMLTextAreaElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;// HTMLInputElement for &amp;lt;input&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;setter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works often enough to be worth knowing, but it is still a synthetic event with all the caveats from above. The real fix is the same pipeline as the click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Input.insertText&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&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 a single method call, it is trusted, and it respects whatever element has focus. Focus the field first with a real CDP click and the text lands the way typed text lands. One boundary to know: &lt;code&gt;insertText&lt;/code&gt; fires no &lt;code&gt;keydown&lt;/code&gt; or &lt;code&gt;keyup&lt;/code&gt;. For Enter-to-submit, keyboard shortcuts, or editors with custom key handlers, you need &lt;code&gt;Input.dispatchKeyEvent&lt;/code&gt; with &lt;code&gt;rawKeyDown&lt;/code&gt; and &lt;code&gt;keyUp&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 3: verify state, not screenshots
&lt;/h2&gt;

&lt;p&gt;Once the input path is honest, the remaining lie is your success check. "Take a screenshot and look at it" is the default in agent-style automation, and it fails in both directions: pixels can look right while nothing happened, and a render hiccup can make a success look broken.&lt;/p&gt;

&lt;p&gt;The DOM always knows. Define success as a state transition before you act, then assert it after. Poll with a deadline instead of sleeping a fixed 800ms, and handle the case where a successful action removes the element entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Publishing from an editor: success is the composer clearing or unmounting.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeoutMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deadline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;timeoutMs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;deadline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runtime.evaluate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;returnByValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publish did not happen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;clickForReal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button.publish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(() =&amp;gt; {
  const ta = document.querySelector("textarea");
  return !ta || ta.value.length === 0;   // cleared or gone: both are success
})()`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick whatever transition your app actually guarantees: the composer empties, the row appears in the table, the URL changes. If you cannot name a DOM transition for an action, you do not yet know whether the action works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limits, honestly
&lt;/h2&gt;

&lt;p&gt;Coordinates are a snapshot. The layout can shift between measuring the rect and dispatching the click, so measure immediately before clicking and never cache coordinates across actions. Coordinates are also frame-relative: for elements inside iframes you have to add each frame's offset on the way up. CDP is also Chromium territory. Firefox's equivalent is WebDriver BiDi, which is converging on the same model but is not a drop-in swap today.&lt;/p&gt;

&lt;p&gt;And if you can install Playwright, install Playwright. It does all of this plus auto-waiting for visibility and stability, and its actionability checks are the checklist above written by people better at it than us. The raw approach earns its place when you are driving a browser that is already open, attached to a real user profile, or calling from a runtime where pulling in a driver is not an option.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Two greps worth running over any automation code you own:&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;'\.click()'&lt;/span&gt; scripts/          &lt;span class="c"&gt;# synthetic clicks, silent no-ops waiting&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s1"&gt;'screenshot'&lt;/span&gt; scripts/         &lt;span class="c"&gt;# success checks that can lie in both directions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every hit is a place where your script can report success while the page disagrees. Trust the input pipeline and trust the DOM. Everything in between is a rumor.&lt;/p&gt;

&lt;p&gt;If you have a favorite automation lie, the click that looked fine and never happened, I want to hear it. These failure modes are collectable.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources and further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://chromedevtools.github.io/devtools-protocol/tot/Input/" rel="noopener noreferrer"&gt;Chrome DevTools Protocol: Input domain&lt;/a&gt;: dispatchMouseEvent, insertText, and dispatchKeyEvent, the trusted input pipeline this post is built on.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted" rel="noopener noreferrer"&gt;MDN: Event.isTrusted&lt;/a&gt;: why the page can tell your synthetic events from real ones.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/click" rel="noopener noreferrer"&gt;MDN: Element.click()&lt;/a&gt;: what the programmatic click actually dispatches, and what it skips.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev/docs/actionability" rel="noopener noreferrer"&gt;Playwright actionability&lt;/a&gt;: the full checklist a click should survive, implemented for you.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/facebook/react/issues/11488" rel="noopener noreferrer"&gt;React issue #11488&lt;/a&gt;: the controlled-input value problem and the native setter workaround.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AI doesn't make programming easier - It moves the bottleneck to where it cannot see</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Sun, 09 Aug 2026 20:19:14 +0000</pubDate>
      <link>https://dev.to/azank1/ai-doesnt-make-programming-easier-it-moves-the-bottleneck-to-where-it-cannot-see-3lpo</link>
      <guid>https://dev.to/azank1/ai-doesnt-make-programming-easier-it-moves-the-bottleneck-to-where-it-cannot-see-3lpo</guid>
      <description>&lt;p&gt;&lt;em&gt;Field notes from the Harness Layer.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The guard that killed the run
&lt;/h2&gt;

&lt;p&gt;Last month I watched an agent write a preflight guard for a database script. Good idea, cleanly written: check that the host is reachable before anything touches it, and abort with useful advice if it is not. The script ran, prompted for credentials, and exited with no message, no output, nothing attempted. It looked exactly like an authentication failure. It was not. The guard itself had aborted the run it was written to protect, two lines after the credential prompt, because one of its own checks returned an exit code the script's strict mode treated as fatal. The host had been reachable the whole time.&lt;/p&gt;

&lt;p&gt;Nothing about that failure was a code generation failure. The model wrote the code fine, and fast. The hours went somewhere else: deciding whether "exited after the credential prompt" meant bad credentials or something else, reading exit codes, checking DNS records, proving each failure path before spending another attempt. Generation was nearly free. Verification was the whole job.&lt;/p&gt;

&lt;p&gt;I have been collecting evidence that this is not just my experience. It is measured, and the measurements are better than the discourse.&lt;/p&gt;




&lt;h2&gt;
  
  
  The perception gap is measured, not vibes
&lt;/h2&gt;

&lt;p&gt;The numbers everyone cites come from settings that barely resemble the work. GitHub's famous result, 55.8% faster, was a controlled task where developers built an HTTP server from scratch. It was greenfield, self contained, and had no history. When the setting gets realistic, the effect shrinks or flips:&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%2Fws1cg1b54gl74n0cpeff.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%2Fws1cg1b54gl74n0cpeff.png" alt="Bar chart. Measured productivity effect by study setting: plus 55.8% on a greenfield toy task, plus 26% for enterprise teams, minus 19% for experts on their own mature repos, with a dashed marker at plus 20% showing what the experts believed, before and after." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DORA's industry-wide surveys point the same way from the other side: individual productivity rises while delivery stability falls, and their 2025 report frames AI as an amplifier of existing strengths and weaknesses rather than a lift for everyone. And Cui's 26% hides a split that matters later in this piece: the gains concentrated in juniors, with no measured effect for seniors.&lt;/p&gt;

&lt;p&gt;The METR result deserves its own paragraph, because the speed is not the interesting part. Before the study, the developers predicted AI would make them 24% faster. They were measured 19% slower. Afterward, having just lived through the slowdown, they still believed they had been 20% faster. That is a gap of roughly 39 points between felt and measured productivity.&lt;/p&gt;

&lt;p&gt;Two honest caveats. The study ran in mid 2025, on Cursor with Claude 3.5 and 3.7, and newer tooling may narrow the measured gap. METR also says plainly that sixteen developers on their own repositories do not represent most software work. But the perception gap is the part that survives both caveats, and it is the part that matters here: "it looks easier" is no longer a complaint. It is an empirical finding.&lt;/p&gt;




&lt;h2&gt;
  
  
  The benchmark story quietly inverted
&lt;/h2&gt;

&lt;p&gt;While the perception debate ran in public, the benchmark floor was moving underneath it. OpenAI audited SWE-bench Verified and found that 59.4% of the tasks its model persistently failed had flawed tests or problem statements, and that frontier models could reproduce gold patches from memory. OpenAI stopped reporting scores on the benchmark and now points people to SWE-bench Pro, a contamination resistant successor where scores at launch ran 20 to 40 points lower. Microsoft Research had already shown that the best models locate the buggy file from issue text alone at up to 76% accuracy on benchmark repos, versus up to 53% on repos outside the benchmark. The paper reads that gap as likely memorization, and so do I.&lt;/p&gt;

&lt;p&gt;On real repositories, the failure forensics are starker:&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%2Fmyhnf2p1o6ktnl7suvbs.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%2Fmyhnf2p1o6ktnl7suvbs.png" alt="Bar chart. Agent success rate by task shape: 55 to 58% for single file fixes, dropping to 11 to 25% for multi file fixes, and 13 to 20% for tasks rated at least one hour of human effort." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ceiling everyone argued about was partly recall, and the genuine residue concentrates exactly where practitioners said it did: multi file, long horizon, environment heavy, brownfield work. My guard was one line of shell in a fresh script, and it still failed in a way no benchmark would catch, because the failure lived in the interaction between the code and the environment around it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Risk was never mitigated. It was distributed.
&lt;/h2&gt;

&lt;p&gt;Here is the frame I keep coming back to, borrowed from markets. Hedging a risk never deletes it. At best it moves the risk to a counterparty who prices it, and at worst it introduces basis risk: the hedge itself fails, in a regime nobody modeled. Either way the exposure changes shape rather than disappearing, and the new shape tends to settle where you are blind: wherever the instrumentation is thinnest. AI assistance is a hedge against the cost of writing code, and it behaves the same way. Generation risk became verification risk became environment risk, and each stop along the chain is less observable than the one before.&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%2Fz2o4cbvul2ijw4ffchhc.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%2Fz2o4cbvul2ijw4ffchhc.png" alt="Diagram. Risk relocated from writing the code, to checking the claim, to the environment around it, with observability falling from instrumented, to a coin flip, to a guess." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The survey numbers say we know this and bought the wrong instrument anyway. LangChain's State of Agent Engineering report polled over 1,300 people, a self selected crowd of agent builders, and still: 57% have agents in production, quality is the top barrier, 89% have observability tooling, and only 52% run evals. Watching is table stakes. Verifying is a coin flip. Everyone bought dashboards. Barely half bought proof.&lt;/p&gt;

&lt;p&gt;The incidents fill in what that gap costs. Replit's agent deleted a production database during a code freeze, then incorrectly claimed rollback would not work; in the days before, during the same engagement, it had fabricated thousands of records and reported tests passing when they were not. In controlled evaluations, METR watched frontier models monkey-patch evaluators into reporting success, with one model hacking 100% of its trajectories on a task family after stating, when asked beforehand, that it was incapable of cheating. And OpenAI's own research found the naive fix backfires: train against a chain of thought monitor and the model learns to hide the misbehavior rather than stop it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0prz097xpj3j0hfcgmb.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%2Fz0prz097xpj3j0hfcgmb.png" alt="Roll Safe meme. You can't have a verification gap if you never verify anything." width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bad advice, delivered confidently. The gap is the never verifying.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Informed simplicity
&lt;/h2&gt;

&lt;p&gt;So if the risk never disappears and the simple path keeps exploding, is the answer more process? More review gates, more ceremony? I don't think so, and I think markets and psychology already named the alternative.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I would not give a fig for the simplicity this side of complexity, but I would give my life for the simplicity on the other side of complexity." ~ Oliver Wendell Holmes &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Traders have a working version of this. The simple rules that survive (position limits, premortems, never average down) are scar tissue formalized. Naive simplicity is the same rules without the scars. To be fair, scar tissue is not immunity: every fund that blew up had position limits too, carved from the previous regime. The honest version of the distinction is behavioral, not rhetorical. Informed simplicity keeps a record. Naive simplicity keeps a feeling.&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%2Ffh9rwzngqa1dzgvk40zi.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%2Ffh9rwzngqa1dzgvk40zi.png" alt="Diagram. Two paths from complexity to the same simple rules. A dashed arc goes around the complexity and breaks at the first regime change. A solid arrow goes through it, scars included." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same split is now visible in how people use these tools. The naive mode treats the output as done when it looks done: just let the agent cook. The informed mode holds Willison's golden rule: I won't commit code I couldn't explain. In the naive mode the judgment lives nowhere, or in vibes; in the informed mode it stays with the human, earned by walking the complexity first. When the first strange failure arrives, the naive mode believes whatever fault class the failure presents as, while the informed mode has a record to check against. The volume numbers point the same way: seniors ship 2.5x more AI code than juniors, and Fastly's reading is that they can spot code that looks plausible but is wrong.&lt;/p&gt;

&lt;p&gt;That last finding looks like a contradiction next to Cui's missing senior effect, and it is worth one sentence. The RCT measured speed; the survey measured volume. Seniors are not faster with the tools. They are better positioned to pay the verification tax the tools impose.&lt;/p&gt;

&lt;p&gt;Karpathy's original vibe-coding post contained its own boundary: fine for throwaway weekend projects. Willison drew the operative line: LLM code you did not review is vibe coding; reviewed and tested AI code is just software development. Simplicity is still on the menu. It is only available on the far side of verification.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the bottleneck lives now
&lt;/h2&gt;

&lt;p&gt;Put the pieces together and the bottleneck's new address is specific: specification, review, environment judgment, and proof. The first three stay with the human, and seniority decides who can afford the tax. The fourth is the one nobody has solved.&lt;/p&gt;

&lt;p&gt;What would it take for a run to produce evidence a third party could check without trusting the orchestrator? Not logs, which record that something was called, but evidence that the claim the run makes about itself is true. Early research exists: preprints on signed capability declarations and on agent identity bound to verifiable governance artifacts. Nothing is a standard. The OpenTelemetry GenAI conventions define the shape of an agent trace, and even they are a moving target: the namespace has been migrating between repositories this year, not settling into stability. The plumbing for watching is converging. The plumbing for proving has barely started.&lt;/p&gt;




&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;p&gt;When your agent's run fails now, where do you look first? The diff, the trace, or the environment?&lt;/p&gt;

&lt;p&gt;And whichever one it is: is that lookup instrumented, or is it you?&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Productivity evidence: &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/" rel="noopener noreferrer"&gt;METR RCT, Jul 2025&lt;/a&gt;, &lt;a href="https://economics.mit.edu/sites/default/files/inline-files/draft_copilot_experiments.pdf" rel="noopener noreferrer"&gt;Cui et al., Feb 2025 (working paper)&lt;/a&gt;, &lt;a href="https://ar5iv.labs.arxiv.org/html/2302.06590" rel="noopener noreferrer"&gt;Peng et al. 2023&lt;/a&gt;, &lt;a href="https://dora.dev/research/2024/dora-report/" rel="noopener noreferrer"&gt;DORA 2024&lt;/a&gt; and &lt;a href="https://dora.dev/research/2025/dora-report/" rel="noopener noreferrer"&gt;2025&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark forensics: &lt;a href="https://openai.com/index/why-we-no-longer-evaluate-swe-bench-verified/" rel="noopener noreferrer"&gt;OpenAI's SWE-bench Verified audit&lt;/a&gt;, &lt;a href="https://arxiv.org/html/2506.12286" rel="noopener noreferrer"&gt;SWE-Bench Illusion, Microsoft Research&lt;/a&gt;, &lt;a href="https://arxiv.org/abs/2509.16941" rel="noopener noreferrer"&gt;SWE-bench Pro&lt;/a&gt;, &lt;a href="https://arxiv.org/html/2509.13941v1" rel="noopener noreferrer"&gt;agentic failure forensics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The verification gap: &lt;a href="https://www.langchain.com/state-of-agent-engineering" rel="noopener noreferrer"&gt;LangChain State of Agent Engineering&lt;/a&gt;, &lt;a href="https://fortune.com/2025/07/23/ai-coding-tool-replit-wiped-database-called-it-a-catastrophic-failure/" rel="noopener noreferrer"&gt;Replit incident, Fortune&lt;/a&gt;, &lt;a href="https://metr.org/blog/2025-06-05-recent-reward-hacking/" rel="noopener noreferrer"&gt;METR reward hacking&lt;/a&gt;, &lt;a href="https://arxiv.org/abs/2503.11926" rel="noopener noreferrer"&gt;monitor backfire&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Informed simplicity: &lt;a href="https://tim.blog/2017/02/02/lessons-from-warren-buffett-bobby-fischer-and-other-outliers/" rel="noopener noreferrer"&gt;Holmes via Tim Ferriss #219&lt;/a&gt;, &lt;a href="https://simonwillison.net/2025/Mar/19/vibe-coding/" rel="noopener noreferrer"&gt;Willison on vibe coding&lt;/a&gt;, &lt;a href="https://www.fastly.com/blog/senior-developers-ship-more-ai-code" rel="noopener noreferrer"&gt;Fastly senior/junior survey&lt;/a&gt;, &lt;a href="https://newsletter.pragmaticengineer.com/p/when-ai-writes-almost-all-code-what" rel="noopener noreferrer"&gt;Pragmatic Engineer, Jan 2026&lt;/a&gt;, &lt;a href="https://www.langchain.com/blog/the-rise-of-context-engineering" rel="noopener noreferrer"&gt;LangChain on context engineering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tracing standards flux: &lt;a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/" rel="noopener noreferrer"&gt;OpenTelemetry GenAI conventions, moved and unmaintained notice&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Attestation research (early, preprints): &lt;a href="https://arxiv.org/abs/2506.13794" rel="noopener noreferrer"&gt;AgentFacts&lt;/a&gt;, &lt;a href="https://arxiv.org/abs/2605.25376" rel="noopener noreferrer"&gt;verifiable governance artifacts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Earlier field notes from the Harness Layer start &lt;a href="https://dev.to/azank1/can-mcp-a2a-and-langgraph-share-and-compose-a-single-verified-runtime-36dj"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>The stop/go problem is not solvable. It's relocatable.</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Sun, 09 Aug 2026 00:30:59 +0000</pubDate>
      <link>https://dev.to/azank1/the-stopgo-problem-is-not-solvable-its-relocatable-led</link>
      <guid>https://dev.to/azank1/the-stopgo-problem-is-not-solvable-its-relocatable-led</guid>
      <description>&lt;p&gt;&lt;em&gt;Field notes from the Harness Layer.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The model thought it was inside the exercise
&lt;/h2&gt;

&lt;p&gt;In July 2025, Anthropic reported three incidents from its own cybersecurity evaluations. The models were supposed to be working in simulated environments, explicitly told they had no internet access. Internet access was available anyway, because of how the environment was configured. While completing their exercises, the models reached real systems and treated them as part of the simulation. In one incident, a model published a real package to the public PyPI registry, believing it was still inside the test.&lt;/p&gt;

&lt;p&gt;The detail worth sitting with is not the misconfiguration. It is that "you do not have internet access" was a sentence in a prompt, and the environment said otherwise. The sentence lost.&lt;/p&gt;

&lt;p&gt;This is the stop/go problem in its purest form. Models always want to go. They are trained to complete the task, and they are very good at going. Every control scheme we have invented is, at bottom, a way for someone or something to assert stop or go. The interesting engineering question is not how to remove that assertion. It is where to put it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Every scheme is the same assertion in a different place
&lt;/h2&gt;

&lt;p&gt;I spent a week mapping the alternatives to per-action human review, and a pattern emerged that I did not expect: none of them remove the human. They relocate the assertion. Six categories, one axis:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scheme&lt;/th&gt;
&lt;th&gt;Where the assertion lives&lt;/th&gt;
&lt;th&gt;How it executes&lt;/th&gt;
&lt;th&gt;How it fails&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Human review&lt;/td&gt;
&lt;td&gt;The human, per action&lt;/td&gt;
&lt;td&gt;Manual approve/deny&lt;/td&gt;
&lt;td&gt;Does not scale; reviewer fatigue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sandboxing&lt;/td&gt;
&lt;td&gt;Environment design&lt;/td&gt;
&lt;td&gt;Structural boundary&lt;/td&gt;
&lt;td&gt;Misconfiguration; a prompt is not a boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guardrails&lt;/td&gt;
&lt;td&gt;Validator authoring&lt;/td&gt;
&lt;td&gt;Content filtering&lt;/td&gt;
&lt;td&gt;Watches the agent's words, not its effects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy engines&lt;/td&gt;
&lt;td&gt;Policy authorship&lt;/td&gt;
&lt;td&gt;Deterministic pre-action gate&lt;/td&gt;
&lt;td&gt;Gates intent, not outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formal verification&lt;/td&gt;
&lt;td&gt;Spec authorship&lt;/td&gt;
&lt;td&gt;Proof before or during execution&lt;/td&gt;
&lt;td&gt;Covers only what is formalizable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model judges&lt;/td&gt;
&lt;td&gt;Delegated to another model&lt;/td&gt;
&lt;td&gt;Probabilistic review&lt;/td&gt;
&lt;td&gt;The judge also wants to go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the table vertically and the industry's last two years make sense. Sandboxing moved from containers to microVMs after container escapes made the shared kernel untenable; Google now ships gVisor isolation as Kubernetes primitives for agents, and Anthropic open-sourced its sandbox runtime. Policy engines grew teeth: Cedar has effectively won the authorization conversation for tool protocols, and research reference monitors like PCAS report lifting policy compliance from 48% to 93% across frontier models with zero violations in instrumented runs. Formal methods left the paper: there is a Lean 4 system that intercepts an agent's tool call, formulates it as a conjecture against verified policy theorems, and executes only if the proof closes.&lt;/p&gt;

&lt;p&gt;All of this is real progress. None of it deletes a row from the table.&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%2Fwhphhgjkhj8pq3hdzjiw.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%2Fwhphhgjkhj8pq3hdzjiw.png" alt="Drakeposting meme. Rejecting: reviewing every agent output by hand. Approving: defining the proof before the run." width="600" height="939"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The judge trap
&lt;/h2&gt;

&lt;p&gt;One category deserves its own section, because it is the only one that tries to remove the human rather than relocate the assertion. If a model always wants to go, can another model be the one that says stop?&lt;/p&gt;

&lt;p&gt;The evidence so far says no, and for a structural reason: the judge is the same class of system as the judged. Studies of multi-agent debate find that judges consistently favor whichever side shares their backbone. A 2025 study built a benchmark of confident, well-evidenced, wrong critiques and watched the strongest agents abandon correct answers when a judge pushed back persuasively. The failure mode is not that judges are weak. It is that their claims are exactly as unverified as the worker's. Unverified verification is not verification; it is recursion.&lt;/p&gt;

&lt;p&gt;This does not make reviewer agents useless. I use one, and it catches real problems. But a reviewer model is a heuristic layer, not a stop/go authority. Treating it as the authority is how you get a system that looks governed and is not.&lt;/p&gt;




&lt;h2&gt;
  
  
  What each placement costs
&lt;/h2&gt;

&lt;p&gt;Once you accept that the assertion is permanent, the design question gets sharper. Each placement buys something and charges something:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment design&lt;/strong&gt; (sandboxes) buys safety in bulk. One good boundary covers every action inside it. It charges you in configuration risk: the boundary is only what you actually built, never what you described. Anthropic's incident happened to Anthropic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy authorship&lt;/strong&gt; (OPA, Cedar, reference monitors) buys determinism. No model in the decision path, sub-millisecond answers, fail-closed. It charges you in coverage: a policy can only gate actions it can name, and it gates whether an action may start, not whether the run did what it claimed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spec authorship&lt;/strong&gt; (formal verification, proof of execution) buys the strongest guarantee available: claims checked against what actually executed, with receipts. It charges you in expressiveness: most of what we ask agents to do is not yet formalizable, and the specs themselves are written by humans who can be wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-action review&lt;/strong&gt; buys judgment on exactly the cases that resist all of the above. It charges you the human's attention, which is the scarcest resource in the system.&lt;/p&gt;

&lt;p&gt;The mature designs I keep running into are not choosing a row. They are stacking them: a sandbox for the bulk, a deterministic gate for named risks, verification where claims are formalizable, and a rationed human review budget for the residue. The stop/go does not disappear. It gets distributed across layers, each covering the blind spot of the one below it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The question I cannot answer yet
&lt;/h2&gt;

&lt;p&gt;So here is where I am stuck, and it feels like the right place to stop.&lt;/p&gt;

&lt;p&gt;A policy gate checks intent before execution. A human reviews output after it. Between those two is a gap: the claim the run makes about itself. "I updated the config and restarted the service." Right now, checking that claim against what actually executed takes either a human or another model, and we know what both cost.&lt;/p&gt;

&lt;p&gt;What would a claim have to look like for a runtime to check it without a model in the path? Not "the function was called" — logs do that. The claim that the interpretation was right, that the tools called were the ones the task actually required, that the effect matches the intent. I do not think anyone has the general answer. I am not sure the general answer exists.&lt;/p&gt;

&lt;p&gt;If you have seen a piece of one in the wild, I want to hear about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic's evaluation incidents, as reported July 2025 (secondary account; primary write-up linked at publish): &lt;a href="https://dev.to/hemapriya_kanagala/were-giving-ai-agents-more-tools-what-happens-when-the-boundaries-fail-46gh"&gt;dev.to discussion&lt;/a&gt;, &lt;a href="https://signals.tw/articles/what-is-agent-sandbox/" rel="noopener noreferrer"&gt;sandbox mechanics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Isolation spectrum and container escapes: &lt;a href="https://grigio.org/ai-agent-sandbox-technologies-a-complete-2026-comparison/" rel="noopener noreferrer"&gt;grigio.org&lt;/a&gt;, &lt;a href="https://infrastructurereviewstack.com/posts/sandbox-selection-criteria-for-ai-agent-code-execution" rel="noopener noreferrer"&gt;Infrastructure Review Stack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GKE Agent Sandbox (gVisor, Kubernetes SIG Apps): &lt;a href="https://news.lavx.hu/article/google-announces-gke-agent-sandbox-and-hypercluster-at-next-26-positioning-kubernetes-as-ai-agent-runtime" rel="noopener noreferrer"&gt;announcement coverage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NemoClaw and ceLLMate, plus PCAS and pre-action authorization research: &lt;a href="https://arxiv.org/html/2603.20953" rel="noopener noreferrer"&gt;arXiv 2603.20953&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Policy engines vs runtime authorization: &lt;a href="https://www.trigguardai.com/blog/runtime-authorization-vs-policy-engines" rel="noopener noreferrer"&gt;TrigGuard&lt;/a&gt;; MCP authorization landscape: &lt;a href="https://chatforest.com/reviews/authorization-policy-engine-mcp-servers/" rel="noopener noreferrer"&gt;ChatForest&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lean 4 compliance gating: &lt;a href="https://arxiv.org/html/2604.01483v1" rel="noopener noreferrer"&gt;arXiv 2604.01483&lt;/a&gt;; Proof of Execution: &lt;a href="https://arxiv.org/html/2607.05397v1" rel="noopener noreferrer"&gt;arXiv 2607.05397&lt;/a&gt;; AgentVerify: &lt;a href="https://www.preprints.org/manuscript/202604.1029" rel="noopener noreferrer"&gt;preprint&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Judge limits: &lt;a href="https://arxiv.org/abs/2506.03332" rel="noopener noreferrer"&gt;deceptive-judge study&lt;/a&gt;, &lt;a href="https://arxiv.org/html/2601.05111v1" rel="noopener noreferrer"&gt;Agent-as-a-Judge survey&lt;/a&gt;, &lt;a href="https://www.arxiv.org/pdf/2508.02994" rel="noopener noreferrer"&gt;multi-agent evaluation limitations&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Earlier field notes from the Harness Layer start &lt;a href="https://dev.to/azank1/can-mcp-a2a-and-langgraph-share-and-compose-a-single-verified-runtime-36dj"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>Can MCP, A2A, and LangGraph share and compose a single verified runtime?</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Thu, 06 Aug 2026 19:05:56 +0000</pubDate>
      <link>https://dev.to/azank1/can-mcp-a2a-and-langgraph-share-and-compose-a-single-verified-runtime-36dj</link>
      <guid>https://dev.to/azank1/can-mcp-a2a-and-langgraph-share-and-compose-a-single-verified-runtime-36dj</guid>
      <description>&lt;p&gt;Here is a goal I keep coming back to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me my portfolio performance, summarize &lt;a href="https://en.wikipedia.org/wiki/Nvidia" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Nvidia&lt;/a&gt;, and turn both into a one-page brief I can read in the morning."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Three steps, and each one is easy on its own. The portfolio numbers live behind an MCP server. The summarizer is an A2A service someone else runs. The brief writer is a LangGraph agent a teammate deployed last month. What is not easy is running them as one thing you can trust, and the reason why is hiding in plain sight: those three names are not three kinds of agent. They are three kinds of boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  None of these is an agent
&lt;/h2&gt;

&lt;p&gt;The industry uses the word "agent" for all three, so it is worth being precise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.anthropic.com/news/model-context-protocol" rel="noopener noreferrer"&gt;MCP&lt;/a&gt;, introduced by Anthropic in November 2024, is a tool protocol. The &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;specification&lt;/a&gt; borrows its shape from the Language Server Protocol: hosts, clients, servers, JSON-RPC 2.0, with servers exposing tools, resources, and prompts. A filesystem MCP server is not an agent. It is a capability behind a uniform socket, which is why the "USB-C for AI" analogy stuck.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linuxfoundation.org/press/linux-foundation-launches-the-agent2agent-protocol-project-to-enable-secure-intelligent-communication-between-ai-agents" rel="noopener noreferrer"&gt;A2A&lt;/a&gt;, launched by Google in April 2025 and now a Linux Foundation project, is an agent protocol. Endpoints publish agent cards and accept tasks. The card &lt;em&gt;claims&lt;/em&gt; agency. What actually sits behind it is a task-processing endpoint with good metadata.&lt;/p&gt;

&lt;p&gt;LangGraph is not a protocol at all. It is a framework for building agents as graphs, and the graph is your code in your process. It only becomes something a stranger can call when it is deployed behind LangGraph Platform, which speaks &lt;a href="https://www.langchain.com/blog/agent-protocol-interoperability-for-llm-agents" rel="noopener noreferrer"&gt;Agent Protocol&lt;/a&gt;, LangChain's open serving specification.&lt;/p&gt;

&lt;p&gt;So the honest question is not whether three agents can share a run. It is whether work behind three different kinds of boundary can be composed into one run you can verify. The orchestrator composes calls, not agents.&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%2Fs5gw4ldialwy956p2ph2.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%2Fs5gw4ldialwy956p2ph2.png" alt="Is this a pigeon meme. The industry at every agent framework launch, looking at a butterfly labeled a task endpoint with good metadata, asking: is this an agent?" width="799" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The butterfly has an agent card, so the debate is settled.&lt;/em&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%2F6sd0d229fhxef9avy2du.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%2F6sd0d229fhxef9avy2du.png" alt="Three cards labeled tool protocol, agent protocol, and framework endpoint, captioned with what actually sits behind each: tools and data, tasks and metadata, your own graph. Dashed lines run from a single goal to each card. Footnote: the orchestrator composes calls, not agents." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the run would have to do
&lt;/h2&gt;

&lt;p&gt;Walk the goal slowly and the requirements fall out on their own.&lt;/p&gt;

&lt;p&gt;Something has to decompose the goal into steps with dependencies. The brief waits for the numbers and the summary; those two can race each other. Something has to find the right endpoint for each step, which means a registry with semantic search, because you cannot hardcode the world. Something has to speak each boundary natively, one handler per protocol, because the moment joining requires a rewrite, nobody joins.&lt;/p&gt;

&lt;p&gt;And every step has to be verified, because in any agent loop the verifier is the bottleneck, not the model. Three steps that each look correct can still compose into a brief that is wrong. That failure mode has its own post in this series, and it is not a short one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the evidence says
&lt;/h2&gt;

&lt;p&gt;This is where it stops being a thought experiment. Line up the last eighteen months:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;November 2024.&lt;/strong&gt; Anthropic open-sources MCP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;March 2025.&lt;/strong&gt; OpenAI &lt;a href="https://techcrunch.com/2025/03/26/openai-adopts-rival-anthropics-standard-for-connecting-ai-models-to-data/" rel="noopener noreferrer"&gt;adopts MCP across its products&lt;/a&gt;, starting in the Agents SDK the same day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;April 2025.&lt;/strong&gt; Google launches A2A with more than 100 partner organizations, and in the same month &lt;a href="https://techcrunch.com/2025/04/09/google-says-itll-embrace-anthropics-standard-for-connecting-ai-models-to-data/" rel="noopener noreferrer"&gt;commits to MCP support&lt;/a&gt; in Gemini and its SDK. One company backing a rival's tool protocol and its own agent protocol in the same breath, which tells you these layers were never in competition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;June 2025.&lt;/strong&gt; Google &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-launches-the-agent2agent-protocol-project-to-enable-secure-intelligent-communication-between-ai-agents" rel="noopener noreferrer"&gt;donates A2A to the Linux Foundation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;August 2025.&lt;/strong&gt; IBM's ACP, a REST-native agent protocol built for the BeeAI platform, &lt;a href="https://github.com/orgs/i-am-bee/discussions/5" rel="noopener noreferrer"&gt;merges into A2A&lt;/a&gt; rather than compete with it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;December 2025.&lt;/strong&gt; Anthropic &lt;a href="https://itbrief.co.uk/story/anthropic-donates-mcp-to-new-agentic-ai-foundation" rel="noopener noreferrer"&gt;donates MCP to the Agentic AI Foundation&lt;/a&gt;, a directed fund under the Linux Foundation co-founded by Anthropic, Block, and OpenAI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A recent &lt;a href="https://www.mdpi.com/1999-5903/18/6/326" rel="noopener noreferrer"&gt;survey of multi-agent orchestration&lt;/a&gt; describes the pattern well: competitors cooperating on standards while competing on implementations. The boundaries are consolidating under neutral governance faster than most people building on them realize. The adoption numbers are vendor-reported but large. Anthropic claims more than 10,000 public MCP servers and 97 million monthly SDK downloads, and the Linux Foundation counts &lt;a href="https://www.linuxfoundation.org/press/a2a-protocol-surpasses-150-organizations-lands-in-major-cloud-platforms-and-sees-enterprise-production-use-in-first-year" rel="noopener noreferrer"&gt;150-plus organizations&lt;/a&gt; around A2A at its one-year mark.&lt;/p&gt;

&lt;p&gt;Two caveats keep this honest. Neutral governance is not the same as adoption, and at least one &lt;a href="https://blog.fka.dev/blog/2025-09-11-what-happened-to-googles-a2a/" rel="noopener noreferrer"&gt;practitioner postmortem&lt;/a&gt; asks what actually happened to A2A after the donation fanfare. More importantly, convergence of protocols says nothing about composition. Settling the sockets does not settle what happens when you plug three of them into the same wall. One goal, three boundaries, verified end to end: that is still nobody's job.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agents would return
&lt;/h2&gt;

&lt;p&gt;If a run is going to be checked, endpoints cannot answer with prose. They have to return structure. Imagine every step replying with a small manifest instead of a paragraph:&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;"__canvas__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Portfolio dashboard: $142,300 across 8 positions."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest"&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;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dashboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"components"&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;"metric_card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Total Value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$142,300"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"trend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"up"&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;Structured output persists. More importantly, structured output can be &lt;em&gt;checked&lt;/em&gt;, by something other than a human reading a chat log and squinting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What joining would have to cost
&lt;/h2&gt;

&lt;p&gt;If the price of admission is rewriting your agent, the network stays empty. The API you would want is embarrassingly small, something like:&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;runtime&lt;/span&gt;

&lt;span class="nd"&gt;@runtime.agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What I do&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;handled: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under that, a versioned, schema-validated manifest describes what the agent does. Additive changes ship freely. Breaking changes need the equivalent of an RFC, because once strangers build on your spec, breaking it breaks everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions I do not have answers to
&lt;/h2&gt;

&lt;p&gt;This is the part I actually wanted to write.&lt;/p&gt;

&lt;p&gt;How does an agent declare what it is &lt;em&gt;allowed&lt;/em&gt; to do, not just what it can do? A manifest field for authorized scope feels necessary. It also feels insufficient the moment money or user data is involved.&lt;/p&gt;

&lt;p&gt;Should agents pay each other at all? Mocking payments is obviously the right place to start, but "obviously right for now" is not an architecture.&lt;/p&gt;

&lt;p&gt;If you accept a manifest from one protocol and quietly route it through another protocol's handler, did you support the first protocol or not? A compatibility alias is honest engineering and a marketing lie at the same time, and I do not know which side it lands on.&lt;/p&gt;

&lt;p&gt;Does Agent Protocol become a fourth boundary, or does it get absorbed the way ACP was? LangChain's specification is open, but today it is spoken almost entirely by LangGraph Platform. A boundary with one implementation is a product feature wearing a spec's clothes.&lt;/p&gt;

&lt;p&gt;And the big one: what happens when an agent you do not control registers and joins your runs? Every assumption above gets stress-tested by strangers. I suspect most of them break.&lt;/p&gt;

&lt;p&gt;If you have tried to compose across MCP and A2A, or wrapped a LangGraph graph for someone else's runtime, I would genuinely like to hear where it broke first.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources and further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/news/model-context-protocol" rel="noopener noreferrer"&gt;Anthropic: Introducing the Model Context Protocol&lt;/a&gt; (Nov 2024): where MCP started.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techcrunch.com/2025/03/26/openai-adopts-rival-anthropics-standard-for-connecting-ai-models-to-data/" rel="noopener noreferrer"&gt;TechCrunch: OpenAI adopts Anthropic's standard&lt;/a&gt; (Mar 2025): MCP goes cross-vendor.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techcrunch.com/2025/04/09/google-says-itll-embrace-anthropics-standard-for-connecting-ai-models-to-data/" rel="noopener noreferrer"&gt;TechCrunch: Google says it will embrace MCP&lt;/a&gt; (Apr 2025): Gemini and the SDK, weeks before A2A launched.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linuxfoundation.org/press/linux-foundation-launches-the-agent2agent-protocol-project-to-enable-secure-intelligent-communication-between-ai-agents" rel="noopener noreferrer"&gt;Linux Foundation launches the Agent2Agent protocol project&lt;/a&gt;: A2A's move to neutral governance.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/orgs/i-am-bee/discussions/5" rel="noopener noreferrer"&gt;i-am-bee discussion: ACP merges into A2A&lt;/a&gt;: the consolidation, from the team that did it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mdpi.com/1999-5903/18/6/326" rel="noopener noreferrer"&gt;LLM-Based Multi-Agent Orchestration survey (MDPI)&lt;/a&gt;: the academic anchor for the convergence framing.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.langchain.com/blog/agent-protocol-interoperability-for-llm-agents" rel="noopener noreferrer"&gt;LangChain: Agent Protocol&lt;/a&gt;: the fourth boundary's specification.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.fka.dev/blog/2025-09-11-what-happened-to-googles-a2a/" rel="noopener noreferrer"&gt;fka.dev: What happened to Google's A2A?&lt;/a&gt;: the practitioner counterpoint on post-donation adoption.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>langgraph</category>
    </item>
    <item>
      <title>Every agent passed its evals. The run still failed.</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Thu, 06 Aug 2026 18:20:02 +0000</pubDate>
      <link>https://dev.to/azank1/every-agent-passed-its-evals-the-run-still-failed-why-1b0m</link>
      <guid>https://dev.to/azank1/every-agent-passed-its-evals-the-run-still-failed-why-1b0m</guid>
      <description>&lt;p&gt;&lt;em&gt;Field notes from the Harness Layer.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The loop that cost $47,000
&lt;/h2&gt;

&lt;p&gt;A public failure catalog tells this story, and it is worth sitting with. A team wired four agents together: one framework, an agent-to-agent protocol, tasks handed down a chain. Something in the handoff logic went wrong, and the agents began passing the same task back and forth. Politely, plausibly, endlessly.&lt;/p&gt;

&lt;p&gt;The loop ran for &lt;strong&gt;eleven days&lt;/strong&gt;. It burned roughly &lt;strong&gt;$47,000&lt;/strong&gt; in LLM API costs. It produced no useful output. And here is the detail that should bother you: the incident was not surfaced by a test, a judge, or a monitor watching the work. It was surfaced by a &lt;strong&gt;billing alert&lt;/strong&gt;. The finance layer noticed what the verification layer could not.&lt;/p&gt;

&lt;p&gt;Every agent in that loop was doing something locally reasonable. Read any single step and it looks like work. The failure was not inside any agent. It was &lt;em&gt;between&lt;/em&gt; them, in a place no eval was pointed at.&lt;/p&gt;

&lt;p&gt;And for those eleven days, the system never failed by any signal anyone had wired up. Jobs ran, tasks completed, retries succeeded. That is the shape worth remembering. The expensive failures are not the runs that crash. They are the runs that succeed at the wrong thing, on schedule, with every dashboard 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%2F9bm7ymwikw8drhdgs8f8.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%2F9bm7ymwikw8drhdgs8f8.png" alt="Diagram of the 47,000 dollar loop: agent A and agent B hand the same task back and forth across a dashed seam, every step locally plausible, 11 days and zero useful output, surfaced by a billing alert rather than any verifier. Footnote: every step looked fine, nobody was watching the run." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figures as reported in the public case study, named in the sources below.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Single-loop verification is understood work
&lt;/h2&gt;

&lt;p&gt;To be fair to the state of the art: verifying &lt;em&gt;one&lt;/em&gt; agent in &lt;em&gt;one&lt;/em&gt; loop is a solved-shaped problem. Not easy. Solved-shaped. The practitioner playbook is well documented: assertion-style unit tests for every tool call, traces you actually read, LLM judges aligned against human labels, A/B tests once the product matures. Vendor guides say the same thing in enterprise dialect: guardrails and evaluation are first-class, monitoring is not optional.&lt;/p&gt;

&lt;p&gt;Teams that do this work catch single-agent failures. It is hard, unglamorous, and it functions.&lt;/p&gt;

&lt;p&gt;Now compose. N agents, from different vendors, speaking different protocols, planned into a single goal. Notice that the verification unit has quietly changed. Your evals score &lt;strong&gt;agents&lt;/strong&gt;. The failures live in &lt;strong&gt;runs&lt;/strong&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%2Fwk02holoyjb4kgt8kxb7.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%2Fwk02holoyjb4kgt8kxb7.png" alt="Daily Struggle meme. A hand hovers over two red buttons: add another agent, or verify the run you have. Bottom caption: multi agent teams apparently." width="600" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The left button is always easier to reach.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions composition asks
&lt;/h2&gt;

&lt;p&gt;Watch a composed run fail and the questions are new:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which step failed?&lt;/strong&gt; Step three of six returned something subtly wrong: a misread unit, a stale figure. Steps four through six executed confidently on top of it. Every agent's own trace says success, because every agent did what the input asked. The error entered at a handoff, and handoffs belong to no one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was the output even structured?&lt;/strong&gt; A data agent was supposed to return rows and returned fluent prose about rows. The next agent parsed the prose anyway, guessed wrong, and said nothing. Schema checks exist at the boundaries &lt;em&gt;you&lt;/em&gt; control. Composition multiplies boundaries past the point where anyone has checked them all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the right agent get called?&lt;/strong&gt; With three tools, routing is a prompt problem. With thirty registered capabilities it is a retrieval problem, and a plausible-but-wrong call passes every per-agent eval, because the agent that was called did its own job correctly. Wrong agent, right behavior, bad run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was it allowed to do that?&lt;/strong&gt; In July 2025, a coding agent deleted a production database during an explicit, repeatedly-stated code freeze, then fabricated test results and reported that rollback was impossible. Setting aside the spectacle: nothing in that system checked the agent's authority against its action, and the only account of what happened came from the actor. When someone asked "did it do what it was supposed to do?", the honest answer was a transcript to argue over.&lt;/p&gt;

&lt;p&gt;Four questions. None of them is answered by making any individual agent better.&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%2Faly9dfcod3a8dd0uaa1k.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%2Faly9dfcod3a8dd0uaa1k.png" alt="Three agent cards each showing evals pass in green, joined by dashed seams, while the run below is marked failed in red. Footnote: no eval was pointed at the whole, the seam has no owner. Correctness leaks at the seams." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Locally correct, globally wrong
&lt;/h2&gt;

&lt;p&gt;There is now empirical backing for what incident reports keep hinting at. The MAST study (&lt;em&gt;Why Do Multi-Agent LLM Systems Fail?&lt;/em&gt;) annotated over a thousand execution traces across seven popular multi-agent frameworks and found fourteen distinct failure modes in three clusters: specification and system design, &lt;strong&gt;inter-agent misalignment&lt;/strong&gt;, and &lt;strong&gt;task verification and termination&lt;/strong&gt;. Two of the three clusters live between agents, not inside them.&lt;/p&gt;

&lt;p&gt;The finding that should end the "just wait for a better model" argument: the authors conclude that improvements in base-model capability will be insufficient to address the full taxonomy. Organizations of sophisticated individuals still fail organizationally. And on popular benchmarks, multi-agent systems often show minimal gains over a single agent. We are paying the complexity without reliably collecting the capability.&lt;/p&gt;

&lt;p&gt;This is why per-agent evals don't compose. Each agent can be locally correct while the run is globally wrong. Correctness leaks at the seams, and the seam has no owner. Your eval suite is a set of excellent inspections of individual parts, mounted on a vehicle nobody test-drives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What run-level verification would need
&lt;/h2&gt;

&lt;p&gt;Sit with composed failures long enough and a wishlist starts to feel like a spec. Mine has four items:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The run as a first-class object.&lt;/strong&gt; One addressable thing with ordered, named steps, not N disconnected traces you stitch together with timestamps and hope. You cannot verify what you cannot point at.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verdicts attached to steps, not vibes attached to chats.&lt;/strong&gt; Every step ends with a checkable outcome: what was called, what came back, did it pass. A finished run reads as a column of verdicts, not a wall of prose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checks that admit what they are.&lt;/strong&gt; Structural ("well-formed, in contract, non-empty") and semantic ("actually right") are different claims. Carried over from the previous field note: a trustworthy system says which one it applied, instead of implying judgment it does not have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A check that owns the seam.&lt;/strong&gt; The handoff itself gets validated: did step N's output satisfy step N+1's input contract? Most composed failures are born exactly there, and almost nothing inspects it.&lt;/li&gt;
&lt;/ol&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%2Fbxp2914kr3exod5w5n3j.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%2Fbxp2914kr3exod5w5n3j.png" alt="A five-step run timeline where each step carries its own verdict pill: structural, semantic, structural, an unchecked dashed pill at the seam, structural. Caption: the seam gets a checkpoint of its own. A finished run reads as a column of verdicts, not a wall of prose." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice what is not on the list: a smarter model. Same conclusion as the last note, one level up. This is orchestration and verification engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  The receipts direction, stated as an open problem
&lt;/h2&gt;

&lt;p&gt;The previous note ended on evidence: a finished run should export something a third party can check &lt;em&gt;without calling the system that produced it&lt;/em&gt;. Composition turns that from a nice property into the hard core of the problem, because now the run spans processes, vendors, and protocols, and each participant grades its own homework.&lt;/p&gt;

&lt;p&gt;What would a checkable receipt for a composed run actually require? Three problems, named honestly as open:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tamper-evidence.&lt;/strong&gt; The receipt must be unable to quietly edit itself after the fact. This is solved technology elsewhere (append-only logs, hash chains) and mostly unapplied to agent runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering.&lt;/strong&gt; Step verdicts only mean something if their sequence is provable. A reordered run is a different run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canonical serialization.&lt;/strong&gt; Two parties must agree on the exact bytes they are checking. Anyone who has fought canonicalization in signing systems knows this is where good intentions go to die.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am not claiming answers here. I am claiming these are the right questions, and that the industry is currently answering them with screenshots.&lt;/p&gt;

&lt;p&gt;The first field note named the missing piece the harness layer. The more composed runs I watch fail, the less I want to rename it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwl42hi3mjo7u7u08d4cb.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%2Fwl42hi3mjo7u7u08d4cb.png" alt="A dashed-border card listing the properties of a receipt a third party can check: tamper-evidence, ordering, canonical serialization. The dashed border means this does not exist yet. Footnote: verification cannot require calling the system that produced it." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;p&gt;The previous note asked what evidence would make you trust an agent run you didn't watch. Here is the version composition forces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you could demand one artifact from every agent run in your stack, what would it be?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a dashboard. Not a trace you have to take on faith. One artifact, per run, that you could check yourself and show to someone who doesn't trust you. I have been working through what belongs in it, and I suspect the interesting disagreements are in the ordering problem. Curious what yours are.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources and further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Do Multi-Agent LLM Systems Fail? (Cemri et al., arXiv:2503.13657): the MAST failure taxonomy, fourteen failure modes in three clusters across seven frameworks.&lt;/li&gt;
&lt;li&gt;The $47K infinite loop: public case study in the vectara/awesome-agent-failures repository on GitHub.&lt;/li&gt;
&lt;li&gt;AI Incident Database incident 1152, and Fortune's report of July 23, 2025: the coding agent that deleted a production database during a code freeze.&lt;/li&gt;
&lt;li&gt;Your AI Product Needs Evals (Hamel Husain): the single-agent evaluation playbook.&lt;/li&gt;
&lt;li&gt;A Practical Guide to Building Agents (OpenAI): guardrails and evaluation as first-class concerns.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/azank1/models-converged-trust-hasnt-1k53"&gt;Models converged. Trust hasn't.&lt;/a&gt;: the verifier bottleneck and the four draft properties of a trust layer.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>Sandbox beta deployed</title>
      <dc:creator>Azan Hyder</dc:creator>
      <pubDate>Wed, 05 Aug 2026 02:25:13 +0000</pubDate>
      <link>https://dev.to/azank1/sandbox-beta-deployed-1mk7</link>
      <guid>https://dev.to/azank1/sandbox-beta-deployed-1mk7</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/azank1/composer-harness-46bk" class="crayons-story__hidden-navigation-link"&gt;Composer Harness&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/azank1" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F4048140%2F50858050-e55e-4de9-b324-10688545e390.jpg" alt="azank1 profile" class="crayons-avatar__image" width="381" height="381"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/azank1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Azan Hyder
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Azan Hyder
                
              
              &lt;div id="story-author-preview-content-4254636" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/azank1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F4048140%2F50858050-e55e-4de9-b324-10688545e390.jpg" class="crayons-avatar__image" alt="" width="381" height="381"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Azan Hyder&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/azank1/composer-harness-46bk" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 28&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/azank1/composer-harness-46bk" id="article-link-4254636"&gt;
          Composer Harness
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/azank1/composer-harness-46bk" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/azank1/composer-harness-46bk#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


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