<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jed Arden</title><description>Everything published on jedarden.com — notes, guides, and TILs on headless LLM systems, agent orchestration, and platform engineering.</description><link>https://jedarden.com/</link><item><title>The agentic coding ladder is a list of things you give up</title><link>https://jedarden.com/notes/agentic-coding-ladder/</link><guid isPermaLink="true">https://jedarden.com/notes/agentic-coding-ladder/</guid><description>Steve Yegge&apos;s eight levels read the climb by how much you trust the AI. Here&apos;s a complementary lens on the same ascent: measure it by what you give up at each rung — which is why some people skip rungs entirely, having no old coding habits to undo. It starts with the search bar and ends with something none of us can name yet.</description><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Steve Yegge has a framework I keep coming back to: eight levels of AI-assisted development, from &quot;no AI&quot; up to &quot;build your own orchestrator.&quot; It reads the climb by &lt;em&gt;trust&lt;/em&gt; — how much of the machine&apos;s output you&apos;ll accept without looking. Permissions on, permissions off, stop reading diffs, abandon the IDE, run a fleet. It&apos;s a genuinely good way to find where you stand, and I&apos;m not trying to replace it.&lt;/p&gt;
&lt;p&gt;What follows is a second instrument pointed at the same mountain. Climbing those levels myself, I kept noticing a quieter move underneath the growing trust: a renunciation. To climb a rung I had to give up something I&apos;d treated as my job — not just trust the machine more, but &lt;em&gt;do less of the thing I was proud of&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So this measures the same ascent off a different dial. Not &quot;how much do you trust it.&quot; &lt;strong&gt;What have you given up.&lt;/strong&gt; Both readings are true; I&apos;ve just found the second one tells me more about the person doing the climbing.&lt;/p&gt;
&lt;p&gt;The first thing you give up is the search bar.&lt;/p&gt;
&lt;h2&gt;How the ladder is shaped&lt;/h2&gt;
&lt;p&gt;There are two halves and two thresholds.&lt;/p&gt;
&lt;p&gt;In the first half, the thing that grows is &lt;strong&gt;the size of the unit you hand off&lt;/strong&gt;. A snippet, then a function, then a file, then a whole application. You&apos;re still writing — you&apos;re just writing less of the code and more of the intent. The first threshold is where that bottoms out: &lt;strong&gt;you stop writing code at all.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the second half, the axis flips. You&apos;ve run out of bigger pieces of code to delegate, so what grows instead is &lt;strong&gt;your distance from the work&lt;/strong&gt;. More sessions, then too many sessions, then sessions you don&apos;t sit in. The second threshold is the important one, and it&apos;s the one I&apos;ve spent years on: &lt;strong&gt;the agent stops being able to ask you anything.&lt;/strong&gt; A live CLI is a conversation — it hits ambiguity, asks, you answer. Headless is a dispatch — it hits ambiguity and must resolve it alone or fail, because you are gone. That crossing is the same line I&apos;ve called &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;pets versus cattle&lt;/a&gt;. It&apos;s where everything I build now lives.&lt;/p&gt;
&lt;h2&gt;The first half — you give up the code&lt;/h2&gt;
&lt;p&gt;Each rung below shows the avatar with the thing it just gave up faded and struck through beside it — the picture &lt;em&gt;is&lt;/em&gt; the renunciation. Read down until the crossed-out object is the last thing you actually stopped reaching for.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;1 · The Student&lt;/strong&gt; — &lt;em&gt;&quot;I ask it to explain the error, but I&apos;d never paste its code in unread.&quot;&lt;/em&gt; Gave up: Google / Stack Overflow.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;2 · The Borrower&lt;/strong&gt; — &lt;em&gt;&quot;I paste in its snippets, but I assemble the program.&quot;&lt;/em&gt; Gave up: writing boilerplate by hand.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;3 · The Reviewer&lt;/strong&gt; — &lt;em&gt;&quot;It writes functions in my codebase, but I read every diff before it lands.&quot;&lt;/em&gt; Gave up: being the author.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;4 · The Delegator&lt;/strong&gt; — &lt;em&gt;&quot;I let it write a whole file and I test the output instead of reading every line.&quot;&lt;/em&gt; Gave up: reading every line.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;5 · The Director&lt;/strong&gt; — &lt;em&gt;&quot;I write the spec; I judge the app by whether it runs, not by how it&apos;s built.&quot;&lt;/em&gt; Gave up: knowing how my own code works inside.&lt;/p&gt;
&lt;p&gt;Rung 3 is where most working developers actually are, and it&apos;s a genuinely good place to be — but notice what you surrendered to get there. You stopped being the person who wrote the function. You became the person who &lt;em&gt;approves&lt;/em&gt; it. That&apos;s not a small thing; it&apos;s the first time the code in your repo wasn&apos;t authored by you. Rung 4 surrenders the diff itself: you stop reading lines and start testing outputs. By rung 5 you&apos;ve handed off whole applications, and the only honest way to do that is to &lt;a href=&quot;/notes/plan-is-the-prompt/&quot;&gt;put your thinking into the plan up front&lt;/a&gt; — because once it&apos;s a whole app, you can&apos;t steer line by line, and when the result is wrong, &lt;a href=&quot;/notes/ending-is-better-than-mending/&quot;&gt;the cheapest fix is to throw it away and re-run, not to repair it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Threshold 1 — you stop writing code.&lt;/strong&gt; Above this line there is no smaller unit to hand off. You are no longer producing code at any granularity. So the ladder has to start measuring something else.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The second half — you give up your presence&lt;/h2&gt;
&lt;p&gt;
&lt;strong&gt;6 · The Juggler&lt;/strong&gt; — &lt;em&gt;&quot;I&apos;ve got two or three sessions going and I bounce between them; I let them recover instead of fixing by hand.&quot;&lt;/em&gt; Gave up: undivided focus.&lt;/p&gt;
&lt;p&gt;Rung 6 is where the surrender stops being technical and starts being uncomfortable. You give up undivided focus — you accept that you&apos;ll never again give one task your whole head — and you start &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;letting agents course-correct instead of correcting them yourself&lt;/a&gt;. That &quot;self-healing&quot; people talk about isn&apos;t a feature that emerges; it&apos;s the thing you&apos;re &lt;em&gt;forced&lt;/em&gt; to build the moment you stop being available to fix every wrong turn.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;7 · The Bottleneck&lt;/strong&gt; — &lt;em&gt;&quot;I&apos;m losing track of which session is doing what. I&apos;m the constraint now — not the model.&quot;&lt;/em&gt; Gave up: the belief that attention scales.&lt;/p&gt;
&lt;p&gt;Rung 7 is not really a capability. It&apos;s a wall. It&apos;s the day you realize you are the slowest component in your own system — that adding another session doesn&apos;t add throughput, it adds the cost of context-switching into your own skull. Everyone I know who runs agents seriously remembers hitting this. It feels like drowning. And it is the entire reason rung 8 exists.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Threshold 2 — the conversation becomes a dispatch.&lt;/strong&gt; This is the crossing. You stop opening sessions and start launching jobs. The agent can no longer reach you, which means it can no longer ask you the question that used to save it. Every discipline I care about lives on this side of the line &lt;em&gt;because&lt;/em&gt; you removed yourself as the answer: &lt;a href=&quot;/notes/anchor-on-a-fact-the-model-cant-see/&quot;&gt;anchoring the work on a fact the model can&apos;t see&lt;/a&gt;, &lt;a href=&quot;/notes/dont-let-the-agent-grade-its-own-homework/&quot;&gt;never letting the agent grade its own homework&lt;/a&gt;, and &lt;a href=&quot;/notes/unit-economics-of-cattle/&quot;&gt;making the unit economics work when nobody&apos;s watching the meter&lt;/a&gt;. None of those are optional up here. They&apos;re the prosthetics that replace the human you just amputated from the loop.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
&lt;strong&gt;8 · The Dispatcher&lt;/strong&gt; — &lt;em&gt;&quot;I kick off runs I don&apos;t sit in and read the log after — they can&apos;t ask me anything.&quot;&lt;/em&gt; Gave up: being reachable.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;9 · The Operator&lt;/strong&gt; — &lt;em&gt;&quot;The volume means I couldn&apos;t inspect any single run even if I tried; I live on dashboards, gates, and cost.&quot;&lt;/em&gt; Gave up: the option to inspect any individual run.&lt;/p&gt;
&lt;p&gt;By rung 9 the surrender is inspectability itself. At rung 8 you still &lt;em&gt;could&lt;/em&gt; open a run&apos;s transcript and follow it if you wanted to. At rung 9 there are too many; you couldn&apos;t audit an individual run if you tried, so you&apos;re forced up onto aggregates — fleet metrics, verification gates, spend. You stop looking at any cow and only ever look at the herd.&lt;/p&gt;
&lt;h2&gt;How to find yourself on this&lt;/h2&gt;
&lt;p&gt;Don&apos;t look for your rung. Look for your &lt;strong&gt;band&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Read from rung 1 upward. Each rung names something you&apos;ve given up. Keep climbing for as long as the surrender is &lt;em&gt;genuinely, permanently true of you&lt;/em&gt; — not &quot;I could,&quot; but &quot;I have.&quot; The first rung whose renunciation you haven&apos;t actually made — where you&apos;d honestly say &quot;no, I still do that&quot; — is your ceiling. You &lt;strong&gt;reside&lt;/strong&gt; on the rung just below it.&lt;/p&gt;
&lt;p&gt;That&apos;s the strict part, and it&apos;s strict on purpose. Owning the infrastructure for a rung doesn&apos;t put you on it. I built a fleet orchestrator; I can &lt;em&gt;reach&lt;/em&gt; rung 9 any afternoon. But I haven&apos;t given up dropping into individual runs — I still do it, constantly — so I don&apos;t &lt;em&gt;live&lt;/em&gt; at 9. I reside lower and reach higher, and the truth about me is the band between them, not a single number. That&apos;s the honest shape of everyone working near the top. Nobody stands on one rung. The renunciation test just keeps you from flattering yourself about which one.&lt;/p&gt;
&lt;h2&gt;Higher isn&apos;t better — it&apos;s heavier&lt;/h2&gt;
&lt;p&gt;A ladder invites you to read it as a ranking: get to the top. This one isn&apos;t. Each rung up buys capability, but it also adds overhead — specification, planning, scaffolding, verification — and that overhead only pays for itself above a certain size of job. So every rung has a &lt;strong&gt;break-even: the smallest piece of work worth bringing to it.&lt;/strong&gt; And the break-even rises as you climb.&lt;/p&gt;
&lt;p&gt;I run headless fleets. I would never point one at a one-line typo. Specifying that fix well enough for a worker that can&apos;t ask me anything, then waiting on orchestration and gates to confirm it, costs far more than opening the file and typing the character at rung 3. It&apos;s chartering a freight train to mail a letter.&lt;/p&gt;
&lt;p&gt;So the rung you should &lt;em&gt;use&lt;/em&gt; for a task is the lowest one whose break-even the task clears — not the highest one you&apos;ve reached. This is what the band was already hinting at: I drop to rung 3 every day, not because I&apos;ve lost rung 9 but because most of what&apos;s in front of me doesn&apos;t clear rung 9&apos;s break-even. Operating below your ceiling isn&apos;t regression; it&apos;s fit.&lt;/p&gt;
&lt;p&gt;Which means the skill the ladder actually rewards at the top isn&apos;t living up high. It&apos;s &lt;em&gt;choosing the rung per task&lt;/em&gt; — and staying honest that a lot of work is small, and small work belongs on a low rung. The most advanced thing you can do with all nine rungs available to you is pick the cheapest one that still clears the job.&lt;/p&gt;
&lt;h2&gt;Some people skip rungs&lt;/h2&gt;
&lt;p&gt;Here&apos;s the part that follows from measuring the climb in renunciations rather than skill: &lt;strong&gt;a rung can only slow you down if you have the habit it asks you to give up.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I learned to code the old way. I have two decades of reflexes — read the diff, own the function, know how it works inside — and every one of them is a thing the ladder demands I surrender. For me the climb is mostly &lt;em&gt;unlearning&lt;/em&gt;. The lower rungs were hard not because the technique was hard but because the identity was.&lt;/p&gt;
&lt;p&gt;Someone who learned to build &lt;em&gt;with&lt;/em&gt; agents from day one doesn&apos;t have that. They never authored a function alone, never formed the reflex to read every line, never built the search-bar muscle, never tied their self-worth to knowing how the internals work. They don&apos;t climb rungs 1 through 5 so much as &lt;em&gt;start above them&lt;/em&gt; — there&apos;s simply nothing there to undo. This is the uncomfortable inversion at the center of the whole framework: &lt;strong&gt;seniority is friction.&lt;/strong&gt; The more experience you have, the more attached you are to exactly the things each rung requires you to drop. The ladder punishes the veteran and waves the newcomer through.&lt;/p&gt;
&lt;p&gt;But skipping a rung is not the same as having earned it. The giving-up was never only a loss — each surrender was also where you built the judgment to survive the next one. The veteran who gives up authorship still knows what good code looks like, because reading every diff for twenty years is how they learned it. The newcomer who never authored may direct an app they have no way to evaluate. So skipping rungs is real and it&apos;s an advantage, but it&apos;s a specific one: &lt;em&gt;nothing to unlearn&lt;/em&gt;, not &lt;em&gt;automatic competence&lt;/em&gt;. Your floor is set by attachment, not ability — and the people with the lowest floor sometimes have the thinnest judgment holding up their ceiling.&lt;/p&gt;
&lt;h2&gt;10+&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;There is a rung above 9. I can&apos;t tell you what it is.&lt;/p&gt;
&lt;p&gt;That&apos;s not a dodge — it&apos;s the most rigorous thing on this page. Look at the pattern. Every rung gives up something the rung &lt;em&gt;below&lt;/em&gt; it considered non-negotiable. Ask the Reviewer at rung 3 to imagine giving up reading the diff and they&apos;ll tell you that&apos;s the whole job. Ask the Bottleneck at rung 7 to imagine being unreachable and they can&apos;t even parse it. &lt;strong&gt;From inside any rung, the next surrender is invisible, because you are still standing on the thing you&apos;re about to give up.&lt;/strong&gt; A ladder that could name its own summit would be lying about how the first nine rungs felt before anyone had climbed them.&lt;/p&gt;
&lt;p&gt;So 10 is defined by its shape, not its content: it is whatever still feels load-bearing at rung 9 — the thing I&apos;d resist giving up hardest, the pillar I can&apos;t currently see &lt;em&gt;as&lt;/em&gt; a pillar. That gives a clean test, and the test is what makes 10 honest. A candidate only qualifies as level 10 if, standing at 9, it still feels indispensable. Which is exactly why &quot;fleets that assign their own work&quot; or &quot;fleets that tune themselves&quot; &lt;em&gt;aren&apos;t&lt;/em&gt; it — I can already picture those. They&apos;re just more of 9. The real one is something I&apos;d argue with you about. Maybe it&apos;s human intent at the root of the work. Maybe it&apos;s human accountability for the output. Maybe it&apos;s the human as the one who still decides what &quot;good&quot; means. I won&apos;t commit to any of them, because if I could name it correctly, it wouldn&apos;t be 10.&lt;/p&gt;
&lt;p&gt;I haven&apos;t reached it. I don&apos;t know anyone who has. On the map it isn&apos;t an arrow — an arrow would mean I know which way is up. It&apos;s fog above the tree line. You can see the mountain keeps going. You can&apos;t see the next ledge, or even where it sits.&lt;/p&gt;
&lt;p&gt;If you can name the thing rung 9 still can&apos;t function without — the necessity none of us can see because we&apos;re all still standing on it — then you might be the first one looking at level 10.&lt;/p&gt;
&lt;p&gt;The whole ladder, in one line: it opens with giving up the search bar, and it ends — if it ends — with giving up something I&apos;m not yet wise enough to miss.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The ladder, in one image&lt;/h2&gt;
&lt;p&gt;The whole thing on a single card — click to open it full size, or save it to share:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/ladder-infographic.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>orchestration</category><category>craft</category></item><item><title>Ending is better than mending</title><link>https://jedarden.com/notes/ending-is-better-than-mending/</link><guid isPermaLink="true">https://jedarden.com/notes/ending-is-better-than-mending/</guid><description>Huxley wrote it as dystopian propaganda for mindless disposability. In the agent era the economics it described have quietly become correct — for code. When the agent produces the wrong artifact, mending spends your scarce attention; ending spends cheap tokens. Revert the commits. Sometimes nuke the repo.</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I once spent the better part of an afternoon reading four hundred lines of code an agent had written wrong. I traced its logic, patched the broken parts, tried to bend it toward the thing I had actually asked for. When I finally gave up — &lt;code&gt;git reset --hard&lt;/code&gt;, sharpened three sentences in the plan, re-dispatched — the correct version existed eight minutes later. The hour I spent mending was the most expensive hour of my day. The eight minutes of ending were nearly free.&lt;/p&gt;
&lt;p&gt;I had the cost structure exactly backwards, and I had it backwards because I learned to write software in a world that no longer exists.&lt;/p&gt;
&lt;h2&gt;A slogan from a dystopia&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;Brave New World&lt;/em&gt;, Aldous Huxley gives his citizens a sleep-taught jingle to keep the economy churning: &lt;strong&gt;&quot;Ending is better than mending.&quot;&lt;/strong&gt; Its companion line is &lt;em&gt;&quot;The more stitches, the less riches.&quot;&lt;/em&gt; The point of the conditioning is to stop people repairing what they own, so they keep consuming. Huxley meant it as a warning — a portrait of a society that had engineered away the instinct to fix things, because thrift was bad for business.&lt;/p&gt;
&lt;p&gt;For software, in the agent era, the slogan has quietly become true. Not as propaganda. As arithmetic.&lt;/p&gt;
&lt;h2&gt;The cost structure inverted&lt;/h2&gt;
&lt;p&gt;For the entire history of software until very recently, code was the expensive thing. Every line was hand-written by a person whose time cost money, so a working artifact represented accumulated human labor. Throwing it away threw away that labor. Mending — debugging, patching, salvaging — was almost always cheaper than rewriting, because rewriting meant paying the labor cost twice. The whole craft was built on that premise. &quot;Don&apos;t rewrite, refactor&quot; is the premise wearing a cardigan.&lt;/p&gt;
&lt;p&gt;That premise broke. Writing code is now cheap: an agent and a pile of tokens produce four hundred lines in minutes for a few cents. What did &lt;em&gt;not&lt;/em&gt; get cheaper — what got relatively &lt;em&gt;more&lt;/em&gt; expensive as everything around it fell — is human time and taste. Your judgment. Your attention. The scarce, slow, irreplaceable act of looking at something and knowing it&apos;s wrong, and knowing what right would look like.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;When the two inputs trade places like that, the optimal move trades places with them:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When the artifact is wrong, fixing it spends the input that got expensive and regenerating it spends the input that got cheap. Default to regenerating.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Mending is a tax on attention&lt;/h2&gt;
&lt;p&gt;The hidden cost of mending isn&apos;t the typing. It&apos;s the comprehension. To fix four hundred lines of wrong code, you first have to load the agent&apos;s reasoning into your own head — including the parts that are wrong, because you can&apos;t tell which parts are wrong until you understand all of it. You pay full price to build a mental model of something you&apos;re going to partially discard. That modelling is the single most expensive thing you do all day, and mending demands it up front, in full, before you&apos;ve fixed a single line.&lt;/p&gt;
&lt;p&gt;Ending skips the tax. You don&apos;t have to understand wrong code to delete it. &lt;code&gt;git reset --hard&lt;/code&gt; costs no comprehension at all. The only thing you have to understand is what you &lt;em&gt;wanted&lt;/em&gt; — which you needed to understand anyway, and which is far cheaper to hold in your head than someone else&apos;s flawed attempt to deliver it.&lt;/p&gt;
&lt;p&gt;This is why the afternoon felt so wrong in retrospect. I spent the expensive resource — hours of my own comprehension — to rescue the cheap one. I should have spent thirty seconds of taste deciding the artifact was unsalvageable, and let the cheap resource run again.&lt;/p&gt;
&lt;h2&gt;Why this is safe now, and wasn&apos;t before&lt;/h2&gt;
&lt;p&gt;You cannot tell people to throw away working code without sounding reckless, so let me be precise about the condition that makes it sane: &lt;strong&gt;ending is only cheap when the value has moved out of the artifact and into something durable.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For an agent fleet, that durable thing is the plan. I&apos;ve argued before that &lt;a href=&quot;/notes/plan-is-the-prompt/&quot;&gt;the plan is the prompt&lt;/a&gt; — that the token-dense, carefully-written specification is the real artifact and the code is a &lt;em&gt;rendering&lt;/em&gt; of it. If that&apos;s true, then nuking the codebase isn&apos;t destroying value; it&apos;s discarding one rendering and asking for another. The plan is the negative; the code is just a print. You can burn the print.&lt;/p&gt;
&lt;p&gt;This is the same move as &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;pets versus cattle&lt;/a&gt;, pushed down one level. Cattle made the &lt;em&gt;agent&lt;/em&gt; disposable: don&apos;t nurse the worker, replace it. &quot;Ending is better than mending&quot; makes the &lt;em&gt;output&lt;/em&gt; disposable too: don&apos;t nurse the artifact, regenerate it. Both rest on the same foundation — value lives in the reproducible source (the template, the plan), never in the running instance.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The other half of the condition is mechanical: reversibility. &lt;code&gt;git reset --hard&lt;/code&gt; and even &lt;code&gt;rm -rf &amp;amp;&amp;amp; regenerate&lt;/code&gt; are only safe moves on top of disciplined version control — atomic commits, clean revert points, a plan and a task graph that survive the deletion. Ending is cheap because the floor is solid. Knock the floor out — uncommitted work, no plan, no clean checkpoint — and ending stops being thrift and becomes the reckless thing it sounds like.&lt;/p&gt;
&lt;h2&gt;When mending still wins&lt;/h2&gt;
&lt;p&gt;The slogan is a default, not a law, and the place it breaks is worth naming exactly.&lt;/p&gt;
&lt;p&gt;Ending discards everything the artifact knows that the plan does not. Most of the time that&apos;s nothing — the code was a faithful rendering of a spec you still have. But sometimes the artifact has accumulated real knowledge that never made it back into the source: a subtle concurrency fix, three edge cases you discovered only by running it, a workaround for a genuine bug in a dependency. If that knowledge lives &lt;em&gt;only&lt;/em&gt; in the code, regenerating throws it away, and the fresh artifact will rediscover the same pain the hard way.&lt;/p&gt;
&lt;p&gt;So the test is not &quot;is this wrong.&quot; The test is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Where does the knowledge live — in the plan, or only in the code?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If it lives in the plan, end: &lt;code&gt;git reset&lt;/code&gt; beats the debugger every time. If it lives only in the code, mend &lt;em&gt;once&lt;/em&gt; — and then do the thing that actually matters, which is to move that knowledge back into the plan. Write the edge case into the spec. Add the concurrency note to the acceptance criteria. You mend the artifact exactly long enough to harvest what it learned, and then you make sure you never have to mend it again, because next time the plan will know.&lt;/p&gt;
&lt;p&gt;There&apos;s a second failure mode that masquerades as ending: regenerating against the same vague plan that produced the mess. That isn&apos;t ending, it&apos;s thrashing — you&apos;ll get a differently-wrong artifact and spend your comprehension budget all over again. Ending only pays off if you change the input. A regeneration that doesn&apos;t sharpen the plan is just a &lt;a href=&quot;/notes/no-is-not-an-instruction/&quot;&gt;bare &quot;no&quot;&lt;/a&gt; wearing a &lt;code&gt;git reset&lt;/code&gt; costume.&lt;/p&gt;
&lt;h2&gt;Owning the dystopia&lt;/h2&gt;
&lt;p&gt;It would be too easy to quote Huxley approvingly and miss that he was describing a nightmare. Mindless disposability &lt;em&gt;is&lt;/em&gt; corrosive — that was the whole point of the book. So what keeps this from being his consumer dystopia rebuilt in a code editor?&lt;/p&gt;
&lt;p&gt;It comes down to where the taste goes. Ending is better than mending only if the attention you save by &lt;em&gt;not&lt;/em&gt; salvaging code gets reinvested upstream — in the plan, the acceptance criteria, the specification that the next regeneration will render. Spend it there and ending is leverage: you move your scarce judgment to the highest point in the system, where one improvement propagates into every future artifact. Pocket it instead — regenerate mindlessly, never sharpen the source, let the agent churn — and you&apos;ve built exactly the thing Huxley was warning about, just with tokens instead of textiles.&lt;/p&gt;
&lt;p&gt;The discipline, in one line: &lt;strong&gt;end the artifact, mend the plan.&lt;/strong&gt; Be ruthless with the disposable thing and precious with the durable one. That&apos;s the opposite of the dystopia. It&apos;s the only version of &quot;ending is better than mending&quot; worth practicing.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I open the debugger on something an agent got wrong, I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is this cheaper to regenerate from the plan than to understand and fix — and if I regenerate, what am I changing about the input so I don&apos;t just get the same thing back?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the knowledge lives in the spec, my hand goes to &lt;code&gt;git reset&lt;/code&gt;, not to the stack trace. If it lives only in the code, I mend once, harvest the lesson into the plan, and end it forever after. The expensive resource is my own attention. I try very hard now to spend it at the source, where it compounds — not in the artifact, where it evaporates the next time the agent runs.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Background: &lt;a href=&quot;/notes/plan-is-the-prompt/&quot;&gt;The plan is the prompt&lt;/a&gt; — why the durable artifact is the spec, not the code that renders it. And &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;Pet agents vs. cattle agents&lt;/a&gt; — the same disposability logic, one level up.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>cost</category><category>planning</category></item><item><title>Don&apos;t let the agent grade its own homework</title><link>https://jedarden.com/notes/dont-let-the-agent-grade-its-own-homework/</link><guid isPermaLink="true">https://jedarden.com/notes/dont-let-the-agent-grade-its-own-homework/</guid><description>An agent reporting &apos;done&apos; is the actor grading its own work. The cheapest guard against silent failure is an independent observer — a second agent, a separate tool, or the live artifact itself. Interactively you spawn the watcher by hand; in a fleet it&apos;s a validation gate. Same principle, two scales.</description><pubDate>Thu, 21 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&quot;Done — the pod is deployed and running.&quot; It wasn&apos;t. The image had built, the manifest had applied, and the pod was sitting in &lt;code&gt;CrashLoopBackOff&lt;/code&gt; because of an env var the agent never set. The agent wasn&apos;t lying. It had done the steps it set out to do, observed that each step returned success, and concluded — reasonably, from inside its own process — that the job was finished. What it had not done was &lt;em&gt;look at the thing it built from the outside.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The correction I now reach for reflexively used to be an afterthought: &lt;em&gt;&quot;have a separate instance monitor the pod to confirm it&apos;s running.&quot;&lt;/em&gt; Not &quot;are you sure?&quot; — asking the same agent to re-grade its own work just gets you the same grade with more words. A &lt;em&gt;different&lt;/em&gt; observer. The shift from the first habit to the second is the whole post.&lt;/p&gt;
&lt;h2&gt;Self-report is the actor grading its own work&lt;/h2&gt;
&lt;p&gt;When an agent tells you it succeeded, you are reading a claim produced by the same process that did the work, using the same assumptions that may have caused the failure. If the agent misunderstood the goal, its definition of &quot;done&quot; inherited the misunderstanding. If it set the wrong env var, its mental model of &quot;running&quot; is the one without that var. Asking it to verify its own output runs the check through the exact mistake you&apos;re trying to catch. The blind spot grades itself and reports 20/20.&lt;/p&gt;
&lt;p&gt;This isn&apos;t an agent flaw so much as a structural one — it&apos;s true of people too, which is why code review exists and why surgeons count the sponges twice with a second person. The fix is the same fix it&apos;s always been:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Verification has to come from somewhere the work didn&apos;t.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&quot;Somewhere else&quot; can be cheaper than it sounds. It does not require a second model or fancy infrastructure. It requires that the &lt;em&gt;check&lt;/em&gt; not share the &lt;em&gt;doing&apos;s&lt;/em&gt; assumptions.&lt;/p&gt;
&lt;h2&gt;Three places &quot;somewhere else&quot; comes from&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The live artifact.&lt;/strong&gt; The strongest observer is the thing itself, queried fresh. Don&apos;t ask the agent whether the pod is running — read the pod status. Don&apos;t ask whether the deploy landed — &lt;em&gt;&quot;confirm the new version actually landed on the server.&quot;&lt;/em&gt; Don&apos;t trust that the UI works — drive it: I&apos;ll have an agent verify a phone app over the live ADB connection rather than accept &quot;the screen should now show X.&quot; The artifact doesn&apos;t share the agent&apos;s assumptions because the artifact isn&apos;t reasoning at all. It just is whatever it is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A separate agent.&lt;/strong&gt; Spawn a second agent whose only job is to confirm, with no stake in the first one&apos;s story and, ideally, no knowledge of how the work was done. &lt;em&gt;&quot;Have a separate instance watch the logs and confirm functionality.&quot;&lt;/em&gt; A fresh agent reads the running system cold and reports what it actually sees, instead of what the builder expected to have produced. The independence is the point — give the verifier the original goal and the artifact, not the builder&apos;s narrative of success.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A different tool.&lt;/strong&gt; Build with one tool, check with another. The compiler said it built; does the test suite agree? The agent says the migration ran; does a raw &lt;code&gt;SELECT&lt;/code&gt; against the table agree? Each tool has its own failure modes, and a claim that survives two unrelated ones is far more likely to be true than one blessed twice by the same path.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The common thread: the verifier must be able to &lt;em&gt;disagree&lt;/em&gt; with the actor. If your check can only ever return &quot;yep, looks good,&quot; it isn&apos;t a check. It&apos;s a mirror.&lt;/p&gt;
&lt;h2&gt;Bake the verification into the request&lt;/h2&gt;
&lt;p&gt;The habit that made this stick for me is small: I no longer ask for the work and the verification as two messages. I ask for them as one. &lt;em&gt;&quot;Build the image, confirm it builds, then update the deployment, then confirm the new pod is actually running.&quot;&lt;/em&gt; The verification step is part of the task, not a follow-up I might forget after the agent&apos;s confident &quot;done&quot; has already half-convinced me.&lt;/p&gt;
&lt;p&gt;This matters because the failure mode isn&apos;t usually that you &lt;em&gt;can&apos;t&lt;/em&gt; verify — it&apos;s that the agent&apos;s success report lowers your guard at exactly the wrong moment. Fluent confidence is sedating. By the time you&apos;ve read &quot;deployed and running,&quot; some part of you has already moved on. Writing the check into the original ask means the verification happens while you&apos;re still paying attention, before the report has a chance to talk you out of looking.&lt;/p&gt;
&lt;h2&gt;The same practice, wearing fleet armor&lt;/h2&gt;
&lt;p&gt;Interactively, this is something I do by hand: I spawn the watcher, I read the pod status, I tell the agent to confirm before it moves on. It&apos;s a reflex, and it&apos;s reversible — if I skip it, I find out a few minutes later and re-run.&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;fleet&lt;/a&gt; there is no &quot;a few minutes later me&quot; watching. A worker that grades its own homework and closes its own task injects a silent failure straight into the system, and the next worker builds on top of it. So the same instinct hardens into structure: in &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;the state machine&lt;/a&gt;, a successful exit code is not a success — it&apos;s a &lt;em&gt;claim&lt;/em&gt; that has to clear a validation gate before the task is allowed to close, and if the gate fails the task is released back to the queue rather than marked done. The acceptance criteria written into each task are exactly the &quot;somewhere else&quot; the check comes from: a definition of done the worker did not get to author.&lt;/p&gt;
&lt;p&gt;It is the identical practice. Interactively, I am the gate, and I stay cheap by staying in the loop. In the fleet, the gate is code, because no one is in the loop. What does not change between the two is the rule that produced both: the thing that did the work does not get to be the thing that certifies it.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I accept an agent&apos;s &quot;done,&quot; I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Who checked this — and was it the same process, with the same assumptions, that did the work?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the answer is &quot;the agent says so,&quot; I haven&apos;t verified anything; I&apos;ve read a press release. The fix is never to interrogate the agent harder. It&apos;s to go find an observer that didn&apos;t share the work&apos;s blind spot — the artifact, another agent, another tool — and let it have the last word.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Background: &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;Deterministic state machines for non-deterministic agents&lt;/a&gt; — where this reflex becomes a validation gate that a worker&apos;s &quot;done&quot; has to clear. And &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;Pet agents vs. cattle agents&lt;/a&gt; — why no-one-is-in-the-loop changes everything about who gets to certify the work.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>collaboration</category><category>verification</category><category>NEEDLE</category></item><item><title>Anchor on a fact the model can&apos;t see</title><link>https://jedarden.com/notes/anchor-on-a-fact-the-model-cant-see/</link><guid isPermaLink="true">https://jedarden.com/notes/anchor-on-a-fact-the-model-cant-see/</guid><description>An agent&apos;s confidence is uncorrelated with whether it&apos;s right. The cheapest correction you can make is to hold out one ground-truth fact it never had in context — usually a number — and check its output against that. Works the same whether you&apos;re watching one session or auditing a fleet&apos;s reports.</description><pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A worker reported it had sized a node pool for the new cluster. The plan looked reasonable, the YAML looked reasonable, the explanation was fluent. The only problem was a number I happened to know and the agent didn&apos;t: the configured Rackspace Spot node is 2 CPU and 3.75 GB of RAM. The agent had quietly reasoned as though it had more. &lt;em&gt;&quot;That doesn&apos;t make sense,&quot;&lt;/em&gt; I typed, &lt;em&gt;&quot;the node is 2CPU/3.75GB&quot;&lt;/em&gt; — and the whole plan unwound, because it had been built on a resource budget that didn&apos;t exist.&lt;/p&gt;
&lt;p&gt;I do some version of this constantly. &lt;em&gt;&quot;5 beads per 24h? The close rate is far higher than that.&quot;&lt;/em&gt; &lt;em&gt;&quot;That can&apos;t be right.&quot;&lt;/em&gt; It is the single most reliable correction I make, and it works because of one fact about these models that took me a while to internalize.&lt;/p&gt;
&lt;h2&gt;Confidence is not correlation&lt;/h2&gt;
&lt;p&gt;A language model&apos;s fluency is uncorrelated with whether it&apos;s right. It will describe a node pool it sized wrong with exactly the same calm competence it uses to describe one it sized right. There is no tremor in the prose when the underlying number is fiction. This is not the model lying — it is the model doing precisely what it does, which is produce the most plausible continuation given what&apos;s in its context. If the true number was never in its context, the most plausible continuation is built on a guess, and the guess is delivered with the same production values as a fact.&lt;/p&gt;
&lt;p&gt;So you cannot audit an agent by reading for hesitation. There is none. You have to audit it against something outside the text.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The model is fluent about a number it can&apos;t see. Your job is to be the one who can see it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Why the number is usually missing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/notes/benchmarks-measure-a-model-you-arent-running/&quot;&gt;Benchmarks measure a model running on an almost-empty context window&lt;/a&gt;, and most real work is the opposite — but &quot;full context&quot; is not the same as &quot;correct context.&quot; The agent can have ten thousand tokens of code in its window and still be missing the one operational fact that decides the task: the actual size of the node, the real close rate, the column that&apos;s secretly nullable, the rate limit on the third-party API, the fact that this cluster is read-only.&lt;/p&gt;
&lt;p&gt;These facts share a property: they live in the &lt;em&gt;world&lt;/em&gt;, not in the repo. They&apos;re in a dashboard, in your head, in a billing console, in something you saw last Tuesday. The agent has no path to them unless you put them in front of it. And because it can&apos;t tell that they&apos;re missing — absence of a fact doesn&apos;t feel like anything from the inside of a context window — it doesn&apos;t ask. It interpolates. Confidently.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This reframes what you, the human, are actually &lt;em&gt;for&lt;/em&gt; in the loop. You are not there to write the code; the agent is faster than you at that. You are there because you are holding facts the agent structurally cannot hold, and the highest-leverage thing you do all session is notice when the output collides with one of them.&lt;/p&gt;
&lt;h2&gt;Numbers are the best anchors&lt;/h2&gt;
&lt;p&gt;Of all the ground-truth facts you might hold, numbers are the ones to reach for first, because they fail loudly. A node has 3.75 GB, not &quot;enough.&quot; A close rate is 40 a day, not &quot;low.&quot; A node pool either fits in the budget or it doesn&apos;t, and you can check by subtracting. Prose claims are slippery — &quot;this should improve latency&quot; is hard to falsify in a glance — but a quantitative claim either matches the number you&apos;re holding or it doesn&apos;t, and the mismatch is instant.&lt;/p&gt;
&lt;p&gt;So when I read an agent&apos;s report, the thing I&apos;m hunting for is the load-bearing number. &lt;em&gt;How big did it think the node was? How many of these did it assume there are? What rate did it design against?&lt;/em&gt; Often the agent doesn&apos;t even state the number — it&apos;s implicit in a decision. Half the work is making the implicit number explicit so I can check it: &lt;em&gt;&quot;what RAM budget did you assume here?&quot;&lt;/em&gt; The moment it commits to a figure, I can compare against the one in my head, and the whole edifice stands or falls on that single comparison.&lt;/p&gt;
&lt;h2&gt;This scales straight into the fleet&lt;/h2&gt;
&lt;p&gt;Interactively, this is a reflex: read the report, find the number, check it, say &quot;that doesn&apos;t make sense&quot; when it collides. Across a fleet it becomes a design requirement, and it&apos;s the same instinct wearing a different hat.&lt;/p&gt;
&lt;p&gt;If the operator&apos;s job is to anchor agents against facts they can&apos;t see, then a fleet needs those facts written &lt;em&gt;into&lt;/em&gt; the work, not held in one human&apos;s head — because there&apos;s no human in a &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;cattle worker&apos;s&lt;/a&gt; loop to say &quot;that can&apos;t be right.&quot; The node size goes in the plan. The expected close rate becomes an &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;acceptance criterion the worker validates against&lt;/a&gt;. The reports the fleet emits get spot-checked against the dashboards, because a confident report is exactly as trustworthy at scale as it was in your single session — which is to say, not at all, until you&apos;ve checked it against something it couldn&apos;t see.&lt;/p&gt;
&lt;p&gt;The pet version and the cattle version are the same practice. Interactively you supply the missing fact in real time. At scale you supply it in advance, in writing, and you keep auditing the output against the world because the model&apos;s confidence never told you anything in the first place.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;When an agent hands me a result that &lt;em&gt;sounds&lt;/em&gt; right, I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What number is this conclusion resting on, and have I seen that number with my own eyes?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the conclusion rests on a figure the agent could only have known by being told — and I never told it — then I&apos;m not reading a finding, I&apos;m reading a plausible guess in the costume of one. The fix is not to argue with the prose. It&apos;s to put the real number on the table and watch what survives contact with it.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Background: &lt;a href=&quot;/notes/benchmarks-measure-a-model-you-arent-running/&quot;&gt;Benchmarks measure a model you are not running&lt;/a&gt; — on context and what the model is actually operating with. And &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;Deterministic state machines for non-deterministic agents&lt;/a&gt; — where the &quot;check it against something external&quot; reflex becomes a validation gate.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>collaboration</category><category>context</category></item><item><title>&apos;No&apos; is not an instruction</title><link>https://jedarden.com/notes/no-is-not-an-instruction/</link><guid isPermaLink="true">https://jedarden.com/notes/no-is-not-an-instruction/</guid><description>The cheapest way to steer an agent is also the most-skipped one. A rejection tells the agent what not to do; the space of &apos;not that&apos; is enormous. Name the alternative in the same breath and you re-steer in one turn instead of N. True for one pet session and for twenty cattle workers.</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The agent picks OpenAI&apos;s API to transcribe some audio. You don&apos;t want that — you have Whisper running on your own cluster, and you&apos;d rather not ship audio to a third party. So you type &quot;no, don&apos;t use OpenAI.&quot; The agent apologizes, agrees, and reaches for... AssemblyAI. Still wrong. You type &quot;no&quot; again. It tries Deepgram. You are now three turns deep and the agent is walking a random path through the space of speech-to-text vendors, because every turn you have told it where &lt;em&gt;not&lt;/em&gt; to go and never once told it where to go.&lt;/p&gt;
&lt;p&gt;This is the most common way I waste turns with a coding agent, and it took me an embarrassing number of sessions to see the shape of it.&lt;/p&gt;
&lt;h2&gt;&quot;Not that&quot; is an enormous space&lt;/h2&gt;
&lt;p&gt;A rejection carries one bit of information: the last thing was wrong. That is genuinely useful — but it is also almost all you&apos;ve given the agent. Everything that is &quot;not the rejected thing&quot; is still on the table, and for most decisions that set is huge. There are a dozen transcription vendors. There are twenty ways to do auth. There are infinite refactors that are &quot;not the one you just did.&quot;&lt;/p&gt;
&lt;p&gt;When you say only &quot;no,&quot; you are asking the agent to guess again from a barely-narrowed space. It will guess confidently, because that is what these models do, and it will frequently guess in a direction you like even less than the first one. You have spent a turn and bought yourself a fresh problem.&lt;/p&gt;
&lt;p&gt;The fix is one clause long:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A rejection that names the alternative re-steers in a single turn. A rejection without one buys another guess.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is what that looks like in my own transcripts, again and again:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&quot;Don&apos;t use OpenAI. Use the Whisper instance running on the cluster.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&quot;No need for an API lookup — checking the profile picture is a free signal, use that.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&quot;Don&apos;t use middleware for auth. Have the page itself ask for the password.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of those is longer than the bare &quot;no&quot; would have been by more than a sentence. Each one collapses the search space to a single point. The agent does not have to guess what I meant, because I told it.&lt;/p&gt;
&lt;h2&gt;Why the asymmetry is brutal at scale&lt;/h2&gt;
&lt;p&gt;With a single interactive session — a &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;pet&lt;/a&gt; — a content-free rejection is merely annoying. You catch the second wrong guess, you sigh, you finally say the thing you should have said the first time. The cost is a couple of turns and a little of your patience.&lt;/p&gt;
&lt;p&gt;Run the same habit across a fleet and the cost stops being linear. A vague correction is information you &lt;em&gt;didn&apos;t&lt;/em&gt; write down, and &lt;a href=&quot;/notes/plan-is-the-prompt/&quot;&gt;the input is the plan&lt;/a&gt;. When a cattle worker misreads the task, it doesn&apos;t pause to ask — it runs to completion in some &quot;not what you wanted&quot; direction, fails validation, and the task goes back on the queue for the next worker to misread differently. A rejection you&apos;d have typed interactively never even reaches them. The only thing that reaches a stateless worker is what you committed to the plan, the bead, the standing rule. So the discipline you build interactively — &lt;em&gt;name the alternative, don&apos;t just veto&lt;/em&gt; — is the same discipline that makes a written task unambiguous enough to hand to a worker you&apos;ll never talk to.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is why I think of it as one practice with two surfaces. Interactively, &quot;don&apos;t X, do Y&quot; saves you a turn. In a plan, &quot;don&apos;t X, do Y&quot; saves you a failed run multiplied by however many workers hit the ambiguity before you noticed.&lt;/p&gt;
&lt;h2&gt;The tell: when you reach for &quot;no,&quot; you know the answer&lt;/h2&gt;
&lt;p&gt;Here is the part that makes this actionable. Almost every time I catch myself typing a bare &quot;no,&quot; I already know what I want instead. The right answer is &lt;em&gt;right there&lt;/em&gt; — I just didn&apos;t type it, because rejecting felt faster than specifying. It isn&apos;t. The half-second I saved by typing &quot;no, don&apos;t do that&quot; I pay back with interest on the next turn when the agent guesses again.&lt;/p&gt;
&lt;p&gt;So the rule I hold myself to now is mechanical: &lt;strong&gt;if I&apos;m about to reject something, I am not allowed to send the message until it contains the alternative.&lt;/strong&gt; If I genuinely don&apos;t know the alternative yet, that&apos;s a different and more honest message — &lt;em&gt;&quot;stop, I need to think about the right approach here&quot;&lt;/em&gt; — and it should not masquerade as a correction. A correction implies I know the target. If I know the target, I should say it.&lt;/p&gt;
&lt;p&gt;There&apos;s a softer version of the same failure that&apos;s worth naming: the rejection that&apos;s &lt;em&gt;technically&lt;/em&gt; a redirect but points nowhere useful. &lt;em&gt;&quot;No, do it properly.&quot;&lt;/em&gt; &lt;em&gt;&quot;That&apos;s not what I meant.&quot;&lt;/em&gt; &lt;em&gt;&quot;Be more careful.&quot;&lt;/em&gt; These feel like instructions and contain none. &quot;Properly&quot; is not a destination. If the agent could infer &quot;properly,&quot; it would have gone there the first time.&lt;/p&gt;
&lt;h2&gt;What it costs&lt;/h2&gt;
&lt;p&gt;Almost nothing, which is exactly why it&apos;s hard. The whole tax is a moment of thinking &lt;em&gt;before&lt;/em&gt; you hit enter instead of after the agent guesses wrong. You have to convert the vague dissatisfaction you feel — &quot;ugh, not that&quot; — into the specific thing you&apos;d prefer, in the same breath. The feeling arrives before the specification; the work is refusing to send the feeling on its own.&lt;/p&gt;
&lt;p&gt;The one real cost: sometimes you&apos;ll discover, in the act of trying to name the alternative, that you don&apos;t actually know what you want. That is not the practice failing. That is the practice doing its most valuable job — surfacing, for the price of one unsent message, that the problem was underspecified in your own head before it was ever underspecified to the agent.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I send a correction — interactive or written into a plan — I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the agent does the literal opposite of what it just did, will that be right?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If yes, a plain &quot;no&quot; is fine; the space really is binary. That case is rare. Far more often the answer is &quot;no, the opposite is also wrong,&quot; which means the space is wide, which means a bare rejection is about to cost me another guess — and the alternative I&apos;m failing to type is already sitting in my head, waiting to be sent.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Background: &lt;a href=&quot;/notes/plan-is-the-prompt/&quot;&gt;The plan is the prompt&lt;/a&gt; — why the information you don&apos;t write down is the information that costs you, multiplied by every worker that hits the gap. And &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;Pet agents vs. cattle agents&lt;/a&gt; — the model that makes the asymmetry visible.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>collaboration</category><category>prompting</category></item><item><title>Benchmarks measure a model you are not running</title><link>https://jedarden.com/notes/benchmarks-measure-a-model-you-arent-running/</link><guid isPermaLink="true">https://jedarden.com/notes/benchmarks-measure-a-model-you-arent-running/</guid><description>SWE-bench problems have a median of 282 tokens. HumanEval: 117. MBPP: 16. Every major coding benchmark evaluates a model operating with essentially an empty context window — which is almost never the condition you run in. Unless you are running cattle.</description><pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When a new model drops, the benchmark scores arrive first. SWE-bench Verified percentage. HumanEval pass@1. APPS accuracy. These numbers travel fast because they are precise and comparable — they feel like a specification sheet for a component you are about to buy.&lt;/p&gt;
&lt;p&gt;What they actually measure is a very specific operating condition that most people are not running in. Understanding what that condition is changes how you interpret the score, and it changes which deployment model actually delivers the capability the score advertises.&lt;/p&gt;
&lt;h2&gt;What the context window looks like during a benchmark&lt;/h2&gt;
&lt;p&gt;I measured the token count of every problem in the seven major coding benchmarks used to evaluate LLM agents. The tokenizer is &lt;code&gt;cl100k_base&lt;/code&gt; (GPT-4 / tiktoken). The numbers are for the raw problem input — the text actually sent to the model.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;Problems&lt;/th&gt;
&lt;th&gt;Median&lt;/th&gt;
&lt;th&gt;P99&lt;/th&gt;
&lt;th&gt;Max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MBPP&lt;/td&gt;
&lt;td&gt;257&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HumanEval&lt;/td&gt;
&lt;td&gt;164&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;117 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;310&lt;/td&gt;
&lt;td&gt;391&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigCodeBench&lt;/td&gt;
&lt;td&gt;1,140&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;129 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;363&lt;/td&gt;
&lt;td&gt;1,216&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SWE-bench Verified&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;294 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2,514&lt;/td&gt;
&lt;td&gt;6,939&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SWE-bench&lt;/td&gt;
&lt;td&gt;2,294&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;282 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2,937&lt;/td&gt;
&lt;td&gt;22,483&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LiveCodeBench&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;421 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,105&lt;/td&gt;
&lt;td&gt;1,521&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;APPS&lt;/td&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;456 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,103&lt;/td&gt;
&lt;td&gt;1,815&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Every single problem across all seven benchmarks fits under 8,000 tokens. The largest problem in the entire dataset — an outlier SWE-bench issue at 22,483 tokens — is still under 12% of Claude&apos;s 200,000-token context window. At the median, HumanEval problems use 0.06% of that window. MBPP problems use 0.008%.&lt;/p&gt;
&lt;p&gt;The model being benchmarked is operating with its context window essentially empty.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Two conditions, not one&lt;/h2&gt;
&lt;p&gt;It is tempting to read &quot;empty context window&quot; as a capacity fact — the model has space available. The more important thing is what that space is &lt;em&gt;not&lt;/em&gt; filled with.&lt;/p&gt;
&lt;p&gt;A benchmark evaluation has two properties that travel together but are worth separating:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Few tokens of input.&lt;/strong&gt; The problem statement is small. At 117 tokens, a HumanEval prompt is shorter than most Slack messages. The model has room to reason freely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No prior turns.&lt;/strong&gt; The context contains nothing except the problem. No corrections from three exchanges ago. No dead end the model went down and you had to redirect. No half-finished implementation that the model is tempted to continue in the wrong direction. No long system prompt from a previous session that is now only partially relevant. The model starts with a blank slate.&lt;/p&gt;
&lt;p&gt;The second condition is the one that matters more, and it is the one people talk about least.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;What fills a pet session&apos;s context&lt;/h2&gt;
&lt;p&gt;A pet agent session accumulates context continuously. After an hour of work, the context window contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The original task description (probably underspecified)&lt;/li&gt;
&lt;li&gt;Your first few corrections and clarifications&lt;/li&gt;
&lt;li&gt;The model&apos;s first attempt, which missed something&lt;/li&gt;
&lt;li&gt;Your redirect&lt;/li&gt;
&lt;li&gt;The model&apos;s second attempt, partially correct&lt;/li&gt;
&lt;li&gt;More corrections&lt;/li&gt;
&lt;li&gt;Tool call outputs — file reads, test runs, error messages&lt;/li&gt;
&lt;li&gt;The model&apos;s current understanding, shaped by all of the above&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not neutral. Research on context window utilization shows that models degrade when relevant content is buried in earlier positions — the &quot;lost in the middle&quot; phenomenon. Information at the start and end of a context is attended to more reliably than information in the middle. A long pet session buries its most important content (the actual task requirements) under layers of accumulated back-and-forth.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Beyond attention degradation, there is a more direct effect: the model&apos;s prior wrong attempts are in the context. It has seen itself go down a particular path. It has a prior. That prior shapes the next attempt, not always in the right direction.&lt;/p&gt;
&lt;p&gt;The benchmark model has none of this. It sees the problem cold. Whatever capability it has for that problem is expressed fully, without interference.&lt;/p&gt;
&lt;h2&gt;The cattle worker is the benchmark condition&lt;/h2&gt;
&lt;p&gt;A stateless cattle worker dispatched by an orchestrator starts each task with a context containing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The project instructions (&lt;code&gt;CLAUDE.md&lt;/code&gt;) — stable, curated, written once&lt;/li&gt;
&lt;li&gt;The task body from the bead — a precise specification of one unit of work&lt;/li&gt;
&lt;li&gt;Whatever reference material the task body explicitly includes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is structurally close to a benchmark evaluation. The input is small and deliberate. There is no accumulated conversation. No prior wrong attempts. No corrections that anchored the model toward a direction it should abandon.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The pet session is not that model. The pet session is a model operating under conditions that are systematically worse than the conditions under which it was benchmarked — and those conditions degrade further the longer the session runs.&lt;/p&gt;
&lt;p&gt;This means benchmark scores are a better predictor of cattle performance than pet performance. When you read that a model scores X% on SWE-bench Verified, the deployment model that will actually realize that capability is a stateless worker with a clean context and a well-scoped task — not an ongoing chat session where the model has been talking to you for two hours.&lt;/p&gt;
&lt;h2&gt;The score you are buying is not what you are running&lt;/h2&gt;
&lt;p&gt;The practical consequence is that most people buy capability — a higher benchmark score, a more expensive model tier — and then run it in a mode that systematically degrades that capability below what the benchmark measured.&lt;/p&gt;
&lt;p&gt;A pet session with a more capable model is better than a pet session with a less capable model, but both are operating below their benchmark-measured ceiling. The gap between &quot;what the benchmark measured&quot; and &quot;what the pet session delivers&quot; grows as the session accumulates context. By hour three, with 50,000 tokens of back-and-forth, you are running a noticeably different model than the one that got the score.&lt;/p&gt;
&lt;p&gt;There is no equivalent degradation in the cattle model. A stateless worker dispatched against a well-specified task is as close to benchmark conditions as production use gets. The score is what you bought. The score is roughly what you run.&lt;/p&gt;
&lt;p&gt;This also changes the economics of model selection. The correct question is not &quot;which model scores highest on SWE-bench?&quot; but &quot;which model scores highest on SWE-bench, and am I running it in conditions that will actually realize that score?&quot; If you are running pet sessions, you are paying for capability you are not fully using. If you are running cattle workers with clean contexts and scoped tasks, you are.&lt;/p&gt;
&lt;h2&gt;What an honest benchmark would measure&lt;/h2&gt;
&lt;p&gt;The benchmarks that exist were designed for a world of clean evaluations, not for the question of how model capability degrades across a production conversation. None of them measure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance at turn 30 of a live session versus turn 1&lt;/li&gt;
&lt;li&gt;Performance with 80K tokens of prior context versus 500&lt;/li&gt;
&lt;li&gt;How much capability is recovered by compressing and distilling the context versus starting fresh&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These would be more diagnostic for real production use. The existing benchmarks tell you the ceiling. What is missing is the curve that describes how quickly you fall away from that ceiling as the context accumulates, and how different deployment models (cattle versus pet) track that curve differently.&lt;/p&gt;
&lt;p&gt;Until those measurements exist, the empirical data points in one direction: the deployment model closest to benchmark conditions is stateless dispatch into clean context. That is the cattle model.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;When someone cites a benchmark to justify a model choice, I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Under what context conditions was that score measured, and are those the conditions we are actually running?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the answer is &quot;a 300-token prompt against a blank context window&quot; and the deployment is &quot;an ongoing chat session that has been running for six hours,&quot; the score is describing a model that is not the model they are running.&lt;/p&gt;
&lt;p&gt;Knowing that does not mean you stop using pet sessions — there are tasks where they are the right tool, and where the accumulated context is a feature rather than a bug. It means you understand that benchmark scores are ceiling measurements, and that the delta between the ceiling and what you actually get is a function of the deployment model you chose.&lt;/p&gt;
&lt;p&gt;Cattle closes that delta. That is the case, made empirically.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Data: token counts measured across 9,755 benchmark problems (HumanEval, MBPP, SWE-bench, SWE-bench Verified, LiveCodeBench, BigCodeBench, APPS) using the &lt;code&gt;cl100k_base&lt;/code&gt; tokenizer. Raw data (per-problem token counts + full statistics): &lt;a href=&quot;/data/benchmark-tokens.json&quot;&gt;benchmark-tokens.json&lt;/a&gt; — schema, license, and citation on the &lt;a href=&quot;/data/&quot;&gt;data page&lt;/a&gt;. Background: &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;pet agents vs. cattle agents&lt;/a&gt; — the deployment model this post argues for. The context reconstruction approach that makes clean-context cattle work: &lt;a href=&quot;/notes/plan-is-the-prompt/&quot;&gt;the plan is the prompt&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>orchestration</category><category>benchmarks</category><category>context</category></item><item><title>The plan is the prompt</title><link>https://jedarden.com/notes/plan-is-the-prompt/</link><guid isPermaLink="true">https://jedarden.com/notes/plan-is-the-prompt/</guid><description>Why a detailed plan document is the most token-dense artifact you will write for a headless agent fleet. What a plan actually needs to contain, how it anchors the genesis bead hierarchy, and why a bad plan is more expensive than it looks when twenty workers are running it simultaneously.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When a pet agent misunderstands the task, you correct it. Two sentences, maybe three. The session absorbs the correction and continues. The cost is negligible — a few seconds of your time and a handful of tokens.&lt;/p&gt;
&lt;p&gt;When a cattle worker misunderstands the task, it runs to completion. It produces output that does not match what you wanted, the orchestrator classifies the outcome as failure, and the task goes back on the queue. Another worker picks it up, starts cold, misunderstands in a slightly different direction, and fails again. If you have twenty workers running the same workspace, the same misunderstanding plays out twenty different ways at twenty different costs before you notice the pattern and fix the input.&lt;/p&gt;
&lt;p&gt;The input is the plan.&lt;/p&gt;
&lt;h2&gt;What you are actually paying for in a pet session&lt;/h2&gt;
&lt;p&gt;Pet sessions accumulate context through dialogue. You describe what you want, the agent responds, you correct, it adjusts. The eventual output reflects not just your initial description but everything you added along the way: the clarifications, the course corrections, the &quot;no, I meant the other thing.&quot; By the time the pet agent produces something useful, it has received substantially more information than what was in your first message.&lt;/p&gt;
&lt;p&gt;This is not a flaw — it is how conversations work. But it conceals the actual input cost. The information the agent needs to do the job was never written down in one place. It accumulated, across turns, in a format that is impossible to hand directly to a stateless worker.&lt;/p&gt;
&lt;p&gt;When you move to cattle, that cost comes due. The worker starts cold. It gets exactly what you wrote, nothing more. The turns of dialogue that would have filled in the blanks do not exist — so the blanks have to be filled before dispatch. The plan is how you fill them.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;The compression problem&lt;/h2&gt;
&lt;p&gt;A running system has enormous context attached to it: commit history, bead history, comments in the code, decisions made and reversed over weeks of work. A worker technically has access to all of this. The question is whether it can find the relevant signal in time to use it.&lt;/p&gt;
&lt;p&gt;Commit history is retrospective and fragmented. You learn &lt;em&gt;what&lt;/em&gt; changed but rarely &lt;em&gt;why the tradeoff landed where it did&lt;/em&gt;. Bead bodies describe individual tasks, not the coherent shape of the whole. Code captures implementation, not intent. None of these is dense enough.&lt;/p&gt;
&lt;p&gt;The plan document is different. It is written to be read by a worker who knows nothing — which means it has to contain everything necessary in as few tokens as possible. A good plan is not comprehensive in the way a specification is comprehensive. It is compressed in a specific way: it records the decisions that were made without recording the deliberations that led to them. It says &lt;em&gt;this tradeoff resolved in this direction&lt;/em&gt; rather than &lt;em&gt;here are both sides of the argument&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The compression is the point. A worker does not need to re-litigate the decisions. It needs to know what was decided and work within that. A plan that explains &lt;em&gt;why&lt;/em&gt; every decision was made is longer than a plan needs to be and often less useful, because the why is background and the worker needs foreground.&lt;/p&gt;
&lt;h2&gt;The hierarchy that makes cold starts work&lt;/h2&gt;
&lt;p&gt;NEEDLE&apos;s task structure has four layers:&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;&lt;a href=&quot;/glossary/#genesis-bead&quot;&gt;genesis bead&lt;/a&gt;&lt;/strong&gt; sits at the root of any significant project. It exists to tie phases together and track overall progress. Its body references the plan document — that reference is the load-bearing connection. The genesis bead does not contain the plan; it points to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase beads&lt;/strong&gt; derive from the plan&apos;s phasing section. Each phase bead describes a coherent unit of work — a vertical slice, a capability, a milestone — with its own acceptance criteria and its own list of child tasks. Phase beads block the genesis bead; when all phases close, the genesis closes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Task beads&lt;/strong&gt; are the atomic units a worker actually executes. A task bead is scoped to a single piece of work a worker can complete in one run: one file, one function, one test suite, one configuration change.&lt;/p&gt;
&lt;p&gt;A worker assigned a task bead reads: the task bead body, the parent phase bead, and the plan document at the genesis bead&apos;s reference path. In that order, smallest to largest scope, most specific to least specific. By the time it has read all three, it knows exactly what it is doing, how it fits into the phase, and what the overall project is trying to accomplish.&lt;/p&gt;
&lt;p&gt;This only works if the plan document is coherent enough to anchor the hierarchy. A plan that is vague at the project level produces phase beads that are vague at the phase level, which produce task beads that leave workers guessing. The failure propagates down; the fix has to start at the top.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;What has to be in the plan&lt;/h2&gt;
&lt;p&gt;There are several categories of content that make a plan useful to a cold-start worker. Missing any of them degrades the plan proportionally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scope lock.&lt;/strong&gt; What the system does, stated precisely enough that a worker can determine whether a given change is in scope or out of scope without asking. This is harder to write than it sounds. The failure mode is a scope statement that is technically accurate but vague enough to be consistent with many different implementations — which means every worker is free to pick a different implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Acceptance criteria.&lt;/strong&gt; The conditions under which the project is done. Not aspirations (&quot;the system should be fast&quot;) but testable criteria (&quot;p99 response time under 200ms with N concurrent users&quot;). Acceptance criteria are what let the orchestrator evaluate whether a worker&apos;s output counts as success. If the criteria are absent or vague, the orchestrator cannot classify the outcome reliably, and the &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;outcome table&lt;/a&gt; loses a row.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase boundaries.&lt;/strong&gt; Where one phase ends and the next begins, stated as conditions rather than calendar dates. A phase boundary is a checkpoint: the system is in &lt;em&gt;this&lt;/em&gt; state before this phase, and in &lt;em&gt;this&lt;/em&gt; state after. If the boundary is defined as a date, it is almost certainly wrong the moment implementation starts. If it is defined as a condition, it stays true regardless of how long the phase takes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Known unknowns.&lt;/strong&gt; The things you do not know yet, stated explicitly. A plan that does not acknowledge its own uncertainty is pretending to more confidence than it has — and workers will act on that pretended confidence. A plan that says &quot;we do not yet know how to handle the edge case of X; this will be resolved in phase 3&quot; gives workers license to defer the question cleanly rather than improvise an answer that may conflict with what phase 3 eventually decides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constraint inventory.&lt;/strong&gt; The fixed points that eliminate solution space: existing interfaces you cannot change, performance budgets you cannot exceed, security requirements you cannot trade away. Constraints are more useful than requirements because they narrow the space of valid implementations without prescribing a specific one. A worker that knows the constraints can make autonomous design decisions within them; a worker that does not know the constraints makes design decisions that may violate them invisibly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rollback plan.&lt;/strong&gt; What happens if phase N fails. Not &quot;we will figure it out&quot; but the actual fallback: which state the system can be safely returned to, which changes are reversible and which are not, what the recovery path is. Workers do not need this to execute normally — they need it to handle the abnormal cases that the orchestrator surfaces.&lt;/p&gt;
&lt;h2&gt;The plan is how you avoid pivoting mid-flight&lt;/h2&gt;
&lt;p&gt;The most expensive thing that can happen to a cattle fleet is not a worker crashing. It is a worker succeeding at the wrong thing — because the plan did not make &quot;the right thing&quot; unambiguous.&lt;/p&gt;
&lt;p&gt;A mid-flight pivot in a pet session costs a turn of conversation. A mid-flight pivot in a cattle fleet costs every task derived from the misunderstanding: the work already done, the retries queued, the downstream tasks that were built on the wrong foundation. The later in the implementation the pivot happens, the more work has to be undone. This is why the plan-review gate exists.&lt;/p&gt;
&lt;p&gt;There is also an asymmetry in how expensive the pivot is depending on what changed. If the plan needed adjustment, the code artifacts can be wholesale deleted and workers restarted from the revised plan. Code is cheap. A well-scoped implementation takes hours for a fleet of workers, not days. The correct response to discovering your plan was wrong is not to patch the existing code — it is to fix the plan, clear the code, and run again. The plan is the expensive artifact; the code is the output.&lt;/p&gt;
&lt;p&gt;This is the rule everything above resolves to: &lt;strong&gt;the plan is the source of truth.&lt;/strong&gt; When the plan and the artifacts disagree, the plan is right by definition, and the artifacts are what gets amended. The direction is not negotiable. Editing the plan to match whatever the code drifted into feels like keeping the document current, but it is laundering a mistake into the source of truth — the next cold-start worker reads the retrofitted plan and treats the drift as intent. You conform the artifacts to the plan, never the plan to the artifacts. The only thing that legitimately changes a plan is a changed &lt;em&gt;decision&lt;/em&gt;, made deliberately — and that change leads the code rather than trailing it.&lt;/p&gt;
&lt;p&gt;This changes what you invest in. You spend the effort on the plan. You spend comparatively little worrying about whether any individual implementation is precious, because it is not — it is reproducible from the plan in a matter of hours.&lt;/p&gt;
&lt;h2&gt;The plan-review gate&lt;/h2&gt;
&lt;p&gt;Before any worker touches the code, the plan goes through &lt;code&gt;/plan-review&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The skill checks 80+ structural patterns across scope, acceptance criteria, architecture, preflight safety, phasing, testing, security, performance, operations, API design, and risk. It was developed from analysis of high-quality planning documents by Jeffrey Emanuel, whose methodology for writing plans that survive contact with implementation influenced how I think about this. The patterns were extracted from what those plans had in common — and, more usefully, from what the plans that failed mid-implementation were missing.&lt;/p&gt;
&lt;p&gt;The most common failure patterns cluster around the same four gaps: no acceptance criteria (workers cannot self-evaluate output), no phase gates (workers do not know when a phase is complete), no rollback plan (failures have no recovery path), and no constraint inventory (workers make design decisions in an unconstrained space and produce incompatible implementations). Plan-review checks for all four explicitly, along with everything else.&lt;/p&gt;
&lt;p&gt;The output of a review is a scorecard with PRESENT / PARTIAL / MISSING ratings for each item, followed by an offer to draft the missing sections. The offer is worth taking. A plan that passes review at 90% is close enough to deploy; a plan at 60% has gaps that will compound across a fleet.&lt;/p&gt;
&lt;p&gt;The skill is available at &lt;a href=&quot;https://github.com/jedarden/jeds-curated-skills&quot;&gt;jedarden/jeds-curated-skills&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The cost multiplier&lt;/h2&gt;
&lt;p&gt;The math for why plan quality matters more in cattle than in pets is straightforward.&lt;/p&gt;
&lt;p&gt;In a pet session, a plan gap costs one correction: a few seconds of your time, a few tokens, the session absorbs the fix. The cost is O(1).&lt;/p&gt;
&lt;p&gt;In a cattle fleet with N workers, a plan gap costs N failed executions before you notice the pattern. Each failed execution burns its full budget — time, tokens, whatever the worker spent before the orchestrator classified the outcome as failure. The cost is O(N × execution budget). At twenty workers with a per-task budget of 100K tokens, one plan gap that takes two failed iterations to surface costs four million tokens in wasted execution before you see it in the outcome distribution and trace it back to the plan.&lt;/p&gt;
&lt;p&gt;This is not hypothetical. It is the most expensive class of bug in a cattle system — more expensive than a bad prompt, more expensive than a misconfigured model, more expensive than a network issue. Network issues affect individual calls; plan gaps affect every worker on every task derived from the plan.&lt;/p&gt;
&lt;p&gt;The fix is to treat the plan as a first-class artifact with a quality gate, not as a rough sketch you clarify in conversation.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;What a plan is not&lt;/h2&gt;
&lt;p&gt;A plan is not a specification. A specification describes every detail of the implementation. A plan describes the decisions that constrain the implementation without prescribing every detail of it. Workers fill in the details; the plan tells them which details are fixed and which are theirs to choose.&lt;/p&gt;
&lt;p&gt;A plan is not a design document. A design document explains &lt;em&gt;how&lt;/em&gt; the system will be built. A plan records &lt;em&gt;what&lt;/em&gt; the system will do and &lt;em&gt;what success looks like&lt;/em&gt;. The how is the worker&apos;s job; the what and the done are the plan&apos;s job.&lt;/p&gt;
&lt;p&gt;A plan is not a changelog. It records the current state of decisions, not the history of how those decisions evolved. A plan that accumulates commentary about why things changed over time is a plan that is getting harder to read with each revision. Keep the plan current and put the history in commit messages.&lt;/p&gt;
&lt;h2&gt;What I&apos;d change&lt;/h2&gt;
&lt;p&gt;Two things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plans should be versioned with the code — and the plan leads.&lt;/strong&gt; The plan lives next to the code it describes, and the two travel together. But &quot;together&quot; has a direction. When the code diverges from the plan — which it always does, in small ways — that divergence is a defect in the code, not a fact to be transcribed into the plan. You amend the artifacts to match the plan in the same commit that surfaces the drift. The plan itself changes only when the &lt;em&gt;decision&lt;/em&gt; changes, and then it changes first: revise the plan, then regenerate the code from it. The habit I want to kill is the reflexive one — editing the plan to describe whatever the implementation drifted into, because that keeps the document looking current while quietly demoting it from source of truth to changelog. The convention is not &quot;plan changes follow code changes.&quot; It is &quot;code changes follow plan changes.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Staleness should be explicit.&lt;/strong&gt; A plan written at the start of a project is not the same as that plan after six weeks of implementation. When a later phase revises an earlier decision, every section that rested on the old decision is now wrong — and there is a window between making the new decision and propagating it through the document. Today there is no marker for that window: nothing in the plan says &quot;§Architecture still describes the phase-1 decision; phase 3 superseded it, rewrite pending.&quot; There should be. A worker that reads an un-updated section and acts on it produces work that has to be undone. The marker is a stopgap, not a resting state — staleness is a defect in the plan to be closed, not a permanent admission that the code has outrun the document. The goal is always a plan with no stale sections, because the plan is what every worker trusts.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I commit a plan and start creating beads from it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Could a worker who has never spoken to me, reading only this document and the task bead it has been assigned, produce something I would accept on the first try?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Both parts matter. The first — &lt;em&gt;never spoken to me&lt;/em&gt; — rules out plans that rely on context you have accumulated in conversation. The second — &lt;em&gt;on the first try&lt;/em&gt; — rules out plans where success requires multiple iterations to clarify. If the answer is no, the plan is not done. I work on the plan, not the code.&lt;/p&gt;
&lt;p&gt;The workers are ready. The question is whether the inputs are.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Plan methodology: derived from Jeffrey Emanuel&apos;s (&lt;a href=&quot;https://github.com/dicklesworthstone&quot;&gt;@dicklesworthstone&lt;/a&gt;) approach to high-quality planning documents. Plan-review skill: &lt;a href=&quot;https://github.com/jedarden/jeds-curated-skills&quot;&gt;jedarden/jeds-curated-skills&lt;/a&gt;. The orchestration layer this sits on: &lt;a href=&quot;https://github.com/jedarden/NEEDLE&quot;&gt;NEEDLE&lt;/a&gt;. The task structure (genesis beads, phase beads): &lt;a href=&quot;https://github.com/dicklesworthstone/beads_rust&quot;&gt;beads_rust&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>orchestration</category><category>planning</category><category>NEEDLE</category></item><item><title>The unit economics of running cattle</title><link>https://jedarden.com/notes/unit-economics-of-cattle/</link><guid isPermaLink="true">https://jedarden.com/notes/unit-economics-of-cattle/</guid><description>How to make the math work when twenty headless workers are spending money without you. Why the pet model has no built-in cost discipline, what the cattle model requires, and what to measure when tokens are the wrong unit.</description><pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The first time I ran headless agents in earnest, I burned through a quarter of my Anthropic monthly limit in three days. I told &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;that story already&lt;/a&gt; as a parable about treating agents as pets. It is also a story about money — and the money story is the one almost nobody plans for before it happens to them.&lt;/p&gt;
&lt;p&gt;The pet model has a hidden cost-control mechanism that nobody designed deliberately and nobody notices until they remove it. The cattle model removes it. If you do not replace it with something explicit before the workers go headless, you find out about the gap at the bottom of an invoice.&lt;/p&gt;
&lt;p&gt;This is the post about the replacement.&lt;/p&gt;
&lt;h2&gt;The pet model&apos;s hidden subsidy&lt;/h2&gt;
&lt;p&gt;When you run a pet agent, you are the spend control. Not metaphorically — literally. The reason your bill stays sane is that your attention is the bottleneck. You can only watch one or two sessions at a time. Each session can only spend money while you are in it. When you walk away from the keyboard, the spending stops because the conversation stops.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is invisible until you scale. As long as the human is in the loop, three things happen automatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You notice runaways.&lt;/strong&gt; The agent that has been chewing on the same prompt for ten minutes producing nothing is something you see and stop. You do not need a metric for it. You see the spinner.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You pace yourself.&lt;/strong&gt; Token-heavy operations — long context windows, large diffs, tool calls with big outputs — get throttled because &lt;em&gt;you&lt;/em&gt; are the one initiating them. You feel the lag and back off.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You self-throttle on quota.&lt;/strong&gt; When the dashboard creeps up, you slow down. The dashboard is your conscience. The agent has no conscience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of those mechanisms exist in cattle. The agent does not see its own cost. The agent does not see the dashboard. The agent does not get tired. Twenty headless workers running unattended, each invoking expensive tool calls in tight loops, will produce a bill that bears no relationship to the value of the work — unless something between them and the provider says no.&lt;/p&gt;
&lt;p&gt;The pet model&apos;s economics work because the human is the rate-limiter. The cattle model&apos;s economics work because &lt;em&gt;something else&lt;/em&gt; is the rate-limiter. That something else is the actual subject of this post.&lt;/p&gt;
&lt;h2&gt;Tokens are the wrong unit&lt;/h2&gt;
&lt;p&gt;The first instinct, when the bills get scary, is to start counting tokens. Token-counting feels rigorous. The provider exposes per-call usage. You can build dashboards. You can say &quot;this prompt averages 12,000 input tokens, this one averages 40,000&quot; and feel like you are doing the work.&lt;/p&gt;
&lt;p&gt;It is the wrong unit.&lt;/p&gt;
&lt;p&gt;Tokens are an input cost, not an output measurement. A worker that burns 200K tokens to close one bead and a worker that burns 800K tokens to close five beads — the second worker is four times cheaper per outcome, even though it spent four times more on tokens. If you optimize the token line you will end up trimming context windows on the second worker until it stops being able to close beads at all, and you will congratulate yourself on the savings.&lt;/p&gt;
&lt;p&gt;The unit that matters is &lt;strong&gt;cost per closed outcome&lt;/strong&gt;. A bead closed. A test passing. A PR merged and reviewed. The denominator is the work the system actually delivered, not the inputs it consumed getting there. Token cost without an outcome attached is just spend.&lt;/p&gt;
&lt;p&gt;This is harder to measure. It requires the orchestrator to &lt;em&gt;know&lt;/em&gt; when a worker produced a real outcome — which is the same exhaustive-handler discipline from the &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;previous post&lt;/a&gt; showing up in a different form. If your state machine cannot tell success from failure, it cannot tell expensive-but-productive from expensive-and-wasted, and you will optimize the wrong column.&lt;/p&gt;
&lt;p&gt;The orchestrator that classifies outcomes is also the orchestrator that can compute outcome cost. They are the same machine, doing the same work, for two reasons.&lt;/p&gt;
&lt;h2&gt;Cost governance is its own component&lt;/h2&gt;
&lt;p&gt;Once you accept that the human cannot be the spend control, the question becomes: where does the spend control live?&lt;/p&gt;
&lt;p&gt;It does not live in the worker. The worker is fungible by design — and a worker that polices its own budget is not fungible, because some workers will refuse work that other workers would accept. It does not live in the provider, because providers are happy to sell you whatever you will buy. It does not live in the orchestrator, because the orchestrator&apos;s job is to dispatch work, not to mediate the financial contract with each provider.&lt;/p&gt;
&lt;p&gt;It lives in a dedicated component sitting between the workers and the providers. A proxy that every model call passes through, with three jobs:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cap spend.&lt;/strong&gt; A hard ceiling on outflow per window — daily, weekly, monthly. When the cap is hit, calls return a structured error and the orchestrator handles it as just another outcome (route to the &quot;rate-limited&quot; handler, sleep the worker, retry later). The cap is not a soft warning. It is enforced at the request layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Throttle in-flight concurrency.&lt;/strong&gt; A semaphore on simultaneous calls. Twenty workers does not mean twenty concurrent provider requests; the proxy holds a smaller number and queues the rest. This is what protects you when a tight retry loop in the orchestrator turns into an accidental DDoS of yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gate against quota.&lt;/strong&gt; Subscription plans (Anthropic Max, &lt;a href=&quot;http://Z.AI&quot;&gt;Z.AI&lt;/a&gt; Max) have weekly or monthly windows. The proxy tracks burn against those windows independently of what the provider reports, and starts shedding load before the provider does. You hit your own ceiling before the provider hits theirs, because hitting the provider&apos;s ceiling is the bad failure mode.&lt;/p&gt;
&lt;p&gt;claude-governor is what this looks like in my setup. It is unromantic plumbing — a Rust process that holds the Anthropic API key, exposes a local HTTP endpoint that workers call instead of &lt;a href=&quot;http://api.anthropic.com&quot;&gt;api.anthropic.com&lt;/a&gt;, and enforces all three policies above. There is nothing clever about it. The cleverness is that the workers do not know it exists; they just see model calls succeed or fail. The policy is hidden behind the same interface the model itself uses, which is the only place a policy of this kind can live without leaking into every worker.&lt;/p&gt;
&lt;h2&gt;Run a portfolio, not a provider&lt;/h2&gt;
&lt;p&gt;There are two pricing models in this market and they do completely different things to your unit economics.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Subscriptions&lt;/strong&gt; (Anthropic Max, &lt;a href=&quot;http://Z.AI&quot;&gt;Z.AI&lt;/a&gt; Max) cap your downside. You pay a fixed amount per month, you get a quota window, and if you exceed it you get cut off — not charged more. The cost per outcome is bounded above. The downside is bounded; the upside is bounded too. Subscriptions are how you fund sustained throughput, the steady drumbeat of cattle work that runs every hour of every day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metered API&lt;/strong&gt; caps nothing. You pay for what you use. There is no ceiling. A bug in your retry loop can turn into a five-figure invoice overnight if there is no governor in front of it. The upside is real — metered access is how you handle bursts, spikes, one-off batches that exceed the subscription window without burning the next week&apos;s quota. The downside is unbounded. You only run metered traffic with the governor enforcing a hard cap, no exceptions.&lt;/p&gt;
&lt;p&gt;A healthy fleet runs both. The subscription is the floor — committed throughput at a known cost. The metered API is the surge capacity. The orchestrator does not care which is which; the proxy makes the routing decision based on which subscription has remaining quota, which model the task asked for, and how much headroom the daily metered cap has.&lt;/p&gt;
&lt;p&gt;The mistake I have watched people make repeatedly is going all-in on metered API &quot;for flexibility.&quot; You get the flexibility. You also get a cost structure that scales linearly with how aggressive your retry policy is, which is exactly the wrong elasticity to give an autonomous fleet.&lt;/p&gt;
&lt;h2&gt;Quota observability is asymmetric&lt;/h2&gt;
&lt;p&gt;The proxy needs to know how much you have spent against each window. This sounds trivial. It is not, because the providers expose this information unevenly.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Anthropic exposes weekly limit headers on every API response. The governor reads them, knows where it is in the window, and shapes traffic accordingly. You can build proper closed-loop control because the loop has a sensor.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://Z.AI&quot;&gt;Z.AI&lt;/a&gt; exposes nothing programmatic. There is no quota endpoint. There are no rate-limit headers. The only place you can see your usage is the web dashboard, which is fine for a human checking in once a day and useless for a process that needs to make a decision every second. The governor is flying blind on &lt;a href=&quot;http://Z.AI&quot;&gt;Z.AI&lt;/a&gt; quota — until the wall, when the API starts returning rate-limit errors and you reverse-engineer what just happened.&lt;/p&gt;
&lt;p&gt;The fix is to model your own quota independently. Measure outflow at the proxy, count it against your own ledger, and treat &lt;em&gt;that&lt;/em&gt; as the source of truth — not what the provider reports, because the provider may not report at all. &quot;You are out of quota&quot; should be a fact your governor knows before the provider tells you, because the only signal the provider gives some of these subscriptions is the failure itself.&lt;/p&gt;
&lt;p&gt;This is more work than it should be. It is the cost of operating against providers whose business model has not yet caught up with the operational needs of customers running unattended fleets. It will get better. Until then, the governor&apos;s quota model is a thing you maintain by hand.&lt;/p&gt;
&lt;h2&gt;The cheap-restart reflex&lt;/h2&gt;
&lt;p&gt;The pet operator has one cost-control reflex that the cattle operator should preserve and amplify: when a worker is stuck, kill it.&lt;/p&gt;
&lt;p&gt;In the pet model this is intuitive. You see the agent thrashing, you stop it, you start over. The cost of the restart is small. The cost of letting it grind for another five minutes is large.&lt;/p&gt;
&lt;p&gt;In the cattle model the same reflex is correct, but the operator cannot apply it manually because the operator is not watching. The reflex has to be built into the orchestrator. Three bounds, enforced at the worker level:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time-bounded executions.&lt;/strong&gt; A worker that has been running on the same task for longer than the budget gets killed. The handler is the timeout handler from the &lt;a href=&quot;/notes/deterministic-state-machines-for-agents/&quot;&gt;previous post&lt;/a&gt; — release the task, mark deferred, loop. No appeal. No &quot;but it might be making progress.&quot; Long-tail tasks are almost always stuck tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Token-bounded executions.&lt;/strong&gt; A worker that has emitted more than N tokens of output on a single task gets killed. Most real outcomes fit comfortably in a budget. The ones that exceed it are usually agents in some kind of loop — emitting the same diff over and over, retrying the same failing tool call, repeating themselves with minor variations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Iteration-bounded executions.&lt;/strong&gt; A worker that has invoked the model more than N times on a single task gets killed. This catches the case the previous two miss: a worker that produces small, fast, expensive calls in tight succession. None of them individually trips the time or token bounds. The count does.&lt;/p&gt;
&lt;p&gt;These three bounds together convert &quot;the worker decides when to stop&quot; into &quot;the orchestrator decides when to stop.&quot; Which is the only correct allocation of that decision in a cattle system, because the worker has no skin in the game and the orchestrator has all of it.&lt;/p&gt;
&lt;p&gt;A worker killed early might have been about to succeed. That is fine. The task goes back on the queue, gets picked up by another worker, possibly with a different prompt or a different model, and tries again. The cost of a wasted execution is a single bounded run. The cost of an unwasted runaway is unbounded.&lt;/p&gt;
&lt;p&gt;Cheap restart, every time.&lt;/p&gt;
&lt;h2&gt;What I&apos;d change&lt;/h2&gt;
&lt;p&gt;Three things, in order of how often I think about them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome cost should be a first-class metric, not a derived one.&lt;/strong&gt; Today I compute cost per closed bead by joining provider invoices to the orchestrator&apos;s outcome log after the fact. It works but it is asynchronous — I find out about expensive failures after they have already happened. The proxy already knows, in real time, what each call cost. The orchestrator already knows what task each call belonged to. The metric should be emitted live: this bead just closed, here is what it cost, here is whether that is in line with the historical distribution. If it is an outlier, alert immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Per-task budgets should be configurable, not fleet-wide constants.&lt;/strong&gt; Right now the time / token / iteration bounds are global. Some tasks legitimately want bigger budgets — a complex refactor across many files genuinely needs more space than a one-line fix. The bead should declare its budget. The orchestrator should enforce it. Today everything gets the same budget and I either set it too low for hard tasks or too high for easy ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The governor should expose its policy decisions as a stream.&lt;/strong&gt; Right now when the governor decides to throttle, the worker just sees a delayed call. There is no record of &lt;em&gt;why&lt;/em&gt; it was delayed, against which window, or what the governor&apos;s view of remaining headroom was. When you go to debug &quot;why did the fleet&apos;s throughput drop in this hour,&quot; the governor&apos;s reasoning is opaque. It should not be. Every policy decision the governor makes — throttled, capped, routed-to-metered, gated-by-quota — should be a structured event that the observability stack can query.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I run any new workload on the cattle pipeline, I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What is each dollar of spend supposed to produce, and how would I know if it didn&apos;t?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Both halves matter. The first half forces you to attach a denominator to the spend — you are not buying tokens, you are buying outcomes. The second half forces you to instrument the answer — if you cannot tell whether the spend produced the outcome, you cannot govern it. You are just hoping.&lt;/p&gt;
&lt;p&gt;The pet model lets you skip both halves because your attention substitutes for both. You see the outcome (or its absence) directly. You see the spend (or its absence) directly. The cattle model takes both signals away from you and forces you to rebuild them in the orchestrator and the governor — explicitly, before the workers go live.&lt;/p&gt;
&lt;p&gt;If you have not built that, you do not have a cattle system. You have a pet system with the supervision removed, which is a different thing. It looks the same right up until the bill arrives.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;If you want to see the governor side: &lt;a href=&quot;https://github.com/jedarden/claude-governor&quot;&gt;claude-governor&lt;/a&gt; is the Rust proxy described here. The fleet side — workers, state machine, outcome handling — is &lt;a href=&quot;https://github.com/jedarden/NEEDLE&quot;&gt;NEEDLE&lt;/a&gt;. Together they bound the system from both ends: NEEDLE decides what work to do; claude-governor decides what that work is allowed to cost.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>orchestration</category><category>cost</category><category>claude-governor</category></item><item><title>Deterministic state machines for non-deterministic agents</title><link>https://jedarden.com/notes/deterministic-state-machines-for-agents/</link><guid isPermaLink="true">https://jedarden.com/notes/deterministic-state-machines-for-agents/</guid><description>Why agent orchestration needs explicit outcome handlers. The control structure that lets twenty headless workers run unattended without the operator becoming the loop.</description><pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A worker crashes mid-task. A model-provider rate-limit kicks in for nine minutes. Two workers race for the same task and one of them loses. The agent finishes successfully but produces output that doesn&apos;t compile. Same workspace, same hour, four different outcomes — and zero of them are &lt;em&gt;wrong&lt;/em&gt;. They are exactly the outcomes a long-running headless agent fleet should expect.&lt;/p&gt;
&lt;p&gt;The question is not how to prevent any of those. The question is what happens &lt;em&gt;after&lt;/em&gt; each one.&lt;/p&gt;
&lt;p&gt;If your answer is some flavor of &quot;I&apos;ll go look,&quot; you are still running &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;pets&lt;/a&gt;. The cattle model needs an answer that does not involve a person — and the answer has to be defined &lt;em&gt;before&lt;/em&gt; the outcome, not improvised after it.&lt;/p&gt;
&lt;p&gt;This is the post about that answer.&lt;/p&gt;
&lt;h2&gt;The shape of the problem&lt;/h2&gt;
&lt;p&gt;Existing agent orchestration tools cluster into two shapes. Neither one quite fits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conversational frameworks&lt;/strong&gt; — LangGraph, AutoGen, CrewAI. These assume a chat loop with a human-in-the-loop or another LLM, and they are good at that. They are bad at sustained autonomous work because the entire abstraction is &lt;em&gt;the conversation&lt;/em&gt;. When the conversation ends, the abstraction ends. Recovering from a crash means starting over and hoping the new conversation lands in roughly the same place. Failure modes are vague — &quot;the agent went off the rails&quot; — because the framework never modeled what &quot;the rails&quot; were.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workflow engines&lt;/strong&gt; — Temporal, Argo Workflows, Inngest. These are excellent at deterministic step orchestration. They assume each step is code that you wrote, that produces a known shape of output, and that fails in known ways. Plug a non-deterministic agent into a Temporal workflow and the type system gives up: the agent&apos;s output is a &lt;code&gt;string&lt;/code&gt;, the failure mode is &quot;any exception with any message,&quot; and the workflow&apos;s retry logic cannot tell a transient rate-limit from a logic bug from &quot;the agent gave up.&quot;&lt;/p&gt;
&lt;p&gt;The missing middle is a &lt;strong&gt;deterministic state machine that drives non-deterministic agents.&lt;/strong&gt; The orchestrator is rigid; the worker is fuzzy. The orchestrator&apos;s job is to enumerate every shape the fuzziness can produce and route each shape to a known handler. The agent&apos;s job is to produce one of those shapes.&lt;/p&gt;
&lt;p&gt;NEEDLE is the shape this idea takes when you write it down. The rest of this post is what falls out.&lt;/p&gt;
&lt;h2&gt;The thesis&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;If an outcome can happen, it has a handler.
If it doesn&apos;t have a handler, it cannot happen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every state transition in NEEDLE has an explicit handler. There are no implicit fallbacks. There is no &lt;code&gt;match _ =&amp;gt; continue&lt;/code&gt;. There is no &quot;swallow the error, log a warning, hope nobody notices.&quot;&lt;/p&gt;
&lt;p&gt;This sounds like a small constraint. It is the largest constraint in the system, and almost everything else falls out of it.&lt;/p&gt;
&lt;h2&gt;What this looks like in practice&lt;/h2&gt;
&lt;p&gt;A NEEDLE worker is a loop that executes six steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SELECT&lt;/strong&gt; — query the bead queue for the next claimable task in deterministic priority order.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CLAIM&lt;/strong&gt; — atomically claim the task via a SQLite transaction. Exactly one worker wins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BUILD&lt;/strong&gt; — construct the prompt from the task definition. Same task → same prompt, every time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DISPATCH&lt;/strong&gt; — load the agent adapter (Claude Code, OpenCode, Codex, Aider, anything CLI) and invoke it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EXECUTE&lt;/strong&gt; — wait for the agent to exit. The only inputs the orchestrator gets back are the exit code and what was written to disk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OUTCOME&lt;/strong&gt; — classify what happened. Run the handler. Loop.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The first five steps are mechanical. Almost any orchestration framework can do them. The whole game is in step six.&lt;/p&gt;
&lt;p&gt;Here is the OUTCOME table for one NEEDLE iteration:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;th&gt;Detection&lt;/th&gt;
&lt;th&gt;Handler&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Success&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;exit code &lt;code&gt;0&lt;/code&gt;, output validates&lt;/td&gt;
&lt;td&gt;close the task, log effort, loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;exit code &lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;log failure reason, release the task, increment retry count, loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;exit code &lt;code&gt;124&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;release the task, mark deferred, loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;exit code &lt;code&gt;&amp;gt;128&lt;/code&gt; (SIGKILL, SIGSEGV, etc.)&lt;/td&gt;
&lt;td&gt;release the task, create an alert task, loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Race lost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;claim transaction returned no row&lt;/td&gt;
&lt;td&gt;exclude this candidate, retry SELECT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queue empty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;no claimable tasks&lt;/td&gt;
&lt;td&gt;enter strand escalation: search other workspaces, do cleanup, alert if all strands exhausted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Six rows. Every row has a handler. Every row was added because the absence of a handler caused a real bug.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;A few of those rows are worth dwelling on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Race lost is its own outcome, not a failure.&lt;/strong&gt; Two workers see the same top-priority task, both try to claim it, exactly one wins. The loser is not broken. It just needs to skip that task and try the next one. If you don&apos;t model &quot;race lost&quot; as a first-class outcome, you end up with workers that retry endlessly on tasks they will never claim — or worse, with workers that crash with cryptic SQLite errors and get auto-replaced by an outer supervisor that has no idea what just happened.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Queue empty is its own outcome, not idle time.&lt;/strong&gt; When a worker has nothing to do, that is a &lt;em&gt;signal&lt;/em&gt;, not a non-event. It means: this workspace is exhausted, look elsewhere. NEEDLE has a &lt;a href=&quot;/glossary/#strand-escalation&quot;&gt;strand escalation&lt;/a&gt; sequence for this — search other workspaces, do cleanup, propose alternatives for blocked tasks, etc. — but none of that runs unless &quot;queue empty&quot; is a recognized outcome that triggers it. A worker that just spins on an empty queue is wasting cycles and obscuring the signal that the queue is empty.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Crash is distinct from failure.&lt;/strong&gt; A failure is the agent saying &quot;I tried and produced nothing useful.&quot; A crash is the agent dying without saying anything. They look superficially similar. They require different handlers: a failure increments a retry count and tries again; a crash creates an alert and may indicate something fundamentally wrong (the agent binary isn&apos;t installed, the workspace is corrupted, the model provider is rejecting all requests). Conflating them is the difference between a system that self-heals and a system that thrashes.&lt;/p&gt;
&lt;h2&gt;What it costs&lt;/h2&gt;
&lt;p&gt;The deterministic state machine is not free. The cost shows up in three places.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Up-front enumeration.&lt;/strong&gt; You have to sit down and think through every shape your worker can produce. This is harder than it sounds. The natural state of any human-built system is &quot;I&apos;ll add the handler when the bug actually happens&quot; — which works fine for human-supervised work and is actively dangerous for unattended fleets. The first month of NEEDLE was mostly me producing new outcome rows because the system kept finding outcomes I hadn&apos;t thought to enumerate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discipline against &lt;code&gt;match _&lt;/code&gt;.&lt;/strong&gt; Rust makes this discipline visible: a non-exhaustive match is a compiler error. Languages without exhaustiveness checks make it tempting to write a wildcard handler that does &lt;em&gt;something reasonable&lt;/em&gt;. Wildcards are how state machines silently grow undefined behavior. The rule has to be: when a new outcome shows up, you stop, you name it, you give it a row in the table, and you write its handler. You do not add it to the wildcard.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Schema-first, not caller-first.&lt;/strong&gt; When you discover a new outcome, you do not patch the call site. You go back to the type that represents the outcome and add a variant. Then the compiler tells you everywhere else that needs to handle the new variant. This is more friction than the alternative — but the alternative is an outcome enum that drifts from reality, with handlers that quietly stop being called.&lt;/p&gt;
&lt;h2&gt;What it&apos;s worth&lt;/h2&gt;
&lt;p&gt;In exchange you get three things that are not available under any other model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workers fail predictably.&lt;/strong&gt; Predictable failure is the foundation of recovery. A worker that returns garbage in a &lt;em&gt;known&lt;/em&gt; failure mode — exit code 1, log line in a known format — is more useful than a worker that silently returns subtly-wrong output. The orchestrator can route the known failure; it has no leverage on the silent corruption. Designing for predictable failure means refusing to wallpaper over the failures you cannot classify, and instead either learning to classify them or rejecting the work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The state machine is the contract; agents are the implementation.&lt;/strong&gt; This is the property that lets NEEDLE be agent-agnostic. The state machine doesn&apos;t know whether the worker is Claude Code, OpenCode, Codex, or Aider. It knows that the worker is something that takes a prompt and produces an exit code. Add a new agent and you add a YAML adapter file — no code changes. Drop a worse agent and replace it with a better one — same. The agents are &lt;em&gt;parts&lt;/em&gt;; the state machine is the &lt;em&gt;machine&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can run twenty of these and reason about what the herd is doing.&lt;/strong&gt; Twenty pet agents are unreasonable. Twenty deterministic state machines, each running the same six-step loop, are reasonable. You stop debugging individual workers and start debugging the &lt;em&gt;outcome distribution&lt;/em&gt; — which row of the table is firing more often than it should? When the failure column trends up, you know to look at the prompts. When the timeout column trends up, you know to look at the model provider. The state machine made each worker&apos;s behavior legible enough that the fleet&apos;s behavior is also legible.&lt;/p&gt;
&lt;h2&gt;What I&apos;d change&lt;/h2&gt;
&lt;p&gt;Three things, with the benefit of running this in production for a while.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome classification should be richer than exit codes.&lt;/strong&gt; Exit codes are a 0–255 alphabet. They are too coarse to express the difference between &quot;agent gave up gracefully&quot; and &quot;agent gave up because the rate-limiter hit it.&quot; Right now NEEDLE squints at stderr to disambiguate. If I rebuilt today, I would have agent adapters return a structured outcome envelope (JSON to a known sentinel path, or a stdout marker) instead of relying on exit codes alone. Exit codes would be the fallback when the envelope is missing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strand escalation should be a separate state machine.&lt;/strong&gt; &quot;Queue empty&quot; routes to a sequence of fallback behaviors — search other workspaces, do cleanup, propose alternatives, alert if exhausted. Today that sequence is a function inside the OUTCOME handler for queue-empty. It really wants to be its own state machine with its own outcome table. Whenever a section of code starts growing its own enum of &quot;what happened,&quot; that is the system asking for a state machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Determinism in the orchestrator does not buy determinism in outcomes.&lt;/strong&gt; Two NEEDLE workers running the same task will produce different outputs because the agent is non-deterministic. That is by design. But it means &lt;em&gt;replaying the orchestration&lt;/em&gt; against a recorded outcome stream is not the same as replaying the work. If I rebuilt today, I would separate &quot;orchestration replay&quot; (deterministic) from &quot;work replay&quot; (impossible without the agent), and design the recording format to make the first kind of replay easy.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I add an outcome handler — before I add any new state to a NEEDLE-like system — I ask:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What outcome am I making explicit, and what was my system doing about it before?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the answer to the second part is &quot;nothing, it was hidden in a wildcard,&quot; that is the bug I am fixing. If the answer is &quot;it was conflated with a different outcome,&quot; that is also a bug. If the answer is &quot;I am inventing this outcome to handle a hypothetical,&quot; I do not add it. The state machine grows by &lt;em&gt;making implicit outcomes explicit&lt;/em&gt;, not by adding speculative variants.&lt;/p&gt;
&lt;p&gt;This is dual to the cattle question from the last post: &lt;em&gt;can a stateless headless worker complete this with only the inputs I write down?&lt;/em&gt; Together they bound the design space:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cattle says: the &lt;em&gt;agent&lt;/em&gt; must be replaceable.&lt;/li&gt;
&lt;li&gt;State machine says: the &lt;em&gt;orchestrator&lt;/em&gt; must be exhaustive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Either alone is a tarpit. Cattle without a state machine is a fleet of identical workers all failing in mysterious ways. A state machine without cattle is a beautiful enum that one operator hand-runs forever.&lt;/p&gt;
&lt;p&gt;The combination is the system that runs unattended.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Background: &lt;a href=&quot;/notes/agents-pets-cattle/&quot;&gt;Pet agents vs. cattle agents&lt;/a&gt; — the mental-model shift this post sits on top of. Code: &lt;a href=&quot;https://github.com/jedarden/NEEDLE&quot;&gt;NEEDLE&lt;/a&gt; — the deterministic state machine described here, in Rust.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>orchestration</category><category>NEEDLE</category><category>state-machines</category></item><item><title>Pet agents vs. cattle agents</title><link>https://jedarden.com/notes/agents-pets-cattle/</link><guid isPermaLink="true">https://jedarden.com/notes/agents-pets-cattle/</guid><description>The infrastructure metaphor that decided how I build with LLMs. Why most people are running pets, why pets do not scale, and what cattle actually require.</description><pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I started running multiple LLM agents in parallel, I burned through a quarter of my Anthropic monthly limit in three days because I was nursing each one. Restarting the long ones when they went off the rails. Hand-curating context windows. Crafting bespoke system prompts. Watching specific sessions like a worried parent.&lt;/p&gt;
&lt;p&gt;The waste was not the tokens. The waste was the model in my head.&lt;/p&gt;
&lt;p&gt;I was treating my agents the way ops teams treated servers in 2008: as &lt;strong&gt;pets&lt;/strong&gt;. Named, hand-tuned, irreplaceable. Each one a small individual project of mine. The day I stopped doing that — the day I started treating agents as &lt;strong&gt;cattle&lt;/strong&gt; — was the day they actually started doing useful work at scale.&lt;/p&gt;
&lt;p&gt;This is the framing decision that sits underneath every other technical choice I make about LLMs in production. It pre-dates the architecture decisions. It pre-dates the framework picks. It is, more than any individual tool, what separates &quot;interesting demo&quot; from &quot;system that runs unattended.&quot;&lt;/p&gt;
&lt;h2&gt;The metaphor&apos;s origin&lt;/h2&gt;
&lt;p&gt;Bill Baker, an engineer at Microsoft, popularized &quot;pets vs. cattle&quot; sometime around 2012 to describe the shift from artisanal server management to fleet-scale operations. The pet was the box you named after a Norse god, ssh&apos;d into to fix manually, panicked about when it went down. The cow was the AMI you spun up in an autoscaling group, terminated without ceremony when it misbehaved, and replaced from the same template five seconds later.&lt;/p&gt;
&lt;p&gt;The shift the industry made — from pets to cattle — was not primarily about scale. It was about &lt;em&gt;amortizing the cost of operating things&lt;/em&gt;. You cannot afford to know every server&apos;s name when you have ten thousand of them. The cattle model says: build the inputs, build the lifecycle, observe the outputs, and stop investing in any individual instance.&lt;/p&gt;
&lt;p&gt;LLM agents in 2025 are sitting roughly where servers sat in 2010. Most people are running pets. The cattle model exists, but it requires more infrastructure than most teams have built yet, and the framing has not made it into the public conversation.&lt;/p&gt;
&lt;h2&gt;What pet agents look like&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You probably already know whether you are running pets, but the symptoms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have a long-lived chat session you have spent hours curating context for, and you actively mourn the day you have to start a new one.&lt;/li&gt;
&lt;li&gt;You hand-tune a system prompt for a specific task, then never reuse it on a different task because it would not transfer.&lt;/li&gt;
&lt;li&gt;When the agent goes off the rails mid-task, you intervene. You re-prompt. You correct. You steer.&lt;/li&gt;
&lt;li&gt;You measure success by the quality of any &lt;em&gt;one&lt;/em&gt; output, not the throughput of the system.&lt;/li&gt;
&lt;li&gt;You are the orchestrator. The agent is the worker. Both jobs cost your attention.&lt;/li&gt;
&lt;li&gt;The cost of failure is high enough that you avoid letting it run unsupervised.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pet agents are not bad. They are the right tool for high-judgment, one-off, exploratory work. The bug fix you cannot describe well enough for autonomous work. The design conversation that benefits from a back-and-forth. Anything where the &lt;em&gt;value&lt;/em&gt; of the output justifies the cost of your attention.&lt;/p&gt;
&lt;p&gt;The trap is using pet agents for work that is bulk, repetitive, or asynchronous. That is where the model breaks.&lt;/p&gt;
&lt;h2&gt;What cattle agents look like&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The cattle model has a few non-negotiable properties:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Headless.&lt;/strong&gt; The agent does not chat. It receives a prompt, does work, exits. The exit code and the diff it produced are the entire interface. There is no human in the loop during the run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stateless.&lt;/strong&gt; Each invocation reconstructs its own context from durable inputs. Same task, same context, same prompt — every time. If the worker dies mid-run, another worker starts the same task fresh from the same state. No &quot;resuming&quot; a session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Replaceable.&lt;/strong&gt; Workers are anonymous. Identified by NATO-alphabet identifiers (&lt;code&gt;alpha&lt;/code&gt;, &lt;code&gt;bravo&lt;/code&gt;, &lt;code&gt;charlie&lt;/code&gt;) — interchangeable enough that the names are deliberately content-free. When a worker fails, you do not investigate that worker. You investigate the task it was working on, then dispatch another worker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observed at the herd level.&lt;/strong&gt; You do not look at individual sessions. You look at fleet metrics: tasks completed per hour, cost per task, failure rate by category, queue depth. The unit of analysis is the herd, not the cow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Governed at the fleet level.&lt;/strong&gt; Spend caps, rate limits, weekly quotas — all enforced at the orchestrator, not the agent. No agent can spend more than the herd is allowed to spend, regardless of what the agent decides to do.&lt;/p&gt;
&lt;p&gt;Concretely, this is what NEEDLE does. A worker is a tmux session running a deterministic state-machine loop: select the next task from a shared queue, claim it atomically, build the prompt from the task definition, dispatch to a headless CLI (Claude Code, OpenCode, Codex, Aider — agent-agnostic), wait for an exit code, classify the outcome, handle it, loop. The agent does the fuzzy work; the orchestrator handles every other dimension.&lt;/p&gt;
&lt;p&gt;It is unromantic on purpose. The agent is a black box that produces work; the orchestration is the part you can reason about.&lt;/p&gt;
&lt;h2&gt;What you give up&lt;/h2&gt;
&lt;p&gt;Treating agents as cattle has real costs. Anyone who tells you otherwise has not actually run them this way:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You give up the warm context.&lt;/strong&gt; A pet session, after an hour of back-and-forth, has accumulated a lot of nuance: corrections you made, dead ends you ruled out, preferences you taught it. Cattle agents start cold, every time. Whatever you have not encoded into the &lt;em&gt;task definition&lt;/em&gt; is gone.&lt;/p&gt;
&lt;p&gt;This forces you to write task definitions seriously. The work that used to live in your conversation history now has to live in the bead, the prompt template, the reference docs. It is more discipline up front and less recovery in the moment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You give up the moment-to-moment steering.&lt;/strong&gt; When a pet agent starts heading the wrong way, you stop it. Cattle agents finish the wrong way. They produce a bad output, the orchestrator classifies it as failure, the task gets retried — possibly with the same wrong approach, possibly with a different worker that happens to be configured differently.&lt;/p&gt;
&lt;p&gt;This forces you to be explicit about &lt;em&gt;what wrong looks like&lt;/em&gt;. Acceptance criteria become real artifacts, because the orchestrator needs to evaluate them automatically. &quot;I will know it when I see it&quot; is not a thing cattle can implement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You give up the artisan&apos;s pride.&lt;/strong&gt; Each pet session has the satisfying texture of &quot;we built this together, you and the agent.&quot; Cattle is fungible by design. You stop having a relationship with any specific worker. You only have a relationship with the throughput of the system.&lt;/p&gt;
&lt;p&gt;This is the part most people resist hardest. It is genuinely a loss.&lt;/p&gt;
&lt;h2&gt;What you cannot have without cattle&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In exchange, you get three things that are simply not available in the pet model:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parallelism.&lt;/strong&gt; I have run twenty NEEDLE workers concurrently on the same workspace. They coordinate through atomic claims on a shared bead queue (SQLite transactions guarantee exactly one worker wins each claim). I cannot manage twenty pet agents. Nobody can. The pet model has a hard ceiling around three or four sessions before the human becomes the bottleneck.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost governance.&lt;/strong&gt; When I had pet agents, I was the spend control. I noticed when one was running long and stopped it. I noticed when one was being expensive and intervened. With twenty headless workers running unattended, I cannot be the spend control — the orchestrator has to be. claude-governor caps spend, throttles workers, and gates against weekly Anthropic limits. None of that is meaningful in a pet model because the pet has only one operator and that operator is paying attention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Failure as a normal mode.&lt;/strong&gt; A pet session &quot;failing&quot; is a small disaster. You console yourself, restart, try to recover the context. A cattle worker failing is &lt;em&gt;expected&lt;/em&gt;. The orchestrator has an explicit handler for every outcome a worker can produce — success, failure, timeout, crash, race-lost, queue-empty. None of those is exceptional. Each has a defined recovery path. Workers fail constantly; the system does not.&lt;/p&gt;
&lt;p&gt;This last point is the one I underestimated longest. In a pet model, failure is the thing you try to avoid. In a cattle model, failure is just one more outcome the orchestrator routes. You stop optimizing for &quot;agents that don&apos;t fail&quot; and start optimizing for &quot;an orchestrator that handles failures cleanly.&quot; That second target is much more tractable.&lt;/p&gt;
&lt;h2&gt;The honest tradeoff&lt;/h2&gt;
&lt;p&gt;This is not a story where one model wins. They are tools for different shapes of work.&lt;/p&gt;
&lt;p&gt;Pets are right when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The task requires high-judgment back-and-forth.&lt;/li&gt;
&lt;li&gt;The unit of value is one specific output, not throughput.&lt;/li&gt;
&lt;li&gt;You cannot fully specify success in advance.&lt;/li&gt;
&lt;li&gt;The cost of getting it wrong is high enough to justify your attention.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cattle are right when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The work is bulk, queueable, asynchronous.&lt;/li&gt;
&lt;li&gt;Success is specifiable enough that the orchestrator can classify outcomes.&lt;/li&gt;
&lt;li&gt;Throughput matters more than any individual artifact.&lt;/li&gt;
&lt;li&gt;The economics only work if a human is not in the loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most teams I see are running pet workflows on tasks that should be cattle. The fix is not just buying more agents — it is rewriting the &lt;em&gt;task&lt;/em&gt; so that an unattended agent can succeed at it. That rewrite is the actual work. Once the task is specified well enough for cattle, the orchestration layer is a handful of weekends.&lt;/p&gt;
&lt;h2&gt;The question I now ask&lt;/h2&gt;
&lt;p&gt;Before I build anything new with LLMs, I ask one question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Could a stateless, headless worker that does not know my name complete this task with only the inputs I write down?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the answer is yes, it goes into the cattle pipeline.&lt;/p&gt;
&lt;p&gt;If the answer is no, I do one of three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Specify the task harder until the answer becomes yes.&lt;/li&gt;
&lt;li&gt;Decide it is genuinely a pet task and budget my attention accordingly.&lt;/li&gt;
&lt;li&gt;Decide LLMs are the wrong tool for this and write the code myself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The point is not that one answer is correct. The point is that I now make the &lt;em&gt;decision&lt;/em&gt;, deliberately, instead of defaulting to pets because pets are what the chat interface trained me to do.&lt;/p&gt;
&lt;p&gt;The chat interface is wonderful for what it is. It is the tutorial. It is not the production system.&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;If you want to see what the cattle model looks like as code: &lt;a href=&quot;https://github.com/jedarden/NEEDLE&quot;&gt;NEEDLE&lt;/a&gt; is the orchestrator (Rust, deterministic state machine, K8s-native fleet); &lt;a href=&quot;https://github.com/jedarden/claude-governor&quot;&gt;claude-governor&lt;/a&gt; is the fleet-level spend and quota gate; &lt;a href=&quot;https://github.com/jedarden/ccdash&quot;&gt;ccdash&lt;/a&gt; is the herd-health TUI. All three exist because none of this works without all three.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>agents</category><category>orchestration</category><category>NEEDLE</category><category>claude-governor</category></item><item><title>A new place for opinions and biases</title><link>https://jedarden.com/notes/welcome/</link><guid isPermaLink="true">https://jedarden.com/notes/welcome/</guid><description>Why I started these notes, what I plan to put on this surface, and what to expect.</description><pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I build agent infrastructure for a living. Most of what I have learned about
running headless LLMs in production lives in commit messages, internal
docs, and conversations that never make it past the room they happen in.
This is the surface where some of it leaks out.&lt;/p&gt;
&lt;h2&gt;What I plan to write about&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Headless LLM systems&lt;/strong&gt; — the work between &quot;agents are interesting&quot;
and &quot;agents run unattended in production.&quot; Orchestration, cost
governance, observability, fleet operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strong opinions, weakly held&lt;/strong&gt; — what I currently believe about how
agent infrastructure should be built, and why. I expect to be wrong
about some of these and look forward to the corrections.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Post-mortems on my own decisions&lt;/strong&gt; — patterns that paid off, patterns
that didn&apos;t, and the difference between the two.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What I won&apos;t write about&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hot takes on the latest model release.&lt;/li&gt;
&lt;li&gt;Frameworks I haven&apos;t actually used in anger.&lt;/li&gt;
&lt;li&gt;Speculation dressed up as expertise.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If something here is useful, it earned its place by being grounded in a
shipped system. The bar is &quot;code I run, problems I hit, fixes that
actually held.&quot;&lt;/p&gt;
&lt;p&gt;— Jed&lt;/p&gt;
</content:encoded><category>meta</category></item></channel></rss>