<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Richard Shade</title>
    <description>The latest articles on DEV Community by Richard Shade (@rshade).</description>
    <link>https://dev.to/rshade</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F977293%2Fb027b676-a6c8-4783-a135-714995cd4eb4.jpeg</url>
      <title>DEV Community: Richard Shade</title>
      <link>https://dev.to/rshade</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rshade"/>
    <language>en</language>
    <item>
      <title>gh-aw-fleet 0.2: cost and risk, fleet-wide</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Mon, 06 Jul 2026 10:33:35 +0000</pubDate>
      <link>https://dev.to/rshade/gh-aw-fleet-02-cost-and-risk-fleet-wide-35fo</link>
      <guid>https://dev.to/rshade/gh-aw-fleet-02-cost-and-risk-fleet-wide-35fo</guid>
      <description>&lt;p&gt;The &lt;a href="https://rshade.github.io/posts/introducing-gh-aw-fleet/" rel="noopener noreferrer"&gt;launch post&lt;/a&gt; ended on a cliffhanger: "a consumption rollup is in flight." Five patch releases later (v0.2.1 through v0.2.5), it's shipped, and on the way. We found a second question the per-repo tools couldn't answer along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  the rollup, finished
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gh-aw-fleet consumption&lt;/code&gt; reads AI-credit usage straight from &lt;code&gt;gh aw logs --json&lt;/code&gt; and rolls it up however you want to slice it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# fleet-wide spend for the trailing 30 days, by cost center&lt;/span&gt;
gh-aw-fleet consumption &lt;span class="nt"&gt;--trailing&lt;/span&gt; 30d &lt;span class="nt"&gt;--by&lt;/span&gt; cost-center

&lt;span class="c"&gt;# flag anything over its budget before the invoice does&lt;/span&gt;
gh-aw-fleet consumption &lt;span class="nt"&gt;--budget&lt;/span&gt; 500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;USD is derived at a fixed $0.01 per AI credit, so a number that used to mean clicking through every repo's Discussions is now one command. &lt;code&gt;--by repo&lt;/code&gt;, &lt;code&gt;--by profile&lt;/code&gt;, or &lt;code&gt;--by workflow&lt;/code&gt; if cost-center isn't the axis you care about; pass a repo name to drill into just its spend. The &lt;code&gt;--budget&lt;/code&gt; flag (v0.2.5) is the payoff: instead of eyeballing a table for anything that looks high, the tool tells you which repos crossed a line you set.&lt;/p&gt;

&lt;h2&gt;
  
  
  why does Renovate keep touching gh-aw's pins?
&lt;/h2&gt;

&lt;p&gt;Cost was the fleet-wide question I went looking for. This one found me.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gh aw upgrade&lt;/code&gt; pins &lt;code&gt;gh-aw-actions&lt;/code&gt; to an SHA coupled to the compiler version baked into each workflow's lock-file metadata. Renovate and Dependabot don't know that: a grep of the fleet's own history for &lt;code&gt;renovate&lt;/code&gt; / &lt;code&gt;packageRules&lt;/code&gt; / &lt;code&gt;gh-aw-actions&lt;/code&gt; turned up nothing. The tool that manages every repo's pins had zero visibility into the &lt;em&gt;other&lt;/em&gt; bots that also touch those files.&lt;/p&gt;

&lt;p&gt;It wasn't hypothetical. While building the scanner, &lt;code&gt;rshade/finfocus#1246&lt;/code&gt; showed up live: a Dependabot PR bumping &lt;code&gt;gh-aw-actions&lt;/code&gt; 0.77.4 → 0.78.3, rewriting 9+ &lt;code&gt;.lock.yml&lt;/code&gt; files it doesn't own. Renovate can be told to leave &lt;code&gt;*.lock.yml&lt;/code&gt; alone entirely via &lt;a href="https://docs.renovatebot.com/configuration-options/#matchfilenames" rel="noopener noreferrer"&gt;&lt;code&gt;matchFileNames&lt;/code&gt;&lt;/a&gt;; Dependabot has no file-glob ignore, only &lt;a href="https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference" rel="noopener noreferrer"&gt;&lt;code&gt;dependency-name&lt;/code&gt;&lt;/a&gt;, so its fix is narrower by construction. Two advisory scanners (v0.2.3) now warn if a managed repo's &lt;code&gt;renovate.json&lt;/code&gt; or &lt;code&gt;dependabot.yml&lt;/code&gt; is missing the rule, and hand you the exact block to paste in.&lt;/p&gt;

&lt;h2&gt;
  
  
  saying so, three times
&lt;/h2&gt;

&lt;p&gt;A scanner nobody reads is a scanner that didn't run. Findings from &lt;code&gt;deploy&lt;/code&gt;/&lt;code&gt;sync&lt;/code&gt;/&lt;code&gt;upgrade&lt;/code&gt; now surface on three independent surfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stderr, above the summary&lt;/li&gt;
&lt;li&gt;an interactive &lt;code&gt;y/N&lt;/code&gt; prompt before anything commits (skippable with &lt;code&gt;--yes&lt;/code&gt;, which bypasses only the prompt)&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;## Security Findings&lt;/code&gt; section in the PR body itself, so a teammate reviewing the diff sees what you saw in your terminal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;--strict&lt;/code&gt; escalates a HIGH finding from a warning to a hard stop before anything lands. It's opt-in, so a routine run still doesn't need a flag to stay dry-run-first.&lt;/p&gt;

&lt;h2&gt;
  
  
  smaller things
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pkg/fleet&lt;/code&gt;&lt;/strong&gt; is a public package now, and the CLI runs on &lt;a href="https://rshade.github.io/posts/introducing-ax-go/" rel="noopener noreferrer"&gt;ax-go&lt;/a&gt;'s config primitives instead of its own copy, the tool eating its own dogfood.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;install.sh&lt;/code&gt; / &lt;code&gt;install.ps1&lt;/code&gt;&lt;/strong&gt; one-liners, so onboarding a new operator doesn't start with a Go toolchain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a real docs site&lt;/strong&gt;, &lt;a href="https://rshade.github.io/gh-aw-fleet/" rel="noopener noreferrer"&gt;rshade.github.io/gh-aw-fleet&lt;/a&gt;, on the same &lt;code&gt;rshade-theme&lt;/code&gt; as everything else here.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  where it's at
&lt;/h2&gt;

&lt;p&gt;v0.2.5, still pre-1.0. Cost and risk turned out to be the same shape of problem: something every per-repo tool is blind to, and the one place with a view across the whole fleet is &lt;code&gt;fleet.json&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/gh-aw-fleet/releases/tag/v0.2.5" rel="noopener noreferrer"&gt;v0.2.5 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/gh-aw-fleet" rel="noopener noreferrer"&gt;Repo + docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>projects</category>
      <category>agents</category>
      <category>ai</category>
      <category>finops</category>
    </item>
    <item>
      <title>ax-go 0.2 and 0.3: contracts worth pinning</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Fri, 03 Jul 2026 02:28:09 +0000</pubDate>
      <link>https://dev.to/rshade/ax-go-02-and-03-contracts-worth-pinning-6n8</link>
      <guid>https://dev.to/rshade/ax-go-02-and-03-contracts-worth-pinning-6n8</guid>
      <description>&lt;p&gt;Two ax-go releases landed close together, and they're really one idea in two halves. v0.2.0 let you pin just the contract an agent depends on, without the runtime behind it. v0.3.0 is the work that keeps that contract from moving once you've pinned it.&lt;/p&gt;

&lt;p&gt;If you're new here: &lt;a href="https://github.com/rshade/ax-go" rel="noopener noreferrer"&gt;ax-go&lt;/a&gt; is the &lt;a href="https://rshade.github.io/posts/introducing-ax-go/" rel="noopener noreferrer"&gt;agentic-experience foundation&lt;/a&gt; under my Go CLIs. stdout is data, traces cross the plugin boundary, every human table has a JSON twin. The contract is the whole point, so the contract is what both releases are about.&lt;/p&gt;

&lt;h2&gt;
  
  
  v0.2.0: import-isolated contract packages
&lt;/h2&gt;

&lt;p&gt;Go imports are all-or-nothing. There's no tree-shaking: import a package, and you compile in its entire transitive dependency graph, whether you call into it or not.&lt;/p&gt;

&lt;p&gt;So until v0.2.0, reusing any ax-go contract (the error envelope, the exit codes, the &lt;code&gt;__schema&lt;/code&gt; shapes) meant importing the root &lt;code&gt;ax&lt;/code&gt; package. And &lt;code&gt;ax&lt;/code&gt; is the full runtime: the OpenTelemetry SDK, zerolog and Loki, HTTP and gRPC instrumentation. A thin orchestrator that only wanted to emit a well-formed error envelope was paying for a telemetry stack it never started.&lt;/p&gt;

&lt;p&gt;v0.2.0 carves four narrow packages out of the root:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;contract&lt;/code&gt;&lt;/strong&gt;: exit codes, mode resolution, context metadata, the success/error envelope, the strict JSON/NDJSON writers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;config&lt;/code&gt;&lt;/strong&gt;: bounded Hujson reads and comment-preserving RFC 6902 patches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;schema&lt;/code&gt;&lt;/strong&gt;: the ax-native and MCP-compatible schema builders, plus the &lt;code&gt;__schema&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;id&lt;/code&gt;&lt;/strong&gt;: UUID v4 idempotency keys and UUID v7 entity IDs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each depends on the standard library and not much else: never on the root facade, never on a runtime adapter. So the import you reach for matches the weight you take on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/rshade/ax-go/contract"&lt;/span&gt;  &lt;span class="c"&gt;// just the shapes&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="s"&gt;"github.com/rshade/ax-go"&lt;/span&gt;          &lt;span class="c"&gt;// the shapes plus the runtime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The root &lt;code&gt;ax&lt;/code&gt; package still works unchanged. It becomes a thin facade that re-exports every moved type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a type &lt;em&gt;alias&lt;/em&gt;, not a new type, and the distinction does real work: &lt;code&gt;ax.Error&lt;/code&gt; and &lt;code&gt;contract.Error&lt;/code&gt; are the same type. So &lt;code&gt;errors.As(err, *contract.Error)&lt;/code&gt; still matches an error built deep inside the runtime. Identity survives the split, and upgrading from v0.1.0 is a &lt;code&gt;go get -u&lt;/code&gt; and nothing else.&lt;/p&gt;

&lt;p&gt;The boundary is a test, not a promise. Each contract package ships an import-isolation test that runs &lt;code&gt;go list -deps&lt;/code&gt; and fails the build if a forbidden runtime import (the root facade, the OTel SDK, Loki, the HTTP or gRPC instrumentation) shows up in its graph. CI enforces the isolation; I don't have to remember to.&lt;/p&gt;

&lt;h2&gt;
  
  
  v0.3.0: keeping the contract from drifting
&lt;/h2&gt;

&lt;p&gt;Splitting the contract out is half the promise. The other half is that the shapes you pin don't move underneath you. v0.3.0 is the guardrails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coverage floors.&lt;/strong&gt; Every contract package now has its own coverage floor enforced in CI, alongside a repo-wide one. The packages a thin consumer pins are exactly the packages that shouldn't quietly lose test coverage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A compatibility matrix and a CONTRIBUTING guide.&lt;/strong&gt; The README now spells out which Go versions and which ax-go versions are supported, so "can I depend on this" has a written answer instead of a guess. CONTRIBUTING documents how the contract surface is allowed to change, and how it isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A gate on breaking changes.&lt;/strong&gt; Public-API changes now run through go-apidiff in CI, so a breaking change to an exported contract can't merge without tripping a gate. The pin you depend on can't move by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  the docs site
&lt;/h2&gt;

&lt;p&gt;Both releases ship against a real home now: &lt;a href="https://rshade.github.io/ax-go/" rel="noopener noreferrer"&gt;rshade.github.io/ax-go&lt;/a&gt;, an Astro Starlight site built on the shared &lt;code&gt;rshade-theme&lt;/code&gt;. Same tokens as &lt;a href="https://rshade.github.io/posts/introducing-finfocus/" rel="noopener noreferrer"&gt;finfocus&lt;/a&gt;, because they came from the same shop.&lt;/p&gt;

&lt;h2&gt;
  
  
  where it's at
&lt;/h2&gt;

&lt;p&gt;Still pre-1.0, still pinnable. v0.2.0 let you pin just the contract; v0.3.0 is the work that keeps pinning it safe. An agent shouldn't have to compile a telemetry stack to read an exit code, and it shouldn't wake up to find the code moved underneath it either.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/ax-go/releases/tag/v0.3.0" rel="noopener noreferrer"&gt;v0.3.0 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/ax-go" rel="noopener noreferrer"&gt;Repo + docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>projects</category>
      <category>go</category>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>agentic experience for Go</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Sat, 20 Jun 2026 01:58:54 +0000</pubDate>
      <link>https://dev.to/rshade/agentic-experience-for-go-36mh</link>
      <guid>https://dev.to/rshade/agentic-experience-for-go-36mh</guid>
      <description>&lt;p&gt;Years ago at RightScale I learned more about distributed systems from broken log files than from any design doc. A request came in the front door, fanned out through a workflow service, hit a plugin, and the plugin called some cloud API. When it failed, the only way to find &lt;em&gt;where&lt;/em&gt; was to line up the logs of every service it passed through. So we threaded a trace ID from the frontend all the way to the cloud call and back. With it, a failure was a search query. Without it, you were guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  the lesson came back
&lt;/h2&gt;

&lt;p&gt;It stuck. I just had to learn it twice.&lt;/p&gt;

&lt;p&gt;Building &lt;a href="https://github.com/rshade/finfocus" rel="noopener noreferrer"&gt;finfocus&lt;/a&gt;, a FinOps CLI that talks to cloud providers through gRPC plugins, I hit the same wall. Except this time there was a second reader who couldn't follow the logs: the agent. I'd ask Claude to find why a plugin call failed, and it would dig through the finfocus logs, reach the gRPC boundary, and find nothing on the other side. Unconnected traces, or no traces at all. I'd reached for &lt;a href="https://github.com/rs/zerolog" rel="noopener noreferrer"&gt;zerolog&lt;/a&gt; early, but I'd wired it up wrong. The CLI logged. The plugin logged. Nothing tied the two together, so neither of us could see across the boundary.&lt;/p&gt;

&lt;p&gt;Once the trace ID actually crossed that boundary, troubleshooting got fast. The agent could make a call, follow it from the finfocus CLI through the gRPC plugin and back, and tell me which side broke. Bug hunting went from a séance to a &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  the second wall
&lt;/h2&gt;

&lt;p&gt;The next problem was stranger. finfocus has a TUI: pretty tables, built for human eyes. Ask an agent to read one and it gets the numbers wrong, because a table is laid out for a person, not a parser. Back then, agents were bad at this.&lt;/p&gt;

&lt;p&gt;So every command that renders a table also got a &lt;code&gt;--json&lt;/code&gt; that runs the &lt;em&gt;same function&lt;/em&gt; as the table does. Two renderings, one source of truth: pretty for humans, structured for agents. They can't disagree about the total, because the total is computed once.&lt;/p&gt;

&lt;h2&gt;
  
  
  canonizing it
&lt;/h2&gt;

&lt;p&gt;Then I did all of it again in gh-aw-fleet.&lt;/p&gt;

&lt;p&gt;By the time I was copying the same plumbing into a third project (stream separation, trace propagation, structured errors, a JSON twin for every human view), the question answered itself. Why am I rewriting this per repo? Canonize it once, let other people use it, and get the wisdom of the crowd (and the clankers) to make it better.&lt;/p&gt;

&lt;p&gt;That's ax-go: Agentic Experience for Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it is
&lt;/h2&gt;

&lt;p&gt;ax-go is a single Go package (&lt;code&gt;github.com/rshade/ax-go&lt;/code&gt;, imported as &lt;code&gt;ax&lt;/code&gt;) that encodes the conventions a CLI needs so an agent can use it as reliably as a human can. The rules I kept rediscovering, written down once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;stdout is data, stderr is everything else.&lt;/strong&gt; The final JSON payload is the only thing on stdout. Logs, progress, and error envelopes go to stderr. An agent pipes stdout into a parser; you still read the logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traces that cross the boundary.&lt;/strong&gt; W3C Trace Context rides &lt;code&gt;context.Context&lt;/code&gt; through OpenTelemetry, so one call stays correlated from the CLI to whatever it calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same input, same bytes.&lt;/strong&gt; Two runs on the same input produce byte-identical stdout. An agent diffs outputs to catch drift, which is the machine version of trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;__schema&lt;/code&gt; command.&lt;/strong&gt; Every tool can describe its own commands, flags, and types as JSON, so an agent grounds itself instead of guessing. There's an MCP adapter too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-safety primitives.&lt;/strong&gt; An auto-generated &lt;code&gt;--idempotency-key&lt;/code&gt; so a retried create can't run twice, a universal &lt;code&gt;--dry-run&lt;/code&gt;, deterministic exit codes, and a structured error envelope.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  where it's at
&lt;/h2&gt;

&lt;p&gt;It's released and pre-1.0. v0.1.0 is the pinnable tag, with output contracts already frozen in code and pinned by golden tests, so the shapes an agent depends on won't move underneath it.&lt;/p&gt;

&lt;p&gt;It starts with what I use: zerolog and OpenTelemetry. Other structured loggers and output formats will follow, but I'd rather ship the opinions I've tested in finfocus and gh-aw-fleet than guess at the ones I haven't. It's the common DNA for my own tools first. If it's useful to yours, even better.&lt;/p&gt;

&lt;p&gt;Years later, I'm still threading trace IDs across plugin boundaries. The only difference is who's reading them now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/ax-go" rel="noopener noreferrer"&gt;Repo + docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/ax-go/releases/latest" rel="noopener noreferrer"&gt;Latest release&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>projects</category>
      <category>agents</category>
      <category>ai</category>
      <category>go</category>
    </item>
    <item>
      <title>where did the knife come from</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Thu, 18 Jun 2026 01:28:54 +0000</pubDate>
      <link>https://dev.to/rshade/where-did-the-knife-come-from-1ebp</link>
      <guid>https://dev.to/rshade/where-did-the-knife-come-from-1ebp</guid>
      <description>&lt;p&gt;In third grade I had to write a how-to for making a peanut butter and jelly sandwich. I thought I'd nailed it. Four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the bread.&lt;/li&gt;
&lt;li&gt;Get the peanut butter and the jelly.&lt;/li&gt;
&lt;li&gt;Spread the peanut butter on one slice, the jelly on the other.&lt;/li&gt;
&lt;li&gt;Put them together.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My teacher read it, looked up, and asked one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where did the knife come from?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had the vision. I'd skipped the environment. The knife was real in my head (I'd seen it in the drawer that morning) so I assumed it was real on the page. It wasn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  three phases of getting this wrong with LLMs
&lt;/h2&gt;

&lt;p&gt;When I started taking prompt engineering seriously last year, I realized I was repeating the third-grade mistake. The arc was familiar enough that I think most people walk it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase one: the one-liner.&lt;/strong&gt; Single sentence. Expect the model to read your mind. When it fails, fight with it in the next turn instead of fixing the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase two: the notebook.&lt;/strong&gt; Start saving prompts that worked. Notice that consistency matters. Notice that some prompts are doing work the model can't actually do without more setup around it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase three: the environment.&lt;/strong&gt; Realize the prompt isn't an instruction. It's a room. The model can only use what's in the room. If the knife isn't in the room, the sandwich doesn't get made, no matter how clearly you described the spreading motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  what I write now
&lt;/h2&gt;

&lt;p&gt;Three things stacked on top of each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt;: where the ingredients live. What the model has access to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraints&lt;/strong&gt;: how to use the tools, and how not to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceptance criteria&lt;/strong&gt;: what "finished sandwich" actually looks like, in enough detail that the model can self-check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no magic word. There's no clever phrasing trick. Prompt engineering is the same skill as writing a good bug report or a clear design doc: assume the reader doesn't have your context, then put the context in the document.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>where the copilot credits went</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Tue, 16 Jun 2026 02:16:01 +0000</pubDate>
      <link>https://dev.to/rshade/where-the-copilot-credits-went-3gn3</link>
      <guid>https://dev.to/rshade/where-the-copilot-credits-went-3gn3</guid>
      <description>&lt;p&gt;A couple weeks ago I &lt;a href="https://rshade.github.io/posts/introducing-gh-aw-fleet/" rel="noopener noreferrer"&gt;introduced gh-aw-fleet&lt;/a&gt; and ended on a confession: I went in solving a drift-tracking problem and walked out with a FinOps one. That was foreshadowing. The bill has since arrived, and I'm late to writing about it, which is about right. The hot takes have burned out and FinOps X has packed up, and what's left is the one question worth keeping: where did the credits actually go?&lt;/p&gt;

&lt;h2&gt;
  
  
  what changed
&lt;/h2&gt;

&lt;p&gt;On June 1, GitHub &lt;a href="https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/" rel="noopener noreferrer"&gt;moved Copilot to usage-based billing&lt;/a&gt;. Premium request units are gone; in their place are AI Credits metered against token consumption: input, output, and cached, at each model's API rate. The sticker prices didn't move (Pro+ is still $39, Business still $19 a seat), but the math underneath did: every chat turn, every agentic session, every Copilot code review now draws down a credit balance. Reviewing a PR with Copilot bills against your Actions minutes too.&lt;/p&gt;

&lt;p&gt;The reaction was about what you'd expect. People &lt;a href="https://www.ghacks.net/2026/06/02/github-copilot-usage-based-billing-takes-effect-drawing-developer-backlash-over-rapid-credit-depletion/" rel="noopener noreferrer"&gt;watched a month of credits evaporate in an afternoon&lt;/a&gt; and called it meter shock.&lt;/p&gt;

&lt;p&gt;Now point that meter at a fleet. I run a dozen-odd repos with agentic workflows on them (code review, daily doc updates, malicious-code scans), and every one of them is now a little metered faucet.&lt;/p&gt;

&lt;h2&gt;
  
  
  why you can't see it
&lt;/h2&gt;

&lt;p&gt;The per-repo tools answer the wrong question. &lt;code&gt;gh aw&lt;/code&gt; sees one repo. The Actions UI sees one repo. Nothing sums it across the fleet, so the credits drain quietly and the first time you see the total is on the invoice. That's the same blind spot drift detection had before &lt;code&gt;status&lt;/code&gt;: the problem was never any single repo, it's that nobody was looking across all of them at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  where the credits went
&lt;/h2&gt;

&lt;p&gt;The same &lt;code&gt;fleet.json&lt;/code&gt; that declares which repos get which workflows is the natural place to attribute what those workflows cost. It already knows the repo, the profile, and (if you set one) the cost center. That's every dimension you'd want to slice spend by, sitting right there in the config.&lt;/p&gt;

&lt;p&gt;So that's what &lt;code&gt;gh-aw-fleet consumption&lt;/code&gt; does. It reads the credits each workflow burned straight from &lt;code&gt;gh aw logs --json&lt;/code&gt; and rolls them up along whichever axis you ask for: the whole fleet, or scoped to a single repo. There's no dollar field to read. Under the new model the unit &lt;em&gt;is&lt;/em&gt; the AI credit, so that's the column, with USD derived at the published rate of a cent per credit.&lt;/p&gt;

&lt;p&gt;Across the repos I run in the open, finfocus was the worst offender, so I pointed &lt;code&gt;consumption&lt;/code&gt; at it, grouped by workflow, and let it name the culprit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh-aw-fleet consumption rshade/finfocus &lt;span class="nt"&gt;--by&lt;/span&gt; workflow &lt;span class="nt"&gt;--trailing&lt;/span&gt; 7d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKFLOW                          AIC       COST
Agentic Dependabot Bundler        2295.78   $22.96
Daily Documentation Updater        796.54    $7.97
Agentic Workflow Audit Agent       487.23    $4.87
Daily Malicious Code Scan Agent    385.92    $3.86
Sub-Issue Closer                   313.61    $3.14
Code Simplifier                    137.49    $1.37
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's one of my own repos, one week: ~4,400 credits, about forty-four dollars. And the rollup answers the question in the title: one workflow, the Agentic Dependabot Bundler, is $22.96 of that. A single agent is more than half the bill.&lt;/p&gt;

&lt;p&gt;None of this appeared overnight. It arrived one release at a time: a billing diagnostic, then the &lt;code&gt;cost_center&lt;/code&gt; and &lt;code&gt;tier&lt;/code&gt; fields, then the rollup, then reading credits straight from the logs, then scoping it to a single repo. I was building toward a bill I knew was coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  the point
&lt;/h2&gt;

&lt;p&gt;Here's what makes it land harder than a personal billing gripe. At this year's FinOps X, the FinOps Foundation and the Linux Foundation announced their intent to form a &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-announces-the-intent-to-launch-the-tokenomics-foundation-to-establish-open-standards-for-ai-cost-management" rel="noopener noreferrer"&gt;Tokenomics Foundation&lt;/a&gt;: open standards for AI token economics, the first job being to extend the FOCUS cost spec to cover token billing. The conference is even renaming itself Tokenomicon for 2027. The unit of account is officially becoming the token.&lt;/p&gt;

&lt;p&gt;My rollup agrees with them. There was no dollar figure to read. The honest unit under usage-based Copilot is the credit, and the dollars are a cent-apiece derivation on top. I didn't build a standards body. I built a subcommand. But it turns out I was hand-rolling, for one fleet, the thing a dozen of the biggest names in the industry just agreed needs a spec: when the unit of work is a fleet, the unit of cost is too. And nothing else in the stack was going to tell me that one dependency bundler was quietly eating half my credits.&lt;/p&gt;

</description>
      <category>finops</category>
      <category>agents</category>
      <category>projects</category>
    </item>
    <item>
      <title>introducing gh-aw-fleet</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Mon, 15 Jun 2026 01:22:31 +0000</pubDate>
      <link>https://dev.to/rshade/introducing-gh-aw-fleet-57f4</link>
      <guid>https://dev.to/rshade/introducing-gh-aw-fleet-57f4</guid>
      <description>&lt;p&gt;I started using &lt;a href="https://github.com/github/gh-aw" rel="noopener noreferrer"&gt;GitHub Agentic Workflows&lt;/a&gt; a couple months ago: small Claude/Copilot agents that run inside your CI for code review, daily doc updates, malicious-code scans, and PR fixes. You author them in markdown, they compile to GitHub Actions, and an AI agent does the work on each run.&lt;/p&gt;

&lt;p&gt;Got the first few repos working and hit the question: how am I actually tracking what's deployed on each of these? What version? What profile? What's drifted?&lt;/p&gt;

&lt;p&gt;So I built a tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it is
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gh-aw-fleet&lt;/code&gt; is a declarative fleet manager for GitHub Agentic Workflows. One &lt;code&gt;fleet.json&lt;/code&gt; declares your repos and which "profiles" of workflows they get; the CLI reconciles (&lt;code&gt;deploy&lt;/code&gt;, &lt;code&gt;sync&lt;/code&gt;, &lt;code&gt;upgrade&lt;/code&gt;, &lt;code&gt;add&lt;/code&gt;), all dry-run by default. It's a thin orchestrator around &lt;code&gt;gh aw&lt;/code&gt;, &lt;code&gt;gh&lt;/code&gt;, and &lt;code&gt;git&lt;/code&gt;, not a fork.&lt;/p&gt;

&lt;p&gt;It never rewrites workflow markdown. It answers one question (&lt;em&gt;who gets what workflow, when, and from which profile&lt;/em&gt;) and dispatches the actual file work to &lt;code&gt;gh aw&lt;/code&gt;. Every operation that touches a repo opens a PR there. Nothing force-pushes or commits to &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# check drift across the whole fleet, no clones, read-only&lt;/span&gt;
gh-aw-fleet status

&lt;span class="c"&gt;# bring a repo in line with its declared profile (PR, after a dry-run)&lt;/span&gt;
gh-aw-fleet &lt;span class="nb"&gt;sync &lt;/span&gt;acme/widgets &lt;span class="nt"&gt;--apply&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dry-run gate is the part I lean on most. &lt;code&gt;deploy&lt;/code&gt;, &lt;code&gt;sync&lt;/code&gt;, and &lt;code&gt;upgrade&lt;/code&gt; print exactly what they'd do and change nothing until you add &lt;code&gt;--apply&lt;/code&gt;. When you're touching a dozen repos at once, "show me first" is the difference between a tool you trust and one you babysit.&lt;/p&gt;

&lt;h2&gt;
  
  
  the part I didn't see coming
&lt;/h2&gt;

&lt;p&gt;Usage-based Copilot billing lands 2026-06-01. Every deployed workflow burns credits at metered rates, and the per-repo tools (&lt;code&gt;gh aw&lt;/code&gt;, the Actions UI) can't see across the fleet to tell you where the credits went.&lt;/p&gt;

&lt;p&gt;The same &lt;code&gt;fleet.json&lt;/code&gt; that declares which repos get which workflows turns out to be the natural place to attribute that consumption: by repo, by profile, or by cost center. A &lt;code&gt;consumption&lt;/code&gt; rollup is in flight. I went in solving a drift-tracking problem and walked out with a FinOps one.&lt;/p&gt;

&lt;h2&gt;
  
  
  what's shipped since launch
&lt;/h2&gt;

&lt;p&gt;v0.1.0 shipped April 21 with the core reconcile loop. It's at v0.2.0 now, and the gap is mostly about trusting the tool against more repos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;status&lt;/code&gt; / drift detection&lt;/strong&gt;: see what's out of line across the fleet without cloning anything, and gate a CI job on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON output + structured logging&lt;/strong&gt;: &lt;code&gt;-o json&lt;/code&gt; on the read commands, &lt;code&gt;zerolog&lt;/code&gt; underneath, so you can pipe results into &lt;code&gt;jq&lt;/code&gt; or an aggregator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;resumable deploys&lt;/strong&gt;: pick a half-finished deploy back up at the commit or push gate instead of starting over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a Layer-1 security scanner&lt;/strong&gt;: secrets and structural rules checked before anything ships.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;observability-plus&lt;/code&gt; profile + an HTTP 402 billing diagnostic&lt;/strong&gt;: early groundwork for the billing story above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HuJSON config&lt;/strong&gt;: comments and trailing commas in &lt;code&gt;fleet.json&lt;/code&gt;, so you can document &lt;em&gt;why&lt;/em&gt; a pin is set right next to the pin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  where it's at
&lt;/h2&gt;

&lt;p&gt;v0.2.0, still pre-1.0: the CLI flags and the &lt;code&gt;fleet.json&lt;/code&gt; schema may move before 1.0. But I'm already using it to manage my own repos, and it's working great.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/gh-aw-fleet" rel="noopener noreferrer"&gt;Repo + docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rshade/gh-aw-fleet/releases/latest" rel="noopener noreferrer"&gt;Latest release&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>projects</category>
      <category>agents</category>
      <category>ai</category>
      <category>finops</category>
    </item>
    <item>
      <title>hello, world</title>
      <dc:creator>Richard Shade</dc:creator>
      <pubDate>Sun, 14 Jun 2026 23:54:44 +0000</pubDate>
      <link>https://dev.to/rshade/hello-world-507n</link>
      <guid>https://dev.to/rshade/hello-world-507n</guid>
      <description>&lt;p&gt;A blog is a long-running argument with yourself about what you actually believe. I've been meaning to start one for years.&lt;/p&gt;

&lt;p&gt;This is mine. It will be about FinOps and Pulumi (my day job), AI tooling and agent design, infrastructure war stories that deserve to exist outside Slack threads, and occasionally brisket and ham radio, because a blog without a person behind it is just SEO.&lt;/p&gt;

&lt;p&gt;Up next: a story about a third-grade peanut butter sandwich assignment, and why it explains most of what I think people get wrong about prompt engineering in their first six months.&lt;/p&gt;

&lt;p&gt;More soon. 73.&lt;/p&gt;

</description>
      <category>meta</category>
    </item>
  </channel>
</rss>
