<?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: TribeROI</title>
    <description>The latest articles on DEV Community by TribeROI (triberoi).</description>
    <link>https://dev.to/triberoi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14204%2F5c87ea66-eb80-4f8e-a147-275b8719f808.png</url>
      <title>DEV Community: TribeROI</title>
      <link>https://dev.to/triberoi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/triberoi"/>
    <language>en</language>
    <item>
      <title>Stop chasing fads. Operate Claude like a startup founder.</title>
      <dc:creator>Mark Birch</dc:creator>
      <pubDate>Sat, 22 Aug 2026 19:07:41 +0000</pubDate>
      <link>https://dev.to/triberoi/stop-chasing-fads-operate-claude-like-a-startup-founder-2l1b</link>
      <guid>https://dev.to/triberoi/stop-chasing-fads-operate-claude-like-a-startup-founder-2l1b</guid>
      <description>&lt;p&gt;I am running an AI-native startup. Early on though, I would spend so much time staying ahead of every new AI hot take that I lost focus on what I needed to do to move the company forward. I didn't need a hundred skills, fifty MCP servers, or Ralph Wiggum loops sending my API bill to the moon. I needed answers to plain questions. What is this model costing me? Is it writing code I keep? Is that tip I saw on X helping, or just making me feel busy?&lt;/p&gt;

&lt;p&gt;Those are operating questions. Using Claude is the easy part. You send a prompt, it writes code, you ship. Operating is what turns prompts into something you can trust at speed. That isn't about model quality. It's about what you build around the model.&lt;/p&gt;

&lt;p&gt;What I didn't realize is that everything I needed, Claude had already shipped. There are three layers Claude provides, depending on the task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extend&lt;/strong&gt; Claude Code itself with configuration,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Call&lt;/strong&gt; the API directly for one small task,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embed&lt;/strong&gt; Claude as an agent inside your own code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The skill is knowing which of the three to reach for, and when. Here are five ways I used these three layers to build an AI-coding configuration I trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. See what it's costing you
&lt;/h2&gt;

&lt;p&gt;Beyond the monthly subscription and the API line item, do you know the numbers behind the numbers? They are sitting on your laptop. Claude Code writes a JSONL log of every session to disk. A free tool called &lt;a href="https://github.com/ryoppippi/ccusage" rel="noopener noreferrer"&gt;ccusage&lt;/a&gt; reads those logs and reconstructs your spend and cache-hit rate across your entire history instantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ccusage@latest daily
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There's a number that matters even more, whether the model's edits are getting accepted or thrown away. You won't find that one in the log. That answer comes from Claude Code's &lt;a href="https://docs.claude.com/en/docs/claude-code/monitoring-usage" rel="noopener noreferrer"&gt;OpenTelemetry export&lt;/a&gt;. You turn it on in settings and point it at a dashboard:&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.claude/settings.json&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;"env"&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;"CLAUDE_CODE_ENABLE_TELEMETRY"&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"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"OTEL_METRICS_EXPORTER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otlp"&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;The accept/reject rate is the closest thing you have to knowing whether the model's output is worth keeping. A high reject rate means you're paying to generate edits you then delete.&lt;/p&gt;

&lt;p&gt;There was no need to write code or make API calls. All it required was simple configuration. That's the first layer, &lt;strong&gt;extending&lt;/strong&gt; Claude Code.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Cache the part that doesn't change
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.claude.com/en/docs/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;Prompt caching&lt;/a&gt; is your biggest single cost lever. A cached read costs roughly 10% of the normal input price. A stable prefix you reuse on every call, like your system prompt, CLAUDE.md, or tool definitions, should be almost free after the first call. Anthropic's caching runs on an explicit &lt;code&gt;cache_control&lt;/code&gt; marker, and the savings on a long stable prefix are around 90%.&lt;/p&gt;

&lt;p&gt;The issue is that caching matches on an exact prefix. If anything at the top of your context changes between calls, like a timestamp, a session ID, or reordered tool definitions, every byte downstream of the change is a cache miss, meaning you pay full write price. One volatile token at the top invalidates everything under it, and it's the most common reason a cache-hit rate sits far lower than it should.&lt;/p&gt;

&lt;p&gt;You can see it working by reading the API response yourself. This is your first direct call to the Messages API, the &lt;strong&gt;Call&lt;/strong&gt; layer, firing the same request twice to print the cache fields:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// cache-probe.ts: a direct Messages API request, twice&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Anthropic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anthropic-ai/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&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;client&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;Anthropic&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// reads ANTHROPIC_API_KEY&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stablePrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample-context.md&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;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// a big, stable body&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;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claude-haiku-4-5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;system&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;text&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;stablePrefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cache_control&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;ephemeral&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// the lever&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;One-line summary.&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache_creation_input_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// tokens written to cache&lt;/span&gt;
    &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache_read_input_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// tokens served from cache&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;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// write &amp;gt; 0, read = 0  -&amp;gt; you paid to fill the cache&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;warm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// write = 0, read &amp;gt; 0  -&amp;gt; served from cache, ~90% cheaper&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The cold call shows a write and no read. The warm call flips, with no write and a read served back at a tenth of the price. If both reads come back zero, your prefix is either below the cache minimum (about 4,096 tokens for Haiku) or it's changing between calls, which is the bug you're searching for. The &lt;code&gt;usage&lt;/code&gt; object tells you exactly what got cached.&lt;/p&gt;

&lt;p&gt;One detail that's easy to miss is that the effective cache TTL defaults to five minutes. Pause to read a diff for ten minutes, and the cache can expire and re-warm at full write price on your next call. Setting &lt;code&gt;ttl: "1h"&lt;/code&gt; on a slow interactive loop is worth it. The five-minute default is tuned for an agent running flat out, not an engineer thinking through a problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Put your guardrails in code, not in a prompt
&lt;/h2&gt;

&lt;p&gt;In July 2025, &lt;a href="https://fortune.com/2025/07/23/ai-coding-tool-replit-wiped-database-called-it-a-catastrophic-failure/" rel="noopener noreferrer"&gt;an AI agent at Replit deleted a production database&lt;/a&gt; during a code freeze. A worse version went viral in 2026 when a founder watched an agent wipe both the production database and its backups, even though the project had explicit written rules telling it not to.&lt;/p&gt;

&lt;p&gt;The lesson most people took was "the AI did it." The actual lesson is that the safety rule lived in a config file and was treated like a prompt. The model can reason its way around it or just lose the thread once the context window fills. If a rule must stick, it has to run as code.&lt;/p&gt;

&lt;p&gt;Claude Code gives you that through &lt;a href="https://docs.claude.com/en/docs/claude-code/hooks" rel="noopener noreferrer"&gt;hooks&lt;/a&gt;. A hook is a deterministic check that runs before a tool call and can block it outright, with no model judgment in the loop. Here's a PreToolUse hook that inspects every shell command for a destructive operation against anything related to production, and blocks it unless it sees a typed confirmation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// a PreToolUse hook: deterministic, runs before the tool, can refuse&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toolInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;??&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;destructive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b(&lt;/span&gt;&lt;span class="sr"&gt;drop&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+table|truncate|rm&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+-rf|reset-db&lt;/span&gt;&lt;span class="se"&gt;)\b&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&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;looksProd&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;prod|production&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&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;destructive&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;looksProd&lt;/span&gt;&lt;span class="p"&gt;)&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="na"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Destructive op against a production target. Requires typed confirmation.&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can use the same method to guard against hallucinated packages. Models will confidently recommend a package that doesn't exist. Attackers register those exact names and wait, an exploit called slopsquatting. A second hook checks that an install target actually exists, and clears a minimum age and download count, before it runs. The key point is that the check runs in code, where the model can't ignore it.&lt;/p&gt;

&lt;p&gt;One warning, though. Once a hook is scanning every command for trigger words, it will block your own commands too, including a harmless &lt;code&gt;echo&lt;/code&gt; that happens to mention one. When you need a trigger word in a command, pipe it in from a text file so it never appears in the command line the hook scans.&lt;/p&gt;

&lt;p&gt;This is another example of &lt;strong&gt;extending&lt;/strong&gt; Claude Code. The key pattern to follow is that anything that has to be reliable belongs in a hook, never left to a model's judgment.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Catch the code that looks right and isn't
&lt;/h2&gt;

&lt;p&gt;The top frustration with AI coding tools isn't the model failing outright. It's output that's almost right but not quite. It is the most common complaint by a wide margin, named by &lt;a href="https://survey.stackoverflow.co/2025/ai" rel="noopener noreferrer"&gt;66% of developers in the Stack Overflow 2025 survey&lt;/a&gt;. Almost-right is worse than plainly wrong. Wrong gets caught immediately. Almost-right looks shippable, passes review, and fails many weeks later because of an edge case that was never checked.&lt;/p&gt;

&lt;p&gt;A normal CI gate won't help. Lint, types, and happy-path tests all go green on code that's subtly off. Take this simple function:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/stats.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;averageEventsPerMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Member&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// looks fine. returns NaN when members is empty.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The happy-path test passes. CI is green. The bug only appears when the members list is empty, and then a user sees &lt;code&gt;NaN&lt;/code&gt; on the screen.&lt;/p&gt;

&lt;p&gt;Two simple tools catch most of this, and neither needs a full agent. The first is a scope check, the &lt;strong&gt;Call&lt;/strong&gt; layer doing one small job. After a change, a single stateless API call scores the diff for whether it stayed inside the task you asked for:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scope-check.ts: a disposable scorer on a single API call&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Anthropic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anthropic-ai/sdk&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;client&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;Anthropic&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&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;scopeScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskSpec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claude-haiku-4-5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// fast and the right tool for a bounded job&lt;/span&gt;
    &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You score code diffs for scope creep. Reply ONLY with JSON: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"in_scope": boolean, "out_of_scope": string[], "risk": "low"|"med"|"high"}.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`TASK:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;taskSpec&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\nDIFF:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;diff&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{}&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;clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```/g, "").trim(); // strip fences the model adds
  try { return JSON.parse(clean); } catch { return { in_scope: true, out_of_scope: [], risk: "low" }; }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That call costs a fraction of a cent and catches the model editing three files when you asked it to touch one. The fence-stripping and the try/catch matter. Models often wrap JSON in markdown, which crashes a raw parse in production, so you strip the fences and catch the failure.&lt;/p&gt;

&lt;p&gt;The second is a verifier subagent. After a change, it writes adversarial tests aimed at exactly what you changed and runs them before you do, so almost-right gets caught by a grader you built. That is the &lt;strong&gt;Extend&lt;/strong&gt; layer again, a subagent you configure once and reuse. I wrote about the checks I run in an earlier post, &lt;a href="https://dev.to/triberoi/i-use-ai-agents-to-code-four-checks-i-built-to-trust-them-2cl4"&gt;Four checks I built to trust them&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Prove a config change helped instead of guessing
&lt;/h2&gt;

&lt;p&gt;Every time you edit your CLAUDE.md, swap a model, or restructure your context, you're running an experiment, but no one treats it like one. You make the change and you keep it or revert it based on a hunch. &lt;a href="https://arxiv.org/abs/2602.11988" rel="noopener noreferrer"&gt;ETH Zurich evaluated LLM-generated context files&lt;/a&gt; and found they decreased task success by a few percent while raising cost by more than 20%. Context the model wrote for itself was making the agent worse and more expensive.&lt;/p&gt;

&lt;p&gt;The only fix is to measure against your own tasks. You build a small harness that runs your setup headless against a fixed set of tasks, under two configurations, and compares them. This is the third layer, where you &lt;strong&gt;embed&lt;/strong&gt; Claude as an agent inside your own code, through the &lt;a href="https://docs.claude.com/en/api/agent-sdk/overview" rel="noopener noreferrer"&gt;Agent SDK&lt;/a&gt;, which makes implementing the headless part simple:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// harness.ts: drive headless Claude agents from your own code&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@anthropic-ai/claude-agent-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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;runTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GoldenTask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;configDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cost&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="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repoPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;settingSources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;configDir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// config A or config B&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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;cost&lt;/span&gt; &lt;span class="o"&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;total_cost_usd&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;passed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repoPath&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="nx"&gt;passed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cost&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Run the task set under config A and config B, several times each,&lt;/span&gt;
&lt;span class="c1"&gt;// then compare pass-rate and cost.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When I ran this on a bloated config against a lean one, the heavy one stuffed with every "best practice" I'd collected, the verdict was that it cost 1.26 times as much for the same pass rate. Based on vibes, I would have kept it, but the numbers told me otherwise.&lt;/p&gt;

&lt;p&gt;One caveat is that agentic eval results drift with API latency by time of day, and a single A-versus-B run can hand you a difference that's pure noise. You have to run the comparison several times to give this eval real rigor.&lt;/p&gt;
&lt;h2&gt;
  
  
  Putting it to work
&lt;/h2&gt;

&lt;p&gt;None of this is hard. Each part is less than an hour of work. Almost all of it ships with Claude, the telemetry, the cache controls, the hooks, the disposable API calls, and the headless SDK.&lt;/p&gt;

&lt;p&gt;These five ideas run across the three layers Claude already provides. You extend it with hooks and telemetry. You call it for simple, bounded jobs. You embed it as an agent when you want it to run on its own. Operating well is knowing which of the three a task needs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Practice&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Telemetry &amp;amp; accept-rate&lt;/td&gt;
&lt;td&gt;Extend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt caching probe&lt;/td&gt;
&lt;td&gt;Call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hooks as guardrails&lt;/td&gt;
&lt;td&gt;Extend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope check + verifier&lt;/td&gt;
&lt;td&gt;Call + Extend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A/B eval harness&lt;/td&gt;
&lt;td&gt;Embed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Small teams usually skip all of this as it feels tedious. Nothing forces the issue until the bill spikes or a database disappears. The hot takes will keep coming. Implementing these now is what lets you ignore them and operate Claude reliably so you can focus on shipping product.&lt;/p&gt;

&lt;p&gt;I built all five into a workshop that walks you through writing each tool yourself, against a sample codebase, one part at a time. It's open source on GitHub, and you can run it solo this weekend or with your team. &lt;strong&gt;&lt;a href="https://github.com/startupmark/claude-founders-workshop" rel="noopener noreferrer"&gt;Start the Claude Founders Workshop.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/startupmark" rel="noopener noreferrer"&gt;
        startupmark
      &lt;/a&gt; / &lt;a href="https://github.com/startupmark/claude-founders-workshop" rel="noopener noreferrer"&gt;
        claude-founders-workshop
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;acme-community&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Ask a founder what their last feature cost in tokens, or whether the model's output is worth keeping, and most can't answer. Not for lack of caring. Nothing on their screen shows them, so the question never gets asked.&lt;/p&gt;

&lt;p&gt;That gap is the whole point of this workshop. The Stack Overflow 2025 survey found that the top frustration with AI coding tools isn't outright failure, it's output that's "almost right but not quite" (66% of developers), the kind that looks shippable until it breaks on an edge case. Many teams are using Claude. Very few are operating it: watching the spend, capping the blast radius, catching the near-misses, and proving a config change actually helped instead of guessing.&lt;/p&gt;

&lt;p&gt;This repo is the reference build for the &lt;strong&gt;Claude Founders Workshop&lt;/strong&gt;, a hands-on, five-part series that takes a small team from using Claude Code to operating it. &lt;code&gt;acme-community&lt;/code&gt; is the…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/startupmark/claude-founders-workshop" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;I'll go deeper on each lesson in the posts that follow, the caching mechanics, the hook patterns, and the eval harness. If you run it and something breaks, or you find it super valuable, please reach out and let me know in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I use AI agents to code. Four checks I built to trust them.</title>
      <dc:creator>Mark Birch</dc:creator>
      <pubDate>Sat, 01 Aug 2026 01:36:05 +0000</pubDate>
      <link>https://dev.to/triberoi/i-use-ai-agents-to-code-four-checks-i-built-to-trust-them-2cl4</link>
      <guid>https://dev.to/triberoi/i-use-ai-agents-to-code-four-checks-i-built-to-trust-them-2cl4</guid>
      <description>&lt;p&gt;I am a solo startup founder. In order to launch &lt;a href="https://triberoi.com/" rel="noopener noreferrer"&gt;TribeROI &lt;/a&gt;in a reasonable amount of time, I use AI agents that read the codebase, write code, run tests, and open pull requests. My job is to review and merge the code.&lt;/p&gt;

&lt;p&gt;What makes this process work is not the agents, but a set of checks I built so I could trust what they produce.&lt;/p&gt;

&lt;p&gt;Everyone worries that AI writes bad code. Bad code was never my problem. The problem is that agents produce more than one person can properly review, and this is when defects surface. A few weeks ago, I hit twelve big defects in a single day, which all passed my tests. I only found them by reading the live system.&lt;/p&gt;

&lt;p&gt;The failures fell into three patterns. First, artifacts that exist but nothing runs them. Second, systems that could not find an answer and substituted a plausible default. Third, work that was rebuilt without checking for already existing code that solved the same issue.&lt;/p&gt;

&lt;p&gt;The second pattern burned me twice. When the agent cannot determine something, it will write a fallback. While this seems reasonable to the agent, the result is a duplicate resource in production.&lt;/p&gt;

&lt;p&gt;Here are the four rules I have now built to give me higher confidence in the agent's code.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A rule is a test, or it doesn't exist
&lt;/h2&gt;

&lt;p&gt;Every safety rule I keep has to name the specific automated check that fails when someone breaks it. If I can't name one, it is not a guarantee and it goes on a list titled "things I have to remember to check."&lt;/p&gt;

&lt;p&gt;When I held my own rules to that standard, eight of the eleven items on my code review checklist enforced nothing whatsoever. They were boxes you tick that change no outcome. Three of the eight rules I'd labeled "hard requirements" were in the same shape, and a batch of alerts had done nothing since the day I wrote them. I'd been reading that checklist and thinking everything was fine.&lt;/p&gt;

&lt;p&gt;An unenforced rule is worse than no rule at all, because you lean on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Never let "unknown" look like "fine"
&lt;/h2&gt;

&lt;p&gt;This is the highest-value rule of the four and the cheapest to act on. Go find every place your code turns "I could not determine this" into a value, and make it stop instead.&lt;/p&gt;

&lt;p&gt;It bites in application code. Mine met a permission role it didn't recognize, fell back to least privilege because that felt like the safe choice, and locked eight paying customers out of the admin screens in their own accounts. Nothing threw. The fallback was doing exactly what I'd told it to do.&lt;/p&gt;

&lt;p&gt;The expensive version was in the ops scripts that create-or-update cloud resources:&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="nv"&gt;CHANNEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gcloud beta monitoring channels list &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"value(name)"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipe hands the exit status to &lt;code&gt;head&lt;/code&gt;, which happily succeeds, so &lt;code&gt;set -e&lt;/code&gt; never fires. A transient auth blip becomes an empty string, the empty string reads as "does not exist," and the script cheerfully creates a second copy of a resource that was already there. That was 7 July. I patched that one script, told myself I was done, and on 21 July the same shape turned up somewhere else and orphaned a notification channel that eleven alert policies were pointing at.&lt;/p&gt;

&lt;p&gt;The second time taught me more than the first. A fact that every code path depends on belongs in one place they all call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;must_read&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;MR_OUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MR_ERR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"!! READ FAILED, refusing to continue: &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   A failed lookup is not proof the resource is absent."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MR_OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Written as VAR=$(must_read ...), it propagates the failure, the assignment fails, and set -e ends the run. Two outcomes survive: the real value, or a halt.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Test the wiring, not just the artifact
&lt;/h2&gt;

&lt;p&gt;A file that exists reads like proof that it works, and almost nothing checks whether anything runs it. I shipped an alert policy as JSON, declared it in the manifest, and the apply script never called &lt;code&gt;apply_policy&lt;/code&gt; on it. The dry-run hid the whole thing, because the validator scans the whole directory, while the applier names each policy by hand, one line per policy. One of them saw the file and the other didn't.&lt;/p&gt;

&lt;p&gt;So there's now a test that greps the apply script for its own calls and checks both directions. Every declared policy gets applied, and every call points at something declared. It's ugly and I'm not proud of it, and it's the only thing that proves the file is reached.&lt;/p&gt;

&lt;p&gt;The other assertion is the one worth copying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_the_policy_pair_is_not_vacuous&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ALERT_POLICIES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no declared policies — the guard would be vacuous&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;_applied_policy_patterns&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no apply_policy calls — same&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A guard that iterates an empty collection passes by looking at nothing. Rename the constant it reads and both sides go empty, so the check stays green while proving absolutely nothing. That's the same failure one level up. If you write guards, assume you already have one of these.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. An agent's authority is a mechanism, not an instruction
&lt;/h2&gt;

&lt;p&gt;I run a mode where an agent works unattended for hours, and it gets &lt;code&gt;Bash(gcloud:*)&lt;/code&gt; outright, because a permission prompt nobody is awake to answer is just a hang with extra steps. That's only safe because the real boundary sits somewhere else: a hook where gcloud is default-deny, letting through reads and a named set of additive commands, and refusing everything else.&lt;/p&gt;

&lt;p&gt;A config file can't do this job. In Claude Code, &lt;code&gt;ask&lt;/code&gt; beats &lt;code&gt;allow&lt;/code&gt; regardless of how specific the allow rule is, so a narrow allow sitting next to a broad &lt;code&gt;ask(gcloud *)&lt;/code&gt; is dead code. Prefix matching is fragile too, and &lt;code&gt;gcloud  run&lt;/code&gt; with two spaces doesn't match &lt;code&gt;Bash(gcloud run *)&lt;/code&gt;. As a way to permit things, that's a hole. Inside a default-deny hook it's harmless, because anything unrecognized gets refused anyway.&lt;/p&gt;

&lt;p&gt;The hook's &lt;code&gt;catch&lt;/code&gt; block denies as well. Every other guard I've written fails open, which is the right call almost everywhere. This one is the only thing standing between a broad grant and production, so a crash has to refuse rather than guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd build first
&lt;/h2&gt;

&lt;p&gt;Take the second rule. Search your codebase for the fallbacks that fire when something couldn't be determined, and make each one stop instead. It is quick, needs no new tooling, and it is a failure that crops up surprisingly often.&lt;/p&gt;

&lt;p&gt;The insight across all these rules is that systems confidently report success when it's clearly not the case. In AI development, agents make this an even higher priority, because they produce code faster than you can read it, so these checks have to scale.&lt;/p&gt;

&lt;p&gt;I'd like to hear what other people are building to harden their agentic development workflows, especially if you've hit a class of failure I haven't. Let me know in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>devops</category>
      <category>claude</category>
    </item>
  </channel>
</rss>
