<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Claude</title><link>https://www.pulumi.com/blog/tag/claude/</link><description>Pulumi blog posts: Claude.</description><language>en-us</language><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate><item><title>Stop Prompting. Design the Loop.</title><link>https://www.pulumi.com/blog/stop-prompting-design-the-loop/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/stop-prompting-design-the-loop/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/stop-prompting-design-the-loop/index.png" /&gt;
&lt;p&gt;For about two years, the unit of work with a coding agent was the prompt. You wrote a good one, you gave it enough context, you read what came back, and you wrote the next one. The agent was a tool, and you were holding it the entire time, one turn after another.&lt;/p&gt;
&lt;p&gt;That part is ending. &lt;a href="https://x.com/addyosmani"&gt;Addy Osmani&lt;/a&gt;, a director of AI at Google Cloud, has a name for what replaces it, and I have not stopped thinking about it since: &lt;a href="https://x.com/addyosmani/status/2064127981161959567"&gt;loop engineering&lt;/a&gt;. You stop being the person who prompts the agent. You design the loop that prompts it for you.&lt;/p&gt;
&lt;p&gt;In my phrasing: you stop being the thing that runs, and start designing the thing that runs. The leverage moves up a layer. What I want to do here is take an honest look at the pieces, and at the part nobody automates.&lt;/p&gt;
&lt;h2 id="the-leverage-moved-up-a-layer"&gt;The leverage moved up a layer&lt;/h2&gt;
&lt;p&gt;The people building these tools have already made the jump. &lt;a href="https://x.com/steipete"&gt;Peter Steinberger&lt;/a&gt; has been posting it as a monthly reminder.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="./steinberger-loops.png" alt="A post by Peter Steinberger (@steipete) on X: 'Here's your monthly reminder that you shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents.'"&gt;
&lt;figcaption&gt;&lt;em&gt;Peter Steinberger (&lt;a href="https://x.com/steipete"&gt;@steipete&lt;/a&gt;) on X.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href="https://x.com/bcherny"&gt;Boris Cherny&lt;/a&gt;, who heads Claude Code at Anthropic, says the same thing about his own job. He does not prompt Claude anymore. He has loops running that prompt Claude and decide what to do next, scanning the issue tracker, the team chat, and the timeline for what to build. &amp;ldquo;My job is to write loops.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;A loop is a goal that prompts itself. You set the purpose, and the system keeps iterating until it&amp;rsquo;s met. In practice it finds the work, hands it out, checks the result, writes down what got finished, and decides the next thing, then it pokes the agent instead of you. You build that small system once and let it run.&lt;/p&gt;
&lt;p&gt;Look closer, and a loop is really two loops nested. The inner one does the work against a spec. The outer one decides what the work should be: it watches an issue tracker, an error feed, a changelog, then writes the next spec and hands it down. Most people are still running that outer loop by hand, in their head, and calling it a backlog.&lt;/p&gt;
&lt;p&gt;The part that surprised me is that this is barely a tooling problem anymore. A year ago a loop meant a pile of bash you wrote and maintained forever. Now the pieces ship inside the products, and the same shapes show up in Claude Code and in Codex. Osmani puts loop engineering one floor above the harness, the context and tooling you wire around a single agent. I wrote about &lt;a href="https://www.pulumi.com/blog/stop-tuning-prompts-build-a-harness/"&gt;that harness&lt;/a&gt; a couple of weeks ago. The loop is the thing that runs on top of it: it runs on a timer, it spawns helpers, and it feeds itself.&lt;/p&gt;
&lt;h2 id="the-five-pieces-and-the-one-that-holds-them-together"&gt;The five pieces, and the one that holds them together&lt;/h2&gt;
&lt;p&gt;Strip loop engineering down and you get roughly five building blocks, plus one place to remember things. Both Claude Code and Codex have all five now. The names differ here and there; the capability is the same.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automations are the heartbeat.&lt;/strong&gt; They are what make a loop an actual loop and not one run you did once. A prompt or command on a cadence, a scheduled task, a hook that fires at a point in the agent&amp;rsquo;s lifecycle, or a job on CI that keeps running after you close the laptop. Discovery and triage run themselves, and the findings that matter come to you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Worktrees keep parallel from turning into chaos.&lt;/strong&gt; The second you run more than one agent, the files start colliding. Two agents writing the same file is the same headache as two engineers committing to the same lines with nobody talking first. A git worktree is a separate working directory on its own branch, so one agent&amp;rsquo;s edits cannot touch another&amp;rsquo;s checkout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills are intent, written down.&lt;/strong&gt; An agent starts every session cold and fills any hole in your intent with a confident guess. A skill is that intent written on the outside: the conventions, the build steps, the &amp;ldquo;we don&amp;rsquo;t do it like this because of that one incident,&amp;rdquo; recorded once where the agent reads it every run. Without skills, the loop re-derives your whole project from zero every cycle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connectors let the loop touch your real tools.&lt;/strong&gt; Built on MCP, they let the agent read the issue tracker, query a database, hit a staging API, or drop a message in chat. This is the difference between an agent that says &amp;ldquo;here is the fix&amp;rdquo; and a loop that opens the pull request, links the ticket, and pings the channel once CI goes green.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sub-agents keep the maker away from the checker.&lt;/strong&gt; The model that wrote the code is far too generous grading its own homework. A second agent with different instructions, and sometimes a different model, catches the things the first one talked itself into. Worktrees and a cold-context reviewer are two pieces I have &lt;a href="https://www.pulumi.com/blog/parallel-coding-playbook-for-pulumi/"&gt;written about before&lt;/a&gt;, back when the question was running agents in parallel without them trampling each other.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then the sixth thing: memory. A markdown file, a Linear board, a state file, anything that lives outside the single conversation and holds what is done and what is next. It sounds too dumb to matter, and it&amp;rsquo;s the whole game. The model forgets everything between runs, so the memory has to live on disk, not in the context window. The agent forgets. The repo does not.&lt;/p&gt;
&lt;h2 id="what-makes-a-loop-hold-together"&gt;What makes a loop hold together&lt;/h2&gt;
&lt;p&gt;A loop running unattended is also a loop making mistakes unattended. The one thing that keeps it honest is verification, and verification needs an oracle, something outside the model that returns a hard yes or no. Passing tests, a clean build, a green pipeline, a real production signal. Without an oracle, the loop compounds confidently wrong work, faster than you can read it.&lt;/p&gt;
&lt;p&gt;The cleanest version of this already ships in the tools. Claude Code&amp;rsquo;s &lt;code&gt;/goal&lt;/code&gt; keeps working across turns until a condition you actually wrote holds, something like &amp;ldquo;every test in &lt;code&gt;auth/&lt;/code&gt; passes and lint is clean,&amp;rdquo; and after every turn a separate, faster model reads the transcript and decides whether you are there yet. The agent that wrote the code is not the one that grades it. That is the maker-and-checker split applied to the stop condition itself. Codex&amp;rsquo;s &lt;code&gt;/goal&lt;/code&gt; reaches the same finish line a different way: the agent audits its own work against the evidence before it can call the goal done.&lt;/p&gt;
&lt;h2 id="what-the-loop-still-wont-do-for-you"&gt;What the loop still won&amp;rsquo;t do for you&lt;/h2&gt;
&lt;p&gt;The loop changes the shape of the work. It does not take it off your desk. And a few things get sharper as the loop gets better, not softer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verification is still on you.&lt;/strong&gt; The split reviewer is what makes &amp;ldquo;it&amp;rsquo;s done&amp;rdquo; mean something, but &amp;ldquo;done&amp;rdquo; is a claim, not a proof. Your job is still to ship code you confirmed works, which is harder to remember when the diff arrived while you were at lunch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The bill comes in two currencies.&lt;/strong&gt; Tokens and attention. A single unattended run can burn through millions of tokens, and that is only worth it when the tokens buy something worth more than they cost. The quieter trap is the second currency: memory is what lets a loop compound over time, and slop compounds right alongside it. A loop pointed at a vague goal does not get tired and stop. It gets faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your understanding rots if you let it.&lt;/strong&gt; The faster the loop ships code you did not write, the wider the gap between what exists in the repo and what you actually understand. A smooth loop grows that gap faster, not slower, unless you read what it made. The comfortable posture, where you stop having an opinion and take whatever the loop gives back, is the risky one. Two engineers can build the exact same loop and get opposite results, one moving faster on work they understand deeply, the other avoiding the work entirely. The loop cannot tell which one you are.&lt;/p&gt;
&lt;h2 id="when-the-loop-reaches-production"&gt;When the loop reaches production&lt;/h2&gt;
&lt;p&gt;Most of this thinking grew up around application code, where a bad run costs you a revert. When the loop reaches into infrastructure, the blast radius is a production outage rather than a revert, and the verification bar has to rise to meet it. The upside is that infrastructure hands the loop a better oracle than application code does. A plan diff is deterministic and machine-readable, a policy check returns a hard verdict, and drift and cost are numbers you can put a threshold on. A reviewer, whether human or agent, can read the change cold, with no memory of the prompt that produced it. That cold-context check is exactly what an unattended loop needs, and it&amp;rsquo;s the reason an infrastructure loop can be built to hold together while you sleep. &lt;a href="https://www.pulumi.com/blog/grounded-ai-why-neo-knows-your-infrastructure/"&gt;Pulumi Neo reasons over the state graph directly&lt;/a&gt;, so the checker grounds every claim in what the change actually does, not in what the writer says it does.&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Give your loop a real oracle&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;Infrastructure gives an agent loop a strong oracle: a deterministic plan diff to read and policy checks that return a hard verdict before anything ships. Wire that verification in through the Pulumi MCP server, Agent Skills, or Pulumi Neo.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/docs/ai/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Get started
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id="where-to-start"&gt;Where to start&lt;/h2&gt;
&lt;p&gt;Pick the loop you can actually trust first. In order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start where &amp;ldquo;done&amp;rdquo; is unambiguous.&lt;/strong&gt; CI triage, dependency bumps, a flaky-test hunt, a failing job you keep re-running by hand. Loops need an oracle, so begin where the oracle already exists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write the memory file before the loop.&lt;/strong&gt; One markdown file, or a board. What is done, what is next, what was tried and failed. This is the spine, and everything else hangs off it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Split the checker from the maker.&lt;/strong&gt; Use &lt;code&gt;/goal&lt;/code&gt; with a verifiable condition, or a second agent with its own instructions. Never let the agent that did the work be the one that decides the work is finished.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cap it, then read everything.&lt;/strong&gt; A max-iteration count, a token budget, a teardown step. Run it once, end to end, then read every line it shipped. The first run is the measurement, not the payoff.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then look at what you built. You designed it once, and it ran without you steering each step. That is the real shift. But the leverage only holds if you wire the loop like an engineer, not like someone looking for permission to stop thinking. Read what it ships. Keep an opinion. Judgment is the one part that does not move up a layer.&lt;/p&gt;
&lt;p&gt;The loop will do the typing. The thinking is the work.&lt;/p&gt;
&lt;a
href="https://www.pulumi.com/product/neo/"
class="btn btn-primary"
&gt;
See how Pulumi Neo closes the loop on your infrastructure
&lt;/a&gt;</description><author>Engin Diri</author><category>ai</category><category>ai-agents</category><category>automation</category><category>claude</category><category>pulumi-neo</category><category>platform-engineering</category></item><item><title>Stop Tuning Prompts. Build a Harness.</title><link>https://www.pulumi.com/blog/stop-tuning-prompts-build-a-harness/</link><pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/stop-tuning-prompts-build-a-harness/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/stop-tuning-prompts-build-a-harness/index.png" /&gt;
&lt;p&gt;Anthropic shipped a piece earlier this month called &lt;a href="https://claude.com/blog/how-claude-code-works-in-large-codebases-best-practices-and-where-to-start"&gt;How Claude Code Works in Large Codebases&lt;/a&gt;. I have not read anything more useful about coding agents this year. The core claim, in their words: &lt;em&gt;&amp;ldquo;the ecosystem built around the model—the harness—determines how Claude Code performs more than the model alone.&amp;rdquo;&lt;/em&gt; In my phrasing: in a real codebase, the model is the smaller variable. The layer of context and tooling you wire around the agent matters more than which version of Sonnet or Opus is behind it.&lt;/p&gt;
&lt;p&gt;The post stays high-level, which is the right move for a launch piece. What I want to do here is land it. Same seven pieces, but with the wiring you would actually put in a repo, in the order I would put it.&lt;/p&gt;
&lt;h2 id="how-claude-code-navigates-without-an-index"&gt;How Claude Code navigates without an index&lt;/h2&gt;
&lt;p&gt;Anthropic&amp;rsquo;s writeup says Claude Code works from the live codebase and does not require a &lt;a href="https://claude.com/blog/how-claude-code-works-in-large-codebases-best-practices-and-where-to-start"&gt;codebase index&lt;/a&gt; to be built, maintained, or uploaded. The agent navigates the way an engineer would, with &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, file reads, and reference-following. Anthropic calls this &lt;a href="https://www.anthropic.com/claude-code"&gt;agentic search&lt;/a&gt;, and the upside is obvious: no separate index exists for you to keep fresh.&lt;/p&gt;
&lt;p&gt;The downside is also obvious. An engineer who has never seen your repo and only has shell tools will flounder if you drop them in the root with no map. That is your agent on day one. Everything that follows is about giving it the map.&lt;/p&gt;
&lt;h2 id="the-ai-layer-in-seven-pieces"&gt;The AI layer in seven pieces&lt;/h2&gt;
&lt;p&gt;Every codebase used to have two artifacts engineers cared about: the code and the tests. A third exists now. Call it the AI layer, or the harness, or whatever you want. This layer is the set of context and tools you give your coding agent to operate in this specific repo. Anthropic breaks it into seven pieces, and each one solves a different scaling problem.&lt;/p&gt;
&lt;p&gt;&lt;img src="./harness-layers.png" alt="Three layers of a codebase: code, tests, and the AI layer expanded into CLAUDE.md, hooks, skills, plugins, LSP, MCP servers, and subagents."&gt;&lt;/p&gt;
&lt;p&gt;Anthropic gives each piece a role: CLAUDE.md is the foundation, hooks do self-improvement, skills are progressive disclosure, plugins handle distribution, LSP gives navigation, MCP is extension, subagents split exploration from editing. They are not equal in usage either. CLAUDE.md is read at the start of each session and stays in context for the duration. The others fire when relevant.&lt;/p&gt;
&lt;h2 id="lean-and-layered-claudemd"&gt;Lean and layered CLAUDE.md&lt;/h2&gt;
&lt;p&gt;The single biggest mistake I see is a root &lt;code&gt;CLAUDE.md&lt;/code&gt; that has grown into a small book. Two thousand lines of conventions for parts of the repo the current task will never touch. Every session pays the tax. Anthropic&amp;rsquo;s own guidance is to keep these files focused on what applies broadly so they do not become a drag on performance, and you can feel that drag in practice: the agent gets cautious, slow, and oddly literal.&lt;/p&gt;
&lt;p&gt;Keep the root file lean. What is this repo, broadly. The tech stack. The commands the agent will need (&lt;code&gt;make test&lt;/code&gt;, &lt;code&gt;make lint&lt;/code&gt;, how to run the dev server). General conventions that apply everywhere. That is most of what belongs there.&lt;/p&gt;
&lt;p&gt;Local conventions go in subdirectory &lt;code&gt;CLAUDE.md&lt;/code&gt; files. When the agent starts in a subdirectory, Claude Code walks upward from the working directory and loads every &lt;code&gt;CLAUDE.md&lt;/code&gt; it finds on the way to the repo root, so root context is never lost and intermediate layers stack in the order you would expect. Claude Code can also discover files below the current working directory when it reads files in those subdirectories. That means &lt;code&gt;services/api/CLAUDE.md&lt;/code&gt; only joins the session when the work reaches that service. Same for &lt;code&gt;services/billing/&lt;/code&gt;, the frontend, the data layer.&lt;/p&gt;
&lt;p&gt;If you already know the task is scoped to one service, start the agent in that subdirectory. The working directory becomes the focus, and the agent stays out of unrelated code unless you tell it otherwise. Most of the time, you know.&lt;/p&gt;
&lt;p&gt;Two more cheap wins live in the same neighborhood. Scope the &lt;code&gt;make test&lt;/code&gt; and &lt;code&gt;make lint&lt;/code&gt; commands so the subdirectory version runs only the slice the agent is working in, instead of the whole repo on every change. And version-control your exclusion rules in &lt;code&gt;.claude/settings.json&lt;/code&gt; so the agent never reads &lt;code&gt;dist/&lt;/code&gt;, generated SDKs, or vendored code. Every file the agent skips is tokens you keep for the work that matters. If your directory layout is unconventional or has historical baggage, Anthropic also suggests adding a short codebase map to the root &lt;code&gt;CLAUDE.md&lt;/code&gt; so the agent has somewhere to anchor.&lt;/p&gt;
&lt;h2 id="hooks-that-make-the-harness-self-improving"&gt;Hooks that make the harness self-improving&lt;/h2&gt;
&lt;p&gt;Most teams use hooks as guardrails. Block edits in &lt;code&gt;vendor/&lt;/code&gt;, refuse to delete migrations, kill the run if a secret turns up in a diff. That is fine and you should do it. But hooks have a second life that almost no one uses, and that second life is the more interesting one.&lt;/p&gt;
&lt;p&gt;Both kinds register the same way, in &lt;code&gt;.claude/settings.json&lt;/code&gt;, against named events Claude Code fires during a session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;SessionStart&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;uv run --directory \&amp;#34;$CLAUDE_PROJECT_DIR\&amp;#34; python .claude/hooks/session_start_context.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Stop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;uv run --directory \&amp;#34;$CLAUDE_PROJECT_DIR\&amp;#34; python .claude/hooks/propose_claude_md.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A &lt;code&gt;SessionStart&lt;/code&gt; hook fires before the agent has done anything. Whatever the script prints to stdout is injected straight into the session as context, so you can preload the things the agent would otherwise have to spend a turn discovering: the current branch, the uncommitted diff, the last few commits. For a larger team you might fetch the Confluence or Notion page that owns the directory the engineer is working in. Every developer starts each session pre-oriented, with no manual setup.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;SessionStart hook — prints orientation Claude reads as session context.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CLAUDE_PROJECT_DIR&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;git&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;# Orientation&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;## Branch&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rev-parse&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--abbrev-ref&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;HEAD&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;## Uncommitted changes&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--porcelain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;(clean)&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;## Recent commits&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--oneline&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;Stop&lt;/code&gt; hook is the more interesting one. It fires when the agent finishes its turn. At that moment the session context is still fresh, the diff is still small, and you have a free shot at a question nobody asks: did anything I changed invalidate the rules I wrote down? Spawn a separate headless Claude session, hand it the diff and the relevant &lt;code&gt;CLAUDE.md&lt;/code&gt; files, ask it to propose updates, and write the result to a markdown review file. You read it when you are ready. The CLAUDE.md files stop going stale on their own.&lt;/p&gt;
&lt;p&gt;The trick is to make the hook itself cheap and dispatch the LLM call in the background, so the end of every turn does not block on a reflection:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Stop hook — dispatch a headless Claude reflection in the background.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# The reflector spawns its own headless Claude, whose Stop hook lands back&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# here. The lock prevents infinite recursion.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;REFLECT_LOCK&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CLAUDE_PROJECT_DIR&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;git&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;diff&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HEAD&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;REFLECT_LOCK&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;uv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;run&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.claude/hooks/reflect_claude_md.py&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;reflect_claude_md.py&lt;/code&gt; is the part that calls a headless &lt;code&gt;claude&lt;/code&gt; against the diff and writes &lt;code&gt;.claude/claude-md-review.md&lt;/code&gt;. You can grow it from twenty lines to two hundred without ever blocking the agent.&lt;/p&gt;
&lt;p&gt;The pattern that ties the two together: hooks let the harness improve itself in the background while you do the actual work.&lt;/p&gt;
&lt;h2 id="path-scoped-skills"&gt;Path-scoped skills&lt;/h2&gt;
&lt;p&gt;Skills are where the agent learns how to do a &lt;em&gt;thing&lt;/em&gt;. CLAUDE.md is conventions (&amp;ldquo;every route is registered here&amp;rdquo;). Skills are workflows (&amp;ldquo;here is how you add a new route in this repo, end to end&amp;rdquo;). The two overlap, but the framing keeps me honest: rules in CLAUDE.md, recipes in skills.&lt;/p&gt;
&lt;p&gt;The piece of the skills system most teams miss is the path scope. A skill can declare which directories it activates in. A &lt;code&gt;create-api-endpoint&lt;/code&gt; skill that only loads when the agent is editing under &lt;code&gt;services/api/&lt;/code&gt; is invisible the rest of the time. With dozens of skills in a real repo, scoping is the difference between a useful library and a wall of irrelevant prompts.&lt;/p&gt;
&lt;p&gt;The mental model: progressive disclosure for expertise. Most knowledge in a large codebase is local. Load it locally.&lt;/p&gt;
&lt;h2 id="symbol-level-search-through-lsp-and-mcp"&gt;Symbol-level search through LSP and MCP&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; is fine until it isn&amp;rsquo;t. Past six-digit line counts, plain string search gets slow, returns too much, and burns tokens reading files the agent did not need to open. You also lose what every IDE has done for decades: jump-to-definition, find-references, hover-for-types.&lt;/p&gt;
&lt;p&gt;You can give the agent the same navigation. Run a language server locally, wrap it in a small MCP server, expose two or three tools: &lt;code&gt;where_is&lt;/code&gt;, &lt;code&gt;find_references&lt;/code&gt;, &lt;code&gt;goto_definition&lt;/code&gt;. The agent now searches by symbol, not by string. A request like &amp;ldquo;find every place &lt;code&gt;monthly_total_cents&lt;/code&gt; is referenced&amp;rdquo; returns one definition and the actual references, instead of fifty grep hits that mention the substring in unrelated comments.&lt;/p&gt;
&lt;p&gt;This is also where bigger orgs invest. Custom MCP servers that expose internal search systems, the code-ownership graph, the design-doc index. The patterns are the same; the targets are domain-specific. The point is that the agent does not have to brute-force its way through your repo when you already have better tools for finding things.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="./harness-frequency.png" alt="The Claude Code harness as a Gantt-style timeline across a session. CLAUDE.md spans the full session as a foundation. Hooks fire at the start and end. Skills, LSP, and MCP servers light up sporadically. Subagents run as a single longer task between exploration and editing."&gt;
&lt;figcaption&gt;&lt;em&gt;Image: &lt;a href="https://claude.com/blog/how-claude-code-works-in-large-codebases-best-practices-and-where-to-start"&gt;Anthropic, How Claude Code Works in Large Codebases&lt;/a&gt;.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="subagents-for-exploration"&gt;Subagents for exploration&lt;/h2&gt;
&lt;p&gt;The rule I follow: split exploration from editing. A subagent runs in its own context window. You ask which files implement the billing webhook flow, or what the user model looks like across services. It does the digging, and only the summary comes back to your primary session.&lt;/p&gt;
&lt;p&gt;The win is context budget, not parallelism. Exploration is wasteful by nature. The agent reads forty files to find the three that matter, and most of those forty get thrown away. If that happens in your primary session, your editing turns start with a context window already half full of noise. If it happens in a subagent, the noise stays there. You get the answer.&lt;/p&gt;
&lt;p&gt;Use the built-in Explore subagent liberally. Custom subagents earn their place when you have a workflow specific enough that a generic explorer is the wrong tool. The file shape is small: a single markdown file under &lt;code&gt;.claude/agents/&lt;/code&gt;, a short frontmatter block, and a prompt body. &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;, and &lt;code&gt;model&lt;/code&gt; are enough to start:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;name: explorer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;description: Read-only repo explorer. Map a service or package without burning the main session&amp;#39;s context, then return findings.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tools: Read, Grep, Glob
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;model: sonnet
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;You are a read-only explorer. The parent agent will hand you one service or
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;package to map. Read its &lt;span class="sb"&gt;`CLAUDE.md`&lt;/span&gt; if there is one, then trace entry points,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;the public surface, and dependencies. Return findings as your final response.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No edits.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restricting &lt;code&gt;tools&lt;/code&gt; to read-only is the load-bearing line. The model only sees the tools you expose, so an explorer subagent without &lt;code&gt;Write&lt;/code&gt; or &lt;code&gt;Edit&lt;/code&gt; has nothing to call when it gets tempted, even if the prompt body forgot to say so. Treat that as a strong default. If you need a hard guarantee, layer a &lt;code&gt;PreToolUse&lt;/code&gt; hook on top.&lt;/p&gt;
&lt;h2 id="dont-let-it-rot"&gt;Don&amp;rsquo;t let it rot&lt;/h2&gt;
&lt;p&gt;The harness is not a one-time setup. Models improve, and rules written for last year&amp;rsquo;s model often constrain this year&amp;rsquo;s. A note like &amp;ldquo;always split refactors into single-file changes&amp;rdquo; might have saved you in 2024 and might block a beneficial cross-file edit in 2026. Anthropic suggests reviewing your CLAUDE.md files every three to six months, or whenever performance feels like it has plateaued after a major model release. The stop-hook reflection gives you a head start. The rest is on you.&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Extend the harness to your stack&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;Package your infrastructure workflows as Agent Skills and expose your Pulumi state through the Pulumi MCP server, so your coding agent works your stack the way your team does.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/docs/ai/skills/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Get started
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id="assign-an-owner"&gt;Assign an owner&lt;/h2&gt;
&lt;p&gt;The last piece is not technical. The teams that get value out of Claude Code at scale have someone who owns the harness. A small platform-engineering team, or one DRI, or a hybrid PM/engineer doing it half-time. Their job is the same shape as owning a CI pipeline: write the conventions, build the skills, run the LSP wrapper, version the hooks, evangelize what works, retire what does not.&lt;/p&gt;
&lt;p&gt;Plugins are the distribution vehicle. A good harness that lives in one engineer&amp;rsquo;s dotfiles stays tribal. The same harness packaged as a plugin (or a private marketplace) is how a team of five hundred ends up running the same skills, the same MCP servers, and the same hooks without anyone having to remember to copy a config.&lt;/p&gt;
&lt;p&gt;The pattern that fails: ship Claude Code to the org on a Friday, hope adoption goes viral, watch every team grow its own slightly different version of &lt;code&gt;CLAUDE.md&lt;/code&gt; for six months. The pattern that works: a quiet build-out period, a small set of approved skills, a working plugin or two, a documented governance story, then broad access.&lt;/p&gt;
&lt;p&gt;Treat the harness like infrastructure.&lt;/p&gt;
&lt;h2 id="where-to-start"&gt;Where to start&lt;/h2&gt;
&lt;p&gt;The order that has worked for me, in any repo:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Trim the root &lt;code&gt;CLAUDE.md&lt;/code&gt; until it fits on one screen. Move the rest into subdirectories.&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Stop&lt;/code&gt; hook that proposes updates to those &lt;code&gt;CLAUDE.md&lt;/code&gt; files in headless mode.&lt;/li&gt;
&lt;li&gt;Convert your three most common repeated tasks into path-scoped skills.&lt;/li&gt;
&lt;li&gt;Run a language server behind an MCP server. Stop searching by string.&lt;/li&gt;
&lt;li&gt;Get comfortable dispatching exploration to subagents.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most teams will plateau on step one for a week and find the agent is already noticeably sharper. The rest compounds. I have written more on the agent-tooling shift this is part of in &lt;a href="https://www.pulumi.com/blog/how-building-ai-agents-has-changed/"&gt;How Building AI Agents Has Changed in 2026&lt;/a&gt;, and on the workflow side in &lt;a href="https://www.pulumi.com/blog/top-8-claude-skills-devops-2026/"&gt;The Claude Skills I Actually Use for DevOps&lt;/a&gt; and &lt;a href="https://www.pulumi.com/blog/claude-code-orchestration-frameworks/"&gt;Superpowers, GSD, and GSTACK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The model will keep getting better. The harness is the work.&lt;/p&gt;</description><author>Engin Diri</author><category>ai</category><category>ai-agents</category><category>claude</category><category>mcp</category><category>devops</category></item><item><title>How Building AI Agents Has Changed in 2026</title><link>https://www.pulumi.com/blog/how-building-ai-agents-has-changed/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-building-ai-agents-has-changed/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-building-ai-agents-has-changed/index.png" /&gt;
&lt;p&gt;Twelve months ago, building an AI agent meant picking a framework, defining your tools, standing up a RAG pipeline, and writing a stack of glue code to wire it all together. That was the default playbook. The post-mortem on six months of work usually went the same way: half the time went into infrastructure that had nothing to do with the agent&amp;rsquo;s actual job.&lt;/p&gt;
&lt;p&gt;That isn&amp;rsquo;t where the work is anymore. Most of the middle layer is gone. The SDKs ship with the tools, the skills system replaced the upfront tool registry, and longer context windows pushed vector search out of the default slot it held all of last year.&lt;/p&gt;
&lt;p&gt;The shape is the same as a lot of infrastructure shifts before it. The hard thing got cheap, the cheap thing got expected, and the question moved up a level.&lt;/p&gt;
&lt;h2 id="the-old-playbook"&gt;The old playbook&lt;/h2&gt;
&lt;p&gt;A 2024 to 2025 agent project looked like this. You picked a framework, usually &lt;a href="https://www.langchain.com/"&gt;LangChain&lt;/a&gt;, &lt;a href="https://www.llamaindex.ai/"&gt;LlamaIndex&lt;/a&gt;, or an early version of &lt;a href="https://ai.pydantic.dev/"&gt;Pydantic AI&lt;/a&gt;. You wrote tool definitions, usually a wrapper around an API the agent would call. You stood up a RAG pipeline: chunk your documents, embed them, pick a vector database, write retrievers, layer reranking on top. Then you wrote the agent loop yourself, including prompt assembly, tool dispatch, retry logic, and observability.&lt;/p&gt;
&lt;p&gt;This was the default for good reasons. Foundation models had short context windows. They didn&amp;rsquo;t ship with file access. They couldn&amp;rsquo;t run code. If you wanted an agent to do anything useful with your data, you had to bring the data to the model in pre-digested chunks.&lt;/p&gt;
&lt;p&gt;The cost wasn&amp;rsquo;t only setup time. It was infra bills, retries against embedding APIs, and a context strategy that fought the model as the model got better. By mid-2025 the retrieval layer was often the bottleneck on quality. The agent would ask a question, get five plausible-looking chunks, and answer from those instead of the document you actually wanted it to read. Chunking decisions made on a Tuesday in March were still hurting answer quality six months later.&lt;/p&gt;
&lt;p&gt;Most teams I talked to in 2025 were tuning their RAG pipeline. Almost nobody enjoyed it.&lt;/p&gt;
&lt;h2 id="the-shift-three-things-changed-at-once"&gt;The shift: three things changed at once&lt;/h2&gt;
&lt;p&gt;Three changes landed close enough together that they collapsed the middle layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Built-in tools.&lt;/strong&gt; The &lt;a href="https://code.claude.com/docs/en/agent-sdk/overview"&gt;Claude Agent SDK&lt;/a&gt; ships with Read, Write, Edit, Bash, Grep, Glob, WebSearch, and WebFetch out of the box. &lt;a href="https://developers.openai.com/codex/sdk"&gt;OpenAI&amp;rsquo;s Codex SDK&lt;/a&gt; is similar in shape, with shell and file tools available to the agent by default. These are the tools every agent project was rebuilding in 2024, often as a side quest to the work the agent was actually meant to do. A &lt;code&gt;Read&lt;/code&gt; that handles binary files. A &lt;code&gt;Bash&lt;/code&gt; that streams output and respects working directory. A &lt;code&gt;Grep&lt;/code&gt; that doesn&amp;rsquo;t choke on large files. The 80% of agent tooling everyone was paying their team to reimplement is now table stakes.&lt;/p&gt;
&lt;p&gt;The consequence is that you can give an agent the ability to do real work with about ten lines of configuration. The flip side is that the differentiator moved up a layer. The value isn&amp;rsquo;t in having &lt;code&gt;Read&lt;/code&gt;. It&amp;rsquo;s in what the agent does with it.&lt;/p&gt;
&lt;p&gt;Anything outside the built-in toolbox plugs in through &lt;a href="https://modelcontextprotocol.io/"&gt;MCP&lt;/a&gt; servers. &lt;a href="https://thenewstack.io/model-context-protocol-roadmap-2026/"&gt;The registry has grown nearly 8x since early 2025&lt;/a&gt;, and every major model vendor now ships first-party support. The picture in 2026 is more layered than that, though. A lot of what used to call for an MCP server is now better served by the agent invoking a CLI through &lt;code&gt;Bash&lt;/code&gt; and wrapping the recipe in a skill. &lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use"&gt;Benchmarks put CLI-based tool calls at a fraction of the context cost of equivalent MCP calls&lt;/a&gt;, with fewer round-trips and fewer failure modes. MCP still earns its place for protocol-heavy work like browser control, OAuth flows, and streaming services, but it stopped being the automatic answer to &amp;ldquo;how do I give my agent a new capability.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Skills replaced tool stuffing.&lt;/strong&gt; The old way was to register every tool the agent might need at startup, eating context every turn whether the agent used the tool or not. A hundred tools meant a heavy system prompt before the agent had thought about anything. The skills pattern flips that. A skill is a small markdown package with a name and a one-line description. The agent sees the description (around 100 tokens) and only loads the body when it decides the skill is relevant. A hundred skills no longer means a hundred tools&amp;rsquo; worth of context tax. &lt;a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills"&gt;Anthropic frames this as progressive disclosure&lt;/a&gt;: because the body only loads on demand, the amount of content you can bundle into a single skill is effectively unbounded.&lt;/p&gt;
&lt;p&gt;Progressive disclosure isn&amp;rsquo;t a new idea. What&amp;rsquo;s new is that the agent harness now treats it as the default loading strategy instead of something you have to engineer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RAG got demoted.&lt;/strong&gt; This is the change with the biggest blast radius and the smallest amount of commentary. A year ago, &amp;ldquo;we need to add RAG&amp;rdquo; was the reflex answer when somebody asked how an agent would handle a corpus. Today that question splits three ways. If the corpus fits in the context window, put it in. If the agent can grep the filesystem, let it grep. If the corpus is genuinely too large for either, vector search is still right, but you&amp;rsquo;ll find that&amp;rsquo;s a smaller set of cases than it used to be. You can see this in the coding agents that already ship today. &lt;a href="https://cursor.com/blog/fast-regex-search"&gt;Cursor&lt;/a&gt;, Claude Code, and Devin lean on grep, find, and direct file reads more than vector search. &lt;a href="https://www.llamaindex.ai/blog/agentic-rag-with-llamaindex-2721b8a49ff6"&gt;LlamaIndex&amp;rsquo;s own writing on agentic retrieval&lt;/a&gt; is one of the clearer reads on where this is going.&lt;/p&gt;
&lt;p&gt;Vector search didn&amp;rsquo;t get worse. The context around it improved enough that it stopped being the right first move.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-building-ai-agents-has-changed/eras.png" alt="Three eras of agent building. 2024 to 2025: framework plus tools plus RAG plus glue code. Early 2026: SDK with built-in tools plus skills plus MCP. Today: same SDK base, with frameworks layered on only for multi-provider or complex orchestration."&gt;&lt;/p&gt;
&lt;p&gt;Taken together, what got pulled into the SDK is the middle of an agent project: the tools layer, the retrieval layer, and the loop. What&amp;rsquo;s left for the team is the system prompt, the skills, and the policies around what the agent is allowed to do.&lt;/p&gt;
&lt;h2 id="when-you-still-need-a-framework"&gt;When you still need a framework&lt;/h2&gt;
&lt;p&gt;The first reaction to a lot of this is to declare that frameworks are over. They aren&amp;rsquo;t, but the cases where you reach for one have narrowed.&lt;/p&gt;
&lt;p&gt;Pydantic AI is still the right choice when you want strong typing, deterministic output schemas, and an evaluation loop that matches how the rest of your Python codebase already thinks. &lt;a href="https://www.langchain.com/langgraph"&gt;LangGraph&lt;/a&gt; is still the right choice when your problem is genuinely a graph of agent states with branching and human approval steps. &lt;a href="https://openai.github.io/openai-agents-python/"&gt;OpenAI&amp;rsquo;s Agents SDK&lt;/a&gt; is built around explicit handoffs between agents and earns its place when that pattern fits how you want to decompose the work. &lt;a href="https://www.crewai.com/"&gt;CrewAI&lt;/a&gt; is the fastest path I&amp;rsquo;ve seen for prototyping a multi-agent system, as long as you can live with its opinions. Any team running production traffic across multiple model providers is going to want a routing layer that the official SDK from any single vendor isn&amp;rsquo;t going to give them. &lt;a href="https://www.anthropic.com/research/building-effective-agents"&gt;Anthropic&amp;rsquo;s own writing on building effective agents&lt;/a&gt; lands in the same place: start with the simplest thing, add complexity only when the problem demands it.&lt;/p&gt;
&lt;p&gt;The mental model that works for me: start with the SDK, reach for a framework when you outgrow it. &amp;ldquo;Outgrow&amp;rdquo; usually means one of four things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multi-provider routing.&lt;/strong&gt; You&amp;rsquo;re running production traffic across more than one model vendor and need a routing layer the official SDKs don&amp;rsquo;t ship.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-agent orchestration.&lt;/strong&gt; Your problem genuinely decomposes into separate agents with handoffs, branching, or human approval steps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deterministic typing.&lt;/strong&gt; You need strong schemas and validation around inputs and outputs, and the rest of your codebase already thinks that way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production observability.&lt;/strong&gt; You need eval loops, replay, or tracing beyond what the SDK provides out of the box.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If none of those four are biting, the SDK is probably enough, and adding a framework on top is a layer you&amp;rsquo;ll regret in six months.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-building-ai-agents-has-changed/decision.png" alt="Decision flow for picking an agent stack. Start with the SDK. If you need multi-provider routing, multi-agent orchestration, deterministic typing, or deeper observability, add a framework. Otherwise stay with the SDK."&gt;&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Point your agent at your infrastructure&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;Give your coding agent grounded context from your Pulumi state graph through the Pulumi MCP server and Agent Skills, or reach for Pulumi Neo when you want an infrastructure agent built in.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/docs/ai/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Get started
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id="where-this-lands-for-infrastructure-work"&gt;Where this lands for infrastructure work&lt;/h2&gt;
&lt;p&gt;Two things from the new agent shape map cleanly onto infrastructure work. The first is that &amp;ldquo;built-in tools plus governed actions&amp;rdquo; is the model an IaC platform was already running. The SDK assumes the agent has tools that do real work. The platform assumes those tools have policies, audit logs, and short-lived credentials around them. Those assumptions stack.&lt;/p&gt;
&lt;p&gt;The second is that a state graph is already structured context. You don&amp;rsquo;t need to chunk it. You don&amp;rsquo;t need to embed it. An agent reasoning over a Pulumi stack can grep its way through the program graph the same way it greps a codebase, and the answers are grounded in the same source of truth the rest of your platform uses. I wrote the deeper version in &lt;a href="https://www.pulumi.com/blog/grounded-ai-why-neo-knows-your-infrastructure/"&gt;Grounded AI: Why Neo Knows Your Infrastructure&lt;/a&gt;. The dark-factory and sprawl posts (&lt;a href="https://www.pulumi.com/blog/dark-factory-pattern-pulumi-autonomous-iac/"&gt;The Dark Factory Pattern for Infrastructure&lt;/a&gt; and &lt;a href="https://www.pulumi.com/blog/agent-sprawl-iac-platform-is-the-answer/"&gt;Agent Sprawl Is Here. Your IaC Platform Is the Answer.&lt;/a&gt;) are the places to go if you want to push on this further.&lt;/p&gt;
&lt;h2 id="start-with-the-sdk"&gt;Start with the SDK&lt;/h2&gt;
&lt;p&gt;A year ago, an agent project was 80% glue code and 20% the thing the agent actually did. On most projects today that ratio is flipped. If you&amp;rsquo;ve been sitting on an agent idea, build it the SDK way first and reach for a framework only when you hit something the SDK genuinely can&amp;rsquo;t do. Most teams will be surprised how often they don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s one agent you don&amp;rsquo;t have to build at all. &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt; is the same SDK-first shape applied to the IaC slice: tools that reason directly over your state graph, governed by the controls the rest of your platform already runs on. Save your own SDK time for the agents only you can build.&lt;/p&gt;
&lt;a
href="https://www.pulumi.com/product/neo/"
class="btn btn-primary"
&gt;
See how Pulumi Neo works
&lt;/a&gt;</description><author>Engin Diri</author><category>ai</category><category>ai-agents</category><category>claude</category><category>mcp</category><category>rag</category></item><item><title>Automatic Diagram Generation for Always-Accurate Diagrams</title><link>https://www.pulumi.com/blog/automating-diagramming-in-your-ci-cd/</link><pubDate>Mon, 22 Sep 2025 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/automating-diagramming-in-your-ci-cd/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/automating-diagramming-in-your-CI-CD/index.png" /&gt;
&lt;p&gt;Architecture diagrams are one of the most tedious aspects of infrastructure management. Teams spend countless hours manually creating and updating visual representations of their systems, only to see them become outdated the moment infrastructure changes.&lt;/p&gt;
&lt;p&gt;When you manage your infrastructure as code, you can automatically generate accurate, up-to-date diagrams directly from your IaC definitions and state. Pulumi&amp;rsquo;s preview capabilities let you anticipate exactly what changes will result from code modifications before deployment, providing the perfect foundation for automated diagramming workflows.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;rsquo;ll explore three practical approaches to automating diagram generation in your CI/CD pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Native Pulumi diagramming&lt;/strong&gt;: Using Pulumi&amp;rsquo;s built-in diagramming capabilities to generate basic architecture diagrams that you can retrieve from GitHub Actions&amp;rsquo; artifact archive&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI-powered diagramming&lt;/strong&gt;: Leveraging the Claude app with Mermaid and Pulumi preview to generate detailed diagrams and automatically add them to pull requests for infrastructure changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pulumi Neo for enterprise diagramming&lt;/strong&gt;: Using Pulumi&amp;rsquo;s AI-powered platform for organization-wide diagramming patterns, project-level and organization-level views, and intelligent customization for different stakeholders&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These approaches eliminate manual diagramming work while ensuring your documentation stays current with your actual infrastructure, from simple automation to enterprise-scale intelligent diagramming.&lt;/p&gt;
&lt;h2 id="native-pulumi-diagramming-with-github-actions"&gt;Native Pulumi diagramming with GitHub Actions&lt;/h2&gt;
&lt;p&gt;Pulumi&amp;rsquo;s built-in &lt;code&gt;pulumi stack graph&lt;/code&gt; command generates architecture diagrams directly from your deployed infrastructure state. This approach provides a reliable foundation for automated diagramming since it reflects your actual deployed resources rather than just the code.&lt;/p&gt;
&lt;div class="note note-info"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;strong&gt;Complete example&lt;/strong&gt;: View the full working example on &lt;a href="https://github.com/pulumi-demos/examples/tree/main/diagrams/native-pulumi-diagramming-capabilities"&gt;GitHub&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;rsquo;s a complete GitHub Actions workflow that deploys infrastructure and automatically generates diagrams:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Pulumi Deploy &amp;amp; Diagram&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;main]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Deploy &amp;amp; Generate Diagram&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Setup Node.js&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/setup-node@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;node-version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;20&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;npm&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Install dependencies&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;npm ci&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pulumi/actions@v5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;up&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;stack-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;PULUMI_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.PULUMI_ACCESS_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Install Pulumi CLI&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; curl -fsSL https://get.pulumi.com | sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;$HOME/.pulumi/bin&amp;#34; &amp;gt;&amp;gt; $GITHUB_PATH&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Generate Architecture Diagram&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pulumi stack graph architecture.dot --stack dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;PULUMI_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.PULUMI_ACCESS_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Install Graphviz&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y graphviz&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Generate PNG from DOT file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;dot -Tpng architecture.dot -o architecture.png&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Upload Architecture Diagrams&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/upload-artifact@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;architecture-diagrams&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; architecture.dot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; architecture.png&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="how-the-workflow-works"&gt;How the workflow works&lt;/h3&gt;
&lt;p&gt;This workflow follows a straightforward pattern that ensures your diagrams always reflect your deployed infrastructure:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deploy first&lt;/strong&gt;: The workflow uses the official &lt;code&gt;pulumi/actions@v5&lt;/code&gt; action to deploy your infrastructure changes. This ensures the state reflects your latest code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generate diagram from state&lt;/strong&gt;: The &lt;code&gt;pulumi stack graph&lt;/code&gt; command reads your deployed stack&amp;rsquo;s state and generates a DOT format file containing the resource relationships and dependencies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Convert to visual format&lt;/strong&gt;: Graphviz converts the DOT file into a PNG image that&amp;rsquo;s easy to view and share.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Archive as artifacts&lt;/strong&gt;: Both the raw DOT file and the rendered PNG are uploaded as GitHub Actions artifacts, making them downloadable from the workflow run.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="key-advantages-and-limitations"&gt;Key advantages and limitations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Highest accuracy&lt;/strong&gt;: Since diagrams are generated from deployed state rather than code, they reflect your actual infrastructure including any drift or manual changes. This is the biggest advantage of the native approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero maintenance&lt;/strong&gt;: Once set up, diagrams update automatically with every deployment without any manual intervention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multiple formats&lt;/strong&gt;: The DOT format can be converted to various output formats (PNG, SVG, PDF) depending on your needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit trail&lt;/strong&gt;: Each workflow run preserves the diagram as an artifact, creating a visual history of your infrastructure changes.&lt;/p&gt;
&lt;p&gt;However, this approach has some limitations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fixed styling&lt;/strong&gt;: You&amp;rsquo;re locked into Pulumi&amp;rsquo;s diagram style and layout algorithms, with limited customization options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic aesthetics&lt;/strong&gt;: The generated diagrams are functional but not always aesthetically polished compared to hand-crafted or AI-generated alternatives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Technical focus&lt;/strong&gt;: Diagrams show all resources and relationships, which can be overwhelming for non-technical stakeholders who need higher-level architectural views.&lt;/p&gt;
&lt;h3 id="accessing-your-diagrams"&gt;Accessing your diagrams&lt;/h3&gt;
&lt;p&gt;After each workflow run, you can download the generated diagrams from the Actions tab in your repository. Click on any workflow run, scroll to the &amp;ldquo;Artifacts&amp;rdquo; section, and download the &lt;code&gt;architecture-diagrams&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;For a more sophisticated setup, you could extend this workflow to automatically commit diagrams back to your repository, post them to Slack, or integrate them with your documentation system.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/automating-diagramming-in-your-ci-cd/architecture.png" alt="Pulumi Natively Generated Diagram"&gt;&lt;/p&gt;
&lt;h2 id="ai-powered-diagramming-with-claude-and-mermaid"&gt;AI-powered diagramming with Claude and Mermaid&lt;/h2&gt;
&lt;p&gt;The second approach leverages Claude&amp;rsquo;s ability to analyze Pulumi preview output and generate aesthetically pleasing, contextually relevant diagrams using Mermaid syntax. This method addresses the styling limitations of native Pulumi diagrams while maintaining accuracy through &lt;code&gt;pulumi preview&lt;/code&gt; integration.&lt;/p&gt;
&lt;div class="note note-info"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;strong&gt;Complete example&lt;/strong&gt;: View the full working example on &lt;a href="https://github.com/pulumi-demos/examples/tree/main/diagrams/claude-and-mermaid-generation"&gt;GitHub&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="the-automated-workflow"&gt;The automated workflow&lt;/h3&gt;
&lt;p&gt;This approach uses two GitHub Actions workflows working together:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Auto-comment workflow&lt;/strong&gt; (&lt;code&gt;auto-pr-comment.yml&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Auto PR Comment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;opened]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;index.ts&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;Pulumi.yaml&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;Pulumi.*.yaml&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github.actor != &amp;#39;claude-code[bot]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull-requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Comment on PR&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/github-script@v7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;github-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.PERSONAL_ACCESS_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; github.rest.issues.createComment({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; issue_number: context.issue.number,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; owner: context.repo.owner,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; repo: context.repo.repo,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; body: &amp;#39;@claude update the diagrams to reflect any infrastructure changes.&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; })&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Claude integration workflow&lt;/strong&gt; (&lt;code&gt;claude.yml&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Claude Code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;issue_comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;created]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull_request_review_comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;created]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;claude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;contains(github.event.comment.body, &amp;#39;@claude&amp;#39;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull-requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run Claude Code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;anthropics/claude-code-action@beta&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;PULUMI_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.PULUMI_ACCESS_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;anthropic_api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;allowed_tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Bash(npm install),Bash(pulumi stack select prod),Bash(pulumi preview --json),Bash(npx mmdc)&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;custom_instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ## Pulumi Infrastructure Diagramming Assistant
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ### Step 1: Analyze Infrastructure State
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Run `npm install` and `pulumi stack select prod`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Execute `pulumi preview --json` to get detailed resource plan
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - Parse JSON output to understand resource relationships and changes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ### Step 2: Generate/Update Diagrams
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Update Mermaid diagram files in `/diagrams/`:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - aws-infrastructure.mmd: Technical AWS resource diagram
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - c4-context.mmd: System context diagram
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - c4-container.mmd: Container-level view
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - c4-component.mmd: Component-level architecture
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ### Step 3: Generate SVG Files
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Convert .mmd files to .svg using mmdc CLI&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="how-the-ai-approach-works"&gt;How the AI approach works&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatic triggering&lt;/strong&gt;: When a pull request with infrastructure changes is opened, the auto-comment workflow adds a &lt;code&gt;@claude&lt;/code&gt; mention, triggering Claude&amp;rsquo;s analysis.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure analysis&lt;/strong&gt;: Claude runs &lt;code&gt;pulumi preview --json&lt;/code&gt; to get detailed information about planned infrastructure changes, including resource relationships and dependencies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Intelligent diagram generation&lt;/strong&gt;: Claude analyzes the preview output and updates multiple Mermaid diagrams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS infrastructure diagram&lt;/strong&gt;: Technical view showing resources, networking, and data flow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C4 diagrams&lt;/strong&gt;: Context, container, and component views for different stakeholder needs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visual rendering&lt;/strong&gt;: The &lt;code&gt;mmdc&lt;/code&gt; CLI converts Mermaid syntax to SVG files for immediate viewing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PR integration&lt;/strong&gt;: Updated diagrams are committed directly to the pull request branch, making them visible in code review.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="example-generated-diagram"&gt;Example generated diagram&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a sample of the AWS infrastructure diagram that Claude generates:&lt;/p&gt;
&lt;pre class="mermaid"&gt;
graph TB
subgraph VPC[&amp;#34;myVpc - CIDR: 10.0.0.0/16&amp;#34;]
subgraph PublicSubnet[&amp;#34;myPublicSubnet - CIDR: 10.0.1.0/24&amp;#34;]
EC2[&amp;#34;myInstance&amp;lt;br/&amp;gt;Type: t2.micro&amp;lt;br/&amp;gt;AMI: Latest Amazon Linux 2&amp;#34;]
end
IGW[&amp;#34;myInternetGateway&amp;lt;br/&amp;gt;Internet connectivity&amp;#34;]
SG[&amp;#34;mySecurityGroup&amp;lt;br/&amp;gt;HTTP (80) + SSH (22) access&amp;#34;]
end
WEB_USER[&amp;#34;Web Users&amp;#34;] --&amp;gt;|HTTP requests| IGW
IGW --&amp;gt; PublicSubnet
SG --&amp;gt; EC2
&lt;/pre&gt;
&lt;h3 id="key-advantages-and-limitations-1"&gt;Key advantages and limitations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Intelligent styling&lt;/strong&gt;: Claude generates aesthetically pleasing diagrams with consistent formatting, colors, and layout that&amp;rsquo;s much more polished than native Pulumi output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multiple perspectives&lt;/strong&gt;: Creates different diagram types (technical infrastructure, C4 architecture views) suitable for different audiences and stakeholder needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Context awareness&lt;/strong&gt;: Claude understands the purpose and relationships of resources, creating meaningful groupings and flow representations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automated maintenance&lt;/strong&gt;: Diagrams update automatically with infrastructure changes, staying current without manual intervention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integration with code review&lt;/strong&gt;: Diagrams appear directly in pull requests, making infrastructure changes visible during review.&lt;/p&gt;
&lt;p&gt;However, this approach also has limitations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Potential for inaccuracy&lt;/strong&gt;: Unlike native Pulumi diagrams that are generated directly from infrastructure state, AI-generated diagrams depend on Claude&amp;rsquo;s interpretation of preview output. While generally reliable, there&amp;rsquo;s inherent risk of misinterpretation or missed details that could lead to diagram inaccuracies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI dependency&lt;/strong&gt;: Diagram accuracy is only as good as Claude&amp;rsquo;s understanding of complex infrastructure relationships, which may occasionally miss nuances or make incorrect assumptions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup complexity&lt;/strong&gt;: Requires more configuration including API keys, GitHub app installation, and workflow permissions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost considerations&lt;/strong&gt;: Each diagram update consumes Claude API credits, though costs are typically minimal for most projects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Preview limitations&lt;/strong&gt;: Diagrams reflect planned changes from &lt;code&gt;pulumi preview&lt;/code&gt; rather than actual deployed state, which may differ if deployments fail.&lt;/p&gt;
&lt;p&gt;This approach works best for teams that value diagram aesthetics and need multiple architectural views for different stakeholders, while accepting slightly more complexity and potential accuracy trade-offs in exchange for significantly better visual presentation.&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Generate diagrams from your code&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;You can produce accurate architecture diagrams directly from your Pulumi state and preview, then wire the workflow into any coding agent through the Pulumi MCP server. Start a project to keep your diagrams in sync with your infrastructure.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/docs/ai/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Get started
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id="pulumi-neo-enterprise-scale-intelligent-diagramming"&gt;Pulumi Neo: Enterprise-scale intelligent diagramming&lt;/h2&gt;
&lt;p&gt;Pulumi Neo brings the next evolution in infrastructure diagramming, with AI-powered intelligence that enables organization-wide diagram generation. Unlike the previous approaches that focus on individual stacks, Pulumi Neo provides enterprise-scale diagramming capabilities with intelligent customization for different stakeholders.&lt;/p&gt;
&lt;h3 id="how-the-workflow-works-1"&gt;How the workflow works&lt;/h3&gt;
&lt;p&gt;Pulumi Neo streamlines enterprise diagramming through an intelligent, organization-aware approach that scales across multiple projects and stakeholders:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Repository setup&lt;/strong&gt;: Create a dedicated repository to host your architecture diagrams and ensure your &lt;a href="https://www.pulumi.com/docs/iac/using-pulumi/continuous-delivery/github-app/"&gt;GitHub App&lt;/a&gt; has access. The repository structure helps Neo understand your diagramming patterns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;overview/&lt;/code&gt; folder for organization-wide infrastructure views&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stacks/&lt;/code&gt; folder for individual stack diagrams&lt;/li&gt;
&lt;li&gt;Template files that Neo can model new diagrams from&lt;/li&gt;
&lt;li&gt;README documentation explaining the repository&amp;rsquo;s purpose and conventions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/automating-diagramming-in-your-ci-cd/folder_structure.png" alt="Folder Structure"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neo prompt engineering&lt;/strong&gt;: Use structured prompts to guide Neo&amp;rsquo;s diagram generation with enterprise-grade specificity. You can also select your architecture diagram repository to tell Neo where to store his work:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. Scope: Create diagrams for all resources in my prod Pulumi stacks
1. Diagram Types:
- Individual stack diagrams showing resources within each stack
- Master overview diagram showing cross-stack dependencies and data flow
1. Visual Standards:
- Use official AWS/Azure/GCP service icons from their architecture icon sets
- Show resource relationships with labeled connections (security groups, subnets, etc.)
- Group related resources visually (VPC boundaries, availability zones)
1. Content Focus:
- Infrastructure resources only (compute, storage, networking, databases)
- Resource dependencies and data flow directions
- Cross-stack references and shared resources
1. Format: Generate diagrams as Mermaid code that can be rendered in documentation
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/automating-diagramming-in-your-ci-cd/neo_prompt.png" alt="Neo Prompt"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automated pull request generation&lt;/strong&gt;: Neo analyzes your entire organization&amp;rsquo;s infrastructure state, identifies changes, and automatically creates pull requests with updated diagrams for review and approval.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="note note-info"&gt;
&lt;div class="icon-and-line"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--fill" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-info-fill"/&gt;&lt;/svg&gt;
&lt;div class="line"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="content"&gt;&lt;strong&gt;Complete example&lt;/strong&gt;: You can see my resulting architecture diagrams repository on &lt;a href="https://github.com/pulumi-csa/Architecture-Diagrams"&gt;GitHub&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="key-advantages-and-limitations-2"&gt;Key advantages and limitations&lt;/h3&gt;
&lt;p&gt;Pulumi Neo offers the most sophisticated approach to infrastructure diagramming, particularly for enterprise environments:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enterprise-scale intelligence&lt;/strong&gt;: Neo understands your entire organization&amp;rsquo;s infrastructure landscape, generating diagrams that show cross-project dependencies and organization-wide resource relationships that individual stack-based approaches cannot capture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Style customization&lt;/strong&gt;: Customize your prompts to request different styles of diagrams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Organizational consistency&lt;/strong&gt;: Maintains consistent diagramming standards across teams and projects, ensuring visual coherence in large organizations with multiple infrastructure teams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-stack visibility&lt;/strong&gt;: Unlike single-stack approaches, Neo provides organization-wide infrastructure views showing how different projects and teams&amp;rsquo; resources interconnect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Template-driven generation&lt;/strong&gt;: Learns from your existing diagram patterns and templates to maintain consistency with your established visual standards and conventions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-powered context understanding&lt;/strong&gt;: Goes beyond simple resource mapping to understand business context, automatically highlighting critical paths, security boundaries, and architectural patterns.&lt;/p&gt;
&lt;p&gt;However, Neo also has limitations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI interpretation risks&lt;/strong&gt;: Like the Claude approach, AI-generated diagrams may miss nuances or make incorrect assumptions about complex enterprise infrastructure relationships.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repository access requirements&lt;/strong&gt;: Neo requires permissions to open pull requests against your repository.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning investment&lt;/strong&gt;: Getting maximum value from Neo requires developing sophisticated prompt engineering skills and understanding how to structure repositories for optimal AI comprehension.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Automating diagram generation transforms one of the most tedious aspects of infrastructure management into a seamless part of your development workflow. All three approaches eliminate the manual overhead of keeping architecture diagrams current while solving the core problem of documentation drift.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choose native Pulumi diagramming&lt;/strong&gt; when accuracy is paramount and you need diagrams that perfectly reflect your deployed infrastructure. This approach offers the highest reliability with minimal setup complexity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choose AI-powered diagramming&lt;/strong&gt; when you need polished, stakeholder-friendly diagrams and can accept slightly more complexity for significantly better aesthetics and multiple architectural perspectives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choose Pulumi Neo&lt;/strong&gt; when you need enterprise-scale diagramming with organization-wide consistency, multi-level views across projects and stacks, and intelligent customization for different stakeholders with varying technical backgrounds.&lt;/p&gt;
&lt;p&gt;Regardless of which approach you choose, you&amp;rsquo;ll eliminate hours of manual work while ensuring your documentation stays synchronized with your infrastructure changes. With Pulumi Neo&amp;rsquo;s AI capabilities now available, intelligent, context-aware diagramming has moved from possibility to reality, making infrastructure documentation both effortless and incredibly powerful.&lt;/p&gt;
&lt;p&gt;The future of infrastructure documentation is here: automated, intelligent, and always accurate. With these examples as your foundation, you&amp;rsquo;re ready to transform how your team approaches architecture visualization at any scale.&lt;/p&gt;</description><author>Elisabeth Lichtie</author><category>aws</category><category>diagram</category><category>architecture</category><category>ci/cd</category><category>anthropic</category><category>claude</category><category>github</category><category>actions</category></item></channel></rss>