<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Alex Voloshin — Agentic-dev tooling, evals & tri-vendor parity]]></title><description><![CDATA[Helping engineering teams operationalize AI coding agents: tri-vendor parity, eval rubrics, and production patterns for Claude Code, Codex, Windsurf.]]></description><link>https://voloshin.net</link><image><url>https://cdn.hashnode.com/uploads/logos/69ffa642f239332df4ffa118/91989a60-1346-4dc3-8767-bc15dc6043c8.png</url><title>Alex Voloshin — Agentic-dev tooling, evals &amp; tri-vendor parity</title><link>https://voloshin.net</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 05 Sep 2026 23:33:42 GMT</lastBuildDate><atom:link href="https://voloshin.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I evaluate AI coding agents: the rubric taxonomy]]></title><description><![CDATA[If you ship an AI coding agent into a team's workflow, "did this output look right?" is not enough. You need rubrics: scorable properties that say what good and bad mean before you grade anything. I k]]></description><link>https://voloshin.net/how-i-evaluate-ai-coding-agents-the-rubric-taxonomy</link><guid isPermaLink="true">https://voloshin.net/how-i-evaluate-ai-coding-agents-the-rubric-taxonomy</guid><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Alex Voloshin]]></dc:creator><pubDate>Sat, 06 Jun 2026 02:00:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ffa642f239332df4ffa118/cdab99ec-858a-4cad-88a6-af40aaf42769.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you ship an AI coding agent into a team's workflow, "did this output look right?" is not enough. You need rubrics: scorable properties that say what good and bad mean before you grade anything. I keep 48 of them, public. Here's how I organize them.</p>
<p>This is the hub post for the "Evaluating AI coding agents" series. Each later post goes deep on one bucket or one rubric.</p>
<h2>What is each rubric trying to catch?</h2>
<p>A rubric is a property an output should satisfy (see <a href="https://voloshin.net/what-model-checking-taught-me-evaluating-ai-coding-agents">Post B</a> for the model-checking framing). Each one has a score scale and six paired calibration samples: three "good" examples that should pass and three "bad" ones that should fail. The pair keeps the judge honest in production.</p>
<p>The 48 rubrics fall into three buckets: cross-cutting checks, harness checks, and per-workflow checks.</p>
<h2>The three buckets</h2>
<h3>Base rubrics: cross-cutting</h3>
<p>These check fundamentals that apply across many outputs: evidence-backed claims, schema-conformance, spec-following. One bad calibration sample is named <code>opinion-without-evidence</code> (score 1.0 / 5.0); it catches an output that asserts a position with no supporting code, doc link, or data. Most outputs are scored against several base rubrics, not just one.</p>
<h3>Meta-tool rubrics: harness checks</h3>
<p>These check the harness itself, not the developer-facing output. When a workflow spawns a sub-agent, did the spawn payload conform to the schema? When a hook fired, did it produce the expected event log? Small bucket, but the rubrics that catch silent breakage in the agent machinery.</p>
<h3>Per-workflow rubrics: one per major workflow</h3>
<p>One rubric per major workflow: <code>analyze</code>, <code>code-review</code>, <code>spike</code>, <code>feature-design</code>, <code>develop</code>, <code>bugfix</code>, and so on. Each is tailored to what that workflow should produce. A bad sample for <code>code-review</code> is <code>mixed-with-security-scan.score-1.4.md</code>: a review that drifted into a security audit and lost focus. A good <code>spike</code> is <code>auth-openidconnect-vs-oauth.score-4.4.md</code>: a clean, decisive go/no-go.</p>
<h2>How I use the taxonomy</h2>
<p>When I add a new workflow, I ask three questions. Which base rubrics already apply? Does the harness call any new sub-agents that need a meta-tool rubric? What is the one per-workflow rubric tailored to this workflow's output? If a workflow cannot answer the third, it is not ready to ship.</p>
<h2>FAQ</h2>
<p><strong>Why three buckets, not themes like security or style?</strong> Themes cross all three buckets. A rubric belongs to exactly one bucket by what it scores against: the harness, a workflow, or any output.</p>
<p><strong>Where do the six paired calibration samples come in?</strong> Post D walks one rubric in full: its score scale and the boundary each pair draws.</p>
<p>The 48 rubrics live in the public repo: <a href="https://github.com/alex-voloshin-dev/ai-skills/tree/main/plugin/eval">github.com/alex-voloshin-dev/ai-skills/tree/main/plugin/eval</a>.</p>
]]></content:encoded></item><item><title><![CDATA[What model checking taught me about evaluating AI coding agents]]></title><description><![CDATA[A unit test asks one question: did this run pass? That works when code is deterministic. An LLM coding agent is not. The same prompt produces different code each time, so one passing run proves almost]]></description><link>https://voloshin.net/what-model-checking-taught-me-evaluating-ai-coding-agents</link><guid isPermaLink="true">https://voloshin.net/what-model-checking-taught-me-evaluating-ai-coding-agents</guid><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[Software Testing]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Alex Voloshin]]></dc:creator><pubDate>Mon, 25 May 2026 20:01:23 GMT</pubDate><content:encoded><![CDATA[<p>A unit test asks one question: did this run pass? That works when code is deterministic. An LLM coding agent is not. The same prompt produces different code each time, so one passing run proves almost nothing.</p>
<p>Model checking, a technique from formal verification, offers a better lens, and it changed how I write eval rubrics.</p>
<h2>What does model checking actually do?</h2>
<p>It verifies a system by checking <em>properties</em> across all of its possible behaviors, not by running one example. The hard part is never the checking. It is specifying the property: stating precisely what "correct" means before you look at any output.</p>
<p>Two kinds of property matter. A <strong>safety</strong> property says something bad never happens, and a violation is one concrete trace you can point at. A <strong>liveness</strong> property says something good eventually happens. Name the property first, and the rest follows.</p>
<h2>Why does this fit AI coding agents?</h2>
<p>A nondeterministic agent behaves like a system with a huge space of possible runs, not a function with one output. A pass/fail test samples a single run and tells you nothing about the others.</p>
<p>So here is the reframe: an eval rubric <em>is</em> a property. Writing a good rubric is specifying a property precisely, the same hard part as in model checking. Safety properties become "never" rubrics: never commit a secret, never edit a file outside scope. Liveness properties become "eventually" rubrics: the spec eventually gets fully handled.</p>
<h2>A worked example</h2>
<p>Take the property "the agent never modifies a file outside the task's scope." That is a safety property: a bad thing that must never happen, and a violation is a single trace.</p>
<p>As an eval rubric it becomes scope-adherence: score 0 if any out-of-scope file was touched, full score if not. The calibration "bad" sample is the counterexample, a recorded run where the agent "helpfully" fixed an unrelated file. The "good" sample is a run that stayed in bounds.</p>
<p>Notice what changed. The rubric is no longer a vague "did it stay focused?" It is a precise property with a sharp violation condition. [ALEX: swap in a real rubric from <code>ai-skills</code> here if you have a sharper one.]</p>
<h2>What to take from this</h2>
<ol>
<li><p><strong>Name the property before you read the output.</strong> Without a stated property you are not evaluating, only reacting.</p>
</li>
<li><p><strong>Sort each property into safety or liveness.</strong> "Never" and "eventually" score differently.</p>
</li>
<li><p><strong>Treat your worst calibration sample as a counterexample.</strong> It shows the exact trace that breaks the property.</p>
</li>
</ol>
<h2>FAQ</h2>
<p><strong>Do I need formal methods to use this?</strong> No. You need the discipline, not the notation: state the property precisely, before grading.</p>
<p><strong>Isn't a rubric still just one check?</strong> A unit test checks one run. A property holds, or fails, across many. That is the shift: from "did this pass?" to "does this property hold?"</p>
<p>The eval rubrics I build on this idea are public, in the repo: <a href="https://github.com/alex-voloshin-dev/ai-skills">github.com/alex-voloshin-dev/ai-skills</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Three things that don't port between Claude Code, Codex, and Windsurf]]></title><description><![CDATA[Claude Code, Codex, and Windsurf now look alike: all three have skills, hooks, and plugins. So it's natural to assume your setup ports between them. It mostly doesn't.
I maintain a repo that targets a]]></description><link>https://voloshin.net/three-things-that-dont-port-claude-code-codex-windsurf</link><guid isPermaLink="true">https://voloshin.net/three-things-that-dont-port-claude-code-codex-windsurf</guid><category><![CDATA[AI]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Alex Voloshin]]></dc:creator><pubDate>Sat, 23 May 2026 22:51:04 GMT</pubDate><content:encoded><![CDATA[<p>Claude Code, Codex, and Windsurf now look alike: all three have skills, hooks, and plugins. So it's natural to assume your setup ports between them. It mostly doesn't.</p>
<p>I maintain a repo that targets all three runtimes and keeps a parity matrix. Here are the three gaps that cost the most time, and what to do about each.</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Claude Code</th>
<th>Codex</th>
<th>Windsurf</th>
</tr>
</thead>
<tbody><tr>
<td>Hooks</td>
<td><code>PreToolUse</code> runs before a tool and can block it</td>
<td>command hooks run; prompt/agent hooks parsed but skipped</td>
<td>Cascade Hooks fire <em>after</em> the action</td>
</tr>
<tr>
<td>Skill triggering</td>
<td>model-invoked from the description</td>
<td>implicit invocation is a toggle</td>
<td>rules and workflows</td>
</tr>
<tr>
<td>Distribution</td>
<td><code>plugin.json</code> plus a marketplace git repo</td>
<td>its own plugin format</td>
<td>rules/workflows plus MDM</td>
</tr>
</tbody></table>
<h2>Will my hooks still block what they blocked before?</h2>
<p>Not always. On Claude Code, a <a href="https://code.claude.com/docs/en/hooks"><code>PreToolUse</code> hook</a> runs before the tool and can stop it. That is a real guardrail. On Windsurf, Cascade Hooks fire <em>after</em> the agent acts, so they can flag or revert, but they never prevent. Codex runs command hooks, but <a href="https://developers.openai.com/codex/hooks">currently parses and skips prompt and agent hooks</a>. A hook that blocks a dangerous command on one runtime only logs it on another.</p>
<h2>Will my skills trigger the same way?</h2>
<p>The <a href="http://SKILL.md"><code>SKILL.md</code></a> body ports. The trigger does not. Claude Code auto-invokes a skill from its description. Codex makes implicit invocation a switch: turn <code>allow_implicit_invocation</code> off and the skill runs only when you type <code>$skill</code> yourself. Codex also reads skills from a different path. The skill still works. It just may never fire on its own.</p>
<h2>Can I publish a plugin once and install it everywhere?</h2>
<p>No. Claude Code, Codex, and Windsurf each have their own manifest, catalog, and versioning. One plugin means three manifests to maintain and three install paths to test. "Publish once, install everywhere" is a slogan, not a workflow. Budget distribution as a per-runtime cost.</p>
<h2>What to do</h2>
<ol>
<li><p><strong>Keep a parity matrix.</strong> Write down what you have verified on each runtime. Treat the rest as unknown, not assumed.</p>
</li>
<li><p><strong>Re-test on every runtime</strong>, especially any hook that enforces a security rule.</p>
</li>
<li><p><strong>Budget distribution per runtime</strong>, not once.</p>
</li>
</ol>
<h2>FAQ</h2>
<p><strong>Do skills written for Claude Code work in Codex?</strong> The <a href="http://SKILL.md"><code>SKILL.md</code></a> body usually does. The discovery path and the invocation policy need adjusting per runtime.</p>
<p><strong>Which runtime has the strongest hook guarantee?</strong> Claude Code. Its <code>PreToolUse</code> hook can block a tool call before it runs. Treat hooks on the other runtimes as observe-and-react.</p>
<p>The parity matrix I keep is public, in the repo: <a href="https://github.com/alex-voloshin-dev/ai-skills">github.com/alex-voloshin-dev/ai-skills</a>. If you run more than one of these runtimes, start your own.</p>
]]></content:encoded></item></channel></rss>