<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Scaffolding on PHP Boy Scout</title><link>https://phpboyscout.uk/tags/scaffolding/</link><description>Recent content in Scaffolding on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Fri, 17 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://phpboyscout.uk/tags/scaffolding/index.xml" rel="self" type="application/rss+xml"/><item><title>The scaffolder that won't hand you code that doesn't compile</title><link>https://phpboyscout.uk/the-scaffolder-that-wont-hand-you-code-that-doesnt-compile/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/the-scaffolder-that-wont-hand-you-code-that-doesnt-compile/</guid><description>&lt;img src="https://phpboyscout.uk/the-scaffolder-that-wont-hand-you-code-that-doesnt-compile/cover-the-scaffolder-that-wont-hand-you-code-that-doesnt-compile.png" alt="Featured image of post The scaffolder that won't hand you code that doesn't compile" /&gt;&lt;p&gt;Most scaffolders hand you a stub. You run the generate command, you get a file with the right shape and a &lt;code&gt;// TODO: implement&lt;/code&gt; where the actual work goes, and the rest is on you. That&amp;rsquo;s fine, it&amp;rsquo;s upfront, and it&amp;rsquo;s most of what the rtb scaffolder does too: it&amp;rsquo;s the Rust port of the &lt;a class="link" href="https://phpboyscout.uk/what-survives-a-port/" &gt;go-tool-base one&lt;/a&gt;, it works from a &lt;a class="link" href="https://phpboyscout.uk/design-your-whole-cli-in-one-file/" &gt;single manifest&lt;/a&gt;, and it &lt;a class="link" href="https://phpboyscout.uk/scaffolding-that-respects-your-edits/" &gt;regenerates without clobbering the code you&amp;rsquo;ve written&lt;/a&gt;. If you want the whole tour, those three cover it.&lt;/p&gt;
&lt;p&gt;This is about the one bit they don&amp;rsquo;t, which is what happens when you ask the scaffolder to write the implementation, not just the stub.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rtb generate command --prompt &amp;quot;add a command that lists the user's pinned repos&amp;quot;&lt;/code&gt; doesn&amp;rsquo;t drop a TODO. It drafts the actual command, with AI doing the drafting. Which sounds great in a demo and is a menace in practice, because AI-drafted code that doesn&amp;rsquo;t compile is strictly worse than a stub. A stub at least admits it&amp;rsquo;s empty. A plausible-looking command that references a function that doesn&amp;rsquo;t exist, or borrows something it can&amp;rsquo;t, or compiles but fails its own test, costs you more time to discover and unpick than writing it yourself would have. The generation isn&amp;rsquo;t the hard part. Trusting the output is.&lt;/p&gt;
&lt;p&gt;Which is why the real work didn&amp;rsquo;t go into the generating at all. It went into &lt;code&gt;ai/verifier.rs&lt;/code&gt;, a repair loop wrapped around the one grader in the Rust ecosystem that cannot be sweet-talked: cargo.&lt;/p&gt;
&lt;p&gt;It runs in stages, and each stage is a gate. &lt;code&gt;cargo check&lt;/code&gt;: does it compile? If not, the compiler&amp;rsquo;s error goes straight back to the AI as the next instruction, and it tries again. Then &lt;code&gt;cargo clippy&lt;/code&gt;: is it idiomatic, does it trip any lints? Same deal, the lint output is the prompt for the next pass. Then &lt;code&gt;cargo test&lt;/code&gt;: does it actually do the thing? A failing test is just another error to feed back. The loop only stops when all three are green, or when it hits &lt;code&gt;--max-repair-iterations&lt;/code&gt; and gives up and tells you, rather than handing you something broken with a confident smile.&lt;/p&gt;
&lt;p&gt;That cap is there on purpose, by the way. The loop is bounded on purpose, because AI repair loops have a lovely failure mode where they churn forever, each pass convinced it&amp;rsquo;s nearly there, burning tokens and time on something that was never going to converge. So there&amp;rsquo;s a cap, and when the cap is reached the scaffolder stops, tells you it couldn&amp;rsquo;t get there, and leaves you the last attempt plus the errors. That&amp;rsquo;s a far better outcome than the alternative, which is a tool that either loops to infinity or ships code that doesn&amp;rsquo;t build.&lt;/p&gt;
&lt;p&gt;The bit I&amp;rsquo;m most attached to, though, is &lt;code&gt;--agentless&lt;/code&gt;. It short-circuits the whole AI path. No drafting, no repair loop, no model in the chain at all, just the deterministic templating you&amp;rsquo;d get from any other scaffolder. And I built that in deliberately, because the moment an AI step becomes load-bearing in a tool other people depend on, you&amp;rsquo;ve added a requirement that there&amp;rsquo;s a model available, a network, an API key, and a budget. Sometimes you want none of that. CI that has to be reproducible. An offline machine. A change where you simply don&amp;rsquo;t want a model anywhere near the diff. &lt;code&gt;--agentless&lt;/code&gt; keeps the AI an accelerator rather than a dependency, and a tool that can&amp;rsquo;t run without a model is a tool with a single point of failure you didn&amp;rsquo;t choose.&lt;/p&gt;
&lt;p&gt;Generating the code is the cheap bit, and verifying it is the actual product. Anyone can wire a model up to write a Rust function. What makes it a tool and not a party trick is that nothing it writes gets anywhere near your project until &lt;code&gt;cargo&lt;/code&gt; has had its say. The AI writes the first draft. Cargo decides whether you ever see it.&lt;/p&gt;</description></item></channel></rss>