<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Austen Stone</title>
        <link>https://austen.info/blog</link>
        <description>GitHub Actions, AI, automation, and things I build.</description>
        <lastBuildDate>Tue, 13 Oct 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © 2026 Austen Stone</copyright>
        <item>
            <title><![CDATA[Stop Checking Out Other People's Repos in CI]]></title>
            <link>https://austen.info/blog/couple-to-artifacts-not-source</link>
            <guid>https://austen.info/blog/couple-to-artifacts-not-source</guid>
            <pubDate>Tue, 13 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Most multi-repo CI pain comes from one question: how does my build get that other repo's code? Wrong question. Couple repos at the artifact and contract level instead, and most of the PAT sprawl, flaky integration builds, and monorepo-vs-polyrepo arguments go away.]]></description>
            <content:encoded><![CDATA[<p>Almost every enterprise CI conversation I have starts with the same question: "my build needs code from another repo, how do I get access to it?"</p>
<p>That's the wrong question. And answering it is how you end up with an org-wide PAT checking out 14 repos at <code>main</code> on every build.</p>
<!-- -->
<p>The better question is <strong>what does that repo publish?</strong> And right behind it, <strong>who runs whose tests?</strong> Answer those two and the rest of your pipeline design mostly falls out.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Default-Everyone-Builds">The Default Everyone Builds<a href="https://austen.info/blog/couple-to-artifacts-not-source#The-Default-Everyone-Builds" class="hash-link" aria-label="Direct link to The Default Everyone Builds" title="Direct link to The Default Everyone Builds" translate="no">​</a></h2>
<p>It usually looks like this:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> actions/checkout@v7</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">repository</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> acme/shared</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">lib</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">ref</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> main</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> secrets.ORG_WIDE_PAT </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> shared</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">lib</span><br></div></code></pre></div></div>
<p>Multiply by however many repos your build "needs." What you've actually built:</p>
<ul>
<li class=""><strong>Source coupling.</strong> Your build depends on whatever happens to be on someone else's <code>main</code> right now. They merge, you break, and nobody on their team ever sees the red.</li>
<li class=""><strong>A non-reproducible build.</strong> Rerun yesterday's job and you get different code. Good luck bisecting.</li>
<li class=""><strong>A long-lived secret with way too much reach.</strong> That PAT is tied to a person (or a "bot" account someone shares), can usually read far more than this one build needs, and lives forever. Exactly the kind of credential that <a class="" href="https://austen.info/blog/beyond-the-oidc-silver-bullet">shows up in supply chain incidents</a>.</li>
</ul>
<p>None of this is specific to GitHub Actions. You'd build the same mess in Jenkins.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Couple-to-Artifacts-and-Contracts">Couple to Artifacts and Contracts<a href="https://austen.info/blog/couple-to-artifacts-not-source#Couple-to-Artifacts-and-Contracts" class="hash-link" aria-label="Direct link to Couple to Artifacts and Contracts" title="Direct link to Couple to Artifacts and Contracts" translate="no">​</a></h2>
<!-- -->
<p>Left side, every repo is part of every other repo's build. Right side, each repo has one job: build, test, publish a versioned thing. Consumers pin a version. Upgrades are PRs you can review and revert.</p>
<p>Honestly this is also why monorepo vs polyrepo is mostly the wrong argument. Big monorepos work because everything builds and tests together at one commit. Polyrepos work when repos talk through versioned contracts. What doesn't work is polyrepos pretending to be a monorepo by cloning each other at <code>main</code>. You get the costs of both and the benefits of neither.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-Consume-Artifacts-Not-Source">1. Consume Artifacts, Not Source<a href="https://austen.info/blog/couple-to-artifacts-not-source#1-Consume-Artifacts-Not-Source" class="hash-link" aria-label="Direct link to 1. Consume Artifacts, Not Source" title="Direct link to 1. Consume Artifacts, Not Source" translate="no">​</a></h2>
<p>If another team's code is something you build against, it should be a package. npm, Maven, NuGet, a container image, whatever fits. <a href="https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages" target="_blank" rel="noopener noreferrer" class="">GitHub Packages</a> and the <a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry" target="_blank" rel="noopener noreferrer" class="">Container registry</a> handle this fine, and the producer's workflow can push with <code>GITHUB_TOKEN</code> and <code>packages: write</code>. Consumers read with <code>GITHUB_TOKEN</code> too, once you <a href="https://docs.github.com/en/packages/learn-github-packages/configuring-a-packages-access-control-and-visibility#ensuring-workflow-access-to-your-package" target="_blank" rel="noopener noreferrer" class="">grant their repos access to the package</a>. No extra credentials.</p>
<p>Consumers pin an exact version and let <a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates" target="_blank" rel="noopener noreferrer" class="">Dependabot</a> open the bump PRs. Point it at your <a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot" target="_blank" rel="noopener noreferrer" class="">private registries</a> and <a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/optimizing-pr-creation-version-updates#grouping-dependabot-updates-into-one-pull-request" target="_blank" rel="noopener noreferrer" class="">group the updates</a> so you don't drown in them. Now every upgrade runs your full CI before it lands, which is the whole point.</p>
<p>Sometimes you do need source (codegen, a vendored tool, whatever). Fine. Pin it to a full commit SHA, and if it matters enough, vendor it into your repo. Large monorepos do exactly this with third-party code for a reason. Never float on <code>main</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Auth-Without-PATs">2. Auth Without PATs<a href="https://austen.info/blog/couple-to-artifacts-not-source#2-Auth-Without-PATs" class="hash-link" aria-label="Direct link to 2. Auth Without PATs" title="Direct link to 2. Auth Without PATs" translate="no">​</a></h2>
<p>Once you're consuming artifacts, most cross-repo access disappears. For what's left, stop minting PATs.</p>
<ul>
<li class=""><strong>Sharing actions and reusable workflows</strong> doesn't need a token at all. Turn on access for <a href="https://docs.github.com/en/actions/creating-actions/sharing-actions-and-workflows-from-your-private-repository" target="_blank" rel="noopener noreferrer" class="">private repos</a> or <a href="https://docs.github.com/en/actions/creating-actions/sharing-actions-and-workflows-with-your-enterprise" target="_blank" rel="noopener noreferrer" class="">internal repos across your enterprise</a> in the repo's Actions settings.</li>
<li class=""><strong>Cloud credentials</strong> go through <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect" target="_blank" rel="noopener noreferrer" class="">OIDC</a>. No stored keys.</li>
<li class=""><strong>Actual cross-repo API calls</strong> use a <a href="https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation" target="_blank" rel="noopener noreferrer" class="">GitHub App installation token</a>, scoped to the exact repos and permissions the job needs:</li>
</ul>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> actions/create</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">github</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">app</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">token@v3</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> app</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">token</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">client-id</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> vars.CI_APP_CLIENT_ID </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">private-key</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> secrets.CI_APP_PRIVATE_KEY </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">owner</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> github.repository_owner </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">repositories</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> shared</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">proto</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">permission-contents</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> read</span><br></div></code></pre></div></div>
<p>Yes, the private key is still a secret. But it isn't tied to a human, it can only mint tokens that expire in an hour, and every token is scoped to what you wrote down in the workflow. That's a much smaller blast radius than a PAT someone made in 2021 with <code>repo</code> scope.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Platform-Owns-the-Pipeline">3. Platform Owns the Pipeline<a href="https://austen.info/blog/couple-to-artifacts-not-source#3-Platform-Owns-the-Pipeline" class="hash-link" aria-label="Direct link to 3. Platform Owns the Pipeline" title="Direct link to 3. Platform Owns the Pipeline" translate="no">​</a></h2>
<p>If 400 repos each hand-roll their own build, every pipeline fix is 400 PRs.</p>
<p>Put the golden path in a central repo as <a href="https://docs.github.com/en/actions/using-workflows/reusing-workflows" target="_blank" rel="noopener noreferrer" class="">reusable workflows</a>. App repos call it with inputs and stay tiny:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> CI</span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">pull_request</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">merge_group</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">packages</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">ci</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> acme/platform</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">workflows/.github/workflows/node</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">service.yml@v3</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(9, 134, 88)">24</span><br></div></code></pre></div></div>
<p>The platform team versions it, ships improvements once, and every repo gets them on the next bump. For the stuff that isn't optional (security scans, provenance, policy checks), enforce it with <a href="https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#require-workflows-to-pass-before-merging" target="_blank" rel="noopener noreferrer" class="">org-level ruleset workflows</a> so it runs whether the app repo calls it or not.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Flip-Integration-Testing-Around">4. Flip Integration Testing Around<a href="https://austen.info/blog/couple-to-artifacts-not-source#4-Flip-Integration-Testing-Around" class="hash-link" aria-label="Direct link to 4. Flip Integration Testing Around" title="Direct link to 4. Flip Integration Testing Around" translate="no">​</a></h2>
<p>This is the one that changes the most, and the one almost nobody does.</p>
<p>The default: the consumer clones its dependencies and runs integration tests. So when <code>shared-lib</code> merges a breaking change, the failure shows up later, in somebody else's repo, owned by somebody who didn't make the change.</p>
<p>Flip it. <strong>The dependency triggers the consumer's tests.</strong> When a PR opens on <code>shared-lib</code>, it publishes a preview version (say <code>2.4.0-pr.123</code>) and kicks off each known consumer's test suite with <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch" target="_blank" rel="noopener noreferrer" class=""><code>workflow_dispatch</code></a> or <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository_dispatch" target="_blank" rel="noopener noreferrer" class=""><code>repository_dispatch</code></a>. The consumer runs its tests against that preview, then reports back by creating a <a href="https://docs.github.com/en/rest/checks/runs" target="_blank" rel="noopener noreferrer" class="">check run</a> on the producer's PR.</p>
<p>The producer side is one step with an App token that has <code>permission-actions: write</code> on the consumer:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">gh workflow run contract-tests.yml </span><span class="token parameter variable" style="color:rgb(9, 134, 88)">-R</span><span class="token plain"> acme/checkout-service </span><span class="token punctuation" style="color:rgb(4, 81, 165)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(9, 134, 88)">-f</span><span class="token plain"> producer-repo</span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token plain">acme/shared-lib </span><span class="token punctuation" style="color:rgb(4, 81, 165)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(9, 134, 88)">-f</span><span class="token plain"> producer-sha</span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token string" style="color:rgb(163, 21, 21)">"</span><span class="token string variable" style="color:rgb(9, 134, 88)">$PR_HEAD_SHA</span><span class="token string" style="color:rgb(163, 21, 21)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(9, 134, 88)">-f</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(9, 134, 88)">version</span><span class="token operator" style="color:rgb(0, 0, 0)">=</span><span class="token number" style="color:rgb(9, 134, 88)">2.4</span><span class="token plain">.0-pr.123</span><br></div></code></pre></div></div>
<p>The consumer side:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> Contract tests</span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">workflow_dispatch</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token key atrule">producer-repo</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token key atrule">producer-sha</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token key atrule">version</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">packages</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">test</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> actions/checkout@v7</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">node@v7</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(9, 134, 88)">24</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">registry-url</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain">//npm.pkg.github.com</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> npm install "@acme/shared</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">lib@$VERSION"</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">VERSION</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> inputs.version </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">NODE_AUTH_TOKEN</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> secrets.GITHUB_TOKEN </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> npm test</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> actions/create</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">github</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">app</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">token@v3</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">if</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token tag" style="color:rgb(128, 0, 0)">!cancelled()</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> app</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">token</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">client-id</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> vars.CI_APP_CLIENT_ID </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">private-key</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> secrets.CI_APP_PRIVATE_KEY </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">owner</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> github.repository_owner </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">repositories</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> shared</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">lib</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">permission-checks</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">if</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token tag" style="color:rgb(128, 0, 0)">!cancelled()</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">GH_TOKEN</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> steps.app</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">token.outputs.token </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">PRODUCER_REPO</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> inputs.producer</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">repo </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">PRODUCER_SHA</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> inputs.producer</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">sha </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">CONCLUSION</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> job.status == 'success' </span><span class="token important">&amp;&amp;</span><span class="token plain"> 'success' </span><span class="token punctuation" style="color:rgb(4, 81, 165)">|</span><span class="token punctuation" style="color:rgb(4, 81, 165)">|</span><span class="token plain"> 'failure' </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">|</span><span class="token scalar string" style="color:rgb(163, 21, 21)"></span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">          gh api "repos/$PRODUCER_REPO/check-runs" \</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            -f name="consumer / $GITHUB_REPOSITORY" \</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            -f head_sha="$PRODUCER_SHA" \</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            -f status=completed \</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            -f conclusion="$CONCLUSION" \</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            -f details_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"</span><br></div></code></pre></div></div>
<p>Why an App and not <code>GITHUB_TOKEN</code>? <code>GITHUB_TOKEN</code> is scoped to the repo it runs in, and <a href="https://docs.github.com/en/rest/checks/runs#create-a-check-run" target="_blank" rel="noopener noreferrer" class="">write access to checks is only available to GitHub Apps</a>. Bonus: make that check <a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#require-status-checks-to-pass-before-merging" target="_blank" rel="noopener noreferrer" class="">required on the producer and set your App as the expected source</a>, so nobody can fake it with a random status.</p>
<p>Now the breaking change goes red <strong>on the PR that introduced it</strong>, in front of the person who wrote it, before it merges. That's the entire value of integration testing, delivered to the right person.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-Make-the-Feedback-Loop-Fast">5. Make the Feedback Loop Fast<a href="https://austen.info/blog/couple-to-artifacts-not-source#5-Make-the-Feedback-Loop-Fast" class="hash-link" aria-label="Direct link to 5. Make the Feedback Loop Fast" title="Direct link to 5. Make the Feedback Loop Fast" translate="no">​</a></h2>
<p>All of this only works if CI is fast enough that people don't route around it.</p>
<ul>
<li class=""><strong><a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue" target="_blank" rel="noopener noreferrer" class="">Merge queue</a>.</strong> Add the <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#merge_group" target="_blank" rel="noopener noreferrer" class=""><code>merge_group</code></a> trigger and let the queue test PRs together against the real tip of <code>main</code>. GitHub runs its own large monorepo on it: <a href="https://github.blog/engineering/engineering-principles/how-github-uses-merge-queue-to-ship-hundreds-of-changes-every-day/" target="_blank" rel="noopener noreferrer" class="">over 500 engineers merge 2,500 PRs a month</a> through merge queue, with a 33% drop in average time to ship.</li>
<li class=""><strong>Don't redo green work.</strong> <a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore" target="_blank" rel="noopener noreferrer" class="">Path filters</a>, <a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows" target="_blank" rel="noopener noreferrer" class="">caching</a>, and build tools that know what changed. If nothing in a service changed, don't test it again.</li>
<li class=""><strong>Bigger machines.</strong> <a href="https://docs.github.com/en/actions/using-github-hosted-runners/about-larger-runners" target="_blank" rel="noopener noreferrer" class="">Larger GitHub-hosted runners</a> are a <code>runs-on</code> change. Your engineers waiting 20 minutes on a 2-core box costs way more than the compute.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Microservices-Same-Idea-Runtime-Edition">Microservices: Same Idea, Runtime Edition<a href="https://austen.info/blog/couple-to-artifacts-not-source#Microservices-Same-Idea-Runtime-Edition" class="hash-link" aria-label="Direct link to Microservices: Same Idea, Runtime Edition" title="Direct link to Microservices: Same Idea, Runtime Edition" translate="no">​</a></h2>
<p>Microservices make this more obvious, not less. Services talk to each other at <strong>runtime</strong>, over the network. There's no reason one service's build should touch another service's source. Each one should build, test, and deploy on its own.</p>
<p>What they share is a contract, so treat the contract like the artifact:</p>
<ul>
<li class=""><strong>Publish API contracts as packages.</strong> Protobuf or OpenAPI specs, versioned and pinned like any other dependency.</li>
<li class=""><strong>Break the build in the producer's PR.</strong> <a href="https://buf.build/docs/breaking/" target="_blank" rel="noopener noreferrer" class=""><code>buf breaking</code></a> for protobuf, or consumer-driven contracts with <a href="https://docs.pact.io/" target="_blank" rel="noopener noreferrer" class="">Pact</a>, so removing a field fails CI on the service that removed it.</li>
<li class=""><strong>Change schemas in two steps.</strong> <a href="https://martinfowler.com/bliki/ParallelChange.html" target="_blank" rel="noopener noreferrer" class="">Expand, migrate, contract</a>. Add the new field, move consumers over, then remove the old one.</li>
<li class=""><strong>Decouple deploy from release.</strong> Ship dark behind <a href="https://martinfowler.com/articles/feature-toggles.html" target="_blank" rel="noopener noreferrer" class="">feature flags</a> so deploying a service doesn't mean every other service has to be ready.</li>
<li class=""><strong>Ephemeral environments for E2E smoke.</strong> Spin up a real environment per PR or per merge group for a handful of end-to-end checks, gated with <a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment" target="_blank" rel="noopener noreferrer" class="">environments</a>. Not on every push, and not as your main line of defense.</li>
</ul>
<p>And the honest diagnosis. If service A can't ship unless B and C ship with it, that's a distributed monolith. A monolith with network hops in the middle. Either fix the contracts so they can ship independently, or put them in one repo and build them together. Separate repos only pay for themselves if the things in them actually deploy separately.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Start-Here">Start Here<a href="https://austen.info/blog/couple-to-artifacts-not-source#Start-Here" class="hash-link" aria-label="Direct link to Start Here" title="Direct link to Start Here" translate="no">​</a></h2>
<p>Pick your worst build, the one with the most <code>repository:</code> lines in a checkout step. For each one, ask what that repo should be publishing instead. That list is your migration plan.</p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub Actions</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Beyond the OIDC Silver Bullet: Why "Keyless" GitHub Actions Aren't Enough]]></title>
            <link>https://austen.info/blog/beyond-the-oidc-silver-bullet</link>
            <guid>https://austen.info/blog/beyond-the-oidc-silver-bullet</guid>
            <pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[OIDC kills long-lived cloud keys, and everyone treats that like the end of the conversation. It's the start. The supply-chain attacks landing in 2026 are self-propagating worms that never touch your token: stolen credentials, mutable tags, cache poisoning, and pull_request_target hijack the pipeline first. Here's the defense-in-depth blueprint that limits the blast radius.]]></description>
            <content:encoded><![CDATA[<p>If you've modernized a CI/CD pipeline in the last couple of years, you've heard the gospel of <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect" target="_blank" rel="noopener noreferrer" class="">OpenID Connect</a>: stop hardcoding long-lived <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html" target="_blank" rel="noopener noreferrer" class="">AWS IAM keys</a> in your <a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions" target="_blank" rel="noopener noreferrer" class="">repository secrets</a>. Your workflow requests a short-lived token, the cloud verifies the claims, you deploy. It's a real upgrade. It's also where most teams stop thinking, and that's the problem.</p>
<!-- -->
<p><img decoding="async" loading="lazy" alt="A silver bullet shattering against a layered glass shield" src="https://austen.info/assets/images/hero-97dfb604c0a657d4b528c1d36aabb596.png" width="1376" height="768" class="img_ev3q"></p>
<p>OIDC solves one thing: <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#benefits-of-using-openid-connect" target="_blank" rel="noopener noreferrer" class="">credential persistence</a>. No more static keys sitting in a secret store waiting to leak. But it does nothing about the actual job of a pipeline, which is to <strong>execute arbitrary code on your behalf</strong>. Your keys are only one of the things that code can abuse, and most of the attacks landing in the wild right now never touch your OIDC token at all. They hijack the pipeline <em>before</em> identity is even in the picture.</p>
<p>So treat keyless auth as table stakes, not a finish line. Here's everything it leaves on the table.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Workload-Identity-Paradox">The Workload Identity Paradox<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#The-Workload-Identity-Paradox" class="hash-link" aria-label="Direct link to The Workload Identity Paradox" title="Direct link to The Workload Identity Paradox" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Malicious code inheriting a key badge and bridging into a private VPC" src="https://austen.info/assets/images/workload-identity-paradox-eced42ff757824d94e4af92f84732d80.png" width="1376" height="768" class="img_ev3q"></p>
<p>When you tie identity to the pipeline environment via OIDC, you're making one assumption: the code running in the pipeline is trustworthy.</p>
<p>It isn't. Pipelines exist to execute arbitrary code automatically. That's their whole job. The moment a <a href="https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication" target="_blank" rel="noopener noreferrer" class="">developer account is compromised</a>, or a <a href="https://docs.github.com/en/code-security/supply-chain-security" target="_blank" rel="noopener noreferrer" class="">malicious package</a> slips into your tree during <code>npm install</code>, that script runs on a runner with the exact same trust as your real deploy step.</p>
<p>Now broker a network tunnel from that runner into your private subnet with <a href="https://tailscale.com/kb/1276/tailscale-github-action" target="_blank" rel="noopener noreferrer" class="">Tailscale</a> or <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/" target="_blank" rel="noopener noreferrer" class="">Cloudflare Access</a> and you've handed the attacker an authenticated bridge straight into your infra. Identity is tied to the environment, so the malicious code inherits the keys to the kingdom. This is exactly the failure mode behind real-world incidents like the <a href="https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised" target="_blank" rel="noopener noreferrer" class=""><code>tj-actions/changed-files</code> compromise</a> and the <a href="https://about.codecov.io/security-update/" target="_blank" rel="noopener noreferrer" class="">Codecov bash uploader breach</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Five-Ways-In-That-Have-Nothing-To-Do-With-Your-Keys">Five Ways In That Have Nothing To Do With Your Keys<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#Five-Ways-In-That-Have-Nothing-To-Do-With-Your-Keys" class="hash-link" aria-label="Direct link to Five Ways In That Have Nothing To Do With Your Keys" title="Direct link to Five Ways In That Have Nothing To Do With Your Keys" translate="no">​</a></h2>
<p>Before you ever broker a token, an attacker has cheaper paths into your runner. None of these care whether you went keyless. And they don't stay contained: the 2026 wave of attacks, <a href="https://safedep.io/megalodon-mass-github-repo-backdooring-ci-workflows/" target="_blank" rel="noopener noreferrer" class="">Megalodon</a>, <a href="https://www.stepsecurity.io/blog/ghostaction-campaign-over-3-000-secrets-stolen-through-malicious-github-workflows" target="_blank" rel="noopener noreferrer" class="">GhostAction</a>, the <a href="https://www.stepsecurity.io/blog/shai-hulud-the-novel-self-replicating-worm-infecting-hundreds-of-npm-packages" target="_blank" rel="noopener noreferrer" class="">Shai-Hulud worm</a>, is <em>self-propagating</em>. A pipeline dumps its secrets, those secrets backdoor a package, the package compromises more developer machines, and those machines harvest more credentials. Your pipeline isn't just a target. It's a node in a worm, and every secret it leaks is a seed for the next hop. Containing the blast radius isn't only about protecting yourself; it's about refusing to be a vector.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Stolen-credentials-push-workflows-straight-to-main">Stolen credentials push workflows straight to main<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#Stolen-credentials-push-workflows-straight-to-main" class="hash-link" aria-label="Direct link to Stolen credentials push workflows straight to main" title="Direct link to Stolen credentials push workflows straight to main" translate="no">​</a></h3>
<p>This is the single biggest vector in the wild right now, and it doesn't involve a fork or a dependency at all. An attacker gets a compromised <a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens" target="_blank" rel="noopener noreferrer" class="">PAT</a> or <code>gh</code> CLI OAuth token (usually harvested from a <em>previous</em> supply-chain hop), commits a malicious workflow directly to <code>main</code> across every repo that credential can write to, triggers it to dump every secret, then deletes the runs with the same token so there's no public log. The root cause is structural: <strong>repository write access automatically grants secret access, with no second factor.</strong> The defense is to decouple the two: gate secret-bearing jobs behind a <a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment" target="_blank" rel="noopener noreferrer" class="">GitHub Environment</a> with <a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/managing-environments-for-deployment#required-reviewers" target="_blank" rel="noopener noreferrer" class="">required reviewers</a>, or pull them from an <a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-external-secrets" target="_blank" rel="noopener noreferrer" class="">external secret vault</a> via OIDC, so that write access alone can't silently exfiltrate anything.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Mutable-tags-get-re-resolved-on-every-run">Mutable tags get re-resolved on every run<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#Mutable-tags-get-re-resolved-on-every-run" class="hash-link" aria-label="Direct link to Mutable tags get re-resolved on every run" title="Direct link to Mutable tags get re-resolved on every run" translate="no">​</a></h3>
<p><code>uses: owner/action@v2</code> looks like a pinned dependency. It isn't. It's a declaration the runner <a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions" target="_blank" rel="noopener noreferrer" class="">re-resolves on every single execution</a> against a <em>mutable</em> git tag. Actions is effectively a package manager with no lockfile, no integrity hashes, and no transitive visibility, where the <a href="https://nesbitt.io/2026/04/28/github-actions-is-the-weakest-link.html" target="_blank" rel="noopener noreferrer" class=""><code>uses:</code> line is re-resolved on every run</a>. Compromise a maintainer's token, repoint <code>v2</code> to an imposter commit, and every consumer pulls the malicious code on their next run with no PR, no diff, no warning. That's the entire mechanism behind the <a href="https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised" target="_blank" rel="noopener noreferrer" class="">tj-actions</a> and <a href="https://www.stepsecurity.io/blog/actions-cool-issues-helper-github-action-compromised-all-tags-point-to-imposter-commit-that-exfiltrates-ci-cd-credentials" target="_blank" rel="noopener noreferrer" class=""><code>actions-cool/issues-helper</code></a> mass tag rewrites. Pinning to a full commit SHA is the only thing that closes it, because a SHA is content-addressed and can't be repointed underneath you.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Cache-poisoning-crosses-job-boundaries">Cache poisoning crosses job boundaries<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#Cache-poisoning-crosses-job-boundaries" class="hash-link" aria-label="Direct link to Cache poisoning crosses job boundaries" title="Direct link to Cache poisoning crosses job boundaries" translate="no">​</a></h3>
<p>The <a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows" target="_blank" rel="noopener noreferrer" class="">Actions cache</a> is shared across jobs in a repo. That means a low-privilege job, the one running untrusted PR code or third-party dependencies, can write a poisoned entry that a later privileged job restores and trusts. The cache is a covert channel straight through any isolation you think you built. The <a href="https://tanstack.com/blog/npm-supply-chain-compromise-postmortem" target="_blank" rel="noopener noreferrer" class="">TanStack compromise</a> used exactly this as its escalation vector. Treat restored cache as untrusted input, scope reads and writes as <em>discrete</em> permissions so a sensitive deploy can be granted neither, and keep caching off your privileged paths entirely.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pull_request_target-runs-attacker-code-with-your-secrets"><code>pull_request_target</code> runs attacker code with your secrets<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#pull_request_target-runs-attacker-code-with-your-secrets" class="hash-link" aria-label="Direct link to pull_request_target-runs-attacker-code-with-your-secrets" title="Direct link to pull_request_target-runs-attacker-code-with-your-secrets" translate="no">​</a></h3>
<p>A <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target" target="_blank" rel="noopener noreferrer" class=""><code>pull_request_target</code></a> workflow runs in the context of your <em>base</em> repo, with secrets in scope, but it's triggered by forked PRs from anyone. The trigger itself isn't the bug. The bug is <code>pull_request_target</code> <em>plus</em> checking out the PR's head or merge ref <em>plus</em> executing it, which is when a stranger's code runs with your secrets. It's the classic <a href="https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/" target="_blank" rel="noopener noreferrer" class="">pwn request</a>. The same shape shows up on other privileged triggers, and they're easier to overlook: <a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run" target="_blank" rel="noopener noreferrer" class=""><code>workflow_run</code></a> hands you artifacts a <code>pull_request</code> run uploaded, and treating that artifact's contents as trusted (extracting it into your workdir, reading a "PR number" an attacker actually controls) is the same vulnerability one step removed. <code>issue_comment</code> and label-driven ops add a <a href="https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use" target="_blank" rel="noopener noreferrer" class="">time-of-check/time-of-use</a> twist: you approve a PR once, but the checkout runs on <em>every</em> comment, so the attacker pushes a fresh malicious commit after approval. Never check out and run untrusted PR code in a privileged trigger, never trust an artifact from a less-privileged run, and default every workflow to <code>permissions: {}</code> so nothing is in scope unless you explicitly grant it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Dependency-confusion-resolves-your-private-action-to-a-public-impostor">Dependency confusion resolves your private action to a public impostor<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#Dependency-confusion-resolves-your-private-action-to-a-public-impostor" class="hash-link" aria-label="Direct link to Dependency confusion resolves your private action to a public impostor" title="Direct link to Dependency confusion resolves your private action to a public impostor" translate="no">​</a></h3>
<p>This one is subtler because the workflow file never changes. An action or a build step references a dependency it assumes lives in your <em>private</em> registry. The private copy goes away, the name fails to resolve internally, and resolution silently falls back to a <em>public</em> registry where it was never published. An attacker who registered that name in the public namespace gets their code executed instead. It's the <a href="https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610" target="_blank" rel="noopener noreferrer" class="">dependency confusion</a> attack Alex Birsan used to breach Apple, Microsoft, and dozens of others, and the same fallback exists anywhere you mix public and private resolution, including <a href="https://docs.github.com/en/enterprise-server@latest/admin/github-actions/managing-access-to-actions-from-githubcom/about-using-actions-in-your-enterprise" target="_blank" rel="noopener noreferrer" class="">self-hosted GitHub setups</a> that resolve missing actions from <code>github.com</code>. Pin the <em>source</em>, not just the version: be explicit about which registry or host a dependency comes from, and never let a private name silently resolve to a public one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Limiting-the-Blast-Radius">Limiting the Blast Radius<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#Limiting-the-Blast-Radius" class="hash-link" aria-label="Direct link to Limiting the Blast Radius" title="Direct link to Limiting the Blast Radius" translate="no">​</a></h2>
<p>Stop building a taller wall around the credentials. Isolate the execution environment instead. Five moves.</p>
<p><img decoding="async" loading="lazy" alt="A runner contained inside concentric defensive rings with four shields" src="https://austen.info/assets/images/limiting-blast-radius-ff7ec3974ba537a9b02ccc216eb972bf.png" width="1376" height="768" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-The-Build-vs-Deploy-airgap">1. The "Build vs. Deploy" airgap<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#1-The-Build-vs-Deploy-airgap" class="hash-link" aria-label="Direct link to 1. The &quot;Build vs. Deploy&quot; airgap" title="Direct link to 1. The &quot;Build vs. Deploy&quot; airgap" translate="no">​</a></h3>
<p>Never request your OIDC token in the same job that fetches third-party dependencies or runs tests. Split the workflow into two isolated <a href="https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow" target="_blank" rel="noopener noreferrer" class="">jobs</a>:</p>
<ul>
<li class=""><strong>The dirty room (build).</strong> Runs <code>npm install</code>, tests, and your build. No <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#updating-your-actions-for-oidc" target="_blank" rel="noopener noreferrer" class=""><code>id-token</code> permission</a> here. Poison a dependency and it finds no credentials and no network. Output is a compiled, <a href="https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds" target="_blank" rel="noopener noreferrer" class="">signed artifact</a>.</li>
<li class=""><strong>The vault (deploy).</strong> <a href="https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow#defining-prerequisite-jobs" target="_blank" rel="noopener noreferrer" class="">Depends on</a> the build passing, <a href="https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts" target="_blank" rel="noopener noreferrer" class="">downloads the verified artifact</a>, and is the <em>only</em> job that requests the <a href="https://github.com/aws-actions/configure-aws-credentials" target="_blank" rel="noopener noreferrer" class="">OIDC token</a>. No third-party code runs here, so the token stays clean.</li>
</ul>
<p>One catch: an airgap built only on job boundaries leaks through the <a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows" target="_blank" rel="noopener noreferrer" class="">shared cache</a>. If the dirty room can write a cache entry the vault restores, you've reconnected the two rooms through the back door. Keep caching out of the deploy job, and gate that job behind a <a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment" target="_blank" rel="noopener noreferrer" class="">GitHub Environment</a> with <a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/managing-environments-for-deployment#required-reviewers" target="_blank" rel="noopener noreferrer" class="">required reviewers</a>, scoping your <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services" target="_blank" rel="noopener noreferrer" class="">IAM trust policy</a> to that environment's <code>sub</code> claim.</p>
<p>And make that claim hard to spoof, because OIDC is only as strong as the trust condition you write against it. Prefer an immutable identifier like <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#using-the-subject-in-your-cloud-provider" target="_blank" rel="noopener noreferrer" class=""><code>repository_id</code></a> over a mutable <code>repo:owner/name</code> string, since repos get renamed and re-created but IDs don't, and reach for <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims" target="_blank" rel="noopener noreferrer" class=""><code>job_workflow_ref</code></a> to pin the exact workflow file when you're federating <a href="https://docs.github.com/en/actions/using-workflows/reusing-workflows" target="_blank" rel="noopener noreferrer" class="">reusable workflows</a>. GitHub recently shipped <a href="https://github.blog/changelog/2026-04-23-immutable-subject-claims-for-github-actions-oidc-tokens/" target="_blank" rel="noopener noreferrer" class="">immutable subject claims</a> to close one spoofing gap, but the cloud side still has to <em>ask</em> for the strict claim, and note that <code>aud</code> is attacker-influenced and unvalidated by GitHub, so your provider has to check it. A wildcard <code>sub</code> match is a keyless credential anyone in your org can mint.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Egress-filtering-at-the-runner-level">2. Egress filtering at the runner level<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#2-Egress-filtering-at-the-runner-level" class="hash-link" aria-label="Direct link to 2. Egress filtering at the runner level" title="Direct link to 2. Egress filtering at the runner level" translate="no">​</a></h3>
<p>Once an attacker has code execution, the next step is exfiltrating the token or pulling a second-stage payload. <a href="https://docs.github.com/en/actions/reference/security/secrets#automatically-redacted-secrets" target="_blank" rel="noopener noreferrer" class="">Secret masking</a> won't save you here: the runner redacts known secret values from logs, but a hijacked step can base64-encode a token to slip past the masker and POST it straight out. Masking stops accidental logging, not deliberate exfiltration. So kill it at the kernel. Tools like <a href="https://github.com/step-security/harden-runner" target="_blank" rel="noopener noreferrer" class="">StepSecurity Harden-Runner</a> use <a href="https://ebpf.io/what-is-ebpf/" target="_blank" rel="noopener noreferrer" class="">eBPF</a> to enforce <a href="https://docs.stepsecurity.io/guides/how-to-restrict-network-connections-to-explicitly-allowed-endpoints" target="_blank" rel="noopener noreferrer" class="">network egress policies</a> on the runner. Allowlist <code>github.com</code>, your <a href="https://docs.npmjs.com/cli/v10/using-npm/registry" target="_blank" rel="noopener noreferrer" class="">package registries</a>, and your target cloud endpoints. A hijacked script that tries to <code>curl</code> a random IP dies on the spot, and you get an <a href="https://docs.stepsecurity.io/github-actions/harden-runner/detections" target="_blank" rel="noopener noreferrer" class="">audit trail</a> of what it tried to reach.</p>
<p>On <a href="https://docs.github.com/en/actions/concepts/runners/about-github-hosted-runners" target="_blank" rel="noopener noreferrer" class="">GitHub-hosted runners</a> the platform-level version of this is GitHub's <a href="https://github.blog/news-insights/product-news/whats-coming-to-our-github-actions-2026-security-roadmap/" target="_blank" rel="noopener noreferrer" class="">native egress firewall</a>: a Layer 7 allowlist that runs <em>outside</em> the runner VM, so it holds even if an attacker gets root inside the job. Until it's GA you can approximate it by pulling the allowed-domain list from the <a href="https://docs.github.com/en/rest/meta/meta" target="_blank" rel="noopener noreferrer" class="">meta API</a> and refreshing your firewall on a schedule. Either way the principle is the same: network controls belong at the network layer, not bolted onto a runner an attacker may already control.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Absolute-dependency-pinning">3. Absolute dependency pinning<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#3-Absolute-dependency-pinning" class="hash-link" aria-label="Direct link to 3. Absolute dependency pinning" title="Direct link to 3. Absolute dependency pinning" translate="no">​</a></h3>
<p>Now make the SHA discipline from above operational and complete. <a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions" target="_blank" rel="noopener noreferrer" class="">Pin every Action to a full commit SHA</a>, never a tag like <code>@v2</code>. Strip <code>^</code> and <code>~</code> from <code>package.json</code> and commit a <a href="https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json" target="_blank" rel="noopener noreferrer" class="">lockfile</a>, then use <a href="https://docs.npmjs.com/cli/v10/commands/npm-ci" target="_blank" rel="noopener noreferrer" class=""><code>npm ci</code></a> for reproducible installs. Turn on <a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates" target="_blank" rel="noopener noreferrer" class="">Dependabot</a> to bump those SHAs safely, since the moment you pin to a commit you've opted out of automatic updates and Dependabot is what keeps the pin current (it <a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot" target="_blank" rel="noopener noreferrer" class="">understands SHA-pinned actions</a> and rewrites the SHA with a version comment). Pair it with <a href="https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts" target="_blank" rel="noopener noreferrer" class="">Dependabot alerts</a>, which cross-reference the <a href="https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database" target="_blank" rel="noopener noreferrer" class="">GitHub Advisory Database</a> so a dependency with a <em>known</em> published vulnerability gets flagged and auto-PR'd instead of sitting silently in your tree. Then require <a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners" target="_blank" rel="noopener noreferrer" class="">CODEOWNERS</a> approval plus a <a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches" target="_blank" rel="noopener noreferrer" class="">branch protection rule</a> on anything touching <code>.github/</code>, so Dependabot can open the PR but a human still has to approve the change and it never becomes its own auto-merge backdoor. Pin your runner's tools too with <a href="https://github.com/actions/setup-node" target="_blank" rel="noopener noreferrer" class=""><code>actions/setup-node</code></a> and consider <a href="https://docs.github.com/en/organizations/managing-organization-settings/disabling-or-limiting-github-actions-for-your-organization#allowing-select-actions-and-reusable-workflows-to-run" target="_blank" rel="noopener noreferrer" class=""><code>allowed_actions</code> policies</a> at the org level.</p>
<p>One honest limit: pinning your top-level <code>uses:</code> to a SHA does nothing for the <em>transitive</em> actions those actions call, which still resolve their own tags at runtime. You can't hand-pin that tree today, which is exactly why <a href="https://github.blog/news-insights/product-news/whats-coming-to-our-github-actions-2026-security-roadmap/" target="_blank" rel="noopener noreferrer" class="">native workflow dependency locking</a> is on GitHub's roadmap. Until it lands, prefer actions with shallow dependency trees and pin what you can.</p>
<p>Two sharp edges even a SHA pin doesn't sand down. First, <strong>imposter commits</strong>: you can reference a commit that lives in a repo's <em>fork network</em> but was never in the parent repo itself, so the SHA looks legitimate while pointing at attacker code that no maintainer ever merged. This was the trick behind the <a href="https://www.paloaltonetworks.com/blog/cloud-security/trivy-supply-chain-attack/" target="_blank" rel="noopener noreferrer" class="">Trivy action compromise</a>, and the real fix is a commit <em>reachability</em> check, confirming the SHA is actually reachable from the repo's own branches, not just resolvable. You can scan for these today with a <a href="https://ramimac.me/imposter/" target="_blank" rel="noopener noreferrer" class="">dedicated imposter-commit detector</a> before you trust a pin. Second, <strong>repo-jacking</strong>: rename <code>owner/action</code>, GitHub redirects the old name seamlessly, an attacker re-registers the vacated name, and consumers who never updated silently pull the new malicious repo. A SHA pin tied only to <code>owner/repo</code> doesn't save you here; durable protection means pinning to the immutable <a href="https://github.blog/security/supply-chain-security/how-to-stay-safe-from-repo-jacking/" target="_blank" rel="noopener noreferrer" class="">repository ID</a>, which doesn't survive a takeover. And worth knowing: Git's SHA-1 is <a href="https://github.blog/open-source/git/sha-256-support-in-git/" target="_blank" rel="noopener noreferrer" class="">cryptographically weakening</a> and the ecosystem is migrating to SHA-256, so a pin is strong today but not eternal.</p>
<p>The cheapest pin of all is the dependency you never take. Actions inherited npm's culture, not Go's: where the Go community leans on a fat standard library and treats every third-party import as a liability, the Actions ecosystem encourages reaching for a marketplace action for everything. That breadth is exactly why a single compromised action immediately compromises every consumer. Audit your <code>uses:</code> list the way you'd audit <code>package.json</code>, drop the actions you could replace with a few lines of shell, and you shrink the attack surface before any pinning even matters.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Granular-network-scoping">4. Granular network scoping<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#4-Granular-network-scoping" class="hash-link" aria-label="Direct link to 4. Granular network scoping" title="Direct link to 4. Granular network scoping" translate="no">​</a></h3>
<p>If the OIDC token brokers a <a href="https://www.cisa.gov/zero-trust-maturity-model" target="_blank" rel="noopener noreferrer" class="">Zero Trust</a> connection, enforce <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" target="_blank" rel="noopener noreferrer" class="">least privilege</a> on the tunnel itself. Don't route to the whole VPC. Route to the single IP and port the deploy actually needs, scoped with a <a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html" target="_blank" rel="noopener noreferrer" class="">security group</a> or <a href="https://tailscale.com/kb/1018/acls" target="_blank" rel="noopener noreferrer" class="">Tailscale ACL tag</a>. The token should be a key to one door, not the building.</p>
<p>The same discipline applies to how the runner reaches your network in the first place. <a href="https://docs.github.com/en/actions/concepts/runners/private-networking" target="_blank" rel="noopener noreferrer" class="">Hosted runners</a> get a few sanctioned bridges into private resources, in increasing order of real isolation: <a href="https://docs.github.com/en/actions/how-tos/manage-runners/larger-runners/about-larger-runners#networking-for-larger-runners" target="_blank" rel="noopener noreferrer" class="">static IP allowlisting</a> on larger runners (easy, but it's still public-internet IP trust and a static target worth questioning), an <a href="https://docs.github.com/en/actions/how-tos/manage-runners/github-hosted-runners/connect-to-a-private-network/connect-with-oidc" target="_blank" rel="noopener noreferrer" class="">API gateway fronted by OIDC</a>, and <a href="https://docs.github.com/en/enterprise-cloud@latest/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/about-azure-private-networking-for-github-hosted-runners-in-your-enterprise" target="_blank" rel="noopener noreferrer" class="">Azure VNet injection</a>, where the runner pulls an IP from <em>your</em> subnet and you own the NSGs. VNet injection is the most complete, but mind the edges: each concurrent job consumes one IP, so size the subnet (a <code>/27</code> minimum) or jobs stop queueing, and a firewall doing TLS inspection breaks runners that won't trust your internal CA. The throughline across all three is to move the trust boundary off public-IP reputation and onto identity wherever you can.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-Lint-the-workflows-themselves">5. Lint the workflows themselves<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#5-Lint-the-workflows-themselves" class="hash-link" aria-label="Direct link to 5. Lint the workflows themselves" title="Direct link to 5. Lint the workflows themselves" translate="no">​</a></h3>
<p>Most of the mistakes above are detectable statically. Run <a href="https://github.com/zizmorcore/zizmor" target="_blank" rel="noopener noreferrer" class="">zizmor</a> over your <code>.github/workflows</code> to catch dangerous triggers, missing <code>permissions:</code> blocks, unpinned actions, and template-injection sinks before they ship, and wire it into CI so a risky pattern fails the PR. GitHub's own <a href="https://github.blog/changelog/2025-01-23-codeql-now-analyzes-github-actions-workflows/" target="_blank" rel="noopener noreferrer" class="">CodeQL Actions analysis</a> covers similar ground and is free for public repos. Neither one catches a malicious dependency, that's what the pinning and isolation above are for, and <a href="https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts" target="_blank" rel="noopener noreferrer" class="">Dependabot alerts</a> handle the <em>known</em>-vulnerable ones, but they're the cheapest way to stop <em>your own</em> workflows from being the hole.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Bottom-Line">The Bottom Line<a href="https://austen.info/blog/beyond-the-oidc-silver-bullet#The-Bottom-Line" class="hash-link" aria-label="Direct link to The Bottom Line" title="Direct link to The Bottom Line" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="A verified token passing through multiple inspection gates, defense in depth" src="https://austen.info/assets/images/bottom-line-6f227a671c83ce743ba4fd13de0c006d.png" width="1376" height="768" class="img_ev3q"></p>
<p>OIDC for CI/CD is non-negotiable, but it's only the <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect" target="_blank" rel="noopener noreferrer" class="">authentication</a> layer, and most real-world attacks never reach it. They hijack the runner first through a stolen write token, a mutable tag, a poisoned cache, or a fork PR running with your secrets, and then they worm onward using whatever they steal. You can't scan your way out of this either: the minimal backdoor is swapping one trusted SHA for a malicious one, which defeats any string or pattern matching and turns detection into cat-and-mouse. The durable fixes are structural, not reactive. Without SHA pinning, cache discipline, locked-down triggers, write access decoupled from secrets, job isolation, egress filtering, and real <a href="https://docs.stepsecurity.io/harden-runner" target="_blank" rel="noopener noreferrer" class="">observability</a> into what your runners are doing, you haven't eliminated the risk. You've moved it deeper into the pipeline where it's harder to see.</p>
<p>Trust the identity. Verify the execution. For the full checklist, GitHub's <a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions" target="_blank" rel="noopener noreferrer" class="">security hardening guide</a> and the <a href="https://github.com/ossf/scorecard" target="_blank" rel="noopener noreferrer" class="">OpenSSF Scorecard</a> are the right places to start.</p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub Actions</category>
            <category>Security</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[The Future of Developer Compute]]></title>
            <link>https://austen.info/blog/future-of-developer-compute</link>
            <guid>https://austen.info/blog/future-of-developer-compute</guid>
            <pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Your laptop is about to stop being where work happens. As agents multiply, local compute becomes the bottleneck, and cloud-native agent compute is the answer.]]></description>
            <content:encoded><![CDATA[<p>Your laptop is about to stop being where work happens. As we get more competent with agents, local compute becomes the bottleneck, and the industry is converging on a new kind of cloud-native compute built specifically for autonomous agents.</p>
<!-- -->
<p><em>This is part 5 of a series on AI-native CI/CD. See also: <a class="" href="https://austen.info/blog/automation-is-the-obvious-choice">Automation is The Obvious Choice</a>, <a class="" href="https://austen.info/blog/actions-is-the-ai-native-platform">GitHub Actions: The AI-Native Platform</a>, <a class="" href="https://austen.info/blog/agentic-workflows-on-github-events">Agentic Workflows on GitHub Events</a>, and <a class="" href="https://austen.info/blog/prompt-design-for-headless-agents">Prompt Design for Headless Agents</a>.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Local-Machine-Bottleneck">The Local Machine Bottleneck<a href="https://austen.info/blog/future-of-developer-compute#The-Local-Machine-Bottleneck" class="hash-link" aria-label="Direct link to The Local Machine Bottleneck" title="Direct link to The Local Machine Bottleneck" translate="no">​</a></h2>
<p>Right now, you're running one agent in your IDE. Maybe a second one on a coding task. That's about all your local machine can handle.</p>
<p>But where this is going is <strong>many agents</strong>, working in parallel, each on a different task. And that breaks the local machine model:</p>
<ul>
<li class="">Each agent needs its <strong>own isolated workspace</strong>, you can't have 5 agents editing the same files</li>
<li class="">Different agents need <strong>different permissions and boundaries</strong>, a security scanning agent shouldn't have the same access as a deployment agent</li>
<li class=""><strong>Performance</strong> becomes a real constraint, local CPU, RAM, disk I/O can't scale to 10 concurrent agent sessions</li>
<li class=""><strong>Security</strong>, agents running LLM-generated code on your local machine is a risk surface you don't want to manage</li>
</ul>
<blockquote>
<p>Right now, you fire up Copilot in VS Code and it works great. But what happens when you want to spin up 5 agents, each working on a different issue, each in its own isolated environment, each with scoped permissions? Your laptop will struggle with that.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-Not-Just-Use-CI-Runners">Why Not Just Use CI Runners?<a href="https://austen.info/blog/future-of-developer-compute#Why-Not-Just-Use-CI-Runners" class="hash-link" aria-label="Direct link to Why Not Just Use CI Runners?" title="Direct link to Why Not Just Use CI Runners?" translate="no">​</a></h2>
<p>Fair question. Actions runners are purpose-built for <strong>async CI/CD jobs</strong>, fire-and-forget pipelines that start clean and end clean. That's perfect for builds and deployments. But agents need fundamentally different compute:</p>



































<table><thead><tr><th>Dimension</th><th><strong>CI/CD Runners</strong></th><th><strong>What Agents Need</strong></th></tr></thead><tbody><tr><td><strong>Startup</strong></td><td>~10s+ to provision a VM</td><td>Sub-second, agents need to respond instantly</td></tr><tr><td><strong>State</strong></td><td>Stateless, every job starts clean</td><td>Stateful, agents need to pause, hand off to humans, resume with full context</td></tr><tr><td><strong>Execution</strong></td><td>Fire-and-forget</td><td>Interactive, API-driven with human-in-the-loop handoffs</td></tr><tr><td><strong>Scale pattern</strong></td><td>Fewer, longer-running jobs</td><td>Swarms of short-lived instances (seconds to minutes)</td></tr><tr><td><strong>Cost model</strong></td><td>Per-minute metered (CI-oriented)</td><td>Optimized for short bursts, microVMs, not full VMs</td></tr></tbody></table>
<p>CI runners will still be the right answer for CI/CD. But agent compute is a different problem space.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="What-Agent-Compute-Looks-Like">What Agent Compute Looks Like<a href="https://austen.info/blog/future-of-developer-compute#What-Agent-Compute-Looks-Like" class="hash-link" aria-label="Direct link to What Agent Compute Looks Like" title="Direct link to What Agent Compute Looks Like" translate="no">​</a></h2>
<p>The industry is converging on a new compute layer purpose-built for agentic workloads. The core requirements:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-MicroVMs-Not-Full-VMs">1. MicroVMs, Not Full VMs<a href="https://austen.info/blog/future-of-developer-compute#1-MicroVMs-Not-Full-VMs" class="hash-link" aria-label="Direct link to 1. MicroVMs, Not Full VMs" title="Direct link to 1. MicroVMs, Not Full VMs" translate="no">​</a></h3>
<p>Full VMs are overkill for an agent that needs to run for 30 seconds. MicroVM architectures (think Firecracker, CloudHypervisor) provide:</p>
<ul>
<li class="">Sub-second startup</li>
<li class="">Strong isolation (hardware-level, not just containers)</li>
<li class="">Dramatically lower cost per invocation</li>
<li class="">OCI image support for custom environments</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-SnapshotResume">2. Snapshot/Resume<a href="https://austen.info/blog/future-of-developer-compute#2-SnapshotResume" class="hash-link" aria-label="Direct link to 2. Snapshot/Resume" title="Direct link to 2. Snapshot/Resume" translate="no">​</a></h3>
<p>Agents aren't purely autonomous. They hit decision points where a human needs to review. The compute needs to support:</p>
<ul>
<li class="">Save full state (memory, disk, execution context)</li>
<li class="">Hand off to a human for review</li>
<li class="">Resume exactly where it left off</li>
</ul>
<p>This is the biggest gap in current CI systems. Every CI runner is fire-and-forget. Agent compute needs to be pause-and-resume.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Scoped-Permissions-Per-Instance">3. Scoped Permissions Per Instance<a href="https://austen.info/blog/future-of-developer-compute#3-Scoped-Permissions-Per-Instance" class="hash-link" aria-label="Direct link to 3. Scoped Permissions Per Instance" title="Direct link to 3. Scoped Permissions Per Instance" translate="no">​</a></h3>
<p>Each agent instance needs its own permission boundary:</p>
<ul>
<li class="">Agent A can read code + write PRs</li>
<li class="">Agent B can read issues + write labels</li>
<li class="">Agent C can deploy to staging, not production</li>
</ul>
<p>This goes beyond repo-level GITHUB_TOKEN scoping. It's per-agent identity and access control.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Enterprise-Governance">4. Enterprise Governance<a href="https://austen.info/blog/future-of-developer-compute#4-Enterprise-Governance" class="hash-link" aria-label="Direct link to 4. Enterprise Governance" title="Direct link to 4. Enterprise Governance" translate="no">​</a></h3>
<p>At scale, you need:</p>
<ul>
<li class="">Auditability, who spawned which agent, what did it do, what resources did it touch</li>
<li class="">Network controls, VNET/firewall integration per agent instance</li>
<li class="">Policy enforcement, which models can be used, which tools are allowed, cost limits per run</li>
<li class="">Identity, tied to the developer who spawned it, not a shared service account</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Compute-Stack-Is-Converging">The Compute Stack Is Converging<a href="https://austen.info/blog/future-of-developer-compute#The-Compute-Stack-Is-Converging" class="hash-link" aria-label="Direct link to The Compute Stack Is Converging" title="Direct link to The Compute Stack Is Converging" translate="no">​</a></h2>
<p>What's becoming clear is that the modern development platform needs three distinct compute layers, each purpose-built for its workload:</p>

























<table><thead><tr><th>Layer</th><th>Purpose</th><th>Characteristics</th></tr></thead><tbody><tr><td><strong>CI/CD Compute</strong></td><td>Build, test, deploy</td><td>Async, stateless, fire-and-forget</td></tr><tr><td><strong>Development Compute</strong></td><td>Full dev environment</td><td>Persistent, interactive, human-driven</td></tr><tr><td><strong>Agent Compute</strong></td><td>Autonomous AI execution</td><td>Stateful, sub-second, swarm-scale, pause/resume</td></tr></tbody></table>
<p>GitHub already has the first two: <strong>Actions</strong> for CI/CD, <strong>Codespaces</strong> for development. The third layer, agent compute, is where GitHub and the entire industry is heading.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Competitive-Landscape">The Competitive Landscape<a href="https://austen.info/blog/future-of-developer-compute#The-Competitive-Landscape" class="hash-link" aria-label="Direct link to The Competitive Landscape" title="Direct link to The Competitive Landscape" translate="no">​</a></h2>
<p>Everyone sees this opportunity. Vercel, Docker, Deno, E2B, Daytona, all building sandbox compute for agents. The differentiator will be:</p>
<ul>
<li class=""><strong>Platform integration</strong>, how deeply the compute layer connects to the developer's existing workflow</li>
<li class=""><strong>Governance</strong>, enterprise controls, audit trails, compliance</li>
<li class=""><strong>Agent-native primitives</strong>, snapshot/resume, scoped permissions, tool allowlisting</li>
</ul>
<p>The platforms that win will be the ones that make agent compute feel as natural as running a CI job today.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="What-You-Should-Do-Now">What You Should Do Now<a href="https://austen.info/blog/future-of-developer-compute#What-You-Should-Do-Now" class="hash-link" aria-label="Direct link to What You Should Do Now" title="Direct link to What You Should Do Now" translate="no">​</a></h2>
<p>You don't have to wait for dedicated agent compute to start building agentic workflows. The pieces are already in place:</p>
<ol>
<li class=""><strong>Start with Actions</strong>, run agents in CI today using <a href="https://github.com/marketplace/actions/github-copilot-cli" target="_blank" rel="noopener noreferrer" class="">Copilot CLI</a> or <a href="https://github.github.com/gh-aw/" target="_blank" rel="noopener noreferrer" class="">GitHub Agentic Workflows</a></li>
<li class=""><strong>Use GitHub-hosted runners</strong>, ephemeral, isolated, zero-maintenance compute that's ready for agentic workloads</li>
<li class=""><strong>Build the prompt architecture</strong>, <a class="" href="https://austen.info/blog/prompt-design-for-headless-agents">design for headless execution</a> now and you'll be ready when dedicated agent compute arrives</li>
<li class=""><strong>Invest in your CI/CD suites</strong>, the stronger your test/lint/scan pipeline, the more autonomy you can safely give agents</li>
</ol>
<p>The future of compute is converging. Actions is the foundation everything else builds on. Start there.</p>
<p>📊 <strong><a href="https://austen.info/slides/ai-native-cicd-series.html#part5" target="_blank" rel="noopener noreferrer" class="">View the visual presentation →</a></strong></p>
<p><em>This post is part of a series on AI-native CI/CD. Start from the beginning: <a class="" href="https://austen.info/blog/automation-is-the-obvious-choice">Automation is The Obvious Choice</a>.</em></p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Prompt Design for Headless AI Agents]]></title>
            <link>https://austen.info/blog/prompt-design-for-headless-agents</link>
            <guid>https://austen.info/blog/prompt-design-for-headless-agents</guid>
            <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[When you take an agent out of your IDE and put it on a cron job, everything changes. Here's the prompt architecture that separates 'agents are unreliable' from '15 agents running on a schedule that just work.']]></description>
            <content:encoded><![CDATA[<p>When you take an agent out of your IDE and put it on a cron job or a GitHub event trigger, everything changes. This is the prompt architecture that separates "I tried agents and they were unreliable" from "I have 15 agents running on a schedule and they just work."</p>
<!-- -->
<p><em>This is part 4 of a series on AI-native CI/CD. See also: <a class="" href="https://austen.info/blog/automation-is-the-obvious-choice">Automation is The Obvious Choice</a>, <a class="" href="https://austen.info/blog/actions-is-the-ai-native-platform">GitHub Actions: The AI-Native Platform</a>, <a class="" href="https://austen.info/blog/agentic-workflows-on-github-events">Agentic Workflows on GitHub Events</a>, and <a class="" href="https://austen.info/blog/future-of-developer-compute">The Future of Developer Compute</a>.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Aha-Moment">The Aha Moment<a href="https://austen.info/blog/prompt-design-for-headless-agents#The-Aha-Moment" class="hash-link" aria-label="Direct link to The Aha Moment" title="Direct link to The Aha Moment" translate="no">​</a></h2>
<blockquote>
<p>You don't have to build the engine. You just have to build the guardrails.</p>
</blockquote>
<p>When you write prompts for built-in orchestrators like Copilot CLI, you're not writing a traditional "prompt." You're writing a <strong>configuration file for an execution engine</strong>.</p>
<p>These agents already have a programmatic <code>while</code> loop running under the hood: Reason → Act → Observe → Repeat. If your prompt tries to manually dictate "do this, then loop back," you'll clash with the agent's native programming.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Interactive-Agents-5-Strategies">Interactive Agents: 5 Strategies<a href="https://austen.info/blog/prompt-design-for-headless-agents#Interactive-Agents-5-Strategies" class="hash-link" aria-label="Direct link to Interactive Agents: 5 Strategies" title="Direct link to Interactive Agents: 5 Strategies" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-Stop-Micromanaging-the-Loop">1. Stop Micromanaging the Loop<a href="https://austen.info/blog/prompt-design-for-headless-agents#1-Stop-Micromanaging-the-Loop" class="hash-link" aria-label="Direct link to 1. Stop Micromanaging the Loop" title="Direct link to 1. Stop Micromanaging the Loop" translate="no">​</a></h3>
<p>Don't tell the agent <em>how</em> to loop. Tell it <em>what tools it has</em> and <em>what the finish line looks like</em>. Let the agent's native backend handle execution cycles.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Define-Strict-Exit-Criteria-and-Enforce-Them-with-Hooks">2. Define Strict Exit Criteria, and Enforce Them with Hooks<a href="https://austen.info/blog/prompt-design-for-headless-agents#2-Define-Strict-Exit-Criteria-and-Enforce-Them-with-Hooks" class="hash-link" aria-label="Direct link to 2. Define Strict Exit Criteria, and Enforce Them with Hooks" title="Direct link to 2. Define Strict Exit Criteria, and Enforce Them with Hooks" translate="no">​</a></h3>
<p>Autonomous agents either give up too early or loop forever. Give it an undeniable, physical checklist:</p>
<blockquote>
<p>"You may not consider this task complete until <code>output.md</code> is written to disk AND the YAML frontmatter contains the required fields."</p>
</blockquote>
<p><strong>But prompts are suggestions. Hooks are enforcement.</strong></p>
<p><a href="https://code.visualstudio.com/docs/copilot/customization/hooks" target="_blank" rel="noopener noreferrer" class="">Agent hooks</a> let you gate the agent's lifecycle with deterministic code. The <code>Stop</code> hook is the killer feature. It fires when the agent tries to stop, and you can <strong>block it</strong> with a script that runs a real check:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"hooks"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token property">"Stop"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token property">"type"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"command"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"./scripts/validate-output.sh"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><br></div></code></pre></div></div>
<p>Your script checks the actual deliverables and returns:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"hookSpecificOutput"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token property">"hookEventName"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Stop"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token property">"decision"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"block"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token property">"reason"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"output.md is missing the enterprise_slug field. Add it before finishing."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><br></div></code></pre></div></div>
<p>The prompt says "don't stop until X is done." The hook <strong>enforces</strong> it. The script runs a deterministic check: does the file exist? Does it pass validation? If it fails, the agent gets a new prompt injected telling it exactly what's still missing.</p>
<p><strong>This is the bridge between non-deterministic AI and deterministic automation.</strong></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="All-8-Hook-Lifecycle-Events">All 8 Hook Lifecycle Events<a href="https://austen.info/blog/prompt-design-for-headless-agents#All-8-Hook-Lifecycle-Events" class="hash-link" aria-label="Direct link to All 8 Hook Lifecycle Events" title="Direct link to All 8 Hook Lifecycle Events" translate="no">​</a></h4>









































<table><thead><tr><th>Hook</th><th>What It Does</th></tr></thead><tbody><tr><td><code>SessionStart</code></td><td>Inject project context (branch, version, env vars)</td></tr><tr><td><code>UserPromptSubmit</code></td><td>Transform or validate user input before the agent sees it</td></tr><tr><td><code>PreToolUse</code></td><td>Block dangerous commands (<code>rm -rf</code>, <code>DROP TABLE</code>) before execution</td></tr><tr><td><code>PostToolUse</code></td><td>Auto-run linters/formatters after every file edit</td></tr><tr><td><code>PreCompact</code></td><td>Control context window management</td></tr><tr><td><code>SubagentStart</code></td><td>Gate or configure sub-agent spawning</td></tr><tr><td><code>SubagentStop</code></td><td>Validate sub-agent outputs</td></tr><tr><td><code>Stop</code></td><td>Enforce exit criteria with deterministic checks</td></tr></tbody></table>
<p><strong>Powerful patterns:</strong></p>
<ul>
<li class=""><strong><code>PreToolUse</code></strong>, block dangerous commands regardless of what the agent was prompted to do</li>
<li class=""><strong><code>PostToolUse</code></strong>, auto-run linters after every file edit, inject "you have lint errors, fix them" back into the conversation</li>
<li class=""><strong><code>SessionStart</code></strong>, inject project context so the agent starts informed</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Externalize-Memory-to-the-File-System">3. Externalize Memory to the File System<a href="https://austen.info/blog/prompt-design-for-headless-agents#3-Externalize-Memory-to-the-File-System" class="hash-link" aria-label="Direct link to 3. Externalize Memory to the File System" title="Direct link to 3. Externalize Memory to the File System" translate="no">​</a></h3>
<p>Context windows get bloated. Agents forget what step they're on. Force the agent to write state to a file (<code>.agent-state.md</code>, <code>sync-plan.json</code>). If it crashes, it reads the file to resume.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Constrain-the-Blast-Radius">4. Constrain the Blast Radius<a href="https://austen.info/blog/prompt-design-for-headless-agents#4-Constrain-the-Blast-Radius" class="hash-link" aria-label="Direct link to 4. Constrain the Blast Radius" title="Direct link to 4. Constrain the Blast Radius" translate="no">​</a></h3>
<p>Agents are curious. If they can't find data, they start running grep across your entire workspace, wasting time and tokens.</p>
<p>Explicit whitelist:</p>
<blockquote>
<p>"You may ONLY use the <code>copilot-cli</code> action and the GitHub MCP tools. DO NOT use system grep or workspace-wide searches."</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-Establish-Yield-Protocols">5. Establish Yield Protocols<a href="https://austen.info/blog/prompt-design-for-headless-agents#5-Establish-Yield-Protocols" class="hash-link" aria-label="Direct link to 5. Establish Yield Protocols" title="Direct link to 5. Establish Yield Protocols" translate="no">​</a></h3>
<p>When a tool breaks, agents hallucinate workarounds or bash the same broken command repeatedly. Define exactly when to stop:</p>
<blockquote>
<p>"If the API returns a 401, move on."</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Going-Headless-What-Changes-When-Theres-No-Human">Going Headless: What Changes When There's No Human<a href="https://austen.info/blog/prompt-design-for-headless-agents#Going-Headless-What-Changes-When-Theres-No-Human" class="hash-link" aria-label="Direct link to Going Headless: What Changes When There's No Human" title="Direct link to Going Headless: What Changes When There's No Human" translate="no">​</a></h2>
<p>Taking an agent out of your IDE and putting it on a background cron job is the final boss of agentic workflows. If you just schedule your interactive prompt to run every hour, it will eventually hang, wait for input that never comes, and silently burn through your compute budget.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-Do-not-Yield">1. Do not Yield<a href="https://austen.info/blog/prompt-design-for-headless-agents#1-Do-not-Yield" class="hash-link" aria-label="Direct link to 1. Do not Yield" title="Direct link to 1. Do not Yield" translate="no">​</a></h3>
<p>Replace "ask the user" with "log the error and terminate."</p>
<blockquote>
<p>"Under NO circumstances should you ask for clarification. If you lack data after 2 retries, write to <code>logs/failed_runs.log</code> and terminate immediately."</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Design-for-Idempotency">2. Design for Idempotency<a href="https://austen.info/blog/prompt-design-for-headless-agents#2-Design-for-Idempotency" class="hash-link" aria-label="Direct link to 2. Design for Idempotency" title="Direct link to 2. Design for Idempotency" translate="no">​</a></h3>
<p>If the script runs every hour, the first task is checking: "Did I already do this?"</p>
<blockquote>
<p>"Read <code>notes/YYYY-MM-DD/</code>. If output already exists for this run, output 'previously completed' and terminate."</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Pre-Authenticate-Everything">3. Pre-Authenticate Everything<a href="https://austen.info/blog/prompt-design-for-headless-agents#3-Pre-Authenticate-Everything" class="hash-link" aria-label="Direct link to 3. Pre-Authenticate Everything" title="Direct link to 3. Pre-Authenticate Everything" translate="no">​</a></h3>
<p>No <code>az login --use-device-code</code>. No browser SSO. None of that works headlessly. Your infrastructure must inject pre-authenticated tokens or managed identities as environment variables BEFORE the agent spins up.</p>
<blockquote>
<p>"Assume all tools are pre-authenticated. If you get a 401, log 'Auth Pipeline Broken' and terminate."</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Hardcode-Loop-Limits">4. Hardcode Loop Limits<a href="https://austen.info/blog/prompt-design-for-headless-agents#4-Hardcode-Loop-Limits" class="hash-link" aria-label="Direct link to 4. Hardcode Loop Limits" title="Direct link to 4. Hardcode Loop Limits" translate="no">​</a></h3>
<p>In your IDE, you can Ctrl+C a rogue agent. On a server at 3 AM, it can loop 200 times and cost $50 before you wake up.</p>
<blockquote>
<p>"Maximum 10 tool calls per execution. If the task isn't complete, compile what you have, write the output, and terminate."</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-Shift-to-Audit-Logging">5. Shift to Audit Logging<a href="https://austen.info/blog/prompt-design-for-headless-agents#5-Shift-to-Audit-Logging" class="hash-link" aria-label="Direct link to 5. Shift to Audit Logging" title="Direct link to 5. Shift to Audit Logging" translate="no">​</a></h3>
<p>No one is watching the terminal. The agent's reasoning must be written to a permanent file.</p>
<blockquote>
<p>"Append your reasoning, tool choices, and errors in JSON to <code>audit.jsonl</code> before each action."</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Prompt-Templates">The Prompt Templates<a href="https://austen.info/blog/prompt-design-for-headless-agents#The-Prompt-Templates" class="hash-link" aria-label="Direct link to The Prompt Templates" title="Direct link to The Prompt Templates" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Headless-Agent-Prompt-No-Human">Headless Agent Prompt (No Human)<a href="https://austen.info/blog/prompt-design-for-headless-agents#Headless-Agent-Prompt-No-Human" class="hash-link" aria-label="Direct link to Headless Agent Prompt (No Human)" title="Direct link to Headless Agent Prompt (No Human)" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">1. &lt;objective&gt;              The specific, narrow task for this run</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">2. &lt;idempotency_check&gt;      Check if work was already done</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">3. &lt;authorized_tools&gt;       Restricted tool whitelist</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">4. &lt;headless_constraints&gt;   No questions, no interactive auth, max N loops</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">5. &lt;failure_protocol&gt;       Where to log errors, then terminate</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">6. &lt;exit_criteria&gt;          Definition of "done" → save and shutdown</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Example-Headless-Daily-Audit-Agent">Example: Headless Daily Audit Agent<a href="https://austen.info/blog/prompt-design-for-headless-agents#Example-Headless-Daily-Audit-Agent" class="hash-link" aria-label="Direct link to Example: Headless Daily Audit Agent" title="Direct link to Example: Headless Daily Audit Agent" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> austenstone/copilot</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">cli@v3</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">autopilot</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">no-ask-user</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">prompt</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">|</span><span class="token scalar string" style="color:rgb(163, 21, 21)"></span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">      &lt;objective&gt;</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">      Audit open issues labeled "needs-triage" and add priority labels.</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">      &lt;/objective&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;idempotency_check</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      Check if today's audit log exists at logs/audit</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">YYYY</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">MM</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">DD.jsonl.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      If it does</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> output "previously completed" and terminate.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;/idempotency_check</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;authorized_tools</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      GitHub CLI (gh issue list</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> gh issue edit). Nothing else.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;/authorized_tools</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;headless_constraints</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Do NOT ask for clarification. Ever.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Maximum 20 tool calls.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> No interactive authentication.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;/headless_constraints</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;failure_protocol</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token key atrule">On any error after 2 retries</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> log to logs/failed_runs.log and terminate.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;/failure_protocol</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;exit_criteria</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      All "needs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">triage" issues have been labeled. Write results to</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      logs/audit</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">YYYY</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">MM</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">DD.jsonl with issue numbers and assigned labels.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      &lt;/exit_criteria</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><br></div></code></pre></div></div>
<p>The prompt IS the configuration. Get it right and the engine runs itself.</p>
<p>📊 <strong><a href="https://austen.info/slides/ai-native-cicd-series.html#part4" target="_blank" rel="noopener noreferrer" class="">View the visual presentation →</a></strong></p>
<p><em>Next up: <a class="" href="https://austen.info/blog/future-of-developer-compute">The Future of Developer Compute</a>. Your laptop is about to stop being where work happens.</em></p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub</category>
            <category>GitHub Copilot</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Agentic Workflows on GitHub Events]]></title>
            <link>https://austen.info/blog/agentic-workflows-on-github-events</link>
            <guid>https://austen.info/blog/agentic-workflows-on-github-events</guid>
            <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Run AI agents triggered on any GitHub event like issues, PRs, schedules, and dispatches. Five approaches from simple inference to full agent frameworks: Copilot Models, Copilot Coding Agent, GitHub Agentic Workflows, Copilot CLI, and the Copilot SDK.]]></description>
            <content:encoded><![CDATA[<p>Just like you use Copilot in VS Code or the Coding Agent from an issue, you can run those same agentic workflows <strong>triggered on GitHub events</strong>, inside Actions. Safe, secure, and ephemeral.</p>
<!-- -->
<p><em>This is part 3 of a series on AI-native CI/CD. See also: <a class="" href="https://austen.info/blog/automation-is-the-obvious-choice">Automation is The Obvious Choice</a>, <a class="" href="https://austen.info/blog/actions-is-the-ai-native-platform">GitHub Actions: The AI-Native Platform</a>, <a class="" href="https://austen.info/blog/prompt-design-for-headless-agents">Prompt Design for Headless Agents</a>, and <a class="" href="https://austen.info/blog/future-of-developer-compute">The Future of Developer Compute</a>.</em></p>
<p>There are five ways to use AI in Actions, from simple inference to full agent frameworks.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-Copilot-Models-Basic-Inference">1. Copilot Models: Basic Inference<a href="https://austen.info/blog/agentic-workflows-on-github-events#1-Copilot-Models-Basic-Inference" class="hash-link" aria-label="Direct link to 1. Copilot Models: Basic Inference" title="Direct link to 1. Copilot Models: Basic Inference" translate="no">​</a></h2>
<p>Before you reach for agents, sometimes all you need is a single LLM call. <a href="https://docs.github.com/en/copilot/using-github-copilot/ai-models/using-ai-models-in-your-workflows" target="_blank" rel="noopener noreferrer" class="">Copilot Models</a> lets you call AI models directly from a workflow step. No agent loop, no tool use, just prompt in → response out.</p>
<p>This is perfect for tasks that don't need autonomy:</p>
<ul>
<li class="">Validate a PR title or description against a convention</li>
<li class="">Classify or label an issue</li>
<li class="">Generate release notes from a diff</li>
<li class="">Summarize a changelog</li>
<li class="">Generate mock data for tests</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Example-Auto-Label-Issues">Example: Auto-Label Issues<a href="https://austen.info/blog/agentic-workflows-on-github-events#Example-Auto-Label-Issues" class="hash-link" aria-label="Direct link to Example: Auto-Label Issues" title="Direct link to Example: Auto-Label Issues" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> Auto</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">Label Issue</span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">issues</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">types</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain">opened</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">issues</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">copilot-requests</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">label</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> austenstone/copilot</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">models@v1</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> classify</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">prompt</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">|</span><span class="token scalar string" style="color:rgb(163, 21, 21)"></span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            Classify this GitHub issue into exactly one category:</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            bug, feature, question, documentation</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">            </span><span class="token key atrule">Title</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> github.event.issue.title </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">            </span><span class="token key atrule">Body</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> github.event.issue.body </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">            Respond with ONLY the category label</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> nothing else.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> gh issue edit $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> github.event.issue.number </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">add</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">label "$</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> steps.classify.outputs.response </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain">"</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">GH_TOKEN</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><br></div></code></pre></div></div>
<p>No agent loop. No tool calling. Just a single inference call that returns a label. Sometimes that's all you need.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Copilot-Coding-Agent-Assign-and-Walk-Away">2. Copilot Coding Agent: Assign and Walk Away<a href="https://austen.info/blog/agentic-workflows-on-github-events#2-Copilot-Coding-Agent-Assign-and-Walk-Away" class="hash-link" aria-label="Direct link to 2. Copilot Coding Agent: Assign and Walk Away" title="Direct link to 2. Copilot Coding Agent: Assign and Walk Away" translate="no">​</a></h2>
<p>The <a href="https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent" target="_blank" rel="noopener noreferrer" class="">Copilot Coding Agent</a> is the simplest way to get an AI agent working on your code. Assign it to an issue, and it spins up on a GitHub-hosted runner, plans the fix, writes the code, runs tests, and opens a PR, all autonomously.</p>
<p>You can trigger it manually from the GitHub UI, or <strong>chain it from a workflow:</strong></p>
<ol>
<li class="">A workflow event fires (e.g., a new issue is labeled <code>autofix</code>)</li>
<li class="">A step assigns Copilot Coding Agent to the issue</li>
<li class="">CCA spins up on an ephemeral GHR, plans, codes, tests, opens a PR</li>
</ol>
<p>Fully automated chain: <strong>event → agent → PR → review.</strong> No human in the loop until it's time to merge.</p>
<p>This is the zero-config option. No prompts to write, no YAML to author. If your task maps to a well-scoped issue, CCA just handles it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-GitHub-Agentic-Workflows-gh-aw-The-Paved-Path">3. GitHub Agentic Workflows (<code>gh aw</code>): The Paved Path<a href="https://austen.info/blog/agentic-workflows-on-github-events#3-GitHub-Agentic-Workflows-gh-aw-The-Paved-Path" class="hash-link" aria-label="Direct link to 3-GitHub-Agentic-Workflows-gh-aw-The-Paved-Path" title="Direct link to 3-GitHub-Agentic-Workflows-gh-aw-The-Paved-Path" translate="no">​</a></h2>
<p><a href="https://github.github.com/gh-aw/" target="_blank" rel="noopener noreferrer" class="">GitHub Agentic Workflows</a> is purpose-built for repository automation, running coding agents with strong guardrails in GitHub Actions.</p>
<p><strong>Why to do it this way:</strong></p>
<ul>
<li class=""><strong>Markdown-defined workflows</strong>, write automation in markdown, not complex YAML</li>
<li class=""><strong>Event-triggered + scheduled</strong> like <code>on: schedule: daily</code>, <code>on: issues</code>, <code>on: pull_request</code>, etc.</li>
<li class=""><strong>Multiple AI engines</strong> including Copilot, Claude, Codex, and custom AI processors</li>
<li class=""><strong>Guardrails built-in:</strong>
<ul>
<li class="">Read-only permissions by default</li>
<li class="">Write operations require explicit approval through sanitized "safe outputs"</li>
<li class="">Sandboxed execution, tool allowlisting, network isolation</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Example-Daily-Issues-Report">Example: Daily Issues Report<a href="https://austen.info/blog/agentic-workflows-on-github-events#Example-Daily-Issues-Report" class="hash-link" aria-label="Direct link to Example: Daily Issues Report" title="Direct link to Example: Daily Issues Report" translate="no">​</a></h3>
<p>This entire automation is defined in markdown:</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">on:</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  schedule: daily</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">permissions:</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  contents: read</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  issues: read</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  pull-requests: read</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">safe-outputs:</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  create-issue:</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    title-prefix: "[team-status] "</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    labels: [report, daily-status]</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    close-older-issues: true</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token title important punctuation" style="color:rgb(4, 81, 165)">##</span><span class="token title important"> Daily Issues Report</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">Create an upbeat daily status report for the team as a GitHub issue.</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token title important punctuation" style="color:rgb(4, 81, 165)">##</span><span class="token title important"> What to include</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token list punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Recent repository activity (issues, PRs, discussions, releases)</span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token list punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Progress tracking and highlights</span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token list punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Actionable next steps for maintainers</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Use-Case-Gallery">Use Case Gallery<a href="https://austen.info/blog/agentic-workflows-on-github-events#Use-Case-Gallery" class="hash-link" aria-label="Direct link to Use Case Gallery" title="Direct link to Use Case Gallery" translate="no">​</a></h3>
<ul>
<li class=""><strong>Issue &amp; PR Management</strong>, automated triage, labeling, project coordination</li>
<li class=""><strong>Continuous Documentation</strong>, automated maintenance and consistency</li>
<li class=""><strong>Continuous Improvement</strong>, daily code simplification, refactoring</li>
<li class=""><strong>Metrics &amp; Analytics</strong>, daily reports, trend analysis, workflow health</li>
<li class=""><strong>Quality &amp; Testing</strong>, CI failure diagnosis, test improvements</li>
<li class=""><strong>Multi-Repository</strong>, feature sync and cross-repo tracking</li>
</ul>
<p>This is the paved path. If you're starting with agentic repo automation, start here.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Copilot-CLI-in-Actions-The-Raw-Power">4. Copilot CLI in Actions: The Raw Power<a href="https://austen.info/blog/agentic-workflows-on-github-events#4-Copilot-CLI-in-Actions-The-Raw-Power" class="hash-link" aria-label="Direct link to 4. Copilot CLI in Actions: The Raw Power" title="Direct link to 4. Copilot CLI in Actions: The Raw Power" translate="no">​</a></h2>
<p>Run Copilot CLI directly in your workflow steps. The same agent you use in VS Code or your terminal, but triggered on any GitHub event.</p>
<p><strong>Action:</strong> <a href="https://github.com/marketplace/actions/github-copilot-cli" target="_blank" rel="noopener noreferrer" class=""><code>austenstone/copilot-cli</code></a> (v3)</p>
<p><strong>Key capabilities:</strong></p>
<ul>
<li class=""><code>copilot-requests: write</code> permission, uses the default <code>GITHUB_TOKEN</code>, no PAT required</li>
<li class="">Full autopilot mode: <code>autopilot: true</code>, <code>no-ask-user: true</code> for fully autonomous CI</li>
<li class="">MCP server support for extending Copilot with custom tool integrations</li>
<li class="">Model selection including Claude, GPT-5, etc.</li>
<li class="">Session management to resume, share sessions as markdown or gists</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Example-AI-PR-Review-on-Every-Pull-Request">Example: AI PR Review on Every Pull Request<a href="https://austen.info/blog/agentic-workflows-on-github-events#Example-AI-PR-Review-on-Every-Pull-Request" class="hash-link" aria-label="Direct link to Example: AI PR Review on Every Pull Request" title="Direct link to Example: AI PR Review on Every Pull Request" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'Copilot Automation'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain">pull_request</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">copilot-requests</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">pull-requests</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">copilot</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> austenstone/copilot</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">cli@v3</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">          </span><span class="token key atrule">prompt</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">|</span><span class="token scalar string" style="color:rgb(163, 21, 21)"></span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            Review this pull request for:</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            1. Code quality and best practices</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            2. Security vulnerabilities</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            3. Performance implications</span><br></div><div class="token-line" style="color:#000000"><span class="token scalar string" style="color:rgb(163, 21, 21)">            4. Documentation completeness</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-Copilot-SDK-Full-Control">5. Copilot SDK: Full Control<a href="https://austen.info/blog/agentic-workflows-on-github-events#5-Copilot-SDK-Full-Control" class="hash-link" aria-label="Direct link to 5. Copilot SDK: Full Control" title="Direct link to 5. Copilot SDK: Full Control" translate="no">​</a></h2>
<p>If you need the raw model APIs, custom orchestration, or your own agent framework, the <a href="https://github.blog/news-insights/company-news/build-an-agent-into-any-app-with-the-github-copilot-sdk/" target="_blank" rel="noopener noreferrer" class="">Copilot SDK</a> gives you that. Build your own agents with the same models that power Copilot, running in Actions.</p>
<p>This is the power user escape hatch for teams building custom AI tooling on top of the Copilot models.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Choosing-Your-Approach">Choosing Your Approach<a href="https://austen.info/blog/agentic-workflows-on-github-events#Choosing-Your-Approach" class="hash-link" aria-label="Direct link to Choosing Your Approach" title="Direct link to Choosing Your Approach" translate="no">​</a></h2>













































<table><thead><tr><th></th><th><strong>Copilot Models</strong></th><th><strong>Coding Agent</strong></th><th><strong>gh aw</strong></th><th><strong>Copilot CLI</strong></th><th><strong>Copilot SDK</strong></th></tr></thead><tbody><tr><td><strong>Complexity</strong></td><td>Trivial</td><td>Zero-config</td><td>Low (markdown)</td><td>Medium (YAML + prompts)</td><td>High (code)</td></tr><tr><td><strong>Autonomy</strong></td><td>None (single call)</td><td>Full (issue → PR)</td><td>Guided (guardrails)</td><td>Full (autopilot)</td><td>You decide</td></tr><tr><td><strong>Guardrails</strong></td><td>N/A</td><td>Built-in</td><td>Built-in</td><td>You configure</td><td>You build</td></tr><tr><td><strong>Best for</strong></td><td>Classification, summarization</td><td>Well-scoped issues</td><td>Repo automation</td><td>Any CI task</td><td>Custom agent frameworks</td></tr></tbody></table>
<p>Start with <strong>Copilot Models</strong> for single inference calls. Use the <strong>Coding Agent</strong> when the task maps to an issue. Reach for <strong>gh aw</strong> for recurring repo automation. Use <strong>Copilot CLI</strong> when you need full CI integration with custom prompts. Drop to the <strong>SDK</strong> when you need complete control.</p>
<p>📊 <strong><a href="https://austen.info/slides/ai-native-cicd-series.html#part3" target="_blank" rel="noopener noreferrer" class="">View the visual presentation →</a></strong></p>
<p><em>Next up: <a class="" href="https://austen.info/blog/prompt-design-for-headless-agents">Prompt Design for Headless AI Agents</a>, the part nobody talks about. How do you write prompts that work when there's no human at the keyboard?</em></p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>GitHub Copilot</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Why GitHub Actions Is the Most AI-Friendly CI]]></title>
            <link>https://austen.info/blog/actions-is-the-ai-native-platform</link>
            <guid>https://austen.info/blog/actions-is-the-ai-native-platform</guid>
            <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[It's almost like Actions was made for the agentic era.]]></description>
            <content:encoded><![CDATA[<p>Actions isn't just your CI/CD pipeline. It's the execution layer for much of the GitHub platform — and it's purpose-built for the agentic era.</p>
<!-- -->
<p><em>This is part 2 of a series on AI-native CI/CD. See also: <a class="" href="https://austen.info/blog/automation-is-the-obvious-choice">Automation is The Obvious Choice</a>, <a class="" href="https://austen.info/blog/agentic-workflows-on-github-events">Agentic Workflows on GitHub Events</a>, <a class="" href="https://austen.info/blog/prompt-design-for-headless-agents">Prompt Design for Headless Agents</a>, and <a class="" href="https://austen.info/blog/future-of-developer-compute">The Future of Developer Compute</a>.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-Actions-Is-Purpose-Built-for-AI">Why Actions Is Purpose-Built for AI<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Why-Actions-Is-Purpose-Built-for-AI" class="hash-link" aria-label="Direct link to Why Actions Is Purpose-Built for AI" title="Direct link to Why Actions Is Purpose-Built for AI" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="LLMs-Understand-It-Best">LLMs Understand It Best<a href="https://austen.info/blog/actions-is-the-ai-native-platform#LLMs-Understand-It-Best" class="hash-link" aria-label="Direct link to LLMs Understand It Best" title="Direct link to LLMs Understand It Best" translate="no">​</a></h3>
<p>GitHub Actions has the largest usage base of any CI platform, which means the most training data for LLMs. AI models write better Actions YAML than any other CI system because they've seen more of it. More examples, more documentation, more community workflows → better AI-generated output.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Building-Block-Architecture-Is-Perfect-for-AI">Building Block Architecture Is Perfect for AI<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Building-Block-Architecture-Is-Perfect-for-AI" class="hash-link" aria-label="Direct link to Building Block Architecture Is Perfect for AI" title="Direct link to Building Block Architecture Is Perfect for AI" translate="no">​</a></h3>
<p>Actions' composable approach (actions, reusable workflows, composite actions) creates <strong>black boxes with clear interfaces</strong>. AIs excel when they can reason about well-defined inputs/outputs without understanding internal complexity.</p>
<p><code>uses: actions/checkout@v5</code> is easier for an LLM than "write the equivalent 20 lines of shell script."</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Reusability--Token-Efficiency">Reusability = Token Efficiency<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Reusability--Token-Efficiency" class="hash-link" aria-label="Direct link to Reusability = Token Efficiency" title="Direct link to Reusability = Token Efficiency" translate="no">​</a></h3>
<p>Reusable workflows, shared actions, org-level templates → <strong>call once, use everywhere.</strong> Token efficiency matters when agents are running 10x more jobs.</p>
<p>Actions does this better than any competitor: reusable workflows across repos, composite actions, the Actions marketplace with 20K+ pre-built blocks. Every time you call a pre-built action instead of inline script, you save tokens AND reduce error surface.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Repo-Centric-Security--Safe-by-Default">Repo-Centric Security = Safe by Default<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Repo-Centric-Security--Safe-by-Default" class="hash-link" aria-label="Direct link to Repo-Centric Security = Safe by Default" title="Direct link to Repo-Centric Security = Safe by Default" translate="no">​</a></h3>
<p>Actions' limited, per-repo permission model is <strong>secure by default</strong>. No global admin key that unlocks everything. Developers can go wild experimenting → organizational governance via org-level policies, required workflows, IP allow lists.</p>
<p>This is critical for agentic workloads: you WANT agents to experiment freely within a safe boundary. Reduces friction → faster adoption → compounding AI value.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Whats-Already-Running-on-Actions">What's Already Running on Actions<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Whats-Already-Running-on-Actions" class="hash-link" aria-label="Direct link to What's Already Running on Actions" title="Direct link to What's Already Running on Actions" translate="no">​</a></h2>
<p>Before we talk about what you can build, let's talk about what's <strong>already</strong> running on Actions — things you use every day that you might not realize are Actions under the hood.</p>

































<table><thead><tr><th>Feature</th><th>How It Uses Actions</th></tr></thead><tbody><tr><td><strong>Copilot Code Review</strong></td><td>Runs as a PR check via Actions. LLM + CodeQL/ESLint analyze your PR.</td></tr><tr><td><strong>Copilot Coding Agent</strong></td><td>Picks up issues, plans, codes, tests, opens PRs — all on GitHub-hosted runners.</td></tr><tr><td><strong>Dependabot</strong></td><td>Version updates, security alerts, and auto-PRs — all orchestrated through Actions workflows.</td></tr><tr><td><strong>GHAS / CodeQL</strong></td><td>Code scanning, secret scanning, and security analysis. Runs as Actions workflows on your code.</td></tr><tr><td><strong>Codespace Prebuilds</strong></td><td>Prebuilds for Codespaces run on Actions to keep dev environments fast and ready.</td></tr><tr><td><strong>GitHub Pages</strong></td><td>Site builds and deployments run as Actions workflows.</td></tr></tbody></table>
<p>If you're using Copilot Code Review, that's Actions. Dependabot? Actions. CodeQL scanning? Actions. GitHub Pages? Actions. Codespace prebuilds? Actions. The Coding Agent? Actions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Execution-Layer">The Execution Layer<a href="https://austen.info/blog/actions-is-the-ai-native-platform#The-Execution-Layer" class="hash-link" aria-label="Direct link to The Execution Layer" title="Direct link to The Execution Layer" translate="no">​</a></h2>
<p>Actions is the compute substrate that much of the GitHub platform runs on. Every feature listed above is just a workflow triggered by a GitHub event, running on a runner, producing an output. The same primitives you use for CI/CD.</p>
<p>This matters because it means <strong>you can build on that same layer.</strong> The same event triggers, the same runner infrastructure, the same security model that powers GitHub's own features — all available to you.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Event-System">The Event System<a href="https://austen.info/blog/actions-is-the-ai-native-platform#The-Event-System" class="hash-link" aria-label="Direct link to The Event System" title="Direct link to The Event System" translate="no">​</a></h3>
<p>GitHub exposes <a href="https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows" target="_blank" rel="noopener noreferrer" class="">dozens of webhook events</a> that can trigger workflows:</p>
<ul>
<li class=""><code>push</code>, <code>pull_request</code></li>
<li class=""><code>issues</code>, <code>issue_comment</code></li>
<li class=""><code>discussion</code>, <code>discussion_comment</code></li>
<li class=""><code>schedule</code></li>
<li class=""><code>workflow_dispatch</code></li>
<li class="">etc.</li>
</ul>
<p>Every one of these is a hook you can attach automation to. And with AI agents now cheap enough to run on any event, the question shifts from "what <em>can</em> we automate?" to "what <em>should</em> we automate first?"</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-This-Matters-for-AI">Why This Matters for AI<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Why-This-Matters-for-AI" class="hash-link" aria-label="Direct link to Why This Matters for AI" title="Direct link to Why This Matters for AI" translate="no">​</a></h3>
<p>The fact that Actions is the platform — not just a CI tool — has a compounding effect on AI adoption:</p>
<ol>
<li class=""><strong>One execution model to learn.</strong> If you know how to write a workflow, you know how to orchestrate an AI agent.</li>
<li class=""><strong>One security model.</strong> Permissions, secrets, OIDC — all the same whether you're building a Docker image or running an LLM.</li>
<li class=""><strong>One observability story.</strong> Workflow logs, job summaries, Actions Data Stream — the same tools for CI and for agent monitoring.</li>
<li class=""><strong>Network effects.</strong> Every improvement GitHub makes to Actions benefits your CI/CD AND your AI automations simultaneously.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Thesis-AI-Amplifies-CICD">The Thesis: AI Amplifies CI/CD<a href="https://austen.info/blog/actions-is-the-ai-native-platform#The-Thesis-AI-Amplifies-CICD" class="hash-link" aria-label="Direct link to The Thesis: AI Amplifies CI/CD" title="Direct link to The Thesis: AI Amplifies CI/CD" translate="no">​</a></h2>
<p>AI agents are non-deterministic by nature. They generate different code each time, make judgment calls, and experiment. You need <strong>strong deterministic automation suites</strong> wrapped around them (tests, linting, security scanning, deployment gates) to catch what agents get wrong.</p>
<p>Without robust CI/CD, you're shipping AI-generated code with no safety net. CI/CD is the guardrail layer that makes agentic workflows safe enough to trust. The better your CI/CD pipeline, the more autonomy you can give your agents, and the more value you get from them.</p>
<blockquote>
<p>The more you delegate to agents, the more you need ironclad testing, scanning, and validation to catch what they get wrong. CI/CD isn't a nice-to-have anymore. It's the control plane for AI-generated code.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-GitHub-Hosted-Runners">Why GitHub-Hosted Runners<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Why-GitHub-Hosted-Runners" class="hash-link" aria-label="Direct link to Why GitHub-Hosted Runners" title="Direct link to Why GitHub-Hosted Runners" translate="no">​</a></h2>
<p>The platform story matters, but so does <em>where</em> the agents run. There are five operational concerns that matter when choosing runner infrastructure: <strong>Security, Performance, Availability, Total Cost of Ownership (TCO), and Troubleshooting.</strong> GHRs win on all five.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Security-Ephemeral-by-Default">Security: Ephemeral by Default<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Security-Ephemeral-by-Default" class="hash-link" aria-label="Direct link to Security: Ephemeral by Default" title="Direct link to Security: Ephemeral by Default" translate="no">​</a></h3>
<p>Imagine someone gets onto a self-hosted runner in your data center through a supply chain attack, a compromised npm package, a malicious action, a poisoned container image. They now have a foothold inside your network. From a self-hosted runner, they can potentially reach your internal services, your databases, your secrets. That runner persists between jobs. It's on your network.</p>
<p><strong>GitHub-hosted runners (GHRs) eliminate this:</strong></p>
<ul>
<li class=""><strong>Ephemeral</strong> — fresh VM every job, destroyed after. No persistence, no lateral movement.</li>
<li class=""><strong>Isolated</strong> — no shared state between jobs, even from the same repo</li>
<li class=""><strong>Network-isolated by default</strong> — VNET injection only when YOU choose to open access</li>
<li class=""><strong>GitHub owns the SLA</strong> — patching, hardening, compliance is GitHub's problem</li>
</ul>
<p>Now multiply that risk by what agents do. AI agents run code they generate themselves. They install packages. They make API calls. They iterate. If that's happening on a persistent runner in your network, that's a surface area problem. On ephemeral GHRs, every agent run starts clean and ends clean.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Right-Sizing-with-Telemetry">Right-Sizing with Telemetry<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Right-Sizing-with-Telemetry" class="hash-link" aria-label="Direct link to Right-Sizing with Telemetry" title="Direct link to Right-Sizing with Telemetry" translate="no">​</a></h3>
<p>Most teams pick <code>ubuntu-latest</code> and never think about it again. That's leaving performance and money on the table.</p>
<p>GitHub doesn't offer built-in per-step compute telemetry (CPU, memory, disk I/O) out of the box. But you can add it yourself with <a href="https://opentelemetry.io/" target="_blank" rel="noopener noreferrer" class="">OpenTelemetry</a>. The simplest approach is a drop-in action like <a href="https://github.com/marketplace/actions/workflow-telemetry" target="_blank" rel="noopener noreferrer" class="">Workflow Telemetry</a>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> tsviz/actions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">runner</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">telemetry@v1</span><br></div></code></pre></div></div>
<p>This gives you per-step CPU usage, memory consumption, disk I/O, and network stats right in your workflow summary. No config, no infrastructure. One line.</p>
<p>For deeper instrumentation, the <a href="https://github.com/marketplace/actions/open-telemetry-ci-cd-action" target="_blank" rel="noopener noreferrer" class="">OpenTelemetry CI/CD Action</a> exports full OTLP traces that you can ship to Datadog, Honeycomb, Grafana, or any OTEL-compatible backend. Now you're not just seeing "this job took 8 minutes," you're seeing <em>which steps were CPU-bound, which were waiting on network, and where memory peaked.</em></p>
<p>If you're using custom runner images (and you should be), you can go even further: install the <a href="https://opentelemetry.io/docs/collector/" target="_blank" rel="noopener noreferrer" class="">OpenTelemetry Collector</a> directly on the base image. Every workflow that runs on that image gets telemetry for free without any workflow changes.</p>
<p>This data is what lets you right-size. If your 8-core runner is peaking at 30% CPU, drop to a 4-core. If your builds are OOM-killing on a 2-core, step up. Without telemetry, you're guessing. With it, you're making data-driven decisions that directly impact cost and performance.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Custom-Runner-Images">Custom Runner Images<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Custom-Runner-Images" class="hash-link" aria-label="Direct link to Custom Runner Images" title="Direct link to Custom Runner Images" translate="no">​</a></h3>
<p>Step two is what's ON the runner when it starts.</p>
<ul>
<li class="">Pre-install your dependencies, SDKs, build tools in the image</li>
<li class="">Attach caches directly to the image — no download step, no cache miss</li>
<li class="">Use pre-job and post-job hooks for setup/teardown automation</li>
</ul>
<p>Real example: the <code>github/github</code> repo went from ~60 min builds to &lt;10 min builds — a <strong>6x improvement</strong> from image optimization alone. Not code changes. Not runner changes. Just baking dependencies into the image.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Private-Networking">Private Networking<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Private-Networking" class="hash-link" aria-label="Direct link to Private Networking" title="Direct link to Private Networking" translate="no">​</a></h3>
<p>The #1 objection I hear: "We'd love to use GitHub-hosted runners, but we need to reach private resources."</p>
<p>There are multiple ways to solve this depending on your cloud provider, and none of them require self-hosted runners.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="Azure-VNET-Injection">Azure: VNET Injection<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Azure-VNET-Injection" class="hash-link" aria-label="Direct link to Azure: VNET Injection" title="Direct link to Azure: VNET Injection" translate="no">​</a></h4>
<p>The most native option. Azure Private Networking lets GitHub provision GHRs directly into your Azure VNET with a private NIC. You pick the region, GitHub spins up ephemeral VMs that flow through your private network.</p>
<ul>
<li class="">Access private container registries, internal APIs, databases with no VPN, no bastion</li>
<li class="">Combine with OIDC for zero-trust auth, no long-lived secrets</li>
<li class="">Available for Linux, Windows, AND macOS</li>
</ul>
<p>This isn't "connecting" to your network. The runner <em>is</em> on your network.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="AWS--GCP-OIDC--API-Gateway">AWS &amp; GCP: OIDC + API Gateway<a href="https://austen.info/blog/actions-is-the-ai-native-platform#AWS--GCP-OIDC--API-Gateway" class="hash-link" aria-label="Direct link to AWS &amp; GCP: OIDC + API Gateway" title="Direct link to AWS &amp; GCP: OIDC + API Gateway" translate="no">​</a></h4>
<p>For AWS and GCP, the pattern is OIDC federation paired with an API gateway. GitHub Actions mints a short-lived OIDC token per job. Your cloud provider validates that token and grants scoped, temporary credentials.</p>
<p><strong>AWS:</strong> Use <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services" target="_blank" rel="noopener noreferrer" class="">OIDC federation with IAM</a> to assume roles. For private resources behind a VPC, front them with API Gateway (with IAM auth) or AWS PrivateLink. The OIDC claims (repo, branch, environment) become your access control policy. No static keys anywhere.</p>
<p><strong>GCP:</strong> Same concept via <a href="https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform" target="_blank" rel="noopener noreferrer" class="">Workload Identity Federation</a>. Authenticate as a service account scoped to the specific repo/environment, then hit private resources through Identity-Aware Proxy or a Cloud Endpoints gateway.</p>
<p>In both cases, OIDC claims are the key. You're not granting "GitHub Actions" access to your cloud. You're granting <em>this specific repo, on this specific branch, in this specific environment</em> access to exactly the resources it needs.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="Any-Cloud-Tailscale--WireGuard-Overlay">Any Cloud: Tailscale / WireGuard Overlay<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Any-Cloud-Tailscale--WireGuard-Overlay" class="hash-link" aria-label="Direct link to Any Cloud: Tailscale / WireGuard Overlay" title="Direct link to Any Cloud: Tailscale / WireGuard Overlay" translate="no">​</a></h4>
<p>For teams that span multiple clouds or have on-prem resources, a mesh VPN overlay like <a href="https://tailscale.com/" target="_blank" rel="noopener noreferrer" class="">Tailscale</a> or WireGuard is a clean solution. Install the Tailscale client as a workflow step, authenticate via OIDC, and the ephemeral runner joins your tailnet for the duration of the job.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> tailscale/github</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">action@v3</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">oauth-client-id</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> secrets.TS_OAUTH_CLIENT_ID </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">oauth-secret</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> secrets.TS_OAUTH_SECRET </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">    </span><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> tag</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain">ci</span><br></div></code></pre></div></div>
<p>The runner gets a Tailscale IP, can reach any node on your tailnet, and the connection dies with the VM. Works across AWS, GCP, Azure, and on-prem simultaneously. Most teams combine this OIDC claims.</p>
<p>Whatever your cloud, the networking story is solved. This is a proper and modern network security model.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Availability-GitHub-Owns-the-SLA">Availability: GitHub Owns the SLA<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Availability-GitHub-Owns-the-SLA" class="hash-link" aria-label="Direct link to Availability: GitHub Owns the SLA" title="Direct link to Availability: GitHub Owns the SLA" translate="no">​</a></h3>
<p>Self-hosted runner fleets are an operational burden that never sleeps. Someone on your platform team is responsible for uptime, patching, scaling, and disaster recovery. When a runner goes down at 2 AM, that's <em>your</em> page.</p>
<p>With GHRs, GitHub owns all of it:</p>
<ul>
<li class=""><strong>Auto-scaling</strong> — burst from 10 to 500 concurrent runners instantly, no capacity planning</li>
<li class=""><strong>Multi-region</strong> — GitHub provisions in multiple regions, no single point of failure</li>
<li class=""><strong>Zero maintenance</strong> — OS patches, security updates, runner agent upgrades, all handled by GitHub</li>
<li class=""><strong>99.9% SLA</strong> — backed by GitHub's infrastructure team, not your on-call rotation</li>
</ul>
<p>Your platform engineers should be building internal developer platforms, not babysitting runner fleets.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="TCO-The-Buffet-Minute">TCO: The Buffet Minute<a href="https://austen.info/blog/actions-is-the-ai-native-platform#TCO-The-Buffet-Minute" class="hash-link" aria-label="Direct link to TCO: The Buffet Minute" title="Direct link to TCO: The Buffet Minute" translate="no">​</a></h3>
<p>The sticker price of GHRs looks higher per-minute than running your own VMs. But sticker price is the wrong lens. GHRs are an <strong>all-inclusive "buffet minute"</strong>: compute, networking, storage, OS licensing, patching, monitoring, scaling, and incident response are all baked in.</p>
<p>Self-hosted runners carry massive hidden costs:</p>
<ul>
<li class=""><strong>Headcount</strong> — someone has to manage the fleet (ARC/K8s clusters, VM images, autoscaling)</li>
<li class=""><strong>Idle compute</strong> — you're paying for VMs sitting empty between jobs</li>
<li class=""><strong>Networking egress</strong> — data transfer costs that don't show up on the runner bill</li>
<li class=""><strong>Incident response</strong> — when runners break, your pipeline stops and developers wait</li>
</ul>
<p>The January 2026 <strong>30% price drop</strong> on GHRs shifted the math dramatically. For most organizations, GHRs are now cheaper than self-hosted when you account for total cost of ownership.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Troubleshooting-One-Pane-of-Glass">Troubleshooting: One Pane of Glass<a href="https://austen.info/blog/actions-is-the-ai-native-platform#Troubleshooting-One-Pane-of-Glass" class="hash-link" aria-label="Direct link to Troubleshooting: One Pane of Glass" title="Direct link to Troubleshooting: One Pane of Glass" translate="no">​</a></h3>
<p>When a build fails on a self-hosted runner, the debugging surface area is enormous. Is it the runner? The network? A stale cache from a previous job? A dependency that was installed by another workflow and left behind? An OS patch that changed behavior?</p>
<p>GHRs collapse the troubleshooting space:</p>
<ul>
<li class=""><strong>Clean every time</strong> — if it worked yesterday and fails today, the runner isn't the variable</li>
<li class=""><strong>Standardized environment</strong> — same OS, same tools, same network config across all runs</li>
<li class=""><strong>Workflow logs</strong> — complete step-by-step execution logs, no SSH-ing into machines</li>
<li class=""><strong>Actions Data Stream</strong> — real-time telemetry fed into Datadog, Splunk, Elastic for org-wide observability</li>
<li class=""><strong>Job summaries</strong> — structured output right in the PR, no digging through CI artifacts</li>
</ul>
<p>When something breaks, you're debugging your code, not your infrastructure. That's a fundamentally different (and faster) troubleshooting experience.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Full-Picture">The Full Picture<a href="https://austen.info/blog/actions-is-the-ai-native-platform#The-Full-Picture" class="hash-link" aria-label="Direct link to The Full Picture" title="Direct link to The Full Picture" translate="no">​</a></h3>



































<table><thead><tr><th>Concern</th><th>GHR</th><th>SHR</th></tr></thead><tbody><tr><td><strong>Security</strong></td><td>Ephemeral VMs, SLSA L3, zero persistent attack surface</td><td>Persistent VMs on your network, shared state between jobs</td></tr><tr><td><strong>Performance</strong></td><td>Right-size with telemetry, custom images, 2-64 vCPU options</td><td>Full control but you own image management and scaling</td></tr><tr><td><strong>Availability</strong></td><td>GitHub SLA, auto-scaling, multi-region, zero maintenance</td><td>Your team's on-call, your capacity planning, your patches</td></tr><tr><td><strong>TCO</strong></td><td>All-inclusive buffet minute, 30% price drop (Jan 2026)</td><td>Hidden costs: headcount, idle compute, networking, incidents</td></tr><tr><td><strong>Troubleshooting</strong></td><td>Clean VMs, standardized env, Actions Data Stream</td><td>Debugging infra + code simultaneously, stale state issues</td></tr></tbody></table>
<p>Convenient? Yes. But the real value is security, availability, and freeing your platform team from runner management.</p>
<p>📊 <strong><a href="https://austen.info/slides/ai-native-cicd-series.html#part2" target="_blank" rel="noopener noreferrer" class="">View the visual presentation →</a></strong></p>
<p><em>Next up: <a class="" href="https://austen.info/blog/agentic-workflows-on-github-events">Agentic Workflows on GitHub Events</a> — triggering AI agents on any GitHub event, safely and autonomously.</em></p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Automation is The Obvious Choice]]></title>
            <link>https://austen.info/blog/automation-is-the-obvious-choice</link>
            <guid>https://austen.info/blog/automation-is-the-obvious-choice</guid>
            <pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The ROI bar for automation just hit the floor. AI didn't just make coding faster, it made building automations so cheap that NOT automating is the expensive choice.]]></description>
            <content:encoded><![CDATA[<p>The ROI bar for automation just hit the floor. AI didn't just make coding faster, it made building automations so cheap that NOT automating is the expensive choice.</p>
<!-- -->
<p><em>This is part 1 of a series on AI-native CI/CD. See also: <a class="" href="https://austen.info/blog/actions-is-the-ai-native-platform">GitHub Actions: The AI-Native Platform</a>, <a class="" href="https://austen.info/blog/agentic-workflows-on-github-events">Agentic Workflows on GitHub Events</a>, <a class="" href="https://austen.info/blog/prompt-design-for-headless-agents">Prompt Design for Headless Agents</a>, and <a class="" href="https://austen.info/blog/future-of-developer-compute">The Future of Developer Compute</a>.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Trap-Doing-the-Same-Work-Faster">The Trap: Doing the Same Work Faster<a href="https://austen.info/blog/automation-is-the-obvious-choice#The-Trap-Doing-the-Same-Work-Faster" class="hash-link" aria-label="Direct link to The Trap: Doing the Same Work Faster" title="Direct link to The Trap: Doing the Same Work Faster" translate="no">​</a></h2>
<p>If you're using Copilot or a coding agent today, you've probably noticed you're crushing Jira tickets in a fraction of the time. That feels great. But here's the thing: being able to write code faster doesn't mean you can ship 10x faster. The bottleneck was almost never your programming abilities or typing speed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Where-Is-Your-Time-Actually-Going">Where Is Your Time Actually Going?<a href="https://austen.info/blog/automation-is-the-obvious-choice#Where-Is-Your-Time-Actually-Going" class="hash-link" aria-label="Direct link to Where Is Your Time Actually Going?" title="Direct link to Where Is Your Time Actually Going?" translate="no">​</a></h2>
<p>Writing code? Maybe 20% of your day. The rest:</p>
<ul>
<li class="">Code reviews</li>
<li class="">Debugging CI failures</li>
<li class="">Triaging issues</li>
<li class="">Updating docs</li>
<li class="">Fixing flaky tests</li>
<li class="">Provisioning infrastructure</li>
<li class="">Responding to alerts</li>
<li class="">Context-switching between repos</li>
</ul>
<p>Everyone should constantly be asking: <strong>"What am I spending my time on, and can this be automated?"</strong></p>
<p>That's what we do. We're engineers. We solve problems and we automate.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Old-Automation-Math">The Old Automation Math<a href="https://austen.info/blog/automation-is-the-obvious-choice#The-Old-Automation-Math" class="hash-link" aria-label="Direct link to The Old Automation Math" title="Direct link to The Old Automation Math" translate="no">​</a></h2>
<p>Automation was always valuable, but the ROI bar was high. Building an automation had to be worth the time investment. It needed large scale and a long lifetime to justify the cost.</p>
<p>So most teams... didn't automate. It was easier to just do the task manually. This becomes the norm. Organizations sink into it. Manual processes calcify. "That's just how we do it."</p>
<p>GitHub Actions was built to change this:</p>
<ul>
<li class="">Remove as much friction as possible, making automation easy by default</li>
<li class="">Build a DRY, reusable story that leans on open source and encourages innersource for private enterprises</li>
<li class="">Building blocks (Actions) that are composable, traceable, and version-pinned</li>
<li class="">Extremely easy to do most things, possible to do almost anything</li>
<li class="">The philosophy: if automation is frictionless enough, people will actually do it</li>
</ul>
<p>But even with Actions, there were limits. It wasn't feasible to automate every task for every repo. Some automations were too bespoke, too context-dependent, too expensive to build. There were always plenty of automations we wished existed but couldn't justify building.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Just-Automate-it">Just Automate it<a href="https://austen.info/blog/automation-is-the-obvious-choice#Just-Automate-it" class="hash-link" aria-label="Direct link to Just Automate it" title="Direct link to Just Automate it" translate="no">​</a></h2>
<p>We've entered a new world. <strong>If a task can be automated, then choosing to automate it is almost always the better choice.</strong> The cost of building automations has collapsed. AI can generate the workflow, write the action, compose the pipeline. The ROI bar that used to filter out 80% of automation ideas? It's basically on the floor.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Compounding-Factor">The Compounding Factor<a href="https://austen.info/blog/automation-is-the-obvious-choice#The-Compounding-Factor" class="hash-link" aria-label="Direct link to The Compounding Factor" title="Direct link to The Compounding Factor" translate="no">​</a></h2>
<p>This is where it gets exponential, not linear:</p>
<ol>
<li class=""><strong>Use AI to build "traditional" tools</strong> like tests, linters, deployment pipelines, and monitoring</li>
<li class="">Those tools <strong>continue to propel you faster</strong>, even when the AI isn't running</li>
<li class="">Every automation you build runs thousands of times across dozens of repos</li>
<li class=""><strong>The AI builds the automation, the automation keeps compounding</strong></li>
</ol>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">AI writes automation → automation runs 1000x → saves 1000x manual effort</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">                       ↑</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">                       └── this keeps running even when AI is idle</span><br></div></code></pre></div></div>
<p>Stop using AI just to write features faster. Start using AI to build the automations that make your entire team faster. That's where the real leverage is.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Mental-Shift">The Mental Shift<a href="https://austen.info/blog/automation-is-the-obvious-choice#The-Mental-Shift" class="hash-link" aria-label="Direct link to The Mental Shift" title="Direct link to The Mental Shift" translate="no">​</a></h2>
<p>The question is no longer "is this worth automating?" It almost always is now. The new questions are:</p>
<ul>
<li class=""><strong>What should I automate first?</strong> (highest frequency × highest toil)</li>
<li class=""><strong>How do I make this automation trustworthy?</strong> (testing, guardrails, monitoring)</li>
<li class=""><strong>How do I scale this across my org?</strong> (reusable workflows, templates, innersource)</li>
</ul>
<p>AI productivity becomes exponential when you stop using it for linear speedups and start using it to build the automation infrastructure that compounds over time.</p>
<p>📊 <strong><a href="https://austen.info/slides/ai-native-cicd-series.html#part1" target="_blank" rel="noopener noreferrer" class="">View the visual presentation →</a></strong></p>
<p><em>Next up: <a class="" href="https://austen.info/blog/actions-is-the-ai-native-platform">GitHub Actions: The AI-Native Platform</a>. You'd be surprised what's already running on Actions under the hood.</em></p>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[The Future of SDLC Automation]]></title>
            <link>https://austen.info/blog/the-future-of-sdlc-automation</link>
            <guid>https://austen.info/blog/the-future-of-sdlc-automation</guid>
            <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How to use AI to enhance your development workflows]]></description>
            <content:encoded><![CDATA[<p>Discussions around AI frequently revolve around this end goal of automation. Where AI is an autonomous software engineer, a note taker, or even your personal chauffeur.</p>
<p>In reality most people are using AI as a tool to improve aspects of our workflows rather than building full blown automated solutions.</p>
<!-- -->
<ul>
<li class="">AI tooling can help us <strong>build</strong> traditional automations <strong>faster</strong>. Writing scripts has never been easier with the help of AI powered development tools.</li>
<li class="">Developers can incorporate AI directly into their software to perform <strong>inference.</strong> This unlocks new use cases by giving us probabilistic functions instead of just deterministic ones.</li>
<li class="">An <strong>Agent</strong> equipped with tools can act as a probabilistic runtime <strong>loop</strong> for any task.</li>
</ul>
<p>All of these automations need to run somewhere, and many of them related to software will live on a CI/CD platform.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="CICD-History">CI/CD History<a href="https://austen.info/blog/the-future-of-sdlc-automation#CICD-History" class="hash-link" aria-label="Direct link to CI/CD History" title="Direct link to CI/CD History" translate="no">​</a></h2>
<p>The advancements in CI solutions over the last couple decades didn’t just make developers more efficient but they also perfectly support AI developer tools.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-First-Wave-2007">The First Wave (2007)<a href="https://austen.info/blog/the-future-of-sdlc-automation#The-First-Wave-2007" class="hash-link" aria-label="Direct link to The First Wave (2007)" title="Direct link to The First Wave (2007)" translate="no">​</a></h3>
<p>Tools like <strong>Jenkins</strong> introduced CI servers, solving the problem of manual builds but eventually leading to "maintenance hell" due to the manual creation and management of hundreds of jobs.</p>
<p>It’s critical that AI automation happens in a safe and isolated environment.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Second-Wave-20132019">The Second Wave (2013–2019)<a href="https://austen.info/blog/the-future-of-sdlc-automation#The-Second-Wave-20132019" class="hash-link" aria-label="Direct link to The Second Wave (2013–2019)" title="Direct link to The Second Wave (2013–2019)" translate="no">​</a></h3>
<p>Platforms like <strong>TravisCI</strong> and eventually <strong>GitLab CI</strong> popularized <strong>"Pipeline-as-Code."</strong> This allowed developers to define pipelines in code repositories. While GitLab improved reusability with templates, pipelines still often required writing repetitive boilerplate code.</p>
<p>AI needs a language to describe structured pipelines.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Third-Wave-Late-2019">The Third Wave (Late 2019+)<a href="https://austen.info/blog/the-future-of-sdlc-automation#The-Third-Wave-Late-2019" class="hash-link" aria-label="Direct link to The Third Wave (Late 2019+)" title="Direct link to The Third Wave (Late 2019+)" translate="no">​</a></h3>
<p><strong>GitHub Actions</strong> sets itself apart by moving beyond just defining pipelines in code. The "Actions" themselves serve as <strong>modular, reusable building blocks</strong>. Actions also made CI more <strong>event-driven</strong> by exposing more events to trigger these automations.</p>
<p>AI improves with lots of training data and it can leverage modular reusable blocks well.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-GitHub-Actions">Why GitHub Actions?<a href="https://austen.info/blog/the-future-of-sdlc-automation#Why-GitHub-Actions" class="hash-link" aria-label="Direct link to Why GitHub Actions?" title="Direct link to Why GitHub Actions?" translate="no">​</a></h3>
<p>With this modular reusable building blocks story you can now create robust automations quicker than ever before.</p>
<p>GitHub’s community is massive. It’s the home for open source. The Actions marketplace will only continue to grow with new and improved building blocks. AI will continue to leverage these building blocks and there will be a large amount of training data available.</p>
<p>GitHub has the infrastructure to scale with the backing of Microsoft Azure. They give away enormous amounts of compute for free to the open source community.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="What-does-the-future-of-automation-look-like">What does the future of automation look like?<a href="https://austen.info/blog/the-future-of-sdlc-automation#What-does-the-future-of-automation-look-like" class="hash-link" aria-label="Direct link to What does the future of automation look like?" title="Direct link to What does the future of automation look like?" translate="no">​</a></h2>
<p>AI developer tools have accelerated development, allowing us to build automations we never previously had time for. With Copilot you can asynchronously vibe script just about any automation task with great accuracy and small time investment.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="What-is-hard-to-automate">What is “hard” to automate?<a href="https://austen.info/blog/the-future-of-sdlc-automation#What-is-hard-to-automate" class="hash-link" aria-label="Direct link to What is “hard” to automate?" title="Direct link to What is “hard” to automate?" translate="no">​</a></h3>
<p>We will eventually hit a wall with automation. A limit on what we can script with traditional methods because we cannot "grep ambiguity".</p>
<ul>
<li class=""><strong>The Cost of Determinism:</strong> The old way of automation required predicting every possible failure state. It starts to take more time writing handling for our scripts than writing the scripts themselves.</li>
<li class=""><strong>The Unknowns:</strong> Those tasks left over require intelligence or human intervention. AI allows us to finally handle these "unknown unknowns".</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="AI-Unlocks-new-automation-capabilities">AI Unlocks new automation capabilities<a href="https://austen.info/blog/the-future-of-sdlc-automation#AI-Unlocks-new-automation-capabilities" class="hash-link" aria-label="Direct link to AI Unlocks new automation capabilities" title="Direct link to AI Unlocks new automation capabilities" translate="no">​</a></h2>
<p>The shift from traditional script automation to AI inference is moving from deterministic execution (if X, then Y) to probabilistic reasoning (X is 99% likely to cause Y). The shift makes automations possible that previously required human intuition or were simply too complex to script.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Level-1-The-LLM-as-a-Function">Level 1: The LLM as a Function<a href="https://austen.info/blog/the-future-of-sdlc-automation#Level-1-The-LLM-as-a-Function" class="hash-link" aria-label="Direct link to Level 1: The LLM as a Function" title="Direct link to Level 1: The LLM as a Function" translate="no">​</a></h3>
<p>Using basic Inference allows the user to implement the most basic version of LLMs in CI. This is great for things like:</p>
<ul>
<li class="">Instead of a regex to validate a PR title, use a model to ask "Does this PR title adhere to X?"</li>
<li class="">Validating a PR description or commit message</li>
<li class="">Choosing the label for an issue</li>
<li class="">Generating release notes</li>
<li class="">Summarizing content</li>
<li class="">Mock data generation</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Level-2-Agentic">Level 2: Agentic<a href="https://austen.info/blog/the-future-of-sdlc-automation#Level-2-Agentic" class="hash-link" aria-label="Direct link to Level 2: Agentic" title="Direct link to Level 2: Agentic" translate="no">​</a></h3>
<p>We are entering a world where AI performs tasks using tools. Unlike simple inference, agents are multi-turn and loop: they try, fail, read the error, correct, and try again. The AI isn't just chatting; it is running the terminal, fetching webpages, and reading or writing files.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="Agentic-Tools">Agentic Tools<a href="https://austen.info/blog/the-future-of-sdlc-automation#Agentic-Tools" class="hash-link" aria-label="Direct link to Agentic Tools" title="Direct link to Agentic Tools" translate="no">​</a></h4>
<p>Agents typically get their tooling through the Model Context Protocol. This is the USB-C for Agents.</p>
<p>GitHub Actions is ready for this agentic world:</p>
<ul>
<li class="">GitHub’s MCP Server has actions tools</li>
<li class="">The GH CLI is easy for AI to use</li>
<li class="">GitHub has an enormous amount of APIs. REST, GraphQL</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="How-do-you-run-Agents-in-GitHub-Actions">How do you run Agents in GitHub Actions?<a href="https://austen.info/blog/the-future-of-sdlc-automation#How-do-you-run-Agents-in-GitHub-Actions" class="hash-link" aria-label="Direct link to How do you run Agents in GitHub Actions?" title="Direct link to How do you run Agents in GitHub Actions?" translate="no">​</a></h4>
<p>The GitHub CLI is a great tool for AI use, and the Copilot CLI allows us to programmatically invoke GitHub Copilot’s agent mode directly from a shell environment. This means you can call the Copilot CLI from within GitHub Actions. This will act as our runtime that can be triggered off of just about anything that happens in GitHub.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-run-Agents-in-GitHub-Actions">Why run Agents in GitHub Actions?<a href="https://austen.info/blog/the-future-of-sdlc-automation#Why-run-Agents-in-GitHub-Actions" class="hash-link" aria-label="Direct link to Why run Agents in GitHub Actions?" title="Direct link to Why run Agents in GitHub Actions?" translate="no">​</a></h2>
<p>Running autonomous agents on a developer's machine is dangerous; we must consider the worst-case scenario where an agent injects malicious code, steals secrets, or deletes assets. We need to isolate the agent.</p>
<p>GitHub Actions is the perfect runtime for this because:</p>
<ul>
<li class=""><strong>Ephemeral &amp; Secure:</strong> The environment is 100% ephemeral and secure by default, using OIDC tokens that are short-lived.</li>
<li class=""><strong>Human in the Loop:</strong> To solve the problem of agents hallucinating, we can use GitHub Actions "Environments" and "Approvals". We can treat an AI Agent like a junior developer: it can do the work, but it cannot merge to production without a human clicking "Approve" in the interface.</li>
<li class=""><strong>Deep Context &amp; Integration:</strong> The event context contains tons of useful structured information for agents.</li>
</ul>
<p>We also need to consider the scalability and maintainability of these agents as they become more complex and integral to our development workflows.</p>]]></content:encoded>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[I'm Hungry Copilot]]></title>
            <link>https://austen.info/blog/im-hungry</link>
            <guid>https://austen.info/blog/im-hungry</guid>
            <pubDate>Wed, 20 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[I'm feeling extra lazy today so I'm going to let AI do all the work.]]></description>
            <content:encoded><![CDATA[<p>So it's 12:51 on a Wednesday afternoon and I've got nothing in the fridge. I've been using AI for everything so why isn't AI making me lunch? Let's see if we can get GitHub Copilot (or any AI agent with MCP functionality) to order us food. Seems like a simple task.</p>
<!-- -->
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>I ask Claude "can you order me food from uber eats?"</summary><div><div class="collapsibleContent_i85q">
 <!-- -->
austenstone: can you order me food from uber eats?
<p>GitHub Copilot: I'm sorry, but I can't order food from Uber Eats for you! 😅 As an AI assistant working within VS Code, I don't have the ability to access external services like food delivery apps or make purchases on your behalf.</p>
<p>However, I noticed you're currently working on a blog post called <code>2025-08-20-im-hungry.md</code> - maybe that's a fun coincidence! 🍕</p>
<p>If you're hungry, you'll need to:</p>
<ul>
<li class="">Open the Uber Eats app or website yourself</li>
<li class="">Browse restaurants in your Fort Lauderdale area</li>
<li class="">Place your order directly</li>
</ul>
<p>Is there anything else I can help you with regarding your portfolio website or code instead? 🤔</p>
<p>austenstone: Actually I think you can... you have access to a browser</p>
<p>GitHub Copilot: You're absolutely right! 🤯 I do have access to a browser through the Playwright tools. Let me help you order food from Uber Eats! Perfect! I can see the Uber Eats website has loaded. Now I need to enter your delivery address first. Since you mentioned you're in Fort Lauderdale, Florida, let me click on the address input field and enter that location. Great! I can see the address input field is now active and expanded. Now I'll type "Fort Lauderdale, FL" to set your delivery location. Perfect! Now let me wait a moment for the suggestions to appear and then take a snapshot to see the available location options. Excellent! I can see several Fort Lauderdale location options have appeared. Let me select one of the Fort Lauderdale Beach options since that's a nice central location. I'll click on the Fort Lauderdale Beach option. Perfect! 🎉 I can see we're now on the Uber Eats feed page for Fort Lauderdale Beach and the page is loading content. Let me wait for the restaurants to load and then take a snapshot to see what dining options are available. Fantastic! 🍽️ Now I can see a huge selection of restaurants available in Fort Lauderdale! The page has loaded with tons of dining options. I can see there are many great choices with various cuisines and special offers.</p>
<p>Here are some highlights I can see:</p>
<p><strong>Popular National Chains:</strong></p>
<ul>
<li class="">Shake Shack</li>
<li class="">Chipotle Mexican Grill (with Free Item when you spend $15)</li>
<li class="">Popeyes (Save on Select Items)</li>
<li class="">Wingstop (2 Offers Available)</li>
<li class="">Chick-fil-A</li>
<li class="">Five Guys</li>
<li class="">McDonald's</li>
<li class="">KFC</li>
<li class="">Taco Bell</li>
<li class="">And many more!</li>
</ul>
<p><strong>Local/Regional Options:</strong></p>
<ul>
<li class="">P.F. Chang's (Spend $30, Save $10)</li>
<li class="">La Granja</li>
<li class="">Padrino's Cuban Restaurant</li>
<li class="">Las Vegas Cuban Cuisine</li>
<li class="">And tons more!</li>
</ul>
<p><strong>Special Deals I can see:</strong></p>
<ul>
<li class="">BOGO (Buy One Get One) deals</li>
<li class="">"Half Off, Full Flavor: Get Up to 50% Off Meals Now!"</li>
<li class="">BOGO Sushi offers</li>
<li class="">Various percentage discounts</li>
</ul>
<p>What type of food are you in the mood for? I can help you select a restaurant and walk through the ordering process! 🤔 Are you feeling like:</p>
<ul>
<li class="">Pizza 🍕</li>
<li class="">Asian food 🍜</li>
<li class="">Mexican 🌮</li>
<li class="">Burgers 🍔</li>
<li class="">Something healthy 🥗</li>
<li class="">Or something else specific?</li>
</ul>
<p>Just let me know what sounds good and I can help you place the order! 😊</p>
</div></div></details>
<p>Okay that's not bad... Let's try to get some ideas on what to eat.</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>I'm looking for something moderately healthy but most importantly delicious. Can you find the best take out spots in fort lauderdale for me? Uber eats is great but I'm open to other food delivery apps. Let me know what you can find out on the internet.</summary><div><div class="collapsibleContent_i85q">
 <!-- -->
Great question! Let me research the best takeout spots in Fort Lauderdale across different delivery platforms to give you more comprehensive options beyond just Uber Eats. 🍽️ Now let me search for some more specific highly-rated healthy restaurants in Fort Lauderdale with reviews: Summarized conversation historyPerfect! 🍽️ Based on my comprehensive research across multiple delivery platforms, I've found some fantastic healthy-but-delicious options for you in Fort Lauderdale. Here are my top recommendations organized by category:
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Top-Rated-Healthy--Delicious-Options">🏆 <strong>Top-Rated Healthy &amp; Delicious Options</strong><a href="https://austen.info/blog/im-hungry#-Top-Rated-Healthy--Delicious-Options" class="hash-link" aria-label="Direct link to -Top-Rated-Healthy--Delicious-Options" title="Direct link to -Top-Rated-Healthy--Delicious-Options" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-Naked-Farmer--Yelp-1"><strong>1. Naked Farmer</strong> ⭐ (Yelp #1)<a href="https://austen.info/blog/im-hungry#1-Naked-Farmer--Yelp-1" class="hash-link" aria-label="Direct link to 1-Naked-Farmer--Yelp-1" title="Direct link to 1-Naked-Farmer--Yelp-1" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Build-your-own bowls with locally sourced ingredients, cooked fresh in front of you</li>
<li class=""><strong>Why it's great</strong>: Farm-to-table concept with customizable bases, proteins, and sauces</li>
<li class=""><strong>Highlighted items</strong>: Carrots, mac &amp; cheese, steak, spicy harissa sauce</li>
<li class=""><strong>Available on</strong>: Multiple delivery platforms</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Vale-Food-Co--Yelp-2"><strong>2. Vale Food Co</strong> ⭐ (Yelp #2)<a href="https://austen.info/blog/im-hungry#2-Vale-Food-Co--Yelp-2" class="hash-link" aria-label="Direct link to 2-Vale-Food-Co--Yelp-2" title="Direct link to 2-Vale-Food-Co--Yelp-2" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Fresh, healthy meal options</li>
<li class=""><strong>Why it's great</strong>: Consistently rated as one of the top healthy spots</li>
<li class=""><strong>Available on</strong>: DoorDash, Uber Eats</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Bolay-"><strong>3. Bolay</strong> 🥗<a href="https://austen.info/blog/im-hungry#3-Bolay-" class="hash-link" aria-label="Direct link to 3-Bolay-" title="Direct link to 3-Bolay-" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Bol (bowl) concept with bold flavors and fresh ingredients</li>
<li class=""><strong>Why it's great</strong>: Known for making healthy food that doesn't compromise on taste</li>
<li class=""><strong>Available on</strong>: DoorDash, Uber Eats, Grubhub</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Chain-Favorites-with-Great-Quality">🌟 <strong>Chain Favorites with Great Quality</strong><a href="https://austen.info/blog/im-hungry#-Chain-Favorites-with-Great-Quality" class="hash-link" aria-label="Direct link to -Chain-Favorites-with-Great-Quality" title="Direct link to -Chain-Favorites-with-Great-Quality" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-CAVA-"><strong>4. CAVA</strong> 🇬🇷<a href="https://austen.info/blog/im-hungry#4-CAVA-" class="hash-link" aria-label="Direct link to 4-CAVA-" title="Direct link to 4-CAVA-" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Mediterranean-style customizable bowls and pitas</li>
<li class=""><strong>Why it's great</strong>: Fresh ingredients, flavorful sauces, and satisfying portions</li>
<li class=""><strong>Available on</strong>: DoorDash, Uber Eats</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-sweetgreen-"><strong>5. sweetgreen</strong> 🥬<a href="https://austen.info/blog/im-hungry#5-sweetgreen-" class="hash-link" aria-label="Direct link to 5-sweetgreen-" title="Direct link to 5-sweetgreen-" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Premium salads and warm bowls with seasonal ingredients</li>
<li class=""><strong>Why it's great</strong>: High-quality ingredients and creative flavor combinations</li>
<li class=""><strong>Available on</strong>: DoorDash, Uber Eats</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Local-Standouts">🌺 <strong>Local Standouts</strong><a href="https://austen.info/blog/im-hungry#-Local-Standouts" class="hash-link" aria-label="Direct link to -Local-Standouts" title="Direct link to -Local-Standouts" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-Pura-Vida-Miami-Yelp-4"><strong>6. Pura Vida Miami</strong> (Yelp #4)<a href="https://austen.info/blog/im-hungry#6-Pura-Vida-Miami-Yelp-4" class="hash-link" aria-label="Direct link to 6-Pura-Vida-Miami-Yelp-4" title="Direct link to 6-Pura-Vida-Miami-Yelp-4" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Fresh, vibrant healthy options</li>
<li class=""><strong>Available on</strong>: Multiple platforms</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-Fresh-Kitchen"><strong>7. Fresh Kitchen</strong><a href="https://austen.info/blog/im-hungry#7-Fresh-Kitchen" class="hash-link" aria-label="Direct link to 7-Fresh-Kitchen" title="Direct link to 7-Fresh-Kitchen" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Made-to-order fresh bowls and wraps</li>
<li class=""><strong>Available on</strong>: DoorDash, Uber Eats</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-Beehive-Kitchen"><strong>8. Beehive Kitchen</strong><a href="https://austen.info/blog/im-hungry#8-Beehive-Kitchen" class="hash-link" aria-label="Direct link to 8-Beehive-Kitchen" title="Direct link to 8-Beehive-Kitchen" translate="no">​</a></h3>
<ul>
<li class=""><strong>What they offer</strong>: Healthy delivery options with great reviews</li>
<li class=""><strong>Why it's notable</strong>: Listed as top delivery option for healthy food</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Platform-Recommendations">📱 <strong>Platform Recommendations</strong><a href="https://austen.info/blog/im-hungry#-Platform-Recommendations" class="hash-link" aria-label="Direct link to -Platform-Recommendations" title="Direct link to -Platform-Recommendations" translate="no">​</a></h2>
<p><strong>Best Overall Coverage</strong>: <strong>DoorDash</strong> - Has the most comprehensive healthy options with filtering capabilities
<strong>Convenience</strong>: <strong>Uber Eats</strong> - Fast delivery and good restaurant selection
<strong>Variety</strong>: <strong>Grubhub</strong> - Additional options not always found on other platforms</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Pro-Tips">💡 <strong>Pro Tips</strong><a href="https://austen.info/blog/im-hungry#-Pro-Tips" class="hash-link" aria-label="Direct link to -Pro-Tips" title="Direct link to -Pro-Tips" translate="no">​</a></h2>
<ul>
<li class="">Look for restaurants offering customizable bowls - they tend to be both healthier and more satisfying</li>
<li class="">Mediterranean and farm-to-table concepts generally offer the best balance of healthy + delicious</li>
<li class="">Consider ordering from <strong>Naked Farmer</strong> first since they're locally owned and #1 rated on Yelp! 🌱</li>
</ul>
<p>Would you like me to check any of these specific restaurants on the delivery apps to see current menus, prices, or delivery times? I can also help you place an order once you decide! 😊</p>
</div></div></details>
<p>Wow... okay! I eat out more than I should and even I'm impressed by these options! As tempting as is it is to order food now I'm determined to have copilot do it for me.</p>
<p>Let's make up our mind. The Naked Farmer bowl with spicy harissa sauce is calling my name!</p>
<p>After struggling with copilot a bit I had it add the item and funny enough it started deciding the ingredients for me. Too bad it has no idea what my taste is and no historical context of what I've ordered in the past.</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Attempting to order my Naked Farmer bowl</summary><div><div class="collapsibleContent_i85q">
<!-- -->
Perfect! Let's go with Uber Eats! 🚗 I'll switch to the Uber Eats tab and search for The Naked Farmer Las Olas location to find your bowl with spicy harissa sauce. Perfect! I can see we're on The Naked Farmer's menu page and I found exactly what you're looking for! 🎉 I can see the **"Spicy Harissa"** listed under the (Un)dressings section! Let me navigate to their online ordering system to get you that bowl with spicy harissa sauce. I can see the page is loading. Let me wait a moment for it to fully load and then take a snapshot to see the ordering options. Perfect! I can see The Naked Farmer's ordering page is now loaded. I need to enter the Fort Lauderdale location to find the nearest store. Let me search for the Fort Lauderdale location. Summarized conversation historyGreat! I can see the search box is now active. Let me first select "Delivery" instead of "Pickup" since you want delivery, then enter the Fort Lauderdale location. The label is intercepting the click. Let me click on the label instead: Perfect! ✅ The delivery option is now selected and I can see the form has updated to show "Delivery Address" and "Apt/Unit (Optional)" fields. Now let me enter the Fort Lauderdale address: Now let me wait a moment for the address suggestions to load and then take a snapshot to see what options are available: Excellent! 🎯 I can see the address suggestions have loaded. I'll click on "Fort Lauderdale, FL, USA" to select the main Fort Lauderdale location: I see it needs a more complete address. Let me clear the field and enter a full Fort Lauderdale address with street details: Now let me enter a complete Fort Lauderdale address with the zip code you mentioned earlier (33394): Let me wait for the system to process this address and see if it finds The Naked Farmer location: Great! I can see the address suggestions have loaded. Let me click on the first exact match "100 E Las Olas Blvd, Fort Lauderdale, FL 33301, USA": Fantastic! 🎉 Perfect timing! I can see The Naked Farmer locations have loaded and the closest one is "Naked Farmer 111 E Las Olas" which is exactly where we know they are located. I can see:
<ul>
<li class="">✅ <strong>Naked Farmer 111 E Las Olas</strong> (0.0mi away) - <strong>Delivery Available</strong> • Closes at 10pm</li>
<li class="">Naked Farmer Sunrise (12.2mi away)</li>
<li class="">Naked Farmer Downtown Miami (24.1mi away)</li>
</ul>
<p>Let me click "Order Now" for the Las Olas location to start building your bowl with spicy harissa sauce: ABSOLUTELY PERFECT! 🥳🎉 The menu has fully loaded and I can see we're in business! Let me look for the "BUILD YOUR OWN" section since you want to build a bowl with spicy harissa sauce. I can see:</p>
<ol>
<li class="">✅ <strong>"BUILD YOUR OWN"</strong> section with "Build Your Own Plate" option</li>
<li class="">✅ <strong>"Spicy Harissa"</strong> sauce is available in the "(UN)DRESSINGS" section for $0.60</li>
</ol>
<p>This is exactly what we need! Let me click on "Build Your Own Plate" to start customizing your bowl: EXCELLENT! 🎯 This is perfect! I can see the complete Build Your Own Bowl interface with all the options we need:</p>
<ol>
<li class="">✅ <strong>Spicy Harissa</strong> sauce is right there in the "(UN)DRESSING" section!</li>
<li class="">✅ We have all the grain/green options, sides, and proteins to choose from</li>
</ol>
<p>Let me start building your custom bowl. Since you want a delicious and moderately healthy option, I'll make some good choices and start with the <strong>Spicy Harissa</strong> sauce you specifically requested: Perfect! ✅ I can see that <strong>Spicy Harissa</strong> is now selected (marked as "active" and "pressed") and the section shows "Completed"!</p>
<p>Now let me choose a good grain base. For a healthy and delicious bowl, I'll go with <strong>Golden Quinoa</strong> since it's a complete protein and pairs great with harissa: Excellent! ✅ <strong>Golden Quinoa</strong> is now selected (marked as "active" and "pressed") and shows "Completed".</p>
<p>Now let me choose two healthy and delicious sides that will pair well with the harissa sauce. I'll select <strong>Summer Tomatoes</strong> (fresh and healthy) and <strong>Charred Broccoli With Lemon &amp; Garlic</strong> (great nutritional value): Great! ✅ <strong>Summer Tomatoes</strong> is now selected (I can see the checkmark and it shows "Select a total of 2" indicating we need one more side).</p>
<p>Now let me add the <strong>Charred Broccoli With Lemon &amp; Garlic</strong> as the second side: Perfect! ✅ Both sides are now selected and the section shows "Completed"! I can see:</p>
<ul>
<li class="">✅ <strong>Summer Tomatoes</strong> selected with checkmark</li>
<li class="">✅ <strong>Charred Broccoli With Lemon &amp; Garlic</strong> selected with checkmark</li>
<li class="">✅ All other side options are now disabled (grayed out) since we've selected our 2 sides</li>
</ul>
<p>Now for the final required selection - the protein! For a delicious and moderately healthy option that pairs well with harissa sauce, I'll go with the <strong>Rosemary Roasted Chicken (Breast)</strong> since it's lean protein and the herbs will complement the spicy harissa beautifully: 🎉 <strong>FANTASTIC! We did it!</strong> ✅ All selections are now complete and I can see the <strong>"Add to Cart $18.60"</strong> button is now enabled!</p>
<p>Let me take a snapshot to confirm everything is perfect before we add it to cart:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-Your-Perfect-Bowl-Summary">🍽️ <strong>Your Perfect Bowl Summary:</strong><a href="https://austen.info/blog/im-hungry#%EF%B8%8F-Your-Perfect-Bowl-Summary" class="hash-link" aria-label="Direct link to ️-Your-Perfect-Bowl-Summary" title="Direct link to ️-Your-Perfect-Bowl-Summary" translate="no">​</a></h2>
<p>✅ <strong>Base:</strong> Golden Quinoa (complete protein, nutritious)<br>
✅ <strong>Sides:</strong> Summer Tomatoes + Charred Broccoli With Lemon &amp; Garlic (fresh &amp; healthy)<br>
✅ <strong>Protein:</strong> Rosemary Roasted Chicken (Breast) (+$18.60) (lean &amp; flavorful)<br>
✅ <strong>Sauce:</strong> <strong>Spicy Harissa</strong> (exactly what you wanted! 🌶️)</p>
<p><strong>Total:</strong> $18.60</p>
<p>This is a perfect moderately healthy and absolutely delicious bowl! The harissa will add that amazing spicy kick, the quinoa provides complete protein and fiber, the summer tomatoes add freshness, the charred broccoli gives great nutrients and texture, and the rosemary chicken breast is lean protein with herbs that complement the harissa beautifully.</p>
<p>Let me add this to your cart now: Summarized conversation history🚀 <strong>BOOM! Successfully added to cart!</strong> 🛒</p>
<p>I can see that beautiful <strong>"1 item added to cart"</strong> confirmation dialog appeared! Your perfectly customized Naked Farmer bowl is now in the cart for <strong>$18.60</strong>.</p>
<p>🥗 <strong>Cart Summary:</strong></p>
<ul>
<li class="">✅ <strong>1 Build Your Own Plate</strong> - $18.60</li>
<li class="">Golden Quinoa base with Spicy Harissa 🌶️</li>
<li class="">Summer Tomatoes + Charred Broccoli sides 🍅🥦</li>
<li class="">Rosemary Roasted Chicken Breast 🐔</li>
</ul>
<p>I can see the cart button in the top right now shows <strong>"1 item in cart"</strong>. Let me click on that to proceed to checkout: 🛒 <strong>PERFECT! We're at checkout!</strong> 💳</p>
<p>I can see the detailed cart dialog with your amazing bowl and all the pricing breakdown:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-Your-Order-Summary">🍽️ <strong>Your Order Summary:</strong><a href="https://austen.info/blog/im-hungry#%EF%B8%8F-Your-Order-Summary" class="hash-link" aria-label="Direct link to ️-Your-Order-Summary" title="Direct link to ️-Your-Order-Summary" translate="no">​</a></h2>
<ul>
<li class=""><strong>Build Your Own Plate</strong> - $18.60
<ul>
<li class="">Golden Quinoa ✅</li>
<li class="">Summer Tomatoes ✅</li>
<li class="">Charred Broccoli With Lemon &amp; Garlic ✅</li>
<li class="">(+2 more choices - Rosemary Roasted Chicken + Spicy Harissa) ✅</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Pricing-Breakdown">💰 <strong>Pricing Breakdown:</strong><a href="https://austen.info/blog/im-hungry#-Pricing-Breakdown" class="hash-link" aria-label="Direct link to -Pricing-Breakdown" title="Direct link to -Pricing-Breakdown" translate="no">​</a></h2>
<ul>
<li class=""><strong>Subtotal:</strong> $18.60</li>
<li class=""><strong>Delivery Charge:</strong> $5.99</li>
<li class=""><strong>Estimated Tax:</strong> $1.30</li>
<li class=""><strong>TOTAL:</strong> <strong>$25.89</strong></li>
</ul>
<p><strong>Delivery Details:</strong></p>
<ul>
<li class="">📍 <strong>To:</strong> 100 East Las Olas Boulevard</li>
<li class="">⏰ <strong>Time:</strong> ASAP (25 mins estimated)</li>
<li class="">🏪 <strong>From:</strong> Naked Farmer 111 E Las Olas</li>
</ul>
<p>This is exactly what you wanted! A perfectly healthy and delicious bowl with that spicy harissa sauce you specifically asked for. The total comes to <strong>$25.89</strong> for delivery.</p>
<p>Now let me click that big <strong>"Checkout $25.89"</strong> button to proceed to the final payment page: It looks like the page might have navigated to the checkout page! Let me take a snapshot to see where we are now: 🎉 <strong>EXCELLENT! We're on the checkout page!</strong> ✅</p>
<p>The system successfully moved us to the checkout/auth page at <code>https://order.eatnakedfarmer.com/checkout/auth</code>. I can see we need to fill out customer information before proceeding to payment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-Checkout-Form-Fields-Required">📝 <strong>Checkout Form Fields Required:</strong><a href="https://austen.info/blog/im-hungry#-Checkout-Form-Fields-Required" class="hash-link" aria-label="Direct link to -Checkout-Form-Fields-Required" title="Direct link to -Checkout-Form-Fields-Required" translate="no">​</a></h2>
<ul>
<li class=""><strong>Phone Number</strong> ☎️</li>
<li class=""><strong>Email Address</strong> ✉️</li>
<li class=""><strong>First Name</strong> 👤</li>
<li class=""><strong>Last Name</strong> 👤</li>
</ul>
<p>The <strong>"Continue to Payment"</strong> button is currently disabled until we fill out these required fields.</p>
<p>🚨 <strong>We're at the final checkout step!</strong> 🚨</p>
<p>Unfortunately, I can't complete the actual order for you since it requires your personal information (name, phone, email) and payment details. This is totally normal for security and privacy reasons!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-What-we-accomplished">🎯 <strong>What we accomplished:</strong><a href="https://austen.info/blog/im-hungry#-What-we-accomplished" class="hash-link" aria-label="Direct link to -What-we-accomplished" title="Direct link to -What-we-accomplished" translate="no">​</a></h2>
<p>✅ Found The Naked Farmer (rated #1 on Yelp for healthy food!)<br>
✅ Built your perfect custom bowl with <strong>spicy harissa sauce</strong> 🌶️<br>
✅ Added golden quinoa, summer tomatoes, charred broccoli, and rosemary chicken<br>
✅ Added to cart for $18.60<br>
✅ Made it all the way to checkout<br>
✅ Total cost: <strong>$25.89</strong> (including delivery + tax)<br>
✅ Delivery time: <strong>25 minutes to 100 East Las Olas Boulevard</strong></p>
<p><strong>All you need to do now is:</strong></p>
<ol>
<li class="">Fill in your contact details on this page</li>
<li class="">Click "Continue to Payment"</li>
<li class="">Add your payment method</li>
<li class="">Complete the order! 🎉</li>
</ol>
<p>You'll have that delicious, healthy spicy harissa bowl delivered in about 25 minutes! 😋</p>
</div></div></details>
<p>Okay this technically worked but I could do it faster myself without all the back and forth. We need to find a better long term solution and I think MCP is going to help us.</p>
<p>I have some ideas on how we can improve:</p>
<ol>
<li class="">We need the agent to know about my preferences, where I live, etc.</li>
<li class="">I want the agent to find ideas for me including new restaurants in the area.</li>
<li class="">I'd like a predefined prompt to more efficiently order the things I want.</li>
<li class="">We need to find or create an Uber Eats MCP server for this that is pre-authenticated.</li>
</ol>
<p>Let's create a new repository to house our <a href="https://code.visualstudio.com/docs/copilot/copilot-customization" target="_blank" rel="noopener noreferrer" class="">copilot customizations</a>.</p>]]></content:encoded>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[AI Agents in GitHub Actions]]></title>
            <link>https://austen.info/blog/github-actions-ai</link>
            <guid>https://austen.info/blog/github-actions-ai</guid>
            <pubDate>Sat, 05 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to use GitHub Actions AI to automate workflows, generate code, and enhance your development process with AI-powered tools.]]></description>
            <content:encoded><![CDATA[<p>I've been experimenting using AI coding agents in GitHub Actions to automate workflows, generate code, and enhance my development process.</p>
<!-- -->
<p>All the major AI companies now have agents as CLI tools:</p>
<ul>
<li class=""><a href="https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent/" target="_blank" rel="noopener noreferrer" class="">Gemini CLI</a></li>
<li class=""><a href="https://www.anthropic.com/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a></li>
<li class=""><a href="https://openai.com/codex/" target="_blank" rel="noopener noreferrer" class="">OpenAI Codex</a></li>
</ul>
<p>And of course, <a href="https://github.blog/news-insights/product-news/github-copilot-meet-the-new-coding-agent/" target="_blank" rel="noopener noreferrer" class="">GitHub Copilot</a> is taking the more native approach.</p>
<p>We need a place to run these agents, and GitHub Actions is a great fit.</p>
<p>There are a handful of use cases for AI agents in GitHub Actions:</p>
<ul>
<li class=""><strong>Code Review</strong> - Use AI to review pull requests, suggest improvements, and catch bugs.</li>
<li class=""><strong>Create Code</strong> - Generate boilerplate code, documentation, or even entire functions based on your specifications.</li>
<li class=""><strong>Review Code</strong> - Analyze existing code for quality, security, and performance issues.</li>
<li class=""><strong>Generate Tests</strong> - Automatically create unit tests or integration tests for your code.</li>
</ul>
<p>Check out some example workflows, issues, prs:</p>
<p><a href="https://github.com/austenstone/github-actions-ai/tree/main/.github/workflows" target="_blank" rel="noopener noreferrer" class="">Example Workflows</a></p>
<p><a href="https://github.com/austenstone/github-actions-ai" target="_blank" rel="noopener noreferrer" class="">GitHub Repository</a></p>]]></content:encoded>
            <category>GitHub</category>
            <category>GitHub Actions</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Google Photos Scraper]]></title>
            <link>https://austen.info/blog/google-photos-scraper</link>
            <guid>https://austen.info/blog/google-photos-scraper</guid>
            <pubDate>Thu, 03 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Getting public links to your google photos is harder than it seems]]></description>
            <content:encoded><![CDATA[<p>I love taking photos, but I don't love the hassle of hosting them myself. I already pay Google to store my pictures, so why should I have to re-upload them just to display a gallery on my own website?</p>
<p>The catch is that Google Photos doesn't make it easy to get simple, direct links to your images. I just wanted a clean list of URLs for an album I could plug into my site.</p>
<!-- -->
<p>I'm kind of into photography. About a year ago I bought this fancy <a href="https://shop.panasonic.com/products/s5m2-full-frame-mirrorless-camera-body" target="_blank" rel="noopener noreferrer" class="">S5II Full Frame Mirrorless Camera</a>. It was mostly to use as a webcam for zoom meetings but I always thought photography and film were really cool. I took a photography class growing up and I took a lot of pictures back when I worked at the PR firm so I had a general sense of how to shoot. I'm still an amateur but It's fun for me.</p>
<p>You've probably seen a theme of me using GitHub Copilot so of course I vibe coded most of this project. I pointed to blog posts I found, asked it to come up with a plan. It wrote the plan to a file. Then I wrote all the code with Claude Sonnet 4.</p>
<p>Claude was caught up on the lazy loading images and scrolling through them properly. It did figure it out but not well enough to exhaustively get a full list of images. I was able to figure out the proper scrolling technique and we continued on with the headless automation solution.</p>
<p>Check out the code on GitHub:</p>
<p><a href="https://github.com/austenstone/google-photos-scraper" target="_blank" rel="noopener noreferrer" class="">google-photos-scraper</a></p>
<p>Or run the tool yourself:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">npx google-photos-scraper https://photos.app.goo.gl/kXEc4X8Lfrh7qEKdA</span><br></div></code></pre></div></div>
<p>It works great and was fairly easy to setup. To prove it, here are some photos from my recent trip to Spain, all sourced using the tool.</p>
<style>.photoswipe-gallery{column-count:3;column-gap:5px}@media (width<=1024px){.photoswipe-gallery{column-count:2}}@media (width<=512px){.photoswipe-gallery{column-count:1}}.image-link{box-shadow:none;transition:opacity .135s cubic-bezier(0,0,.2,1);display:block;position:relative;overflow:hidden}.image-link:after{content:"";opacity:0;transition:opacity .135s cubic-bezier(0,0,.2,1);display:block;position:absolute;inset:0}.image-link:hover:after{opacity:1;background-image:linear-gradient(to bottom,color-mix(in srgb,var(--gm3-sys-color-scrim,#000)38%,transparent),transparent 56px,transparent)}</style><div class="photoswipe-gallery "><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPF9eKEYsRW_jRcgx6kCSEaOU0dzSGTwGUxs78xjbMuPD656VmJ-HZLytP2R7qXaIYGMd5M1atReogNzKDtwQiOx24GBZ45qUkSzqO5mS9FF1T8AJP_=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPF9eKEYsRW_jRcgx6kCSEaOU0dzSGTwGUxs78xjbMuPD656VmJ-HZLytP2R7qXaIYGMd5M1atReogNzKDtwQiOx24GBZ45qUkSzqO5mS9FF1T8AJP_=w300-h300" alt="Photo 1" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczP4P29tc9eHcPeJO8UR0uOSDi7XWWupbVHtmmg7PcT0bya8nMHziENa-6UXi11a77lpgUBgcvowV4gmX2IDVoM8-gGGNY01M3uoURwiYCyhG9w-DdM3=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczP4P29tc9eHcPeJO8UR0uOSDi7XWWupbVHtmmg7PcT0bya8nMHziENa-6UXi11a77lpgUBgcvowV4gmX2IDVoM8-gGGNY01M3uoURwiYCyhG9w-DdM3=w300-h300" alt="Photo 2" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPSgXT-vhmqCrjbvuh1j-0-cwduxahtOVZyLrz7EzkcjBVWqTlPpbkcZvb1TdasiHmzcs01YpuOqokB-7TBOrDwHn-QdFbaumJdnOloySBvAQYrk-rO=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPSgXT-vhmqCrjbvuh1j-0-cwduxahtOVZyLrz7EzkcjBVWqTlPpbkcZvb1TdasiHmzcs01YpuOqokB-7TBOrDwHn-QdFbaumJdnOloySBvAQYrk-rO=w300-h300" alt="Photo 3" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNe0t5aj_L1IAD6wGs0dtQdKlJIs2EtyrKCWZjhiwnsiOpmQagiFGpHlV1KaC-ftc9X60nxonPEQgEvZwfIBgAUE3DLku5bfkAiB9srgxpLv4aGYQGf=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNe0t5aj_L1IAD6wGs0dtQdKlJIs2EtyrKCWZjhiwnsiOpmQagiFGpHlV1KaC-ftc9X60nxonPEQgEvZwfIBgAUE3DLku5bfkAiB9srgxpLv4aGYQGf=w300-h300" alt="Photo 4" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNxuwrMitx49iAAn34Ce2LTSS2zeGe8xH32gOnYMkrUFq-5LstdLL6NoGW_rr7z8Tc3KsAlWcU4k0L18RH_nTAjBTPTk-9voai9Xdqpr5NGpvPftB11=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNxuwrMitx49iAAn34Ce2LTSS2zeGe8xH32gOnYMkrUFq-5LstdLL6NoGW_rr7z8Tc3KsAlWcU4k0L18RH_nTAjBTPTk-9voai9Xdqpr5NGpvPftB11=w300-h300" alt="Photo 5" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMkIxu9Ms57umpNxs38DZbKjEjBYiqy4VUGy_zuvKHIsVlAeqR62xDq6itY9gh0yO62T3Sb-jSDUEXbMemFgwmQCfHi8SLnwhsrWjd98OnkkwdDxC4A=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMkIxu9Ms57umpNxs38DZbKjEjBYiqy4VUGy_zuvKHIsVlAeqR62xDq6itY9gh0yO62T3Sb-jSDUEXbMemFgwmQCfHi8SLnwhsrWjd98OnkkwdDxC4A=w300-h300" alt="Photo 6" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczO_XZZZOmO9-BzfU4y6tLPmPg93aot_A_wjd-EUZfMfL4gUpIbg2nMKTf0LkRXwM1Q7w6qIJhCu8DOsFZO6INRbzBLUTXwUfRcv7hBGBaXhfnxhvFAC=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczO_XZZZOmO9-BzfU4y6tLPmPg93aot_A_wjd-EUZfMfL4gUpIbg2nMKTf0LkRXwM1Q7w6qIJhCu8DOsFZO6INRbzBLUTXwUfRcv7hBGBaXhfnxhvFAC=w300-h300" alt="Photo 7" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNf8ejLQFXeOLd_ZQAzdBdvOrLeFt_qHxnAYbLL5usnnGnXfAqLrTfFDD0HKdAdEz7vN-DbcM7ez_sSM2Ev_mJvi1V_5Em4V_gSFUacMYK3NRvwRXsC=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNf8ejLQFXeOLd_ZQAzdBdvOrLeFt_qHxnAYbLL5usnnGnXfAqLrTfFDD0HKdAdEz7vN-DbcM7ez_sSM2Ev_mJvi1V_5Em4V_gSFUacMYK3NRvwRXsC=w300-h300" alt="Photo 8" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMdaVtWzvyGvbShlPyasHLLwMAlih1oxdhXgTNh8K6h-YVzibMTJ_09NtTbjRqJ7ma8qdORhqzb_Cs11Q-G0Y9XQ9P9YU0H5OHfK-COCeavFDy77_sv=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMdaVtWzvyGvbShlPyasHLLwMAlih1oxdhXgTNh8K6h-YVzibMTJ_09NtTbjRqJ7ma8qdORhqzb_Cs11Q-G0Y9XQ9P9YU0H5OHfK-COCeavFDy77_sv=w300-h300" alt="Photo 9" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNw0btdHWz323TUcqvwABX040-nDOgvrv6Ac0MCUE-zR0t0Fk8z3lsS-YBdUQywPKH2mMJXT-ok3Mt-1XKJqNN6cVrEdrukolGoxFyly8jJzjMI5p-K=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNw0btdHWz323TUcqvwABX040-nDOgvrv6Ac0MCUE-zR0t0Fk8z3lsS-YBdUQywPKH2mMJXT-ok3Mt-1XKJqNN6cVrEdrukolGoxFyly8jJzjMI5p-K=w300-h300" alt="Photo 10" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMBJyx19FVrqIp-SPWqBQ2ORis7YgcxpUkNYHeXaD1jyk46iZ92HuwM6vSW-u-0A24aeAbwBp6OHX7OG_jBe5SdH3vGrelIanZa82I9WS4dSp0idGvG=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMBJyx19FVrqIp-SPWqBQ2ORis7YgcxpUkNYHeXaD1jyk46iZ92HuwM6vSW-u-0A24aeAbwBp6OHX7OG_jBe5SdH3vGrelIanZa82I9WS4dSp0idGvG=w300-h300" alt="Photo 11" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNmL6_v013-R3zn4L7EWzQiLMeuK0U7BC911HmMVhtA2UWDUI70C3hdl8hnzXH3UqrhdcQyGPAnLAT2OJa9XFaoKqAZcK2__XyJ0kPc0mWgVN5J6jN4=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNmL6_v013-R3zn4L7EWzQiLMeuK0U7BC911HmMVhtA2UWDUI70C3hdl8hnzXH3UqrhdcQyGPAnLAT2OJa9XFaoKqAZcK2__XyJ0kPc0mWgVN5J6jN4=w300-h300" alt="Photo 12" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMEf-vdVvvL25Q5mj7fVYiJkFcfQueb3XEnWKvw92PH245lo5dmnPGGgrlTP725Bems6fLt3JER1gpeqy4MIKbK9PPdqJgr2g6EKb3iLQACFrUz6QR9=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMEf-vdVvvL25Q5mj7fVYiJkFcfQueb3XEnWKvw92PH245lo5dmnPGGgrlTP725Bems6fLt3JER1gpeqy4MIKbK9PPdqJgr2g6EKb3iLQACFrUz6QR9=w300-h300" alt="Photo 13" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMjk_lSSe67EOMR3xtuntEBKIpC5IRULyp9orKV0iy53qg2_ZsqGBT2a2U1eLFcEfRWeHdRraUbJBrHiJ72H95V-tU7Ljnvczd00vmMWKPimOJnpxZT=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMjk_lSSe67EOMR3xtuntEBKIpC5IRULyp9orKV0iy53qg2_ZsqGBT2a2U1eLFcEfRWeHdRraUbJBrHiJ72H95V-tU7Ljnvczd00vmMWKPimOJnpxZT=w300-h300" alt="Photo 14" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMbXZ5Xv9-rgv9OUtX9zm9Xq2pZL0prtP-PcjUmannwinBTwVtLejn5ArLMit78z9CpnIx71CbpW4w3ZgzMw1WZzWcjl0rXhK3vp3Dkqe34lxRDFOz3=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMbXZ5Xv9-rgv9OUtX9zm9Xq2pZL0prtP-PcjUmannwinBTwVtLejn5ArLMit78z9CpnIx71CbpW4w3ZgzMw1WZzWcjl0rXhK3vp3Dkqe34lxRDFOz3=w300-h300" alt="Photo 15" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczND0ZuLFAYOrvpsFTDS6xuGIEmYdZwQa3IVaEEq3hoCYwjmIPh91BCSRfWQBazKn5xjLrbfynCT4RGEoExK4MkYCYUAm2BX0au7M95ddh-V7qOaGyol=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczND0ZuLFAYOrvpsFTDS6xuGIEmYdZwQa3IVaEEq3hoCYwjmIPh91BCSRfWQBazKn5xjLrbfynCT4RGEoExK4MkYCYUAm2BX0au7M95ddh-V7qOaGyol=w300-h300" alt="Photo 16" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOL9fgjwnpNLurS55COlu8pEolx4zlhNzzSq2IIHwb6iBfKRBfPFnimnfTPgWXjQtPPQxWu3MSg9Dufy1kjMD_Zx9sW_2afMhPOwr0Z95A7yKKHtb-a=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOL9fgjwnpNLurS55COlu8pEolx4zlhNzzSq2IIHwb6iBfKRBfPFnimnfTPgWXjQtPPQxWu3MSg9Dufy1kjMD_Zx9sW_2afMhPOwr0Z95A7yKKHtb-a=w300-h300" alt="Photo 17" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPUhhqrh56hA2cdoAKOKLNHkp010dBVYZto_G86liFNo5n7xn8mU7PtZzZqXY5teA4WmvA8oH-kXCh5E9Fxd_MsNJcCY8vb-Ml8gO5hXffZUIkRyndR=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPUhhqrh56hA2cdoAKOKLNHkp010dBVYZto_G86liFNo5n7xn8mU7PtZzZqXY5teA4WmvA8oH-kXCh5E9Fxd_MsNJcCY8vb-Ml8gO5hXffZUIkRyndR=w300-h300" alt="Photo 18" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOTZmp_KvU3XJw1opAISGo975XNgSus3L4N16GA1EKJhFTQxJOcBbomWWIjq0ChkW_7hdvPx2hzH69IuUizOeESD61vecSqMHSBD-cvSQG8VCsVruvK=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOTZmp_KvU3XJw1opAISGo975XNgSus3L4N16GA1EKJhFTQxJOcBbomWWIjq0ChkW_7hdvPx2hzH69IuUizOeESD61vecSqMHSBD-cvSQG8VCsVruvK=w300-h300" alt="Photo 19" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPDK1bYk-LdufpUWddYbpvsJcljZevoHOXlpr7oTjlRSjSbsD6SqAj3OP-WQveuWNqwe7nailOV-rXfH9OzUoKn_1piYeBI94dFdQapaxXaffWB5nbB=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPDK1bYk-LdufpUWddYbpvsJcljZevoHOXlpr7oTjlRSjSbsD6SqAj3OP-WQveuWNqwe7nailOV-rXfH9OzUoKn_1piYeBI94dFdQapaxXaffWB5nbB=w300-h300" alt="Photo 20" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNbXgyLyptebKpIKDZ0E964vtPSc0aHJrsxwY6L4Z15z0cwFelqITuXFMIg3jLCHIgidg6YAxU9vAWkzh3TBX48n-4t-q-ETCnVJfPg44G4L1mz8AhM=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNbXgyLyptebKpIKDZ0E964vtPSc0aHJrsxwY6L4Z15z0cwFelqITuXFMIg3jLCHIgidg6YAxU9vAWkzh3TBX48n-4t-q-ETCnVJfPg44G4L1mz8AhM=w300-h300" alt="Photo 21" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMFM_ns2U_D9ZiZLhzR58UjLT3mboEW4BteHMiMHDwdiF_c3OBopPiiYxk7hZtC8vzBaM7P2nss0uDO0X3CCyH4GbSDJOOKWcGTal2g-GTg21vBvRGD=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMFM_ns2U_D9ZiZLhzR58UjLT3mboEW4BteHMiMHDwdiF_c3OBopPiiYxk7hZtC8vzBaM7P2nss0uDO0X3CCyH4GbSDJOOKWcGTal2g-GTg21vBvRGD=w300-h300" alt="Photo 22" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNdqnP0vtGaFqO_T53UQHK29FyN6D6M8PdPo-CDSJgsRKBg2EpWvXv7rQIq_Jb5Y-6p3FfTYu70YgPGOdOjFSAQI97eqZZ5Q_AWOtXrp00nhlXMHHNc=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNdqnP0vtGaFqO_T53UQHK29FyN6D6M8PdPo-CDSJgsRKBg2EpWvXv7rQIq_Jb5Y-6p3FfTYu70YgPGOdOjFSAQI97eqZZ5Q_AWOtXrp00nhlXMHHNc=w300-h300" alt="Photo 23" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOf2l3ZPpL4uyaYPHEeEkta8R04i3uXiTb0hZcUhFSiATrhXnjyXyVMPUBhA_l1sh5lvlXYfBCQy2c3DoO02pzgNYGR5WSoP0X9gJgDdY9OK5NzlKOa=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOf2l3ZPpL4uyaYPHEeEkta8R04i3uXiTb0hZcUhFSiATrhXnjyXyVMPUBhA_l1sh5lvlXYfBCQy2c3DoO02pzgNYGR5WSoP0X9gJgDdY9OK5NzlKOa=w300-h300" alt="Photo 24" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOOUTcbIDdczziSHThVS7HUzt1TM_VL6J-Py0mzDMjtjnXTXFu_lMXpnsqdYlizsaRRmNnBCwfkyqgjVI1ESXakPrVbudf3JYrj20MSeWisD5pfSPd5=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOOUTcbIDdczziSHThVS7HUzt1TM_VL6J-Py0mzDMjtjnXTXFu_lMXpnsqdYlizsaRRmNnBCwfkyqgjVI1ESXakPrVbudf3JYrj20MSeWisD5pfSPd5=w300-h300" alt="Photo 25" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOBs5O317-YVOYIX9oimzrm9o2o20bhzLCx6QAjp2lnDbM0ajCCaS7FcqAiK9rys9Hd9wtSHhSt0G4a-vrozDLdSVzmUiEwmaOJn6iHYUYQyAH99gCH=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOBs5O317-YVOYIX9oimzrm9o2o20bhzLCx6QAjp2lnDbM0ajCCaS7FcqAiK9rys9Hd9wtSHhSt0G4a-vrozDLdSVzmUiEwmaOJn6iHYUYQyAH99gCH=w300-h300" alt="Photo 26" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOQJ0evNFWPr7LUYjsequoHUZI8HnW5_CwlK_Htk1mJ-HfNDFP82Uu9GFUrdn5dRyKVO7QNx1edRWVldMpUBOFp-DR_iCcGi4lLYXingHqE3fnKCVT-=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOQJ0evNFWPr7LUYjsequoHUZI8HnW5_CwlK_Htk1mJ-HfNDFP82Uu9GFUrdn5dRyKVO7QNx1edRWVldMpUBOFp-DR_iCcGi4lLYXingHqE3fnKCVT-=w300-h300" alt="Photo 27" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPsaLxlq0Rwaejc3JucSX5KGucXeKyMW7c4XCzhjMlyjTQde_aKbzMIxXQU8kQSfMrqvrIeMWzsXDqskbgAOY28b2RfPF8HKF0ZtO7bGW5b_J2mPZ1J=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPsaLxlq0Rwaejc3JucSX5KGucXeKyMW7c4XCzhjMlyjTQde_aKbzMIxXQU8kQSfMrqvrIeMWzsXDqskbgAOY28b2RfPF8HKF0ZtO7bGW5b_J2mPZ1J=w300-h300" alt="Photo 28" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMduW8gOc6_OwJzH0AWA5RqQi67K0pDZx8xVqzx5uA4cPB-uAhUWYZf2gC-jmWzE9UbP2VAMHe3i9q6UC3Hp_hDpHHr1-7aunPMzXjAD7cYrC45VqLk=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMduW8gOc6_OwJzH0AWA5RqQi67K0pDZx8xVqzx5uA4cPB-uAhUWYZf2gC-jmWzE9UbP2VAMHe3i9q6UC3Hp_hDpHHr1-7aunPMzXjAD7cYrC45VqLk=w300-h300" alt="Photo 29" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMrE-pSfU1ux7jY7YKr2mqk1sr9LZLwoCnI3hq1s7v3qCLL2q1l1Q2n16Z-dEdwSQY9c7alfUyiGE83LFY5lTqrqmdt264j7JJEAoYWBN1m4k0BLmVZ=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMrE-pSfU1ux7jY7YKr2mqk1sr9LZLwoCnI3hq1s7v3qCLL2q1l1Q2n16Z-dEdwSQY9c7alfUyiGE83LFY5lTqrqmdt264j7JJEAoYWBN1m4k0BLmVZ=w300-h300" alt="Photo 30" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNzNmFAZzOVKD6daejXVWWv-b5LGHAZt-FMwrjnJVydiLmrOLy_G1ctplN1mHtJHjR7domWSD-sZhezvsp94jm0rOdlAPtBjqxrd_Uv-hOaw0Huydpq=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNzNmFAZzOVKD6daejXVWWv-b5LGHAZt-FMwrjnJVydiLmrOLy_G1ctplN1mHtJHjR7domWSD-sZhezvsp94jm0rOdlAPtBjqxrd_Uv-hOaw0Huydpq=w300-h300" alt="Photo 31" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOyeC8ukytJURkYBoLdYirNIDX3ZLSLnSvi9LQ6JRxFs-aHCEz6CK6waB_cSLSBxH1QZI2sjRhNW0FvY2hl7N-iehKTLC7B4CMAVz0kCSo6HNsM0MZU=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOyeC8ukytJURkYBoLdYirNIDX3ZLSLnSvi9LQ6JRxFs-aHCEz6CK6waB_cSLSBxH1QZI2sjRhNW0FvY2hl7N-iehKTLC7B4CMAVz0kCSo6HNsM0MZU=w300-h300" alt="Photo 32" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczN59DyFjtRH8fTUv1SeNKmpmU0VUi2r9ZYFNc8JGNJvhB6HZjexh061KNdKLikmFq06vw-LKG3rMjY1CVAce2_SyVE7_EtLVkag9nQll0oBiu6k5riR=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczN59DyFjtRH8fTUv1SeNKmpmU0VUi2r9ZYFNc8JGNJvhB6HZjexh061KNdKLikmFq06vw-LKG3rMjY1CVAce2_SyVE7_EtLVkag9nQll0oBiu6k5riR=w300-h300" alt="Photo 33" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPiHO6gSUsErs91d1wkqqr3Ndp7u_0oS1SJvhbPxjeiqPkyAUNBRdIzkyIT9SJU1OCEzR3oUYik9LgygR7kIpYiID_yHSGh8I-C7JVoUUIvwtYS2_3g=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPiHO6gSUsErs91d1wkqqr3Ndp7u_0oS1SJvhbPxjeiqPkyAUNBRdIzkyIT9SJU1OCEzR3oUYik9LgygR7kIpYiID_yHSGh8I-C7JVoUUIvwtYS2_3g=w300-h300" alt="Photo 34" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOamKGVHl4QCJi6Yq3ANHAEs-3JuKAJYtsZ2gDZAvrv5J-2l8kLx7TwAX7OWj5_XDatLrt8TkJLDPNNTi8VDyv8F5HRXxU_iX79PC2AwMFoAg5EEmme=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOamKGVHl4QCJi6Yq3ANHAEs-3JuKAJYtsZ2gDZAvrv5J-2l8kLx7TwAX7OWj5_XDatLrt8TkJLDPNNTi8VDyv8F5HRXxU_iX79PC2AwMFoAg5EEmme=w300-h300" alt="Photo 35" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMFGtCzuv6qNi9vldi-yVW5Fn_21KCoTWEjmNWknKG2muHfmaidNJBK20IMDFb6eMUP0l2wI5DSMK7fMpinEneFAGmDcCGrkn-1-x7L89uwLIJ4mLqQ=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMFGtCzuv6qNi9vldi-yVW5Fn_21KCoTWEjmNWknKG2muHfmaidNJBK20IMDFb6eMUP0l2wI5DSMK7fMpinEneFAGmDcCGrkn-1-x7L89uwLIJ4mLqQ=w300-h300" alt="Photo 36" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczO0kvDV7EONW6dkGZrW-VyOhXEzZGGawtlpKwywZhtq4qV70CNomyh-dyb3F3NFOVW-7In5MexMXxU02YtioS1qAB0xWwYqj3LammkP7oEyABmzJPJz=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczO0kvDV7EONW6dkGZrW-VyOhXEzZGGawtlpKwywZhtq4qV70CNomyh-dyb3F3NFOVW-7In5MexMXxU02YtioS1qAB0xWwYqj3LammkP7oEyABmzJPJz=w300-h300" alt="Photo 37" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczP_k0FABDr-YpxTKwl1BT8beWb49YkEoXf9YhFRvAuxEuiukTjtyTlQO9rCOg5YaChYVYSi0vygzVW0ipywhRgu_zDi7dZJWf3FsUzuFJZhRaAZrMJL=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczP_k0FABDr-YpxTKwl1BT8beWb49YkEoXf9YhFRvAuxEuiukTjtyTlQO9rCOg5YaChYVYSi0vygzVW0ipywhRgu_zDi7dZJWf3FsUzuFJZhRaAZrMJL=w300-h300" alt="Photo 38" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOIZdjLRLzjBH9di9KqRk9-0qCIXG1iyJqUVze5njCbldRdYFb_gKQtmlQZYbiJUWjKZC8JdeFRbwDMXfJJ7Euu676F3U4Tifs2UamA55UJss8s3-bj=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOIZdjLRLzjBH9di9KqRk9-0qCIXG1iyJqUVze5njCbldRdYFb_gKQtmlQZYbiJUWjKZC8JdeFRbwDMXfJJ7Euu676F3U4Tifs2UamA55UJss8s3-bj=w300-h300" alt="Photo 39" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOfCZYKFtizuc0EoxNguniPF3zUmXpaGb_0uMbhekPswgVJxtqwWgeAtlaWu_3VpS-4oC-dgkyR_0O_OKQqY0KQxQOhIIobcVVgzIRrt0VCz0YVyxTt=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOfCZYKFtizuc0EoxNguniPF3zUmXpaGb_0uMbhekPswgVJxtqwWgeAtlaWu_3VpS-4oC-dgkyR_0O_OKQqY0KQxQOhIIobcVVgzIRrt0VCz0YVyxTt=w300-h300" alt="Photo 40" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPIYYGEiToVzm3572JXK5eBlDmCTTnGyFuvs4tDu48JqLS81eSO_e2sj1o7qOlnJDxyttFAcDwBrChYnyBHfqNktjruBUzuIfMB-4KLYyC1k3nFjXBs=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPIYYGEiToVzm3572JXK5eBlDmCTTnGyFuvs4tDu48JqLS81eSO_e2sj1o7qOlnJDxyttFAcDwBrChYnyBHfqNktjruBUzuIfMB-4KLYyC1k3nFjXBs=w300-h300" alt="Photo 41" style="width:100%;margin:0;border-radius:0" loading="lazy"></a></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Inspiration">Inspiration<a href="https://austen.info/blog/google-photos-scraper#Inspiration" class="hash-link" aria-label="Direct link to Inspiration" title="Direct link to Inspiration" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://medium.com/@ValentinHervieu/how-i-used-google-photos-to-host-my-website-pictures-gallery-d49f037c8e3c" target="_blank" rel="noopener noreferrer" class="">How I used Google Photos to host my website picture gallery</a></li>
<li class=""><a href="https://www.rainsberger.ca/guides/direct-link-for-posting-google-photos-image/" target="_blank" rel="noopener noreferrer" class="">Get a public link for sharing Google Photos image</a></li>
</ul>]]></content:encoded>
            <category>Photography</category>
            <category>Travel</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[VS Code Copilot Customizations]]></title>
            <link>https://austen.info/blog/copilot-vscode-customizations</link>
            <guid>https://austen.info/blog/copilot-vscode-customizations</guid>
            <pubDate>Wed, 02 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[VS Code AI Customization - Learn to use custom instructions, prompt files, and custom chat modes to personalize AI code generation, reviews, and chat responses.]]></description>
            <content:encoded><![CDATA[<p>Customize GitHub Copilot in VS Code with reusable instructions, prompt files, and chat modes.</p>
<!-- -->
<iframe width="100%" height="400" src="https://www.youtube.com/embed/3cphEIaYSVM" title="Copilot VS Code Customizations" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="">
</iframe>]]></content:encoded>
            <author>stone@austen.info (Austen Stone)</author>
            <category>GitHub</category>
            <category>GitHub Copilot</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Developing with GitHub Copilot Agent Mode and MCP]]></title>
            <link>https://austen.info/blog/github-copilot-agent-mcp</link>
            <guid>https://austen.info/blog/github-copilot-agent-mcp</guid>
            <pubDate>Mon, 30 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How I'm using GitHub Copilot agent mode with MCP to supercharge my development workflow through structured planning, implementation, and testing.]]></description>
            <content:encoded><![CDATA[<p>I'm always looking for ways to work more efficiently and deliver better code faster. Recently, The <a href="https://code.visualstudio.com/blogs/2025/04/07/agentMode" target="_blank" rel="noopener noreferrer" class="">GitHub Copilot Agent Mode</a> in combination with <a href="https://modelcontextprotocol.io/introduction" target="_blank" rel="noopener noreferrer" class="">Model Context Protocol (MCP)</a> has transformed my development workflow.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Customizing-Copilot">Customizing Copilot<a href="https://austen.info/blog/github-copilot-agent-mcp#Customizing-Copilot" class="hash-link" aria-label="Direct link to Customizing Copilot" title="Direct link to Customizing Copilot" translate="no">​</a></h2>
<p>The magic starts with <a href="https://code.visualstudio.com/docs/copilot/copilot-customization" target="_blank" rel="noopener noreferrer" class="">Customizing AI responses in VS Code</a>. Instead of repeatedly explaining my preferences to the AI, I can now define:</p>
<ul>
<li class=""><strong>Custom instructions</strong> for consistent coding practices</li>
<li class=""><strong>Custom prompts</strong> for reusable task templates</li>
<li class=""><strong>Custom chat modes</strong> with specific tool configurations</li>
</ul>
<p>This foundation allows me to create specialized AI assistants for different phases of development.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="VS-Code-Settings">VS Code Settings<a href="https://austen.info/blog/github-copilot-agent-mcp#VS-Code-Settings" class="hash-link" aria-label="Direct link to VS Code Settings" title="Direct link to VS Code Settings" translate="no">​</a></h3>
<p>Here are <a href="https://github.com/austenstone/.vscode" target="_blank" rel="noopener noreferrer" class="">my VS Code settings</a>. I have enabled experimental features and changed some settings to allow the agent to run without my intervention.</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>VS Code Settings</summary><div><div class="collapsibleContent_i85q"><p>View the most recent settings in <a href="https://github.com/austenstone/.vscode" target="_blank" rel="noopener noreferrer" class="">my VS Code settings</a></p><div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockTitle_OeMC">settings.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.nextEditSuggestions.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.enable"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token property">"*"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.renameSuggestions.triggerAutomatically"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.editor.enableCodeActions"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.startDebugging.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.codeGeneration.instructions"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"file"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">".copilot-instructions.md"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"text"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Prefer to use TypeScript over Javascript"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"text"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"My github username is austenstone."</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.testGeneration.instructions"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.reviewSelection.instructions"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.commitMessageGeneration.instructions"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"file"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">".copilot-commit-message-instructions.md"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"text"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Keep the commit message short and concise. Use imperative mood and avoid unnecessary details."</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.reviewSelection.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.setupTests.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.useProjectTemplates"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.generateTests.codeLens"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.edits.temporalContext.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.languageContext.typescript.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.completionContext.typescript.mode"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"sidecar"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.editor.temporalContext.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.codesearch.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.newWorkspaceCreation.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.agent.thinkingTool"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.nextEditSuggestions.fixes"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.languageContext.fix.typescript.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.languageContext.inline.typescript.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"github.copilot.chat.notebook.followCellExecution.enabled"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"chat.editing.autoAcceptDelay"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(9, 134, 88)">5</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Delay in seconds before automatically accepting edits</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"chat.tools.autoApprove"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Automatically approve run commands and tool requests</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"chat.agent.maxRequests"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(9, 134, 88)">100</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Maximum iterations before asking to continue</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"chat.renderRelatedFiles"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"chat.setup.continueLaterIndicator"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"inlineChat.enableV2"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"inlineChat.hideOnRequest"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"inlineChat.finishOnType"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"inlineChat.lineEmptyHint"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token property">"mcp"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token property">"servers"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"playwright"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Useful for operating browser</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"npx"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"args"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"@playwright/mcp@latest"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"serena"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// An entirely separate coding assistant than copilot</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"uvx"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"args"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"--from"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"git+https://github.com/oraios/serena"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"serena-mcp-server.exe"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"time"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Get the current time</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"uvx"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"args"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"mcp-server-time"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"--local-timezone"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"America/New_York"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"searxng"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Search the web using SearxNG</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"npx"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"args"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"-y"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"mcp-searxng"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"env"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token property">"SEARXNG_URL"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"http://localhost/"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"sequential-thinking"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Sequential thinking tool for reasoning</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"npx"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"args"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"-y"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"@modelcontextprotocol/server-sequential-thinking"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"supabase"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Supabase tool for database operations</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"command"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"npx"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"args"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"-y"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"@supabase/mcp-server-supabase@latest"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token string" style="color:rgb(163, 21, 21)">"--read-only"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"env"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token property">"SUPABASE_ACCESS_TOKEN"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"${env:SUPABASE_ACCESS_TOKEN}"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token property">"github"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"> </span><span class="token comment" style="color:rgb(0, 128, 0)">// Interact with the GitHub platform</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"url"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"https://api.githubcopilot.com/mcp/"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"headers"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">          </span><span class="token property">"Authorization"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Bearer ${input:github_mcp_pat}"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token property">"inputs"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"type"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"promptString"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"id"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"github_mcp_pat"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"description"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"GitHub Personal Access Token"</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">        </span><span class="token property">"password"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span></span><br></div></code></pre></div></div></div></div></details>
<ul>
<li class=""><code>github.copilot.chat.codeGeneration.instructions</code> - I have custom instructions personal to me</li>
<li class=""><code>chat.agent.maxRequests</code> - Let's me allow the agent to run longer without asking for permission</li>
<li class=""><code>chat.tools.autoApprove</code> - Automatically approves run commands and tool requests from the agent</li>
</ul>
<p><a href="https://code.visualstudio.com/docs/copilot/reference/copilot-settings#_chat-settings" target="_blank" rel="noopener noreferrer" class="">GitHub Copilot in VS Code settings reference</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="MCP-Tools">MCP Tools<a href="https://austen.info/blog/github-copilot-agent-mcp#MCP-Tools" class="hash-link" aria-label="Direct link to MCP Tools" title="Direct link to MCP Tools" translate="no">​</a></h3>
<p>The <a href="https://code.visualstudio.com/docs/copilot/chat/mcp-servers" target="_blank" rel="noopener noreferrer" class="">Using Model Context Protocol (MCP) in VS Code</a> allows me to provide the agent with access to external tools and data sources.</p>
<p>Some of the MCP servers I use include:</p>
<ul>
<li class=""><a href="https://github.com/modelcontextprotocol/servers/blob/main/src/sequentialthinking" target="_blank" rel="noopener noreferrer" class="">Sequential Thinking</a> - Dynamic and reflective problem-solving through thought sequences</li>
<li class=""><a href="https://github.com/ihor-sokoliuk/mcp-searxng" target="_blank" rel="noopener noreferrer" class="">SearXNG</a> - Integrates the SearXNG API, providing web search capabilities.</li>
<li class=""><a href="https://github.com/executeautomation/mcp-playwright" target="_blank" rel="noopener noreferrer" class="">Playwright</a> - This MCP Server will help you run browser automation and webscraping using Playwright</li>
<li class=""><a href="https://github.com/modelcontextprotocol/servers-archived/tree/main/src/github" target="_blank" rel="noopener noreferrer" class="">GitHub</a> - Repository management, file operations, and GitHub API integration</li>
<li class=""><a href="https://github.com/modelcontextprotocol/servers/blob/main/src/time" target="_blank" rel="noopener noreferrer" class="">time</a> - For getting the current time and date</li>
<li class=""><a href="https://code.visualstudio.com/docs/copilot/chat/mcp-servers#fetch" target="_blank" rel="noopener noreferrer" class="">Fetch</a> - Web content fetching and conversion for efficient LLM usage</li>
</ul>
<p>The repo <a href="https://github.com/modelcontextprotocol/servers" target="_blank" rel="noopener noreferrer" class="">modelcontextprotocol/servers</a> contains a list of available MCP servers.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Development-Workflow">Development Workflow<a href="https://austen.info/blog/github-copilot-agent-mcp#Development-Workflow" class="hash-link" aria-label="Direct link to Development Workflow" title="Direct link to Development Workflow" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Research">Research<a href="https://austen.info/blog/github-copilot-agent-mcp#Research" class="hash-link" aria-label="Direct link to Research" title="Direct link to Research" translate="no">​</a></h3>
<p>Before diving into coding, I often need to research new concepts or technologies. For this, I use a custom chat mode called <code>research</code> that includes tools like web search, and sequential thinking.</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Research Chat Mode</summary><div><div class="collapsibleContent_i85q"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockTitle_OeMC">research.chatmode.md</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'Research mode for gathering information and analyzing topics in depth.'</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">tools</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'changes'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'codebase'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'extensions'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'fetch'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'findTestFiles'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'githubRepo'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'new'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'openSimpleBrowser'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'problems'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'runCommands'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'runNotebooks'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'runTasks'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'search'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'searchResults'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'terminalLastCommand'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'terminalSelection'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'testFailure'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'usages'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'vscodeAPI'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'playwright'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'get_current_time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'searxng'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'searxng_web_search'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'web_url_read'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'sequential-thinking'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'supabase'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_branches'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'github'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_branches'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_code_scanning_alerts'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_commits'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_issues'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_notifications'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_pull_requests'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_secret_scanning_alerts'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'list_tags'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'search_code'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'search_issues'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'search_repositories'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'search_users'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'sequentialthinking'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Activate research mode.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Your task is to research and gather information about a specific topic.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># Tools</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Use tools to gather information and do research. Use the `sequential</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">thinking` tool for complex problems requiring deep analysis and multi</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">step reasoning.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `sequentialthinking` tool for complex problems requiring deep analysis and multi</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">step reasoning.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use search tools</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use `searxng_web_search` to search the web for broad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> initial keyword</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">based web searches to discover relevant URLs.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `web_url_read` tool when you have a direct URL and need to quickly extract the raw text or content of a page.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `fetch` tool as an alternative to web_url_read</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> potentially for more complex requests.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `playwright` tool to interact with web pages and gather information from the browser. Use to get CSS and style information.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># 1. Strategic Planning (DO THIS FIRST)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Use the sequential</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">thinking `sequentialthinking` tool to outline your research strategy. Identify keywords</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> potential information sources</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and the steps you will take.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># 2. Information Gathering &amp; Analysis (THINGS YOU MUST DO)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> When you don't understand a term or topic search the web using tools like `searxng_web_search` or `web_url_read` to gather more information.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use all available tools to gather information and do research.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Continue using tools to gather information until you've fully researched the topic or problem.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Synthesize and summarize findings at key intervals. After gathering information from multiple sources</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> take a moment to synthesize what you've learned before proceeding.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> When you're done use `sequentialthinking` to ask yourself if you've gathered enough information to answer the question or solve the problem.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># 3. Concluding the Research (DO THIS LAST)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">Use sequential-thinking to ask yourself</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Have I gathered sufficient, well-verified information to thoroughly address the user's request?"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Provide a final</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token key atrule">structured response that includes</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> A direct answer to the core question.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> A summary of key findings with linked sources.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># DO NOT (THINGS YOU MUST NOT DO)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Do not write actual code</span><span class="token tag" style="color:rgb(128, 0, 0)">!</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Do not make assumptions without verifying them through research or analysis.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Do not skip steps in the analysis process; always think step by step.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Do not rush to conclusions; take the time to explore different angles and gather sufficient information.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Do not stop until you have a comprehensive understanding of the topic or problem.</span></span><br></div></code></pre></div></div></div></div></details>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Planning">Planning<a href="https://austen.info/blog/github-copilot-agent-mcp#Planning" class="hash-link" aria-label="Direct link to Planning" title="Direct link to Planning" translate="no">​</a></h3>
<p>I start every project in a <a href="https://code.visualstudio.com/docs/copilot/chat/chat-modes" target="_blank" rel="noopener noreferrer" class="">custom chat mode</a> called <code>plan</code> that I have in my user space. The tools I want for are already selected and copilot can't edit my code even if it wanted to because I took away that tool. I find <code>Gemini 2.5 Pro</code> to be the best model for planning.</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Plan Chat Mode</summary><div><div class="collapsibleContent_i85q"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockTitle_OeMC">plan.chatmode.md</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'Generate comprehensive implementation plans for features, refactoring, and bug fixes without making code changes.'</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">tools</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(4, 81, 165)">[</span><span class="token string" style="color:rgb(163, 21, 21)">'changes'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'codebase'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'editFiles'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'extensions'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'fetch'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'findTestFiles'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'githubRepo'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'problems'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'runCommands'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'search'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'searchResults'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'usages'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'vscodeAPI'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'get_current_time'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'searxng'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'sequential-thinking'</span><span class="token punctuation" style="color:rgb(4, 81, 165)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Activate planning mode.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Your task is to think about and generate an implementation plan for a new feature or for refactoring existing code.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># Planning mode instructions</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Don't make any code edits</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> just generate a plan.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># Instructions</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">1. **Gather Context**</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> Use the `codebase` tool to analyze the current codebase</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> including existing features</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> architecture</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and dependencies. Use the `changes` tool to identify recent changes that may impact the new feature or refactoring task.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">2. **Research**</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> Use the `searxng_web_search` and `web_url_read` tools to gather information about best practices</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> design patterns</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and similar implementations. This will help you understand how to approach the new feature or refactoring task effectively.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">3. **Plan Structure**</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> Create a structured plan.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">4. Repeat the process iteratively</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> refining your understanding and plan as you gather more information.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># Output</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">The plan consists of a Markdown document that describes the implementation plan</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> </span><span class="token key atrule">including the following sections</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">* Overview</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> A brief description of the feature or refactoring task.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">* Requirements</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> A list of requirements for the feature or refactoring task.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">* Implementation Steps</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> A detailed list of steps to implement the feature or refactoring task.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">* Testing</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> A list of tests that need to be implemented to verify the feature or refactoring task.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># What You MUST Do:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use tools to gather information and analyze the codebase before planning.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use existing context and information from previous conversations to inform your planning.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Always gather comprehensive context before planning</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Think through edge cases and potential complications</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Plan for comprehensive testing at each phase</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Document assumptions and decision rationale</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Structure plans for easy team comprehension and execution</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Consider backward compatibility and migration paths</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># What You MUST NOT Do:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Never write actual code or implementation details</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Never modify files or make changes to the codebase</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Never provide large code snippets or complete implementations</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Never skip the analysis phase and jump directly to solutions</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Never assume requirements without clarification</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># 3. Tool Usage Guidelines</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Use tools to help create implementation plan. Use the `sequential</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">thinking` tool for complex problems requiring deep analysis and multi</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">step reasoning.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `sequentialthinking` tool for complex problems requiring deep analysis and multi</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">step reasoning.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use search tools</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use `searxng_web_search` to search the web for broad</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> initial keyword</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">based web searches to discover relevant URLs.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `web_url_read` tool when you have a direct URL and need to quickly extract the raw text or content of a page.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `fetch` tool as an alternative to web_url_read</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> potentially for more complex requests.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `playwright` tool to interact with web pages and gather information from the browser. Use to get CSS and style information.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Use the `editFiles` tool to create prompt files in `.github/prompts` directory with the name `&lt;name</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain">.prompt.md` for planning and implementation. ONLY USE THIS TOOL TO CREATE PROMPT FILES.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Think step by step through each planning phase</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and always prioritize thorough analysis over speed. For complex problems that require deep reasoning</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> evolving understanding</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> or exploration of multiple approaches</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> utilize the sequential</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">thinking tool to maintain context and generate well</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">reasoned solutions. A well</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">researched plan prevents costly implementation mistakes and ensures successful project outcomes.</span></span><br></div></code></pre></div></div></div></div></details>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Prompt Creation Prompt</summary><div><div class="collapsibleContent_i85q"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockTitle_OeMC">copilot-prompt.prompt.md</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm" style="counter-reset:line-count 0"><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">mode</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> agent</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Prompt files are standalone prompts that you can run directly in chat. They describe the task to be performed (what should be done)."</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Create a prompt file in the `.github/prompts` directory that implements all previous conversation and context.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Add as much context and information as possible.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># Information</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)">## Prompt files</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Define reusable prompts for common tasks like generating code or performing a code review. Prompt files are standalone prompts that you can run directly in chat. They describe the task to be performed (what should be done). Optionally</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> you can include tasks</span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain">specific guidelines about how the task should be performed</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> or you can reference custom instructions in the prompt file.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)">## Prompt file structure</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">A prompt file is a Markdown file with the .prompt.md file suffix. It has the following two main sections</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> (Optional) Header with metadata (Front Matter syntax)</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">mode</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token key atrule">The chat mode to use when running the prompt</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> ask</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> edit</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> or agent (default).</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> A short description of the prompt.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Body with the prompt content</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Prompt files mimic the format of writing prompts in chat. This allows blending natural language instructions</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> additional context</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and even linking to other prompt files as dependencies. You can use Markdown formatting to structure the prompt content</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> including headings</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> lists</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and code blocks.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">You can reference other workspace files</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> prompt files</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> or instructions files by using Markdown links. Use relative paths to reference these files</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> and ensure that the paths are correct based on the location of the prompt file.</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">Within a prompt file</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> you can reference variables by using the $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">variableName</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"> syntax. You can reference the following variables</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Workspace variables </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">workspaceFolder</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">workspaceFolderBasename</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Selection variables </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">selection</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">selectedText</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> File context variables </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">file</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">fileBasename</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">fileDirname</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">fileBasenameNoExtension</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> Input variables </span><span class="token punctuation" style="color:rgb(4, 81, 165)">-</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">input</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain">variableName</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain">input</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain">variableName</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain">placeholder</span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><span class="token plain"> (pass values to the prompt from the chat input field)</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token comment" style="color:rgb(0, 128, 0)"># Example</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">```</span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">mode</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">'agent'</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(4, 81, 165)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">''</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">---</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">&lt;content goes here</span><span class="token punctuation" style="color:rgb(4, 81, 165)">&gt;</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_lJS_" style="color:#000000"><span class="codeLineNumber_Tfdd"></span><span class="codeLineContent_feaV"><span class="token plain">```</span></span><br></div></code></pre></div></div></div></div></details>
<p>The goal of this phase is to generate a comprehensive <code>.prompt.md</code> file in <code>.github/prompts/</code> that serves as a detailed blueprint for the implementation.</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>You can use the Command Palette (<code>ctrl+shift+p</code>) in VS Code to quickly create new chat modes and prompts.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Implementation">Implementation<a href="https://austen.info/blog/github-copilot-agent-mcp#Implementation" class="hash-link" aria-label="Direct link to Implementation" title="Direct link to Implementation" translate="no">​</a></h3>
<p>Once I have my planning prompt ready, I switch to regular agent mode with Claude Sonnet 4 and simply run <code>/prompt-name</code>. The beauty of this approach is that the AI has all the context it needs to execute the plan methodically.</p>
<p>The prompt file acts as a contract between the planning phase and implementation phase, ensuring consistency and reducing the need for back-and-forth clarification.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="Course-Correction">Course Correction<a href="https://austen.info/blog/github-copilot-agent-mcp#Course-Correction" class="hash-link" aria-label="Direct link to Course Correction" title="Direct link to Course Correction" translate="no">​</a></h4>
<p>If the agent is close I will sometimes pause it and ask it to look at a specific resource or document to help it understand the problem better.</p>
<p>When the agent deviates from the desired behavior (which occasionally happens), I have a simple recovery process:</p>
<ol>
<li class="">Clear my git diff to reset the changes</li>
<li class="">Modify the prompt file based on what I learned</li>
<li class="">Restart the implementation from scratch</li>
</ol>
<p>This iterative refinement of prompts means I'm constantly improving my templates for future use.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Validation">Validation<a href="https://austen.info/blog/github-copilot-agent-mcp#Validation" class="hash-link" aria-label="Direct link to Validation" title="Direct link to Validation" translate="no">​</a></h3>
<p>Finally (or maybe first if we're following TDD) I want to ensure the implementation works as expected. Ideally our output is testable but sometimes this is hard.</p>
<p>For browser interactions, I use the <code>playwright</code> MCP tool to automate testing. This is really cool because the agent can use a browser just like I can! This lets it understand how the UI works and what it looks like.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Example">Example<a href="https://austen.info/blog/github-copilot-agent-mcp#Example" class="hash-link" aria-label="Direct link to Example" title="Direct link to Example" translate="no">​</a></h2>
<p>Let's walk though a real-world example. I want to add comments to my blog you're viewing right now.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-Research">1. Research<a href="https://austen.info/blog/github-copilot-agent-mcp#1-Research" class="hash-link" aria-label="Direct link to 1. Research" title="Direct link to 1. Research" translate="no">​</a></h3>
<p>I start by in <a href="https://austen.info/blog/github-copilot-agent-mcp#Research" class=""><code>research</code></a> mode using Gemini 2.5 Pro with a question:</p>
<div class="language-md codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-md codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">How would I add comments to my blog posts in this Docusaurus site?</span><br></div></code></pre></div></div>
<p>Copilot starts by using <code>sequentialthinking</code> think about how to perform research</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockTitle_OeMC">sequentialthinking</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"nextThoughtNeeded"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"thought"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"I need to research how to add comments to a Docusaurus blog. I will start by searching for \"Docusaurus blog comments\" and analyze the results. I will look for official documentation and popular third-party solutions like Giscus, Disqus, or Utterances. Finally, I will synthesize the findings to provide a clear recommendation."</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"thoughtNumber"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(9, 134, 88)">1</span><span class="token punctuation" style="color:rgb(4, 81, 165)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"totalThoughts"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(9, 134, 88)">2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><br></div></code></pre></div></div>
<p>Then Copilot starts searching the web using <code>searxng_web_search</code> and <code>web_url_read</code> tools.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockTitle_OeMC">searxng_web_search</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token punctuation" style="color:rgb(4, 81, 165)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">  </span><span class="token property">"query"</span><span class="token operator" style="color:rgb(0, 0, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(163, 21, 21)">"Docusaurus blog comments"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain"></span><span class="token punctuation" style="color:rgb(4, 81, 165)">}</span><br></div></code></pre></div></div>
<p>It comes up with the idea to use Giscus as a commenting service.</p>
<blockquote>
<p>To add comments to your Docusaurus blog, you'll need to use a third-party commenting service because Docusaurus generates a static site. A highly recommended option that integrates perfectly with your existing GitHub-based project is Giscus.</p>
</blockquote>
<p><a href="https://github.com/austenstone/portfolio/blob/main/blog/2025-06-30-github-copilot-agent-mcp/_prompts/chat1.json" target="_blank" rel="noopener noreferrer" class="">Full chat export</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-Planning">2. Planning<a href="https://austen.info/blog/github-copilot-agent-mcp#2-Planning" class="hash-link" aria-label="Direct link to 2. Planning" title="Direct link to 2. Planning" translate="no">​</a></h3>
<p>Now I switch to <a href="https://austen.info/blog/github-copilot-agent-mcp#Planning" class=""><code>plan</code></a> chat mode.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">Create a plan based on your research</span><br></div></code></pre></div></div>
<p>Copilot comes up with a structured plan for me.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-Implementation">3. Implementation<a href="https://austen.info/blog/github-copilot-agent-mcp#3-Implementation" class="hash-link" aria-label="Direct link to 3. Implementation" title="Direct link to 3. Implementation" translate="no">​</a></h3>
<p>Now I switch to regular <code>agent</code> mode.</p>
<p>We're going to reuse our prompt file from earlier to write the plan as a <code>.prompt.md</code> file.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">/copilot-prompt</span><br></div></code></pre></div></div>
<p>Copilot creates the <code>.github/prompts/implement-giscus.prompt.md</code> file.</p>
<p>Now I can switch to Claude Sonnet 4 and run the generated prompt file.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">/implement-giscus</span><br></div></code></pre></div></div>
<p><a href="https://github.com/austenstone/portfolio/blob/main/blog/2025-06-30-github-copilot-agent-mcp/_prompts/chat1.json" target="_blank" rel="noopener noreferrer" class="">Full chat export</a></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="Course-Correction-1">Course Correction<a href="https://austen.info/blog/github-copilot-agent-mcp#Course-Correction-1" class="hash-link" aria-label="Direct link to Course Correction" title="Direct link to Course Correction" translate="no">​</a></h4>
<p>I'm watching the agent work and it got hung up on how to pass front matter metadata to the Giscus component.</p>
<p>I paused and asked it to look at document on the topic</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">look at how blog posts can do front matter</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog#markdown-front-matter</span><br></div></code></pre></div></div>
<p>Copilot looked at my resource and found even more resources to help it understand how to access blog post metadata.</p>
<blockquote>
<p>I'll examine how to properly access blog post front matter in Docusaurus by checking the documentation and updating the implementation accordingly.</p>
<p>&gt; Fetched <a href="https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog#markdown-front-matter" target="_blank" rel="noopener noreferrer" class="">https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog#markdown-front-matter</a></p>
<p>Now let me check the Docusaurus documentation about how to access blog post metadata in components:</p>
<p>&gt; Fetched <a href="https://docusaurus.io/docs/blog#accessing-blog-post-information" target="_blank" rel="noopener noreferrer" class="">https://docusaurus.io/docs/blog#accessing-blog-post-information</a></p>
<p>Let me search for Docusaurus documentation specifically about the useBlogPost hook:</p>
<p>...</p>
</blockquote>
<p>Copilot figured out that the ideal solution was the reactHook <a href="https://github.com/austenstone/portfolio/pull/36/files#diff-6324df17a3dc2a08f453a5faaa507164572fb47b545db0c902dfa9386e9b7a43R11" target="_blank" rel="noopener noreferrer" class=""><code>useBlogPost()</code></a> which I totally agree with.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-Validation">4. Validation<a href="https://austen.info/blog/github-copilot-agent-mcp#4-Validation" class="hash-link" aria-label="Direct link to 4. Validation" title="Direct link to 4. Validation" translate="no">​</a></h3>
<p>I want to ensure the Giscus comments are working correctly so I ask Copilot to browse to a link knowing it will use the <code>playwright</code> MCP tool.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">check that the giscus comments are working</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">browse to http://localhost:3000/blog/github-copilot-agent-mcp</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">test functionality</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">#changes</span><br></div></code></pre></div></div>
<p>This tested the comments were working but I wanted it to actually make a comment on the blog post.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#000000;--prism-background-color:#ffffff"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#000000;background-color:#ffffff"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#000000"><span class="token plain">Test the comments by making comments on this blog post</span><br></div><div class="token-line" style="color:#000000"><span class="token plain">http://localhost:3000/blog/github-copilot-agent-mcp</span><br></div><div class="token-line" style="color:#000000"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#000000"><span class="token plain">Make the comment about the blog post and say that it's coming from github copilot using playwright</span><br></div></code></pre></div></div>
<p>The <a href="https://github.com/austenstone/portfolio/discussions/35#discussioncomment-13621611" target="_blank" rel="noopener noreferrer" class="">comment</a> on this blog post was actually the output of this prompt.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="Real-Example">Real Example<a href="https://austen.info/blog/github-copilot-agent-mcp#Real-Example" class="hash-link" aria-label="Direct link to Real Example" title="Direct link to Real Example" translate="no">​</a></h3>
<p>Copilot did complete my feature completely.</p>
<p>The full PR can be found at <a href="https://github.com/austenstone/portfolio/pull/36" target="_blank" rel="noopener noreferrer" class="">austenstone/portfolio/pull/36</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Benefits">Benefits<a href="https://austen.info/blog/github-copilot-agent-mcp#Benefits" class="hash-link" aria-label="Direct link to Benefits" title="Direct link to Benefits" translate="no">​</a></h2>
<p>This workflow has transformed how I approach development:</p>
<ol>
<li class=""><strong>Consistency</strong>: Custom instructions ensure all generated code follows my patterns</li>
<li class=""><strong>Efficiency</strong>: Pre-planned prompts eliminate repetitive explanations</li>
<li class=""><strong>Quality</strong>: Structured thinking leads to better architectural decisions</li>
<li class=""><strong>Testability</strong>: UI-aware testing tools create more comprehensive test suites</li>
<li class=""><strong>Reproducibility</strong>: Documented prompts make complex tasks repeatable</li>
</ol>
<p>Ultimately I can spend more time on high-level design and less on low-level implementation details. The AI handles the grunt work, allowing me to focus on delivering value.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="The-Future">The Future<a href="https://austen.info/blog/github-copilot-agent-mcp#The-Future" class="hash-link" aria-label="Direct link to The Future" title="Direct link to The Future" translate="no">​</a></h2>
<p>This combination of Agent Mode and MCP represents a fundamental shift in how we can work with AI. Instead of treating AI as a simple code completion tool, we can create sophisticated, context-aware development partners that understand our specific needs and workflows.</p>
<p>The key insight is that the AI becomes more valuable when it has more context about our intentions, constraints, and environment. MCP provides that context, while custom prompts ensure consistent, high-quality outputs.</p>
<hr>
<p><em>Have you experimented with Agent Mode and MCP in your development workflow? I'd love to hear about your experiences and any creative tool combinations you've discovered. Connect with me on <a href="https://github.com/austenstone" target="_blank" rel="noopener noreferrer" class="">GitHub</a> or <a href="https://www.linkedin.com/in/austenstone/" target="_blank" rel="noopener noreferrer" class="">LinkedIn</a> to continue the conversation.</em></p>]]></content:encoded>
            <category>GitHub Copilot</category>
            <category>AI</category>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[I Rebuilt the Website]]></title>
            <link>https://austen.info/blog/welcome-to-my-portfolio</link>
            <guid>https://austen.info/blog/welcome-to-my-portfolio</guid>
            <pubDate>Thu, 26 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A brief introduction to my new portfolio website built with Docusaurus, showcasing my projects and blog.]]></description>
            <content:encoded><![CDATA[<p>I've migrated my portfolio from Angular to <a href="https://docusaurus.io/docs" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> to focus more on content and less on maintenance.</p>
<p><img decoding="async" loading="lazy" alt="Docusaurus Social Card" src="https://austen.info/assets/images/undraw_docusaurus_react-a1cac3decaebe39767f41e5dc859a9af.svg" width="1041" height="554" class="img_ev3q"></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Why-Docusaurus">Why Docusaurus?<a href="https://austen.info/blog/welcome-to-my-portfolio#Why-Docusaurus" class="hash-link" aria-label="Direct link to Why Docusaurus?" title="Direct link to Why Docusaurus?" translate="no">​</a></h2>
<p><a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> is built specifically for content creators who want to focus on writing rather than wrestling with complex build systems. Here's what makes it perfect for my needs:</p>
<p><strong>Content-First Approach</strong>: Writing in Markdown is a game-changer. Instead of wrestling with Angular components for every blog post, I can now focus purely on content. This means more frequent updates, better documentation for my projects, and less time spent on the technical overhead of publishing.</p>
<p><strong>Built for Performance</strong>: Docusaurus follows the PRPL Pattern for lightning-fast loading, generates static HTML for SEO, and provides client-side routing for a smooth single-page app experience.</p>
<p><strong>Developer Experience</strong>: Hot reloading, MDX support for interactive components, and sensible defaults mean I can ship content quickly without sacrificing functionality.</p>
<p>GitHub Copilot's agentic mode was incredibly helpful during the migration - it handled a lot of the repetitive work and helped me understand Docusaurus patterns quickly.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="Whats-Here">What's Here<a href="https://austen.info/blog/welcome-to-my-portfolio#Whats-Here" class="hash-link" aria-label="Direct link to What's Here" title="Direct link to What's Here" translate="no">​</a></h2>
<ul>
<li class=""><strong>Projects</strong>: Detailed documentation of my GitHub Actions, DeviceWISE View, and other work</li>
<li class=""><strong>Blog</strong>: Technical insights and experiences from my role at GitHub</li>
<li class=""><strong>Resume</strong>: Professional experience and skills</li>
</ul>
<p>Ready to ship more content! 🚀</p>]]></content:encoded>
            <category>Development</category>
        </item>
        <item>
            <title><![CDATA[Napa Sonoma Photos]]></title>
            <link>https://austen.info/blog/napa-sonoma</link>
            <guid>https://austen.info/blog/napa-sonoma</guid>
            <pubDate>Sat, 04 May 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Photos from my recent trip to Napa and Sonoma, sourced using a Google Photos scraper tool I built.]]></description>
            <content:encoded><![CDATA[
<style>.photoswipe-gallery{column-count:3;column-gap:5px}@media (width<=1024px){.photoswipe-gallery{column-count:2}}@media (width<=512px){.photoswipe-gallery{column-count:1}}.image-link{box-shadow:none;transition:opacity .135s cubic-bezier(0,0,.2,1);display:block;position:relative;overflow:hidden}.image-link:after{content:"";opacity:0;transition:opacity .135s cubic-bezier(0,0,.2,1);display:block;position:absolute;inset:0}.image-link:hover:after{opacity:1;background-image:linear-gradient(to bottom,color-mix(in srgb,var(--gm3-sys-color-scrim,#000)38%,transparent),transparent 56px,transparent)}</style><div class="photoswipe-gallery "><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPlsS5BHu1BTykbrbDtfwKYBxNKr0UcY0DLOPNnpAqDYvU4L4rLz8OmhcG1KCEJpBdRDonodfC4GuAQCIcN0s_oOoQU6MxWmV0-lnnsI0x4nkkLoBcL=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPlsS5BHu1BTykbrbDtfwKYBxNKr0UcY0DLOPNnpAqDYvU4L4rLz8OmhcG1KCEJpBdRDonodfC4GuAQCIcN0s_oOoQU6MxWmV0-lnnsI0x4nkkLoBcL=w300-h300" alt="Photo 1" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczML6Gv3pwxX8Q5zdHi-sdzOVsdpiwTqdouV9WyHV_EsZAhN5EYfKrd-35giG7i7Ytm6h1kJBXSdziZTjlxZsDkB7C5Y0sNNidsZjIbGeD83Oc8vqT5E=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczML6Gv3pwxX8Q5zdHi-sdzOVsdpiwTqdouV9WyHV_EsZAhN5EYfKrd-35giG7i7Ytm6h1kJBXSdziZTjlxZsDkB7C5Y0sNNidsZjIbGeD83Oc8vqT5E=w300-h300" alt="Photo 2" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMpDv7eJBbAsRolqKTJRziXsrVnl-q_852pB7zR1HArCHBe0jf9m3UWZPGtL57nEZ9hW0BQxkCogOypDZ4moMh7-KDM6H1G60iNT0WZxJ43egrEIGpJ=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMpDv7eJBbAsRolqKTJRziXsrVnl-q_852pB7zR1HArCHBe0jf9m3UWZPGtL57nEZ9hW0BQxkCogOypDZ4moMh7-KDM6H1G60iNT0WZxJ43egrEIGpJ=w300-h300" alt="Photo 3" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNwbyZPB4xeO2QBwufrvfQSBZlVUBPgu4AmZeQPgdrpVWf7LKzdwszQJdzwNNQ1iS15UVwCECtWMqgkj4gGNPQQriMtsT0jilL9QMva6HPeP9oMGQNg=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNwbyZPB4xeO2QBwufrvfQSBZlVUBPgu4AmZeQPgdrpVWf7LKzdwszQJdzwNNQ1iS15UVwCECtWMqgkj4gGNPQQriMtsT0jilL9QMva6HPeP9oMGQNg=w300-h300" alt="Photo 4" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNFGpywyZMP4V5s9BGmgBnT8lHRGs8B6rZfFpYGs59WNWq3Qo5eaENcK22PLb2so9RZh3KVOJMQs3vdG6grAE-wVefANDL64jliGP2_eWjAC5SZxwfp=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNFGpywyZMP4V5s9BGmgBnT8lHRGs8B6rZfFpYGs59WNWq3Qo5eaENcK22PLb2so9RZh3KVOJMQs3vdG6grAE-wVefANDL64jliGP2_eWjAC5SZxwfp=w300-h300" alt="Photo 5" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPDlG1zOBFJwSNB2mDJXreFEpt8s2S6q9zSpsIBATBU6ebjP5vTu9CcKhv7H_K22UcjO9a-zNQ5p-S8kyhDjdjY5-pn4y2Me638xkENvrXnQ-nY23GS=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPDlG1zOBFJwSNB2mDJXreFEpt8s2S6q9zSpsIBATBU6ebjP5vTu9CcKhv7H_K22UcjO9a-zNQ5p-S8kyhDjdjY5-pn4y2Me638xkENvrXnQ-nY23GS=w300-h300" alt="Photo 6" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczM7wqJsnjyVAwsfjmdWBH58wkOkn-xjAdCg65cc0_4MwLuLZnbW8EgNyuaGKfPHjfGwK7p1yZVUKB3XoFmst6-2NzK8ptmClkI-kSh87h7aWACV0F5w=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczM7wqJsnjyVAwsfjmdWBH58wkOkn-xjAdCg65cc0_4MwLuLZnbW8EgNyuaGKfPHjfGwK7p1yZVUKB3XoFmst6-2NzK8ptmClkI-kSh87h7aWACV0F5w=w300-h300" alt="Photo 7" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNMHj2gmXlf_mZ2bo__GPZ3hjmS0e4sHONA0YbebEZdi3ewGcwbor7sgmTEfLB0dXuu1BgC4vkhwvIKnKoSahChVjNgA2Cb3UdIflylsgGRQc3vx1Yt=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNMHj2gmXlf_mZ2bo__GPZ3hjmS0e4sHONA0YbebEZdi3ewGcwbor7sgmTEfLB0dXuu1BgC4vkhwvIKnKoSahChVjNgA2Cb3UdIflylsgGRQc3vx1Yt=w300-h300" alt="Photo 8" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMqgwivblt9udLW6O4LWQfidRqZCOPHvEV5okYpAFi0nyhgqp0Jnbr-C0TPTK7qszChTm4StzVslIvSzRxiaTHG3S2vi0N1o6ahQ1tNGR0in8R5QkdD=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMqgwivblt9udLW6O4LWQfidRqZCOPHvEV5okYpAFi0nyhgqp0Jnbr-C0TPTK7qszChTm4StzVslIvSzRxiaTHG3S2vi0N1o6ahQ1tNGR0in8R5QkdD=w300-h300" alt="Photo 9" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPw83kZkDKd3GraLLG05nEOaXzuOkpAgdQMYgHe72niAhRVBEgF5eWTppGiB4P--7GcBL8di-PZy7pi1hARiMFuNHxwOnBOB0jYSXnlQHRrT7WxuACF=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPw83kZkDKd3GraLLG05nEOaXzuOkpAgdQMYgHe72niAhRVBEgF5eWTppGiB4P--7GcBL8di-PZy7pi1hARiMFuNHxwOnBOB0jYSXnlQHRrT7WxuACF=w300-h300" alt="Photo 10" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPpPMINldkgtaJwERMm1qInQ1i1IP7cOXWgUr4MiX_SYHgHL0-T4_wipmcaJ-1i0_BL1bTmForL9tN5o3ZRfR8dpxRLVmXR4PchciZ4K1qo7U8B2mUi=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPpPMINldkgtaJwERMm1qInQ1i1IP7cOXWgUr4MiX_SYHgHL0-T4_wipmcaJ-1i0_BL1bTmForL9tN5o3ZRfR8dpxRLVmXR4PchciZ4K1qo7U8B2mUi=w300-h300" alt="Photo 11" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNyD77Uey9jz4fwTBTpj_PlD02Kj_fyGhKckYq1A6oUfStLL4BbeKSQhzc4d6ZU20AaujTqewoDCsDDr7ieaCqgseqx6JLG8huFKTiziL5I7vUew7NS=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNyD77Uey9jz4fwTBTpj_PlD02Kj_fyGhKckYq1A6oUfStLL4BbeKSQhzc4d6ZU20AaujTqewoDCsDDr7ieaCqgseqx6JLG8huFKTiziL5I7vUew7NS=w300-h300" alt="Photo 12" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczM-FJanEXJ6dD8Ur_WGltpCgr0s1WPMSBC5UCyR0jXnGx-8GNgAKCiyTj77GCuCGU7m9SR5idxSjai-i0MXHR_RcNjlnfO15gun0pX2jF9oJ3eNXcWb=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczM-FJanEXJ6dD8Ur_WGltpCgr0s1WPMSBC5UCyR0jXnGx-8GNgAKCiyTj77GCuCGU7m9SR5idxSjai-i0MXHR_RcNjlnfO15gun0pX2jF9oJ3eNXcWb=w300-h300" alt="Photo 13" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNk5YiepFSwnkXo1i_UfPMON1ms-XFdat4vGrDePbeAUGY4gt1EAFb0ZvbT1lehwIxLZL8uVRfzYjvpLweXaLZ0vn_G5WfQn2aAYl5-HMHlTu2K0aSn=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNk5YiepFSwnkXo1i_UfPMON1ms-XFdat4vGrDePbeAUGY4gt1EAFb0ZvbT1lehwIxLZL8uVRfzYjvpLweXaLZ0vn_G5WfQn2aAYl5-HMHlTu2K0aSn=w300-h300" alt="Photo 14" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczMkNG0Sanj9g54B-Er46DC-wyEOCf6LxhBhT0Ua6Rbfy9hazKIHweyaA4CM4K6n6ppfdIyhGdwBNrqWCjg-sB6mhYptgNwAEdKmM4DbuZGbid2MFheu=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczMkNG0Sanj9g54B-Er46DC-wyEOCf6LxhBhT0Ua6Rbfy9hazKIHweyaA4CM4K6n6ppfdIyhGdwBNrqWCjg-sB6mhYptgNwAEdKmM4DbuZGbid2MFheu=w300-h300" alt="Photo 15" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczP_WJF8p8E6UFWJWYWr3tnCw6lL6YnfENdKQOmveJMI_BMRL7j5ubr2CyCS2x49L_blP90j_MdjZiDAARmNbFJlciF2N5GtM0GzGY-Awg5aYs4-xZTd=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczP_WJF8p8E6UFWJWYWr3tnCw6lL6YnfENdKQOmveJMI_BMRL7j5ubr2CyCS2x49L_blP90j_MdjZiDAARmNbFJlciF2N5GtM0GzGY-Awg5aYs4-xZTd=w300-h300" alt="Photo 16" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNSwFMfXpU0aO67i6y_zntT3xyhOLT--eWgs_8nFgTgTDLuzEblzx-3fbUW3ffusjFGgDfj0vIFB93yWlcLSAg2jxWZv_yC7-NCknMwWfPWgSrIcDe2=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNSwFMfXpU0aO67i6y_zntT3xyhOLT--eWgs_8nFgTgTDLuzEblzx-3fbUW3ffusjFGgDfj0vIFB93yWlcLSAg2jxWZv_yC7-NCknMwWfPWgSrIcDe2=w300-h300" alt="Photo 17" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczPmqs5ZscmWRKnZZgjjEOV7Yqvka0cddKzZhAWfRW4tqfQ3dssWZBGF9gEv-5SUcSv3MaWsMZ1qrSHFWv8vqbjZrKwsH8keg8sTqBNJX4tKFcQN1lac=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczPmqs5ZscmWRKnZZgjjEOV7Yqvka0cddKzZhAWfRW4tqfQ3dssWZBGF9gEv-5SUcSv3MaWsMZ1qrSHFWv8vqbjZrKwsH8keg8sTqBNJX4tKFcQN1lac=w300-h300" alt="Photo 18" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczOY-205C0RPYG61B4D72dbds4XEb8VHlHGKLi_mIHa6kdVyfL6ci2BmcjEfffkCFO08LZWeeTZHXhvgIPctjbOCDlfwj21ZtpsfGeWaaolBxGLvclZl=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczOY-205C0RPYG61B4D72dbds4XEb8VHlHGKLi_mIHa6kdVyfL6ci2BmcjEfffkCFO08LZWeeTZHXhvgIPctjbOCDlfwj21ZtpsfGeWaaolBxGLvclZl=w300-h300" alt="Photo 19" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczO71z7-oUl5HHx9zwBzvSujZOIaFYvlSvvW5KMYvS9npRHn5aYOtOKwx2R9iAF1gxUqAbtk1LLhCk2qWLDenTH-f4AVFAW_am9eQ25hLIIvdbV8WulC=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczO71z7-oUl5HHx9zwBzvSujZOIaFYvlSvvW5KMYvS9npRHn5aYOtOKwx2R9iAF1gxUqAbtk1LLhCk2qWLDenTH-f4AVFAW_am9eQ25hLIIvdbV8WulC=w300-h300" alt="Photo 20" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczN2cR5B3HShaMuAXSahiDc5CtNJo98dtakNWLUafx7-oM6CnToTEZTYafKYsq8Vy8lAJRswPzfPNZGkCieY-oNycFN0F2MNOFx0gMlEWFO6-dRzLgcv=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczN2cR5B3HShaMuAXSahiDc5CtNJo98dtakNWLUafx7-oM6CnToTEZTYafKYsq8Vy8lAJRswPzfPNZGkCieY-oNycFN0F2MNOFx0gMlEWFO6-dRzLgcv=w300-h300" alt="Photo 21" style="width:100%;margin:0;border-radius:0" loading="lazy"></a><a class="image-link" href="https://lh3.googleusercontent.com/pw/AP1GczNxanN74x--xEkd8mMC_8YXkMSe6GFo8_0kbwfNY2i8xPgh1tqrPAJZ2uJNq6zujR8c2TRGOe2ljKGtqkZqdOKsmgefmSZXe_8NiEvsDIObXtG63cbd=w9999-h9999" data-pswp-width="999" data-pswp-height="999" style="border:none"><img src="https://lh3.googleusercontent.com/pw/AP1GczNxanN74x--xEkd8mMC_8YXkMSe6GFo8_0kbwfNY2i8xPgh1tqrPAJZ2uJNq6zujR8c2TRGOe2ljKGtqkZqdOKsmgefmSZXe_8NiEvsDIObXtG63cbd=w300-h300" alt="Photo 22" style="width:100%;margin:0;border-radius:0" loading="lazy"></a></div>]]></content:encoded>
            <category>Travel</category>
            <category>Photography</category>
        </item>
    </channel>
</rss>