<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://richardmurillo.com/blog/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    
    <title>Richard Murillo</title>
    <link>https://richardmurillo.com/blog/</link>
    <atom:link href="https://richardmurillo.com/blog/feed.xml" rel="self" type="application/rss+xml" />
    <description>Posts by Richard Murillo about his work and projects.</description>
    <language>en</language>
    <item>
      <title>Move the deployment failure to the keystroke</title>
      <link>https://richardmurillo.com/blog/move-the-failure-to-the-keystroke/</link><description>&lt;p&gt;A rollout stops halfway. The validator says a configuration value is not allowed in that region. The engineer who wrote the line did it days ago, has moved on to other work, and now has to rebuild the context, find the rule, fix one value, and wait for the pipeline again. Nothing about the mistake was hidden. It sat in the source the whole time. The system just didn&#39;t say so until deploy time.&lt;/p&gt;
&lt;p&gt;That is the problem I want to talk about: failures that are visible in source but get reported at the last possible moment.&lt;/p&gt;
&lt;h2&gt;The rule&lt;/h2&gt;
&lt;p&gt;If the cause of a failure is visible in source, the failure belongs where the source is written: in the editor and in the build. Deploy time validation stays as a safety net. It should rarely be the first place anyone hears about a mistake they could have seen while typing.&lt;/p&gt;
&lt;p&gt;Here is the claim in a form you can test. Take the deployment failures from your last quarter and sort them into two piles: causes that were visible in source when the change was authored, and causes that were not (capacity, a dependency outage, a race). For every class in the first pile that you move to authoring time with an analyzer, a type, or a build gate, that class should stop showing up at deploy time. If it keeps showing up, the check is wrong or incomplete, and you have learned something specific about the check.&lt;/p&gt;
&lt;h2&gt;A small, shipped example&lt;/h2&gt;
&lt;p&gt;I maintain &lt;a href=&quot;https://github.com/rjmurillo/moq.analyzers&quot;&gt;Moq.Analyzers&lt;/a&gt;, a set of Roslyn analyzers for the Moq mocking library. It is a small version of the same problem.&lt;/p&gt;
&lt;p&gt;Moq builds a mock by generating a subclass, so it can only intercept members that can be overridden. This compiles without complaint:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;class SampleClass
{
    int Property { get; set; }
}

var mock = new Mock&amp;lt;SampleClass&amp;gt;()
    .Setup(x =&amp;gt; x.Property);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without an analyzer, you find out when the test runs. Moq 4 throws a &lt;code&gt;NotSupportedException&lt;/code&gt; whose message says that &amp;quot;Non-overridable members (here: ...) may not be used in setup / verification expressions&amp;quot; (&lt;a href=&quot;https://github.com/devlooped/moq/blob/v4.21.0/src/Moq/Guard.cs&quot;&gt;Moq source&lt;/a&gt;). That is a deploy time failure in miniature: the cause was visible in the code, and the report came later, somewhere else.&lt;/p&gt;
&lt;p&gt;With Moq.Analyzers installed, the same line gets &lt;a href=&quot;https://github.com/rjmurillo/moq.analyzers/blob/main/docs/rules/Moq1200.md&quot;&gt;Moq1200&lt;/a&gt;, &amp;quot;Setup should be used only for overridable members,&amp;quot; at Error severity. The build fails at the line that caused it, with the fix in the rule doc: mock an interface, or make the member &lt;code&gt;virtual&lt;/code&gt;. Moq1200 has shipped since release 0.0.7 and is in the current release, &lt;a href=&quot;https://github.com/rjmurillo/moq.analyzers/releases/tag/v0.4.2&quot;&gt;v0.4.2&lt;/a&gt; (March 11, 2026). NuGet listed &lt;a href=&quot;https://www.nuget.org/packages/Moq.Analyzers&quot;&gt;3,834,656 downloads&lt;/a&gt; of the package on October 17, 2026.&lt;/p&gt;
&lt;p&gt;A mocking mistake is low stakes. The mechanism isn&#39;t. A Roslyn analyzer runs inside the compiler and the IDE, so a rule written once is enforced on every keystroke, on every build, for every team that references the package. Nobody has to remember it.&lt;/p&gt;
&lt;h2&gt;Why this is a capability gain&lt;/h2&gt;
&lt;p&gt;A diagnostic gives the author knowledge at the moment they can act on it. The engineer doesn&#39;t need to have read the right wiki page or sat through the right incident review. The rule shows up where the decision is being made, with the reason and the fix.&lt;/p&gt;
&lt;p&gt;The same holds for coding agents. An agent that edits a project gets the compiler&#39;s diagnostics as output it can read and act on. In my &lt;a href=&quot;https://richardmurillo.com/blog/capability-not-competence/&quot;&gt;last post&lt;/a&gt; I argued that tools should raise what an agent can do while it has them. An analyzer at Error severity is exactly that kind of instrument: it turns a rule that lived in someone&#39;s head into a fact the agent cannot miss.&lt;/p&gt;
&lt;h2&gt;What a good authoring time check needs&lt;/h2&gt;
&lt;p&gt;Moving a check earlier only helps if engineers trust it. The checks that hold up have four things in common.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The diagnostic explains itself.&lt;/strong&gt; It names the rule, the value it expected, and the fix. A bare &amp;quot;denied&amp;quot; is a wall, not feedback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;There is an escape hatch.&lt;/strong&gt; Moq1200&#39;s doc shows how to suppress one violation with &lt;code&gt;#pragma&lt;/code&gt; or turn the rule off in &lt;code&gt;.editorconfig&lt;/code&gt;. A rule that cannot be overridden on purpose gets worked around without a record.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Severity means something.&lt;/strong&gt; Errors block. Warnings inform. If every line has a squiggle, nobody reads any of them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Missing input is not a pass.&lt;/strong&gt; A check that found nothing to evaluate should say so, not report success.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Better than earlier: impossible&lt;/h2&gt;
&lt;p&gt;An analyzer catches a mistake sooner. A typed API can make the mistake impossible to write. If a deployment option only accepts the regions a service is allowed to use, the invalid value never compiles, and no check has to look for it.&lt;/p&gt;
&lt;p&gt;Rico Mariani named this the &lt;a href=&quot;https://ricomariani.medium.com/the-pit-of-success-cfefc6cb64c8&quot;&gt;Pit of Success&lt;/a&gt; in a 2003 talk to platform engineers, as &lt;a href=&quot;https://learn.microsoft.com/en-us/archive/blogs/brada/the-pit-of-success&quot;&gt;Brad Abrams recorded it&lt;/a&gt;: &amp;quot;we want our customers to simply fall into winning practices by using our platform and frameworks. To the extent that we make it easy to get into trouble we fail.&amp;quot; An analyzer at the keystroke makes trouble harder to get into. A type that refuses the wrong value makes it impossible.&lt;/p&gt;
&lt;p&gt;Dan North put this well when he wrote about shifting testing left: it &amp;quot;means doing different things,&amp;quot; and &amp;quot;we aren&#39;t doing that work earlier, we are setting ourselves up to never need it at all&amp;quot; (&lt;a href=&quot;https://dannorth.net/blog/we-need-to-talk-about-testing/&quot;&gt;We Need to Talk About Testing&lt;/a&gt;). Analyzers are the detection half. Types are the prevention half. A good platform uses both, and keeps deploy time validation for what neither can see.&lt;/p&gt;
&lt;h2&gt;The same problem at platform scale&lt;/h2&gt;
&lt;p&gt;My team, Policy and Configuration in Microsoft Security, works on this problem at a much larger scale. Per the &lt;a href=&quot;https://apply.careers.microsoft.com/careers/job/1970393557001935&quot;&gt;public job description&lt;/a&gt;, the team builds the shared deployment and policy capabilities that help Microsoft Security services ship safely across Azure, regions, and sovereign clouds. The work includes turning deployment, compliance, and security requirements into typed APIs, analyzers, and build gates, and moving data residency, rollout, and configuration failures from deployment time to authoring time.&lt;/p&gt;
&lt;p&gt;The goal stated there is to make secure deployment self-service, reliable, and eventually invisible to service teams. That means clear diagnostics, safe upgrade paths, and self-service workflows, guided by support demand and adoption data. It also means a migration path from proprietary tools to standard .NET Aspire and Azure platform capabilities.&lt;/p&gt;
&lt;p&gt;Moq1200 is one rule in one package. The interesting version of this problem is doing it for deployment policy across many services, where every rule you move to the keystroke is a class of failure service teams stop hitting.&lt;/p&gt;
&lt;h2&gt;We&#39;re hiring&lt;/h2&gt;
&lt;p&gt;I&#39;m hiring a Principal Software Engineer in Redmond, WA to set technical direction for this work. If moving failures from deploy time to the keystroke sounds like your kind of problem, apply here: &lt;a href=&quot;https://apply.careers.microsoft.com/careers/job/1970393557001935&quot;&gt;Principal Software Engineer, Policy and Configuration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please apply through the link rather than DMing me. If you know someone who would want this, send them the link.&lt;/p&gt;
</description><pubDate>Wed, 21 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Richard Murillo</dc:creator>
      <guid>https://richardmurillo.com/blog/move-the-failure-to-the-keystroke/</guid>
    </item>
    <item>
      <title>Capability, not competence: agent skills as maps and GPS</title>
      <link>https://richardmurillo.com/blog/capability-not-competence/</link><description>&lt;p&gt;Ask an agent how many of the 185 skill folders in a large public catalog contain a &lt;code&gt;scripts/&lt;/code&gt; directory. If it reads the listing and answers, it&#39;s estimating. A script answers by counting: 3. The model didn&#39;t lack intelligence. It lacked an instrument.&lt;/p&gt;
&lt;p&gt;That&#39;s the whole argument in one task. I don&#39;t care whether a model can do the job after I take its tools away. I care how much it can do with them. Some skills are maps: they show the route and the branch points, a script measures what the model can&#39;t see by reading, and the model still makes the call. Some are GPS: they drive deterministic work end to end. Most real work needs both, in sequence.&lt;/p&gt;
&lt;h2&gt;The rule&lt;/h2&gt;
&lt;p&gt;Build instruments for facts a program can measure. Keep judgment with the agent when objectives or evidence are ambiguous. Automate end to end when postconditions are checkable and failure is contained. Measure task quality, cost, and recoverability against a no-skill baseline.&lt;/p&gt;
&lt;p&gt;The common shape is a three-step hybrid:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;+---------------------+     +----------------------+     +--------------------------+
| 1. Script extracts  | --&amp;gt; | 2. Agent judges the  | --&amp;gt; | 3. Script validates and  |
|    the facts        |     |    ambiguous cases   |     |    commits the result    |
|    (deterministic)  |     |    (judgment)        |     |    (checkable)           |
+---------------------+     +----------------------+     +--------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Four questions decide where each step sits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ambiguity.&lt;/strong&gt; Is the objective or the evidence open to interpretation? Then the agent holds that step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reversibility.&lt;/strong&gt; Can you undo a wrong result? Then automating it is safer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error cost.&lt;/strong&gt; What does a wrong answer break, and who notices?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Checkable postconditions.&lt;/strong&gt; Can a separate program confirm the result? Then a script can own it end to end.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Map or GPS: who holds the decision?&lt;/h2&gt;
&lt;p&gt;Map and GPS are the two ends of a spectrum, not two boxes. A skill that drives a task end to end can beat a map on the exact task it was built for. So the choice comes down to who should hold the decision, and who checks it.&lt;/p&gt;
&lt;p&gt;Those are two different authorities. Execution authority is who takes the step. Verification authority is who confirms it was right. A map keeps both with the model: the model reads the evidence a script prints, compares it with what it expected, and chooses the next branch. When the route is wrong, it can notice. A GPS takes execution authority. A GPS that executes without independent checks may hide errors; a GPS with explicit checks can remain auditable. Counting, parsing, and checking a reference are good GPS work, because a script can prove the answer.&lt;/p&gt;
&lt;p&gt;Which arrangement wins on a given task is an empirical question, and the test is below.&lt;/p&gt;
&lt;h2&gt;Capability vs competence&lt;/h2&gt;
&lt;p&gt;Capability is what a tool lets a model do while it has the tool. Competence is what the model can still do after the tool is taken away. For agents, I optimize capability.&lt;/p&gt;
&lt;p&gt;David Krakauer&#39;s paper, &lt;a href=&quot;https://arxiv.org/abs/2607.18460&quot;&gt;Competitive and Complementary Tools&lt;/a&gt; (arXiv 2607.18460), gives both terms precise meanings. &lt;strong&gt;Competence&lt;/strong&gt; is &amp;quot;the performance that remains when the tool is withdrawn.&amp;quot; &lt;strong&gt;Capability&lt;/strong&gt; &amp;quot;is what tools add.&amp;quot;&lt;/p&gt;
&lt;p&gt;Here I need to separate the model, the session, and the system. Without weight updates, using a skill does not increase the base model&#39;s learned competence; a persistent agent system can still accumulate knowledge outside its weights. When I say competence after withdrawal, I mean the base model with the skills, scripts, and external memory all removed. Measured that way, no skill I give an agent is complementary, because nothing transfers into the weights. That&#39;s fine. I&#39;m trying to raise capability, and capability is measured with the tool present.&lt;/p&gt;
&lt;h2&gt;Where the frame comes from&lt;/h2&gt;
&lt;p&gt;Krakauer, president of the Santa Fe Institute, discussed whether AI tools are complementary or competitive on StarTalk in &lt;a href=&quot;https://startalkmedia.com/show/what-is-intelligence-with-david-krakauer/&quot;&gt;What Is Intelligence? With David Krakauer&lt;/a&gt; (Oct 2, 2026). His paper defines the split. &amp;quot;I call a tool complementary when competence transfers to the user and persists after withdrawal, and competitive when the user grows more dependent the more it is used and tool-free competence is compromised.&amp;quot; An abacus or a map is complementary. A GPS is competitive. He doesn&#39;t put language models in a single column: &amp;quot;When its output is reconstructed and verified, it transfers a method and augments intelligence. When its answers are accepted uncritically ... it diminishes intelligence.&amp;quot; The paper also quotes Henri Bergson&#39;s definition of intelligence as &amp;quot;the faculty of manufacturing artificial objects, especially tools to make tools.&amp;quot; Tools that build tools is the goal of &lt;a href=&quot;https://github.com/rjmurillo/ai-agents&quot;&gt;ai-agents&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Research on people and AI argues for human independence. &lt;a href=&quot;https://www.preprints.org/manuscript/202605.1603&quot;&gt;The GPS for Thinking&lt;/a&gt; proposes a compass model so AI will &amp;quot;strengthen, not supplant, the student&#39;s inner compass.&amp;quot; &lt;a href=&quot;https://pmc.ncbi.nlm.nih.gov/articles/PMC13420724&quot;&gt;Not all cognitive offloading is equal&lt;/a&gt; separates dependent offloading from autonomous offloading. That goal is right for a student. For an agent, dependence on a good tool is the goal.&lt;/p&gt;
&lt;h2&gt;Claude skills with scripts: maps with instruments&lt;/h2&gt;
&lt;p&gt;A map with no instruments tells the model where to look but gives it nothing to look with. The script is the instrument. It does the deterministic work and reports what it found, and the model makes the call.&lt;/p&gt;
&lt;p&gt;Anthropic&#39;s skill guidance already favors scripts. &lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;Its engineering post&lt;/a&gt; says &amp;quot;many applications require the deterministic reliability that only code can provide.&amp;quot; Its &lt;a href=&quot;https://resources.anthropic.com/hubfs/The-Complete-Guide-to-Building-Skill-for-Claude.pdf&quot;&gt;building guide&lt;/a&gt; says &amp;quot;Code is deterministic; language interpretation isn&#39;t.&amp;quot; That&#39;s the reliability argument. Mine adds one thing: a script that prints evidence keeps the model checking the route.&lt;/p&gt;
&lt;p&gt;In ai-agents, &lt;code&gt;skill-bootstrap&lt;/code&gt; produces workflow skills with a first useful action, discriminating observations, decision branches, and outcome checks (&lt;code&gt;.claude/skills/skill-bootstrap/references/authoring-and-gates.md&lt;/code&gt;). The model still chooses the branch. &lt;a href=&quot;https://github.com/rjmurillo/ai-agents/pull/6642&quot;&gt;ai-agents PR #6642&lt;/a&gt;, open as of Oct 17, 2026, adds the instrument side. Each verified item is sorted as a &lt;code&gt;scaffold&lt;/code&gt; (prose the model reasons with), an &lt;code&gt;instrument&lt;/code&gt; (a script that computes the answer), or both. Then &lt;code&gt;bootstrap_forms.py check&lt;/code&gt; rejects a computable item that has no script.&lt;/p&gt;
&lt;p&gt;The skill started as &lt;a href=&quot;https://gist.github.com/rjmurillo/c6565f419a5dc861e4d9b93bc5506dca&quot;&gt;a gist&lt;/a&gt; I adapted in July from a prompt Fable 5 generated for u/Rodbourn, shared in &lt;a href=&quot;https://www.reddit.com/r/ClaudeAI/comments/1ukynrw/friendly_reminder_to_have_fable_5_write_skills/&quot;&gt;an r/ClaudeAI thread&lt;/a&gt; started by u/oj93-rd. It asks a stronger model to write skills so smaller models can &amp;quot;carry this project forward without you.&amp;quot; It also asks for instruments: &amp;quot;MEASURE instead of eyeball ... ship actual scripts inside the skill&#39;s scripts/ dir where they exist or where you can write them.&amp;quot;&lt;/p&gt;
&lt;h2&gt;Skill catalogs: count is not capability&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/wshobson/agents&quot;&gt;wshobson/agents&lt;/a&gt; has 185 skill folders, and at commit &lt;code&gt;044b4b2&lt;/code&gt; (Oct 15, 2026), 3 of them contain a &lt;code&gt;scripts/&lt;/code&gt; directory. In &lt;a href=&quot;https://github.com/rjmurillo/ai-agents&quot;&gt;ai-agents&lt;/a&gt;, 56 of 121 skill folders do, at commit &lt;code&gt;e2b9c1e&lt;/code&gt; (Oct 17, 2026). Treat those counts as inventory, not evidence of capability. A count tells you how broad a catalog is. It doesn&#39;t tell you whether any one skill makes the model more capable.&lt;/p&gt;
&lt;p&gt;The primary measure is outcome quality: the task scored with the skill and without it. Cost, latency, variance, and how often a person has to step in are guardrails. A skill that raises quality but makes runs erratic hasn&#39;t earned its place yet.&lt;/p&gt;
&lt;p&gt;For every skill, I want five answers. Which baseline failure does it fix? What part is deterministic? Who makes the ambiguous decisions? How are failures detected? What measured improvement justifies maintaining it?&lt;/p&gt;
&lt;h2&gt;How to test whether a skill helps&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ai-agents&lt;/code&gt; scenario runner already compares with and without. It scores a &lt;code&gt;baseline&lt;/code&gt; cell with no skill, a &lt;code&gt;description&lt;/code&gt; cell, and a &lt;code&gt;full&lt;/code&gt; cell. A skill passes only when its best cell averages at least 3.5 and beats baseline by at least 0.5 (&lt;code&gt;scripts/eval/eval-rule-activation.py&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;That runner measures whether the model follows the skill&#39;s guidance. This argument also needs to know whether the task came out right, so &lt;a href=&quot;https://github.com/rjmurillo/ai-agents/pull/6642&quot;&gt;PR #6642&lt;/a&gt; adds a capability eval that scores the task itself, with the instruments and without them. Its first run showed no demonstrated accuracy gain on these four tasks with Haiku. F1 went from 0.867 to 0.872 on one task and stayed at 1.000 on the other three. Cost per run went up on all four.&lt;/p&gt;
&lt;p&gt;That doesn&#39;t mean instruments have no benefit. For a crisp question, Haiku wrote its own throwaway script, so the task got an instrument either way and a shipped script had no gap to fill. Accuracy is one outcome. The next runs also need execution time, consistency across runs, and recovery after a failure.&lt;/p&gt;
&lt;h2&gt;What I measure next&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Outcome quality on an unfiltered, representative task set, plus a harder subset the model fails without help.&lt;/li&gt;
&lt;li&gt;Repeated paired runs with confidence intervals, success criteria declared before the run, and held-out scenarios.&lt;/li&gt;
&lt;li&gt;Four conditions with equal tool-access budgets: no skill, map, GPS, and the hybrid.&lt;/li&gt;
&lt;li&gt;Guardrails for each condition: cost, latency, variance, intervention rate, and wrong turns the agent catches on its own.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&#39;ll publish the numbers when I have them, including any that go against me. The skills, scripts, and evals are in &lt;a href=&quot;https://github.com/rjmurillo/ai-agents&quot;&gt;ai-agents&lt;/a&gt;. To try it on your own repo, start with &lt;a href=&quot;https://github.com/rjmurillo/ai-agents/tree/main/.claude/skills/skill-bootstrap&quot;&gt;&lt;code&gt;skill-bootstrap&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What task does your agent keep failing until you hand it a tool? If you&#39;ve measured a skill with and without it, or have one you think should be a GPS, &lt;a href=&quot;https://github.com/rjmurillo/ai-agents/issues/new&quot;&gt;open an issue on ai-agents&lt;/a&gt;.&lt;/p&gt;
</description><pubDate>Sat, 17 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Richard Murillo</dc:creator>
      <guid>https://richardmurillo.com/blog/capability-not-competence/</guid>
    </item>
  </channel>
</rss>