<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ci-cd — jd:/dev/blog</title><description>Posts tagged &quot;ci-cd&quot; on jd:/dev/blog.</description><link>https://julien.danjou.info/</link><item><title>Steve Yegge Deleted the Wrong Thing</title><link>https://julien.danjou.info/blog/steve-yegge-deleted-the-wrong-thing/</link><guid isPermaLink="true">https://julien.danjou.info/blog/steve-yegge-deleted-the-wrong-thing/</guid><description>Yegge&apos;s agents write 250 commits a day, so he stopped gating main. The math is right. But he didn&apos;t delete the merge queue, he deleted the one thing that makes a fix cheap.</description><pubDate>Tue, 18 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Steve Yegge deleted the wrong thing. He&apos;s right that merging one pull request at a time is mathematically dead at agent scale, and I&apos;ll grant that without hedging, because seven weeks ago I wrote down this exact scenario as the one way my bet loses. I sell merge queues for a living. Discount me accordingly, then keep reading, because the part he got wrong is more interesting than the part he got right.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/steve-yegge-deleted-the-wrong-thing/switchboard.webp&quot; alt=&quot;A wall of dark empty patch-panel sockets above a huge tangled heap of severed glowing cables on the floor, with one single cable still cleanly plugged into a lit socket at the center.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://yegge.ai/essays/the-shape-of-things-to-come/&quot;&gt;The Continuous Thunderdome&lt;/a&gt;, Yegge describes running about forty agents around the clock on Wyvern, the MMO he&apos;s been writing for twenty-five years. They produce roughly 175 real commits a day, some days 250. His build takes about half an hour. He started with batches of ten and bisection on failure, and watched his merge queue grow past a hundred, then to a hundred and sixty-six.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/steve-yegge-deleted-the-wrong-thing/thunderdome-header.webp&quot; alt=&quot;Header of Steve Yegge&apos;s essay on yegge.ai, August 2026: &amp;quot;The Shape of Things to Come&amp;quot;, subtitled &amp;quot;Part 1: The Continuous Thunderdome&amp;quot;.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then he stopped:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just slam all the commits onto main, and then just friggin&apos; deal with it. No bisections, no sequencing, no blame, none of that old crap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He calls it the Land Rush. Let the queue build to a hundred, drop 120 to 150 commits onto main at once, and when main goes red, point agents at the wreckage. &quot;Swarm diagnosis (&lt;em&gt;not&lt;/em&gt; bisection).&quot; He found out afterwards that AAA studios have done a version of this for years and call it Game DevOps, where main generally stays red and nobody minds.&lt;/p&gt;
&lt;h2&gt;The math is not in dispute&lt;/h2&gt;
&lt;p&gt;Here&apos;s the line worth keeping:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Once your commit rate outruns your build slots, one commit per green build becomes mathematically impossible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s true, and it&apos;s the whole diagnosis. If you can run two builds an hour and you receive ten changes an hour, no amount of clever ordering saves you. The serial queue was sized for humans opening four pull requests a day. &lt;a href=&quot;https://julien.danjou.info/blog/merge-queues-built-for-humans/&quot;&gt;I said as much in June&lt;/a&gt;: the human-scale queue strains under agent churn, grant it and move on.&lt;/p&gt;
&lt;p&gt;So we agree on the problem. We disagree about what he actually removed.&lt;/p&gt;
&lt;h2&gt;He didn&apos;t delete the queue, he deleted the intent&lt;/h2&gt;
&lt;p&gt;Look at what a batch of 150 commits is before it lands, and what it becomes after.&lt;/p&gt;
&lt;p&gt;Before, it&apos;s 150 changes, each with an agent that wrote it, each with a goal, a plan, and a reason. Every failure in there, if it fails, belongs to something.&lt;/p&gt;
&lt;p&gt;After, it&apos;s one merged artifact. Main is red. The mapping from &quot;this test is failing&quot; back to &quot;this agent was trying to do this&quot; is gone, and it&apos;s gone permanently, because by then those agents have finished and moved on. David Crawshaw put his finger on why that&apos;s fatal in &lt;a href=&quot;https://blog.exe.dev/replace-your-ci&quot;&gt;a post that reaches the opposite conclusion from the same premises&lt;/a&gt;: by the time CI reports a failure, &quot;the agent context window is dead and gone.&quot;&lt;/p&gt;
&lt;p&gt;So the swarm arrives at a red main with no author, no intent, and no plan, and has to reconstruct from scratch what 150 agents were collectively attempting. That is the most expensive debugging context you can possibly build. Yegge is right that binary search is too slow. He&apos;s wrong that the fix is to stop attributing, because attribution is the thing that makes the repair cheap.&lt;/p&gt;
&lt;p&gt;And look at what &quot;swarm diagnosis, not bisection&quot; actually is. It&apos;s a parallel search for which change broke the build. That&apos;s still attribution. He kept the expensive half of bisection and ran it on the worst possible substrate, then threw away the half that was free.&lt;/p&gt;
&lt;p&gt;Bisection was never really about finding the guilty commit. It was about finding the guilty &lt;em&gt;intent&lt;/em&gt;, and handing the problem back to whoever had it.&lt;/p&gt;
&lt;h2&gt;Fix one thing, in one context&lt;/h2&gt;
&lt;p&gt;The alternative isn&apos;t the queue he abandoned. It&apos;s the queue nobody has built yet.&lt;/p&gt;
&lt;p&gt;You still gate before merging. Main stays green, because a green main is what lets everything downstream stop thinking about it. But the queue stops behaving like a turnstile that admits or rejects, and starts behaving like the place where you sort things out as fast as possible.&lt;/p&gt;
&lt;p&gt;A batch goes red. Instead of evicting the offender and making it start again from the back of the line, the queue works out what&apos;s actually broken and hands one diff back to the one agent that owns it, with the failure attached. Not &quot;your pull request was dequeued, resubmit.&quot; Closer to &quot;this assertion fails because of this thing you changed, here it is, fix it.&quot;&lt;/p&gt;
&lt;p&gt;That&apos;s a different job than any merge queue does today, and I&apos;m not going to pretend we ship it. But it&apos;s cleaner in the way that matters: you fix one piece of code, in one context, with the intent still attached, instead of untangling a hundred and fifty changes that have already been stirred together.&lt;/p&gt;
&lt;p&gt;Yegge&apos;s own vision needs this more than mine does. What he&apos;s building toward, across Gas Town and these essays, isn&apos;t a framework you download. It&apos;s a civilization of colleagues running on datacenter silicon. Civilizations need infrastructure, and the CI and the merge queue that one would run on don&apos;t exist yet. That&apos;s the interesting problem. Not whether Git survives.&lt;/p&gt;
&lt;h2&gt;Where I&apos;d lose&lt;/h2&gt;
&lt;p&gt;Two places, and both are real.&lt;/p&gt;
&lt;p&gt;The first is coherence. Sometimes a batch fails and no single change owns it: three agents each wrote something defensible and together they contradict. Attribution doesn&apos;t help, because there&apos;s nothing to attribute to. Green is not coherent. A merge queue keeps main buildable, it does not keep main good, and that half is &lt;a href=&quot;https://julien.danjou.info/blog/the-code-review-bottleneck-is-you/&quot;&gt;a review problem&lt;/a&gt; that nobody has answered.&lt;/p&gt;
&lt;p&gt;The second is that a red main costs Yegge nothing. He&apos;s the only human on that codebase, and he says so, in the same essay where he enters Sam Altman&apos;s solo unicorn contest. When you&apos;re alone, &quot;main generally stays red&quot; is a workflow. The moment a second team pulls from your main, the same sentence is an outage, and the cost stops landing on the person who chose it. That isn&apos;t a refutation of the Land Rush for him. It&apos;s the reason it doesn&apos;t travel.&lt;/p&gt;
&lt;h2&gt;The real constraint was never the queue&lt;/h2&gt;
&lt;p&gt;Strip it back and the thing that forced his hand is a thirty-minute build and a finite number of build slots. That&apos;s it. He didn&apos;t have an ordering problem, he had a throughput budget, and he balanced it by setting the quality bar to zero.&lt;/p&gt;
&lt;p&gt;Which is a legitimate trade. It&apos;s just a trade, and it deserves to be stated as one.&lt;/p&gt;
&lt;p&gt;Main stays green. Changes land fast. CI spend stays under a number. Most days you get all three, and when they collide, something has to give. Nobody running a merge queue today gets to say which one. What they get is a batch size, which is a mechanism, and then they get to work backwards from it to an outcome they were never invited to state. &lt;a href=&quot;https://julien.danjou.info/blog/your-ci-pipeline-wasnt-built-for-this/&quot;&gt;Your CI pipeline wasn&apos;t built for this&lt;/a&gt;, and neither was the settings screen bolted to the front of it.&lt;/p&gt;
&lt;p&gt;Most of the work ahead is making CI cheap enough that none of this requires a sacrifice. Yegge&apos;s real innovation was noticing that when you can&apos;t afford the gate, you can simply stop paying for it.&lt;/p&gt;
&lt;p&gt;I&apos;d rather make it cheap enough to keep.&lt;/p&gt;
</content:encoded><category>ci-cd</category><category>ai</category><category>engineering</category></item><item><title>GitHub Actions Isn&apos;t a CI</title><link>https://julien.danjou.info/blog/github-actions-isnt-a-ci/</link><guid isPermaLink="true">https://julien.danjou.info/blog/github-actions-isnt-a-ci/</guid><description>GitHub Actions runs your tests, so technically it&apos;s CI. It&apos;s still not a CI system, and the whole industry spent a decade mistaking one for the other.</description><pubDate>Tue, 11 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GitHub Actions runs your tests when you push. By the 2006 definition of continuous integration, that&apos;s all it takes, so technically, yes, it&apos;s CI. And yet nobody who has run a real pipeline on it believes that&apos;s what they have.&lt;/p&gt;
&lt;p&gt;Here&apos;s the confusion I want to clear up: a runner is not a CI system. Actions is a superb runner. It is not a CI system, and the industry has spent a decade mistaking one for the other.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/github-actions-isnt-a-ci/category-error.svg&quot; alt=&quot;Diagram: a CI-system layer (understands tests, build graph and caching, merge discipline, insight) floats disconnected above the workflow-engine layer that Actions actually is (deploy, release, bots, cron, your tests). The gap between them is the category error.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Say the awkward part first: Mergify sells into this layer, so I have a horse in this race. I&apos;ll disclose it once and let the argument stand on its own.&lt;/p&gt;
&lt;h2&gt;The word is doing two jobs&lt;/h2&gt;
&lt;p&gt;&quot;CI&quot; means two different things now, and the whole mess comes from smashing them together.&lt;/p&gt;
&lt;p&gt;There&apos;s the &lt;em&gt;practice&lt;/em&gt;: integrate often, run the build, catch breakage early. Martin Fowler wrote that down in 2006, and Actions does it fine. And there&apos;s the &lt;em&gt;system&lt;/em&gt;: the machine teams actually picture when they say &quot;our CI,&quot; the one that understands their tests, owns the build graph, keeps the pipeline fast, and decides what&apos;s safe to merge. Buying a runner gets you the practice, and it feels like it should get you the system. It doesn&apos;t. That gap is the entire post.&lt;/p&gt;
&lt;p&gt;The 2006 definition predates everything hard about CI today: flaky tests at scale, monorepo build graphs, thousand-job pipelines, merge queues. The bar didn&apos;t move. The problems did. Actions clears a bar that was set before the hard part existed.&lt;/p&gt;
&lt;h2&gt;What Actions actually is&lt;/h2&gt;
&lt;p&gt;Strip the branding and Actions is a general-purpose, event-driven job runner. Something happens (a push, a label, a schedule, a webhook) and it runs jobs in containers. That&apos;s it, and that&apos;s a lot. Deploys, releases, bots, cron, search reindexing at 3am, and yes, your tests, which sit in that list right next to &quot;post to Slack.&quot;&lt;/p&gt;
&lt;p&gt;To the engine, your test suite is a command that exits zero or non-zero. It has no idea it&apos;s a test suite. That isn&apos;t a flaw in the runner. That&apos;s the definition of a runner, and Actions is a good one. Good is generous (look closely and it&apos;s a fairly basic runner too), but I&apos;ll grant it anyway, because even a great runner still isn&apos;t a CI system.&lt;/p&gt;
&lt;h2&gt;The four places the seam shows&lt;/h2&gt;
&lt;p&gt;Every daily pain with &quot;Actions as CI&quot; is the same thing underneath: a runner with no model of the work you handed it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It has no model of your tests.&lt;/strong&gt; It runs a script and reads an exit code, and that&apos;s the whole relationship. It can&apos;t flag a flaky test, because it doesn&apos;t know a test ran. It can&apos;t run only the tests your change affects, because it doesn&apos;t know what a test is or what you touched. It can&apos;t quarantine anything. A CI system treats tests as objects with history and behavior. Actions treats them as stdout and a number.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Its primitives are runner primitives, not CI primitives.&lt;/strong&gt; Caching exists, as a step you wire by hand with keys you manage (&lt;code&gt;actions/cache&lt;/code&gt;), not as a build graph the system understands, so it can&apos;t reuse or invalidate intelligently. Secrets are a model everyone quietly works around. You can re-run a failed job, but not a single step inside it, so retrying one broken thing re-runs the whole job. And yes, there are matrices, reusable workflows, composite actions. They&apos;re clever ways to script a runner. None of them make it understand what it&apos;s running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You author it blind.&lt;/strong&gt; There&apos;s no faithful local run. &lt;code&gt;act&lt;/code&gt; approximates it, but it won&apos;t match the runner images, the secrets, or the hosted context, so real debugging is still edit, commit, push, wait four minutes, read the logs, change one line, push again. The development loop for the thing that guards all your other development loops is the worst one you own. There&apos;s no model to test against locally, only a remote runner to execute against.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The logic has nowhere to live but YAML.&lt;/strong&gt; Expressions, &lt;code&gt;if:&lt;/code&gt; conditionals, matrix expansions, bash templated inside a workflow key. Because the engine has no model of your process, every scrap of process logic gets crammed into config, and YAML quietly becomes a programming language. Every gnarly workflow file is a program in exile.&lt;/p&gt;
&lt;p&gt;None of these are Actions failing at its job. They&apos;re a runner being asked to do a job that needs a model it was never built to have.&lt;/p&gt;
&lt;h2&gt;Why the mislabel costs you&lt;/h2&gt;
&lt;p&gt;If this were only semantics I wouldn&apos;t bother. It isn&apos;t, because names set expectations.&lt;/p&gt;
&lt;p&gt;Listen to how teams talk about CI. &quot;Is CI green?&quot; &quot;CI is slow again.&quot; &quot;CI is flaky.&quot; Nobody says &quot;CI caught the flaky one&quot; or &quot;CI ran only the two hundred tests that mattered,&quot; because their CI can&apos;t do those things, so the words for them fell out of use. The vocabulary shrank to a status light, because the tool is a status light. A whole generation now believes that&apos;s the ceiling of what CI is. It&apos;s the floor. Teams mistake the floor for the building.&lt;/p&gt;
&lt;p&gt;The real capabilities all exist. &lt;a href=&quot;https://julien.danjou.info/blog/the-github-platform-era-is-over/&quot;&gt;I&apos;ve argued that GitHub stopped tending the primitives everyone builds on&lt;/a&gt;; this is the same story from the other side. You stopped asking for a CI system because you were handed a runner and told it was one.&lt;/p&gt;
&lt;h2&gt;What to do instead&lt;/h2&gt;
&lt;p&gt;Keep Actions. It&apos;s a great runner and you have a hundred legitimate uses for one: deploys, releases, automation, glue. Keep all of it.&lt;/p&gt;
&lt;p&gt;Just stop expecting the runner to be the system, and start closing the gap on purpose. If you&apos;re building it yourself, that means putting a real test layer in front of the exit code (flaky detection, test selection, quarantine), giving caching an actual dependency graph instead of hand-managed keys, and putting a &lt;a href=&quot;https://julien.danjou.info/blog/merge-queues-built-for-humans/&quot;&gt;merge queue&lt;/a&gt; between &quot;passed&quot; and &quot;merged&quot; so that green-in-isolation stops being mistaken for safe-to-integrate. If you&apos;d rather buy it, several tools do this, mine included, and now I&apos;ve said so twice. Either way the move is identical: stop letting a runner set the ceiling for what CI is allowed to be.&lt;/p&gt;
&lt;p&gt;A screwdriver is a fine tool. It&apos;s a terrible hammer. We&apos;ve spent ten years hammering with it and calling the bent nails &quot;CI.&quot;&lt;/p&gt;
</content:encoded><category>github</category><category>ci-cd</category><category>devops</category></item><item><title>The Hidden Corruption Tax of AI Delegation</title><link>https://julien.danjou.info/blog/the-hidden-corruption-tax-of-ai-delegation/</link><guid isPermaLink="true">https://julien.danjou.info/blog/the-hidden-corruption-tax-of-ai-delegation/</guid><description>Frontier LLMs corrupt 25% of what you delegate. The fix isn&apos;t going back to writing by hand. It&apos;s the same linters and CI we built for humans, finally pointed at the new worker.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/the-hidden-corruption-tax-of-ai-delegation-teaser.webp&quot; alt=&quot;Three documents (a graph diagram, a textile pattern, and a 3D palm tree) progressively corrupted across 20 LLM editing interactions: each starts intact and ends as garbled noise, with reconstruction scores dropping from 100% to as low as 6%. Figure 1 from Laban et al., LLMs Corrupt Your Documents When You Delegate (Microsoft Research, 2026), reused under MIT.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Frontier LLMs corrupt 25% of what you delegate to them in long workflows. The new genre of &quot;I went back to writing code by hand&quot; essays says the answer is to fire the LLM. They have the diagnosis wrong. We solved this exact problem for humans twenty years ago, with linters and CI. We just forgot to apply any of it to the new worker.&lt;/p&gt;
&lt;p&gt;A new paper from Microsoft Research, &lt;em&gt;&lt;a href=&quot;https://arxiv.org/abs/2604.15597&quot;&gt;LLMs Corrupt Your Documents When You Delegate&lt;/a&gt;&lt;/em&gt; by Laban, Schnabel and Neville, put a number on something most of us had been seeing for months. Across 52 professional domains, in long delegated workflows, even the frontier (Gemini 3.1 Pro, Claude 4.6 Opus, GPT 5.4) corrupted an average of &lt;strong&gt;25% of document content&lt;/strong&gt; by the end of a 20-interaction relay. The average across all 19 models tested was &lt;strong&gt;50%&lt;/strong&gt;. Errors were sparse but severe. They compounded with document size, turn count, and distractor files in context. Agentic tool use made it worse, not better.&lt;/p&gt;
&lt;p&gt;The 25% comes from a backtranslation simulation (forward edit, reverse edit, compare to original), so it&apos;s a proxy for cumulative drift, not a literal claim about your next delegated diff. The point isn&apos;t the exact number. The point is the proxy holds across 19 models, 52 domains, document sizes and turn counts, in the same direction. The shape of the failure is real even when the headline number is conservative.&lt;/p&gt;
&lt;p&gt;The honest reaction to that shape isn&apos;t &quot;see, AI is useless.&quot; It&apos;s: &lt;em&gt;of course it drifts, what did you build to catch the wrong slice?&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Sparse but severe is the worst kind of bug&lt;/h2&gt;
&lt;p&gt;If LLM errors were dense and obvious, we would catch them. The whole industry would have rejected vibe coding by now. The paper&apos;s finding is more uncomfortable: the model is right &lt;em&gt;most&lt;/em&gt; of the time, then quietly wrong about one critical thing, then right again. The result reads correctly. It compiles. It even ships.&lt;/p&gt;
&lt;p&gt;This is the worst possible error profile for human reviewers. Rare anomalies in a sea of normal-looking output are exactly what humans miss, every time, in every field that has bothered to study it. The LLM accidentally built an attention trap.&lt;/p&gt;
&lt;p&gt;It also gets worse with size. Bigger document, longer interaction, more distractor files in the context: degradation compounds. So the more useful you try to make delegation (&quot;rewrite the whole spec&quot; instead of &quot;fix this one line&quot;), the worse the corruption gets. The delegation interface rewards exactly the workflow that fails hardest.&lt;/p&gt;
&lt;h2&gt;Going back to writing by hand is firing the junior&lt;/h2&gt;
&lt;p&gt;Every few weeks now, a senior engineer publishes the same essay. &lt;em&gt;I quit the AI assistants. I am writing code by hand again. I am faster, the bugs are gone, my brain is back.&lt;/em&gt; Each one gets three hundred HN comments saying &quot;finally, someone said it.&quot;&lt;/p&gt;
&lt;p&gt;I read those posts and I see the same mistake a stretched manager makes about a struggling junior: &lt;em&gt;I should just write the code myself, they suck.&lt;/em&gt; No. They don&apos;t suck. You skipped onboarding. You didn&apos;t give them a feedback loop. You didn&apos;t pair-review their first ten PRs. You set them up to fail, then concluded that hiring juniors is broken.&lt;/p&gt;
&lt;p&gt;LLMs are the same shape of problem. We have been onboarding humans into software projects for decades, and we long ago stopped trusting them blindly. We require their code to compile. We require it to pass a linter. We make them write tests. We force it through type checkers, static analysis, security scanners, dependency auditors. We don&apos;t do this because junior engineers are bad people. We do this because &lt;em&gt;anyone&lt;/em&gt; producing code at speed quietly breaks things, and the deterministic harness catches it before production does. (As I wrote a while back, &lt;a href=&quot;https://julien.danjou.info/blog/ai-wont-kill-juniors-it-will-expose/&quot;&gt;AI won&apos;t kill juniors, it&apos;ll expose stagnant seniors&lt;/a&gt;: same problem, same diagnostic mistake.)&lt;/p&gt;
&lt;p&gt;Now the industry has hired the most prolific junior in history, and most teams forgot to point any of that harness at its output.&lt;/p&gt;
&lt;h2&gt;We already built the fix. It&apos;s called a linter.&lt;/h2&gt;
&lt;p&gt;The first thing you do when you start a new repository is &lt;em&gt;not&lt;/em&gt; turn on Ruff, ESLint, Biome, &lt;code&gt;cargo clippy&lt;/code&gt;, mypy, gosec, semgrep. It should be. It almost never is. Most new repos start out with a default GitHub Action template at best. Rarely a security linter. Almost never a custom rule for the smells that actually bite &lt;em&gt;this&lt;/em&gt; codebase. And those are the projects humans are writing.&lt;/p&gt;
&lt;p&gt;Then we plug an LLM into the same unguarded repo and act surprised when the same failure mode shows up in the diff.&lt;/p&gt;
&lt;p&gt;Linters are the cheapest deterministic check we have. They exist precisely because we already learned the lesson the DELEGATE-52 paper had to re-prove with a small mountain of inference: confident producers of code make sparse but severe errors at scale. That&apos;s the entire reason the toolchain exists. Pointing it at an LLM doesn&apos;t require a research breakthrough. It requires turning on the thing we built for humans and forgot to wire up.&lt;/p&gt;
&lt;p&gt;The corruption tax compounds &lt;em&gt;because&lt;/em&gt; we removed the harness when we changed the worker. Same mistake we make when we send a junior into a repo with no CI and then blame the junior.&lt;/p&gt;
&lt;p&gt;The obvious objection: linters catch syntactic bugs, not semantic ones. A regex flipped from &lt;code&gt;^foo$&lt;/code&gt; to &lt;code&gt;^foo&lt;/code&gt; passes every static check. So does a dosage that went from 5mg to 50mg, an off-by-one in a business rule, a swapped argument when both types match. That is correct. It&apos;s also exactly why the current agentic coding tools (Cursor, Claude Code, Aider) run linters and unit tests inside their loop, and the paper&apos;s degradation numbers are still collected on top of harnesses like that. A single linter pass isn&apos;t the harness. That&apos;s the whole point. We &lt;em&gt;also&lt;/em&gt; built property-based testing, fuzzing, golden-file diffs, contract tests, snapshot review, and the boring discipline of two humans signing off on a change. The harness is layered. The frontier LLMs are sparse-but-severe at every layer of it, which is what the paper actually shows. You don&apos;t pick one tool; you stack them, because every layer catches a different class of wrong.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/the-hidden-corruption-tax-of-ai-delegation-harness.svg&quot; alt=&quot;Horizontal flow: LLM output forks into two paths. With harness it passes through Linter, Types, Tests, Property/fuzz, Human review, then ships clean. Without harness it goes straight to &amp;quot;Ship corrupted, ~25% corruption tax.&amp;quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The paper itself makes this point in passing: &lt;strong&gt;out of 52 domains tested, Python was the only one where most LLMs reached &quot;ready&quot; status&lt;/strong&gt;. The paper credits &quot;verifiable rewards&quot;, the fact that code can be checked deterministically. That is just another way of saying Python comes with a harness, the one programmers built precisely because &lt;em&gt;any&lt;/em&gt; writer, junior or LLM, will quietly drift unless something deterministic catches them. Crystallography, music notation and accounting ledgers have spell-check at best. The domain with the densest harness is the domain where delegation actually works. That is not a coincidence.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/the-hidden-corruption-tax-of-ai-delegation-domains.webp&quot; alt=&quot;Grid of the 52 professional domains DELEGATE-52 covers, grouped into Code &amp;amp; Configuration, Science &amp;amp; Engineering, Creative &amp;amp; Media, Structured Records, and Everyday. Python sits in the first group; everything else in the other four lacks a comparable deterministic toolchain. Figure 3 from the paper, reused under MIT.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In every other domain, DELEGATE-52&apos;s 25% becomes a corrupted file you ship and nobody catches, because there&apos;s no automated reader behind the human one. If you delegate to an LLM in a domain without deterministic checks, you are the harness. Act like it.&lt;/p&gt;
&lt;h2&gt;The interface helps too, but it&apos;s not the whole game&lt;/h2&gt;
&lt;p&gt;There&apos;s still a real interface problem on top of this. The dominant UX for delegated work is &quot;here is the doc, do your thing&quot;: the model returns a new version, you skim it, you ship it. Implicit acceptance, hidden diff. That is the worst possible UI for a tool whose failure mode is silent corruption.&lt;/p&gt;
&lt;p&gt;Cursor, Claude Code and Aider all show diffs. Real ones. But &quot;show a diff&quot; is not &quot;force a diff&quot;. You can still click accept-all on a thousand-line refactor at three in the morning. The fix is making review the default path, not an option: forced span-level acceptance, drift indicators across long sessions, distractor-file warnings, visible quality-loss curves as the doc grows. As I argued in &lt;a href=&quot;https://julien.danjou.info/blog/ai-is-a-human-interface-nightmare/&quot;&gt;AI is a human interface nightmare&lt;/a&gt;, the pattern is depressingly consistent. Decent models surrounded by terrible UI for catching when they&apos;re wrong.&lt;/p&gt;
&lt;p&gt;These are product problems, not research problems. They are exactly the same guardrail work we did for code review tooling over the last fifteen years. Same job, new worker.&lt;/p&gt;
&lt;h2&gt;You have to read what you delegate. You also have to lint it.&lt;/h2&gt;
&lt;p&gt;Until both the interface and the harness catch up, the defenses are boring. Diff what you delegated. Re-read every line the model touched. Run the linter. Run the tests. Run the security scanner. If you don&apos;t have time to do that, you didn&apos;t have time to delegate the task in the first place. You were borrowing against your future self, and a slice of the document is already gone.&lt;/p&gt;
&lt;p&gt;I have shipped features at Mergify where I wrote maybe five percent of the lines myself and Claude wrote the other ninety-five. It worked. The reason it worked is not that I read every line with a microscope. It&apos;s that Biome, mypy, our test suite, and a Copilot review pass read the diff before I did. The harness was the reviewer I didn&apos;t run. The corruption tax was paid quietly in the background, and the linters caught what mattered. Take the harness away, and that same PR is a future incident.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://julien.danjou.info/blog/the-ai-free-lunch-is-almost-over/&quot;&gt;AI free lunch&lt;/a&gt; was about who pays for the compute. The corruption tax is about who pays for the silence. Both bills come due. Both are starting to.&lt;/p&gt;
&lt;p&gt;The answer to both, weirdly, is the same boring thing: build the safety net you always should have had. The LLM didn&apos;t change that. It just punished the teams that never bothered.&lt;/p&gt;
</content:encoded><category>ai</category><category>coding</category><category>engineering</category><category>ci-cd</category></item><item><title>Your CI Pipeline Wasn&apos;t Built for This</title><link>https://julien.danjou.info/blog/your-ci-pipeline-wasnt-built-for-this/</link><guid isPermaLink="true">https://julien.danjou.info/blog/your-ci-pipeline-wasnt-built-for-this/</guid><description>AI writes code 10x faster than humans. CI still runs at the same speed, fails for the same flaky reasons, and costs more every month. Something has to give.</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://julien.danjou.info/images/blog/your-ci-pipeline-wasnt-built-for-this/ci-overload.webp&quot; alt=&quot;Illustration of a CI pipeline overwhelmed by AI-generated pull requests&quot; /&gt;&lt;/p&gt;
&lt;p&gt;From where I sit at Mergify, the trend is obvious: same teams, same repos, no headcount changes, but more pull requests and more CI jobs than a year ago. The driver isn&apos;t a mystery. AI started writing code.&lt;/p&gt;
&lt;p&gt;And the number will keep climbing. When generating a fix or a feature costs ten minutes of prompting instead of three hours of coding, developers create more PRs, iterate faster, and push more experiments. Creating code got cheap. Testing it didn&apos;t.&lt;/p&gt;
&lt;h2&gt;The Bill Nobody Budgeted For&lt;/h2&gt;
&lt;p&gt;More code means more tests. More tests means more CI minutes. More CI minutes means a bill that grows faster than the team.&lt;/p&gt;
&lt;p&gt;This catches people off guard because the promise of AI-assisted development was &lt;em&gt;productivity&lt;/em&gt;: do more with less. And that&apos;s true on the code side. But CI doesn&apos;t care who wrote the code. Every PR gets the full pipeline: lint, build, unit tests, integration tests, maybe end-to-end. Human PR or AI PR, same cost.&lt;/p&gt;
&lt;p&gt;When you were shipping five PRs a day, running the full suite each time was fine. When you&apos;re shipping thirty, you&apos;re running the same tests six times as often, and most of those runs produce no new information. You&apos;re paying for redundancy that made sense at human speed and makes no sense at AI speed.&lt;/p&gt;
&lt;p&gt;The solution isn&apos;t to skip tests. It&apos;s to stop running tests that can&apos;t tell you anything new. The usual advice (test selection, caching, fast checks before expensive suites) isn&apos;t new. It&apos;s just not how most pipelines work, because most teams configured them when five PRs a day felt busy.&lt;/p&gt;
&lt;h2&gt;Flaky Tests Are Poisoning Your Agents&lt;/h2&gt;
&lt;p&gt;Cost is painful but manageable. You can throw money at it. For a while. The real problem is signal.&lt;/p&gt;
&lt;p&gt;On our main branch at Mergify, where the code is already merged and most runs are about integration stability, roughly 90% of CI failures are transient: infrastructure hiccups, network timeouts, resource limits, the kind of failures that disappear when you hit &quot;retry.&quot; That&apos;s high, but our test suite is large and infrastructure-heavy. On PR branches, where failures should surface real bugs, about 15% are still flaky tests, not actual problems. &lt;a href=&quot;https://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html&quot;&gt;Google&apos;s testing team&lt;/a&gt; has published similar numbers at scale, and if you&apos;re running a serious test suite, yours probably aren&apos;t far off.&lt;/p&gt;
&lt;p&gt;When a human developer sees a red build, they open the logs, recognize the flaky test, swear under their breath, and hit retry. They carry context. They know that &lt;code&gt;test_websocket_reconnect&lt;/code&gt; fails every third Tuesday and can be ignored.&lt;/p&gt;
&lt;p&gt;An LLM doesn&apos;t know that.&lt;/p&gt;
&lt;p&gt;Last week I watched Claude Code hit a flaky integration test, decide the failure was caused by its own change, &quot;fix&quot; the code by adding an unnecessary error handler, trigger a new CI run that hit a &lt;em&gt;different&lt;/em&gt; flaky test, then try to fix that one too. Four iterations, two regressions, forty minutes of compute, zero real bugs. I killed the session and hit retry myself. Green on first try.&lt;/p&gt;
&lt;p&gt;That&apos;s the loop. At human pace, flaky tests are an annoyance. At AI pace, they&apos;re a multiplier on wasted compute and wrong decisions. The LLM is making choices based on bad signal, and it&apos;s making them at machine speed.&lt;/p&gt;
&lt;p&gt;Yes, agents will get smarter about this. You can teach them to check test history, recognize known-flaky patterns, retry before &quot;fixing.&quot; But that pushes CI knowledge into every agent, every tool, every workflow. It&apos;s the wrong layer. The CI system should know which signals to trust.&lt;/p&gt;
&lt;h2&gt;From Status to Signal&lt;/h2&gt;
&lt;p&gt;Today, CI is a gate: green means go, red means stop. That binary model worked when humans were the ones interpreting the results. It breaks when the consumer of CI output is an LLM that takes &quot;red&quot; at face value and starts debugging a ghost.&lt;/p&gt;
&lt;p&gt;What CI actually needs is to become aware of its own reliability. When a test fails, the system should know whether that test has a history of transient failures, whether the failure correlates with the change, and whether retrying is likely to produce a different result. That context exists in build history and test failure patterns. Almost no pipeline uses it.&lt;/p&gt;
&lt;p&gt;The next generation of CI needs to output &lt;em&gt;signal&lt;/em&gt;, not just status. Not &quot;failed&quot; but &quot;failed, likely flaky, recommend retry.&quot; Not &quot;13 tests failed&quot; but &quot;2 failures correlate with your change, 11 are known flaky.&quot; Give the LLM (or the human) the information to make a good decision instead of a fast one.&lt;/p&gt;
&lt;p&gt;This matters beyond individual PRs. Merge queues depend on CI signal to decide what lands in main. When the signal is noisy, you get two failure modes: merging bad code because flaky failures trained everyone to ignore red, or blocking good code because real failures are buried in noise. Both get worse as volume increases.&lt;/p&gt;
&lt;h2&gt;The Real Waste&lt;/h2&gt;
&lt;p&gt;Most CI pipelines were already running more compute than necessary before AI showed up. Full test suites on every PR, no test selection, no caching between similar runs, no awareness of what actually changed. The waste was tolerable at human speed because the volume was low.&lt;/p&gt;
&lt;p&gt;It&apos;s not tolerable when &lt;a href=&quot;https://julien.danjou.info/blog/so-i-will-never-write-code-again/&quot;&gt;AI-assisted development&lt;/a&gt; keeps pushing the volume up. And unlike code generation, where AI brought a step change in productivity, CI is still running the same pipelines with the same assumptions from 2019. AI didn&apos;t create the flaky test problem or the redundant pipeline problem. It just made both impossible to ignore.&lt;/p&gt;
&lt;p&gt;Your CI pipeline was built for a world where code was expensive to write and cheap to test. That world is gone.&lt;/p&gt;
</content:encoded><category>ai</category><category>ci-cd</category><category>engineering</category></item></channel></rss>