<?xml version="1.0" encoding="utf-8"?>
<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>atmos Blog</title>
        <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog</link>
        <description>atmos Blog</description>
        <lastBuildDate>Tue, 06 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>
        <item>
            <title><![CDATA[Select build, test, and deploy work in a monorepo]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-data-queries</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-data-queries</guid>
            <pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Build, test, and deploy what changed. In a monorepo, that means identifying the]]></description>
            <content:encoded><![CDATA[<p>Build, test, and deploy what changed. In a monorepo, that means identifying the
affected projects and their dependents, then running the work that matters for
that change.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language">Atmos Automation Language</a> lets you turn
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/describe/affected">affected components and stacks</a> into build,
test, and deployment tasks. Write your project logic once and run it locally or
in CI.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">Select work across your monorepo<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-data-queries#the-problem" class="hash-link" aria-label="Direct link to Select work across your monorepo" title="Direct link to Select work across your monorepo" translate="no">​</a></h2>
<p>Monorepos bring related projects together. A single change can span an
application, its shared configuration, and the components that depend on it.
Knowing those relationships lets you focus validation and deployment on the
parts of the repository that need them.</p>
<p>Atmos identifies affected components and stacks from its configuration and
declared dependencies. The Atmos Automation Language puts those results in your
script, where you can apply your project's build, test, and release rules.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">Turn affected components into tasks<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-data-queries#the-fix" class="hash-link" aria-label="Direct link to Turn affected components into tasks" title="Direct link to Turn affected components into tasks" translate="no">​</a></h2>
<p>Query the affected set, iterate over the results, and choose the commands to run
for each component. You can use that set to select tests, prepare deployments,
or run independent work in parallel.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/atmos.run#query-defaults">query wrappers</a> capture
output and request JSON by default. This applies to <code>atmos.list</code>,
<code>atmos.describe</code>, and the config and stack config getters. Explicit formats
and streaming options still take precedence.</p>
<p>Command results expose decoded JSON through <code>data</code> and retain raw <code>stdout</code>,
<code>stderr</code>, and <code>exit_code</code>. The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/steps.run#results">step library</a>
also exposes <code>data</code>, decoded from its primary <code>value</code>. Decoding happens only
when accessed, so text results remain usable. Decoded collections are read-only
and can be shared with parallel tasks.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-data-queries#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Run this script in an Atmos project with the comparison ref available locally:</p>
<div class="language-python codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-python codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">result </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> atmos</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">describe</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token string" style="color:rgb(173, 219, 103)">"affected"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    flags </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token string" style="color:rgb(173, 219, 103)">"base"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"origin/main"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"include-dependents"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">True</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain"></span><span class="token keyword" style="color:rgb(127, 219, 202)">for</span><span class="token plain"> item </span><span class="token keyword" style="color:rgb(127, 219, 202)">in</span><span class="token plain"> result</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token keyword" style="color:rgb(127, 219, 202)">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(127, 219, 202)">not</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(173, 219, 103)">"deleted"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">False</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        ui</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">info</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(173, 219, 103)">"{} in {}"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token builtin" style="color:rgb(130, 170, 255)">format</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">item</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(173, 219, 103)">"component"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(173, 219, 103)">"stack"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span></span><br></div></code></pre></div></div>
<p>Use the records to select project commands or construct parallel tasks.
Affected detection follows Atmos configuration and declared dependencies;
the loop does not establish execution order between dependent components.</p>
<p>For individual child processes, <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/exec.run">exec.run</a>
and <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/component.exec">component.exec</a> now accept
<code>timeout</code> and <code>retry</code> directly. A timeout bounds the entire call, including
retry delays. Only retry operations that are safe to repeat.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-data-queries#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try the query defaults in your project automation and
<a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with feedback.</p>]]></content:encoded>
            <category>Feature</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Write Git hook checks inline with Starlark]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/git-hook-script-steps</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/git-hook-script-steps</guid>
            <pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Pre-commit hooks put your team's standards to work on every commit.]]></description>
            <content:encoded><![CDATA[<p>Pre-commit hooks put your team's standards to work on every commit.
With AI agents generating more code, those automated checks matter even more.
At Cloud Posse, we use them to enforce formatting, run linters, catch broken
symlinks, and keep agent instruction files within size limits.</p>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/git#local-git-hooks">Atmos already manages Git hooks</a>.
Now you can write the checks themselves in the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language">Atmos Automation Language</a>, based on Starlark, and keep
small checks inline beside the hook configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">Two checks from our own repository<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/git-hook-script-steps#the-problem" class="hash-link" aria-label="Direct link to Two checks from our own repository" title="Direct link to Two checks from our own repository" translate="no">​</a></h2>
<p>Two checks in the Atmos repository illustrate why this matters. One verifies
that tracked symlinks resolve. The other limits the size of <code>CLAUDE.md</code> and
agent instruction files, so guidance stays focused and detailed material moves
to linked documentation.</p>
<p>We implemented both as Bash scripts. The symlink check uses shell parameter
expansion to unpack Git's file listing and <code>readlink</code> to inspect targets. The
size check pipes <code>wc</code> into <code>tr</code> to produce a number it can compare. Each script
also formats its own error messages and instructions.</p>
<p>Writing these checks in Starlark gives them a common interpreter and filesystem
API across operating systems. It is also part of a broader goal for Atmos:
connect the tools developers use throughout delivery with readable, testable
automation that runs locally and in CI.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">Keep the checks with the hook<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/git-hook-script-steps#the-fix" class="hash-link" aria-label="Direct link to Keep the checks with the hook" title="Direct link to Keep the checks with the hook" translate="no">​</a></h2>
<p>Keep each check as a separate named step in <code>atmos.yaml</code>. Write the rules with
loops, lists, filesystem functions, and
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/errors.build">structured errors</a>. Atmos executes the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/steps/type/script">Starlark script steps</a> directly using its embedded
interpreter.</p>
<p>The example below still asks Git which symlinks are tracked, but checks their
targets through <code>fs.exists</code>. The size check uses <code>fs.glob</code> and <code>fs.stat</code> to read
file metadata directly. Both report failures through the same error builder,
including a hint about how to fix them.</p>
<p>Atmos provides the interpreter and these filesystem functions in one binary.
The checks use the same language and filesystem API across machines, independent
of the installed Bash version. External commands still have their own
requirements; the symlink check requires Git, for example.</p>
<p>Hooks accept either a <code>command</code> or a <code>steps</code> list. Existing command hooks continue
to work, and steps can use other registered types alongside scripts. This brings
Git hook checks into the same execution model used by your other Atmos
automation.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/git-hook-script-steps#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add two separate checks to <code>atmos.yaml</code> in your repository root:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">git</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">hooks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">pre-commit</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> check</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">symlinks</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> script</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">interpreter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> starlark</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">script</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">|</span><span class="token scalar string" style="color:rgb(173, 219, 103)"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            entries = exec.run(["git", "ls-files", "-s", "-z"], output = "capture").stdout</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            broken = []</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            for entry in entries.split("\x00"):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                if not entry:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    continue</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                metadata, _, path = entry.partition("\t")</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                if metadata.split(" ")[0] == "120000" and not fs.exists(path):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    broken.append(path)</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            if broken:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                (errors.build("Broken tracked symlinks")</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    .with_explanation("\n".join(broken))</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    .with_hint("Restore the targets or remove obsolete symlinks.")</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    .fail())</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> check</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">agent</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">sizes</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> script</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">interpreter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> starlark</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">          </span><span class="token key atrule">script</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">|</span><span class="token scalar string" style="color:rgb(173, 219, 103)"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            limits = [</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                ("CLAUDE.md", 40000),</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                (".conductor/*/CLAUDE.md", 40000),</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                (".claude/agents/*.md", 25000),</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            ]</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            oversized = []</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            for pattern, limit in limits:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                for path in fs.glob(pattern):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    if not fs.exists(path):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                        continue</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    info = fs.stat(path)</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    if info.is_file and info.size &gt; limit:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                        oversized.append("{}: {} bytes (limit {})".format(path, info.size, limit))</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">            if oversized:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                (errors.build("Agent instruction files exceed size limits")</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    .with_explanation("\n".join(oversized))</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    .with_hint("Move detailed guidance into linked docs; preserve mandatory instructions.")</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">                    .fail())</span></span><br></div></code></pre></div></div>
<p>The symlink check asks Git which paths are tracked, then checks their targets
in the working tree. The size check reads file metadata directly through Atmos.
These checks inspect working-tree files, including unstaged changes.</p>
<p>Install the hook with <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/git/hooks/install">atmos git hooks install</a>:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos </span><span class="token function" style="color:rgb(130, 170, 255)">git</span><span class="token plain"> hooks </span><span class="token function" style="color:rgb(130, 170, 255)">install</span><br></div></code></pre></div></div>
<p>Run the same checks manually or in CI with
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/git/hooks/run">atmos git hooks run</a>:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos </span><span class="token function" style="color:rgb(130, 170, 255)">git</span><span class="token plain"> hooks run pre-commit</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/git-hook-script-steps#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try moving a small repository check into an inline script and
<a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> if another filesystem
operation would help.</p>]]></content:encoded>
            <category>Feature</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[From shell glue to testable automation]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-step-library</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-step-library</guid>
            <pubDate>Mon, 05 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Over the past year, we've used Atmos custom commands and workflows extensively]]></description>
            <content:encoded><![CDATA[<p>Over the past year, we've used Atmos custom commands and workflows extensively
at Cloud Posse. That experience keeps bringing us back to the code between the
tools: the project-specific logic that turns individual commands into a delivery
process. Much of it lives in shell scripts. We want to give it the same
structure, reuse, and testing we expect from application code.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language">Atmos Automation Language</a>, based on Starlark, lets
us write that logic with ordinary functions and structured data, call the
capabilities Atmos already provides, and <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/testing">test it</a> with
Atmos itself.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">Give project automation a language<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-step-library#the-problem" class="hash-link" aria-label="Direct link to Give project automation a language" title="Direct link to Give project automation a language" translate="no">​</a></h2>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/commands">Custom commands</a> give a team familiar entry points,
and <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/workflows">workflows</a> organize the process. The language gives you a way
to express the decisions within it.</p>
<p>Declarative workflows and Makefiles give tasks and their dependencies a clear
structure. A language is useful for the decisions between those tasks: iterate
over affected projects, inspect a command's results, choose what runs next, and
reuse the logic elsewhere.</p>
<p>In YAML workflows and Makefiles, that logic often lives in embedded shell or
expressions inside configuration. The Atmos Automation Language gives it
ordinary variables, loops, conditionals, functions, and structured data. You can
read the logic directly and test a function independently of the deployment
that calls it.</p>
<p>Keep workflows for orchestration and use the language where code expresses the
work clearly. Atmos supplies the tools and conventions to execute both as part
of the same delivery process.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">Compose your delivery process<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-step-library#the-fix" class="hash-link" aria-label="Direct link to Compose your delivery process" title="Direct link to Compose your delivery process" translate="no">​</a></h2>
<p>Write the project logic in the Atmos Automation Language and call Atmos's
existing capabilities from your script. Use consistent prompts, formatted
output, and structured errors to give your team a familiar CLI experience.
Reuse functions across your automation and run independent tasks in parallel.</p>
<p>The interpreter and test runner ship in the Atmos binary. Distribute your
scripts with your project and invoke them from a terminal or a CI job. Configure
the tools and credentials they use for each environment.</p>
<p>Direct access to the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/steps.run">step library</a> extends what
these scripts can do. Calls such as <code>steps.input</code>, <code>steps.http</code>,
<code>steps.container</code>, and <code>steps.archive</code> use the same fields as YAML steps and
return values, metadata, and named outputs. You can compose those existing
operations inside your functions and loops.</p>
<p>Scripts can run as standalone tools or within custom commands, workflows, and
hooks. Steps keep their existing prerequisites: container operations need a
configured runtime, prompts need a terminal or a default, and background-job
controls require the workflow runner.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-step-library#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Save this as <code>select-service.star</code>:</p>
<div class="language-python codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-python codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">service </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token builtin" style="color:rgb(130, 170, 255)">input</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">prompt </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"Service name?"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> default </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"api"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">value</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">selection </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">choose</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    prompt </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"Environment?"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    options </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(173, 219, 103)">"dev"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"prod"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    default </span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"dev"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">value</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">ui</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">success</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(173, 219, 103)">"Selected "</span><span class="token plain"> </span><span class="token operator" style="color:rgb(127, 219, 202)">+</span><span class="token plain"> service </span><span class="token operator" style="color:rgb(127, 219, 202)">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">" in "</span><span class="token plain"> </span><span class="token operator" style="color:rgb(127, 219, 202)">+</span><span class="token plain"> selection</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span></span><br></div></code></pre></div></div>
<p>Run it with <code>atmos ./select-service.star</code>. It prompts in a terminal and uses the
configured defaults without one. Collect input before starting
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/steps.parallel">parallel tasks</a>.</p>
<p>For a step type selected at runtime, call <code>steps.run(type, **fields)</code>. See the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/steps.run">step-library reference</a> for available functions,
result fields, and execution-context requirements.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/automation-step-library#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try expressing part of your build, test, or deployment process in the language and
<a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> if an operation needs
better support in the language.</p>]]></content:encoded>
            <category>Feature</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Write Braces in Your Steps, and Trust Your Command Inputs]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/literal-steps-and-reliable-command-inputs</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/literal-steps-and-reliable-command-inputs</guid>
            <pubDate>Mon, 05 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Every step in a custom command or workflow passes through Go templates before it runs.]]></description>
            <content:encoded><![CDATA[<p>Every step in a custom command or workflow passes through Go templates before it runs.
That is convenient until a script needs literal braces: a Starlark format string, a
Helm-style placeholder, or <code>{{ }}</code> text meant for another tool. Escaping does not help,
because a custom command renders its steps more than once, and <code>atmos.yaml</code> refused
the <code>!literal</code> function that stack manifests already support. Custom-command inputs had
rough edges too: an argument containing a comma was split in two, integer flags never
reached the step, and a step's <code>timeout:</code> was not enforced.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/literal-steps-and-reliable-command-inputs#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Templates are rendered over the whole step definition, including the script body. A
step with a line such as <code>print("{{ name }}")</code> failed before it ever ran, and the error
named an internal template instead of your step. The same rendering also ran over the
environment Atmos inherited from your shell, so a variable that happened to contain
braces could break an unrelated script step or reach child processes rewritten.</p>
<p>Custom commands also had input problems that were easy to miss:</p>
<ul>
<li class="">An argument value such as <code>api,v2</code> arrived split into separate arguments.</li>
<li class="">A flag declared with <code>type: int</code> showed up as a string flag and was missing from the
values passed to steps.</li>
<li class="">An optional argument without a default failed as if it were required.</li>
<li class=""><code>timeout:</code> on script, shell, and Atmos steps was accepted but never enforced, and an
unknown <code>output:</code> mode was silently ignored.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/literal-steps-and-reliable-command-inputs#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Tag any step field with <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/literal"><code>!literal</code></a>, in <code>atmos.yaml</code> or in a
workflow file, and Atmos uses it exactly as written. It works for <code>script</code>, <code>command</code>,
<code>interpreter</code>, <code>working_directory</code>, and individual <code>env</code> values, in sequential steps,
parallel and matrix children, workflows, and lifecycle hooks.</p>
<p>Atmos now renders only the environment values you declare; everything inherited from
your shell passes through unchanged. Template errors name the step, the field, and the
included file, and suggest <code>!literal</code> when the body contains braces. Parallel and matrix
children render with the same template functions as sequential steps.</p>
<p>For custom commands, argument values arrive exactly as typed,
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/commands/flags#integer-flags">integer flags</a> are typed values, and
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/commands/arguments#optional-arguments">optional arguments</a> can omit a
default. Step <code>timeout:</code> values are enforced, and <code>output:</code> accepts only <code>raw</code>, <code>log</code>,
<code>viewport</code>, or <code>none</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/literal-steps-and-reliable-command-inputs#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Mark a script body as literal when it contains braces:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">commands</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> greet</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Print a greeting</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">arguments</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> name</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Who to greet</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">false</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">flags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> times</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> int</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">default</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> How many greetings</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> greet</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> script</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">interpreter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> starlark</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">timeout</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 30s</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">script</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!literal</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">|</span><span class="token scalar string" style="color:rgb(173, 219, 103)"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">          name = ctx.arguments["name"] or "world"</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">          for _ in range(ctx.flags["times"]):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">              print("{{ Hello }}, %s!" % name)</span></span><br></div></code></pre></div></div>
<p>Running <code>atmos greet platform --times=2</code> prints <code>{{ Hello }}, platform!</code> twice. The
script reads its inputs through <code>ctx.arguments</code> and <code>ctx.flags</code>, so nothing is
interpolated into the source. See the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/steps/type/script#literal-script">script step reference</a> for details.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/literal-steps-and-reliable-command-inputs#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Tell us how you use templates and scripts together in
<a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>, or open an issue
if a step field still renders when you expect it to stay literal.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>Bug Fix</category>
        </item>
        <item>
            <title><![CDATA[Build Custom CLI Apps with the Atmos Interpreter]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter</guid>
            <pubDate>Sun, 04 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Give your team one command to build, ship, and deploy containers, whether they]]></description>
            <content:encoded><![CDATA[<p>Give your team one command to build, ship, and deploy containers, whether they
run it locally or in CI. Write your release tool in the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language">Atmos Automation Language</a>, a Python-like DSL based on
Starlark, and run it with <code>atmos ./release.star</code>. Compose image builds, registry
pushes, deployments, and checks using your existing Atmos configuration.</p>
<p>The same approach works for inventory reports, deployment checks, and other CLI
apps. Atmos supplies the interpreter, typed flags, generated help, and automation
functions so you can focus on the work your tool performs.</p>
<p>Your app can install pinned tool dependencies, run commands under configured
cloud identities, and execute independent tasks in parallel. Return structured
data for other tools to consume, and use <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/automation/errors.build">error builders</a>
to report failures with explanations, hints, and examples through Atmos's own
error formatting and reporting.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">Build a release tool you can maintain and test<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#the-problem" class="hash-link" aria-label="Direct link to Build a release tool you can maintain and test" title="Direct link to Build a release tool you can maintain and test" translate="no">​</a></h2>
<p>As a release script grows, Bash plumbing can take over: parsing arguments and
JSON, coordinating background processes, and handling errors between commands.
The Atmos Automation Language gives you structured values, reusable functions,
and parallel tasks for that work. Test your functions and command behavior with
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/testing">test steps</a>, then run those checks locally and in CI.</p>
<p>Distribute the tool with your project and use the same entry point in both
environments. Atmos runs it with the stacks, identities, and pinned tools you
configure. See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/standalone-cli-apps#what-your-program-can-do">release example</a>
for a tool that builds and pushes an image, then deploys with Terraform.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">Run a .star file with Atmos<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#the-fix" class="hash-link" aria-label="Direct link to Run a .star file with Atmos" title="Direct link to Run a .star file with Atmos" translate="no">​</a></h2>
<p>Install Atmos and put it on your <code>PATH</code>. You can pass the script directly to
Atmos, or put <code>#!/usr/bin/env atmos</code> on its first line, make it executable, and
run it as <code>./my-tool.star</code> on systems with shebang support.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="why-build-on-starlark">Why Starlark?<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#why-build-on-starlark" class="hash-link" aria-label="Direct link to Why Starlark?" title="Direct link to Why Starlark?" translate="no">​</a></h3>
<p>See <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language#why-starlark">why Atmos uses Starlark</a> for the language
choices and how they affect your scripts.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="run-your-first-app">Run your first app<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#run-your-first-app" class="hash-link" aria-label="Direct link to Run your first app" title="Direct link to Run your first app" translate="no">​</a></h3>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/examples/starlark-script">summarize example</a> reads a JSON service manifest
and reports replica counts. It uses only functions built into Atmos; it needs
no project configuration or external tools:</p>
<div><div class="window_X9dN"><div class="titlebar_DN7h"><span class="dots_R2sg" aria-hidden="true"><i></i><i></i><i></i></span><span class="title_Dael">Custom CLI app: ./summarize.star services.json</span></div><pre class="screen__b5c noPreWrap_ImkX screenLoading_abuO"><span> </span></pre><div class="controls_eyLV"><button type="button" class="playButton_kD9r" aria-label="Pause cast"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M6 5H8V19H6V5ZM16 5H18V19H16V5Z"></path></svg></button><input aria-label="Cast position" type="range" min="0" max="0" step="0.01" value="0"><span>00:00.0<!-- --> / <!-- -->00:00.0</span></div></div><div class="castActions_M13G"><div class="container_zGFV"><div class="group_ncGU" role="group" aria-label="Share this demo"><button type="button" class="primary_hpkh" title="Copy a link to this demo" aria-live="polite"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg><span>Share</span></button><button type="button" class="caret_pPxC" aria-expanded="false" aria-label="More share options" title="More share options"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div><div class="container_EXko"><button type="button" class="trigger_WxG7" aria-expanded="false" aria-label="Download cast"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg><span>Download</span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div></div>
<p>Save <code>summarize.star</code> and <code>services.json</code> from the example in the same directory,
then run from that directory:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token function" style="color:rgb(130, 170, 255)">chmod</span><span class="token plain"> +x summarize.star</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">./summarize.star services.json</span><br></div></code></pre></div></div>
<p>It reports two services with five replicas in total. On systems without shebang
support, use <code>atmos ./summarize.star services.json</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="give-the-app-an-interface">Give the app an interface<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#give-the-app-an-interface" class="hash-link" aria-label="Direct link to Give the app an interface" title="Direct link to Give the app an interface" translate="no">​</a></h3>
<p>Declare arguments and flags with <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/steps/type/script#declaring-a-standalone-command"><code>cli.command</code></a>,
then put the work in a main callback. This app accepts a service name and a typed
replica count, checks the count, and reports the result:</p>
<div><div class="file"><div class="file-header"><div class="file-title"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="file" class="svg-inline--fa fa-file fa-1x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 288c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128z"></path></svg><span>examples/starlark-script/capacity.star</span></div></div><div class="viewport"><div class="language-python codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-python codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div></code></pre></div></div></div></div></div>
<p>Save it as <code>capacity.star</code> and run:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token function" style="color:rgb(130, 170, 255)">chmod</span><span class="token plain"> +x capacity.star</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">./capacity.star api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--replicas</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">./capacity.star </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--help</span><br></div></code></pre></div></div>
<p>The first command prints <code>api: 3 replicas x 4 workers = 12 workers</code>. The second
shows the required service argument, replica flag, and default. Help runs neither
the validation callback nor <code>main</code>. A replica count of zero fails validation;
<code>--replicas=many</code> fails integer parsing.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="use-it-locally-and-in-ci">Use it locally and in CI<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#use-it-locally-and-in-ci" class="hash-link" aria-label="Direct link to Use it locally and in CI" title="Direct link to Use it locally and in CI" translate="no">​</a></h3>
<p>Commit the app with your project and invoke the same file in your terminal and
pipeline. Keep the CI or CD process's reusable logic in the app, and let the CI
system control its triggers and approvals. Tools and credentials used by the
app's subprocesses must be available in that environment.</p>
<p>Use <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language#data-status-and-diagnostics"><code>ui</code> for progress and <code>log</code> for diagnostics</a>.
UI messages go to stderr, leaving stdout available for data consumed by another
process. Test your automation with <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/testing">script checks and test steps</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Next steps<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter#get-involved" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<p>Build your first tool with the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/standalone-cli-apps">Custom CLI Apps guide</a>.
To run scripts as Atmos custom commands, workflow steps, or hooks,
see <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands">Introducing Atmos Automation Language</a>.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[See why a Helm release failed, right in the Atmos error]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics</guid>
            <pubDate>Sun, 04 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A controller rollout times out. The deploy fails with "release did not become ready within 5m0s" and nothing else. So you switch to the cluster, run kubectl get pods, then describe, then logs on whichever pod looks wrong - and in CI you often can't do any of that. Worse, if the release is set to roll back on failure, the rollback has already deleted the crashing pods by the time you look, taking the evidence with it.]]></description>
            <content:encoded><![CDATA[<p>A controller rollout times out. The deploy fails with "release did not become ready within 5m0s" and nothing else. So you switch to the cluster, run <code>kubectl get pods</code>, then <code>describe</code>, then <code>logs</code> on whichever pod looks wrong - and in CI you often can't do any of that. Worse, if the release is set to roll back on failure, the rollback has already deleted the crashing pods by the time you look, taking the evidence with it.</p>
<p>Native Helm releases in Atmos now capture that evidence at the moment of failure and fold it straight into the error: which pod is failing, what the container is reporting, and - at debug level - the crash log and recent events.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>A failed readiness wait tells you <em>that</em> a release did not come up, but not <em>why</em>. The error names the release and namespace, yet the actual cause - a <code>CrashLoopBackOff</code>, an <code>ImagePullBackOff</code> from a bad registry mirror, a container exiting non-zero on a bad config value - lives on the pods, not in the release record.</p>
<p>So the cause is one <code>kubectl</code> session away. Except:</p>
<ul>
<li class="">In CI there is usually no interactive cluster access, so the run just fails with a timeout and no cause.</li>
<li class="">When a release is configured to roll back or uninstall on failure, that recovery deletes the failing pods first. By the time anyone looks, the pod - and its logs - are gone.</li>
</ul>
<p>The result is a dependency-ordered rollout that stops at a release nobody can diagnose from the output alone.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>On a release failure, and <strong>before</strong> any rollback or uninstall runs, Atmos now enumerates the release's pods, finds the not-ready containers, and appends their diagnostics to the same error:</p>
<div class="language-text codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-text codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">Error: failed to perform helm release operation</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  workload diagnostics:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    pod keda-operator-7d9f  keda-operator CrashLoopBackOff (exit 1, 5 restarts)</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      last log (keda-operator):</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        panic: failed to load config: invalid duration "5x"</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      events:</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        BackOff  Back-off restarting failed container</span></span><br></div></code></pre></div></div>
<p>The container-status summary (reason, exit code, restart count) is always included on failure. The log tail and the pod's recent events are added when you run at debug or trace level, so normal output stays concise.</p>
<p>To guarantee the evidence survives, Atmos now performs the configured <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle"><code>on_failure</code> rollback or uninstall</a> itself, after collecting the diagnostics rather than before - the rollback and history-retention behavior you configure is unchanged, it just no longer races the diagnostics.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>There is nothing to enable. Any native Helm <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/helm/apply"><code>apply</code></a> or <code>deploy</code> that fails readiness surfaces the diagnostics automatically. To include the log tail and events, raise the log level:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos helm apply keda </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod --logs-level</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">Debug</span><br></div></code></pre></div></div>
<p>Diagnostics are best-effort: if the cluster cannot be reached, Atmos reports the original failure unchanged rather than masking it.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-crashloop-diagnostics#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>This pairs with the native Helm <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle">release lifecycle</a> controls. If there is a failure signal you want surfaced that Atmos does not yet capture, open an issue or discussion on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">GitHub</a>.</p>]]></content:encoded>
            <category>Feature</category>
            <category>Experimental</category>
        </item>
        <item>
            <title><![CDATA[Atmos Is Now a Language for Automation]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands</guid>
            <pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos is now a language for automation. Introducing the]]></description>
            <content:encoded><![CDATA[<p>Atmos is now a language for automation. Introducing the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language">Atmos Automation Language</a>: a Python-like language based
on Starlark for writing, composing, and testing your build, test, and deployment
processes.</p>
<p>Templating made configuration reusable. Custom commands gave teams their own
CLI. The Atmos Automation Language is the next major step: you can program the
process itself, with functions, control flow, structured data, and tests, using
the capabilities already built into Atmos.</p>
<p>Build containers, ship them, and deploy them through the same scripts locally
and in CI. Install pinned tools, assume configured identities, run commands,
and parallelize independent work. Use consistent inputs, formatted output, and
structured errors to make that automation useful to the whole team. It works
for applications as well as infrastructure.</p>
<p>Write your code in a <code>.star</code> file and run it with <code>atmos ./release.star</code>, or
embed it in custom commands, workflow steps, and hooks. The interpreter ships
in the Atmos binary, together with the automation functions and test runner.
Your scripts use the tools, credentials, and configuration you provide for each
environment.</p>
<p>Use the same language inside stack manifests with the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/starlark"><code>!starlark</code> YAML function</a>. Derive resource tags,
names, and environment-specific settings from each component's configuration,
and return typed values directly to YAML.</p>
<p>For standalone <code>.star</code> apps with their own arguments and help, see the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-interpreter">Atmos interpreter announcement</a>.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">Keep your release process in one place<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#the-problem" class="hash-link" aria-label="Direct link to Keep your release process in one place" title="Direct link to Keep your release process in one place" translate="no">​</a></h2>
<p>Keep release logic with your project. Put it in functions, pass structured
values between them, and test their behavior locally. Your CI pipeline invokes
the same process your team uses during development, with its own triggers,
credentials, and approvals.</p>
<p>Use custom commands for your team's entry points, workflows to compose the
process, and lifecycle hooks for checks tied to component operations. They all
run the same language and can load shared functions.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">Run scripts inside Atmos<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#the-fix" class="hash-link" aria-label="Direct link to Run scripts inside Atmos" title="Direct link to Run scripts inside Atmos" translate="no">​</a></h2>
<p>Set <code>type: script</code> and <code>interpreter: starlark</code> on a step inside a custom command,
workflow, or hook. The interpreter is included in Atmos.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="why-starlark">Why Starlark?<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#why-starlark" class="hash-link" aria-label="Direct link to Why Starlark?" title="Direct link to Why Starlark?" translate="no">​</a></h3>
<p>See <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language#why-starlark">why Atmos uses Starlark</a> for the language's
syntax, built-in capabilities, and execution model.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="add-an-atmos-subcommand">Add an Atmos subcommand<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#add-an-atmos-subcommand" class="hash-link" aria-label="Direct link to Add an Atmos subcommand" title="Direct link to Add an Atmos subcommand" translate="no">​</a></h3>
<p>Save this configuration as <code>atmos.yaml</code>. It defines a <code>capacity</code> command with a
<code>--replicas</code> flag and a script that calculates total worker capacity:</p>
<div><div class="file"><div class="file-header"><div class="file-title"><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="file-type-icon file-type-icon--yaml" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="m0 .97 4.111 6.453v4.09h2.638v-4.09L11.053.969H8.214L5.58 5.125 2.965.969Zm12.093.024-4.47 10.544h2.114l.97-2.345h4.775l.804 2.345h2.26L14.255.994Zm1.133 2.225 1.463 3.87h-3.096zm3.06 9.475v10.29H24v-2.199h-5.454v-8.091zm-12.175.002v10.335h2.217v-7.129l2.32 4.792h1.746l2.4-4.96v7.295h2.127V12.696h-2.904L9.44 17.37l-2.455-4.674Z"></path></svg><span>examples/starlark-commands/atmos.yaml</span></div></div><div class="viewport"><div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div></code></pre></div></div></div></div></div>
<p>With Atmos installed, run from the directory containing that file:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos capacity </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--replicas</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3</span><br></div></code></pre></div></div>
<p>The command prints <code>3 replicas x 4 workers = 12 workers</code>. The script reads
<code>ctx.flags["replicas"]</code>, converts it to an integer, and rejects counts below one.
Run <code>atmos capacity --help</code> for the command's generated help. This example needs
no stacks or external tools.</p>
<div><div class="window_X9dN"><div class="titlebar_DN7h"><span class="dots_R2sg" aria-hidden="true"><i></i><i></i><i></i></span><span class="title_Dael">Custom command: atmos capacity --replicas 3</span></div><pre class="screen__b5c noPreWrap_ImkX screenLoading_abuO"><span> </span></pre><div class="controls_eyLV"><button type="button" class="playButton_kD9r" aria-label="Pause cast"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M6 5H8V19H6V5ZM16 5H18V19H16V5Z"></path></svg></button><input aria-label="Cast position" type="range" min="0" max="0" step="0.01" value="0"><span>00:00.0<!-- --> / <!-- -->00:00.0</span></div></div><div class="castActions_M13G"><div class="container_zGFV"><div class="group_ncGU" role="group" aria-label="Share this demo"><button type="button" class="primary_hpkh" title="Copy a link to this demo" aria-live="polite"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg><span>Share</span></button><button type="button" class="caret_pPxC" aria-expanded="false" aria-label="More share options" title="More share options"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div><div class="container_EXko"><button type="button" class="trigger_WxG7" aria-expanded="false" aria-label="Download cast"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg><span>Download</span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div></div>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/examples/starlark-commands">View the full example</a></p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="guard-an-operation-with-a-hook">Guard an operation with a hook<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#guard-an-operation-with-a-hook" class="hash-link" aria-label="Direct link to Guard an operation with a hook" title="Direct link to Guard an operation with a hook" translate="no">​</a></h3>
<p>A lifecycle hook runs automatically when Atmos reaches a component event.
The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/examples/starlark-hooks">owner-check example</a> reads <code>ctx.component.vars</code>
before a Terraform plan and requires the component to have an owner.</p>
<p>With Atmos and Terraform installed, run these commands from the example's directory:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> unowned</span><br></div></code></pre></div></div>
<p>The <code>dev</code> stack passes the check and Terraform reports no changes. The <code>unowned</code>
stack fails with <code>Set an owner before planning api</code>, stopping the plan. The
example module has no providers or resources and needs no cloud credentials.</p>
<div><div class="window_X9dN"><div class="titlebar_DN7h"><span class="dots_R2sg" aria-hidden="true"><i></i><i></i><i></i></span><span class="title_Dael">Check ownership before Terraform plans</span></div><pre class="screen__b5c noPreWrap_ImkX screenLoading_abuO"><span> </span></pre><div class="controls_eyLV"><button type="button" class="playButton_kD9r" aria-label="Pause cast"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M6 5H8V19H6V5ZM16 5H18V19H16V5Z"></path></svg></button><input aria-label="Cast position" type="range" min="0" max="0" step="0.01" value="0"><span>00:00.0<!-- --> / <!-- -->00:00.0</span></div></div><div class="castActions_M13G"><div class="container_zGFV"><div class="group_ncGU" role="group" aria-label="Share this demo"><button type="button" class="primary_hpkh" title="Copy a link to this demo" aria-live="polite"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg><span>Share</span></button><button type="button" class="caret_pPxC" aria-expanded="false" aria-label="More share options" title="More share options"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div><div class="container_EXko"><button type="button" class="trigger_WxG7" aria-expanded="false" aria-label="Download cast"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg><span>Download</span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="grow-into-reusable-functions">Share functions between scripts<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#grow-into-reusable-functions" class="hash-link" aria-label="Direct link to Share functions between scripts" title="Direct link to Share functions between scripts" translate="no">​</a></h3>
<p>Move shared code into <code>.star</code> files. Include a script in a YAML step with
<code>script: !include scripts/plan.star</code>, and use <code>load()</code> inside the script to import
functions from other files. Imports resolve relative to the file containing
the <code>load()</code> statement. See <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/language#files-and-shared-code">files and shared code</a>.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/steps/type/script#custom-components">release-plan example</a> loads a shared
function and reads three components with at most two tasks running at once.
It prints their configuration without deploying anything:</p>
<div><div class="window_X9dN"><div class="titlebar_DN7h"><span class="dots_R2sg" aria-hidden="true"><i></i><i></i><i></i></span><span class="title_Dael">Starlark custom commands and parallel functions</span></div><pre class="screen__b5c noPreWrap_ImkX screenLoading_abuO"><span> </span></pre><div class="controls_eyLV"><button type="button" class="playButton_kD9r" aria-label="Pause cast"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M6 5H8V19H6V5ZM16 5H18V19H16V5Z"></path></svg></button><input aria-label="Cast position" type="range" min="0" max="0" step="0.01" value="0"><span>00:00.0<!-- --> / <!-- -->00:00.0</span></div></div><div class="castActions_M13G"><div class="container_zGFV"><div class="group_ncGU" role="group" aria-label="Share this demo"><button type="button" class="primary_hpkh" title="Copy a link to this demo" aria-live="polite"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg><span>Share</span></button><button type="button" class="caret_pPxC" aria-expanded="false" aria-label="More share options" title="More share options"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div><div class="container_EXko"><button type="button" class="trigger_WxG7" aria-expanded="false" aria-label="Download cast"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg><span>Download</span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="derive-configuration-from-the-stack-that-uses-it">Derive configuration from the stack that uses it<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#derive-configuration-from-the-stack-that-uses-it" class="hash-link" aria-label="Direct link to Derive configuration from the stack that uses it" title="Direct link to Derive configuration from the stack that uses it" translate="no">​</a></h3>
<p>Define resource tags once and let each environment supply its own stage,
region, and owner. Add the rule to a shared component definition:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">vars</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">resource_tags</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!starlark</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">|</span><span class="token scalar string" style="color:rgb(173, 219, 103)"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">    return {</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">        "Environment": ctx.vars["stage"],</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">        "Region": ctx.vars["region"],</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">        "Owner": ctx.metadata.get("owner", "platform"),</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token scalar string" style="color:rgb(173, 219, 103)">    }</span></span><br></div></code></pre></div></div>
<p>Atmos supplies a read-only context after imports, inheritance, and overrides.
An inherited expression reads the consuming component's values: development
gets development tags, and production gets production tags. Returned maps,
lists, booleans, and numbers retain their types.</p>
<p>Use conditions, comprehensions, and helper functions to express configuration
rules. Atmos resolves computed dependencies when accessed and reports cycles
with the fields involved. Inspect the result with
<code>atmos describe component &lt;component&gt; -s &lt;stack&gt;</code> before deploying.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/starlark"><code>!starlark</code> reference</a> covers context fields,
return types, and evaluation scope. Use automation scripts for commands and
steps, and YAML expressions for computing configuration values.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Next steps<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/starlark-custom-commands#get-involved" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<p>Follow the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/custom-commands">custom command guide</a>,
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/workflows">workflow guide</a>, or <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/lifecycle-hooks">hook guide</a>
to add a script to your project. Use the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/automation/testing">testing guide</a>
to check its behavior and report failures.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Clear server-side apply conflicts without leaving the deploy path]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts</guid>
            <pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Kubernetes server-side apply tracks who owns every field of a managed object. When two actors write the same field - a controller that reconciles an object a release also sets, or an object whose ownership ledger was lost - the next apply fails with a field-ownership conflict. The standard escape is a one-off kubectl apply --server-side --force-conflicts or hand-editing managedFields, then re-running your deploy. That is fine on a laptop and impossible in CI, and a conflicted release blocks every dependent waiting on it.]]></description>
            <content:encoded><![CDATA[<p>Kubernetes server-side apply tracks who owns every field of a managed object. When two actors write the same field - a controller that reconciles an object a release also sets, or an object whose ownership ledger was lost - the next apply fails with a field-ownership conflict. The standard escape is a one-off <code>kubectl apply --server-side --force-conflicts</code> or hand-editing <code>managedFields</code>, then re-running your deploy. That is fine on a laptop and impossible in CI, and a conflicted release blocks every dependent waiting on it.</p>
<p>Native Helm components now expose the two Helm 4 controls that resolve this - <code>server_side_apply</code> and <code>force_conflicts</code> - as release-policy settings and command-line flags, so a conflict clears in the normal <code>atmos helm apply</code> path.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Helm 4 applies release manifests with server-side apply by default, so field ownership is shared with any other actor that writes the same fields. Two situations routinely put another manager on a field a release also declares:</p>
<ul>
<li class="">A controller continuously reconciles an object it also received from a release, and takes ownership of fields the release sets.</li>
<li class="">An object's <code>managedFields</code> ledger is orphaned - for example, a custom resource whose CRD hosts a conversion webhook loses its ledger when a conversion fails during a controller disruption. The next apply synthesizes a stand-in manager that owns the pre-existing fields, and a later release apply that changes those fields conflicts with it.</li>
</ul>
<p>In both cases the apply reports a conflict and the install or upgrade aborts. Because Atmos set no conflict-resolution option, there was no way to clear it through the deploy path: you had to repair the object out of band and re-run. That breaks dependency-ordered rollouts, cannot be remediated in CI, and hid a control Helm 4 already implements.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Two keys are added to the native Helm <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle"><code>release</code> policy</a>, alongside the existing wait, timeout, history, install, and upgrade controls:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">helm</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">my-component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">release</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Apply method. Omit to use the Helm default.</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">server_side_apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Resolve field-ownership conflicts by overwriting the contested</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># fields and becoming their sole manager. Opt-in; default false.</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">force_conflicts</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">false</span></span><br></div></code></pre></div></div>
<p>With <code>force_conflicts</code> enabled, a release apply that meets a field owned by another manager overwrites the contested fields and becomes their sole owner, so the release reaches a successful completion state and its dependents proceed. It is opt-in by design: forcing overrides other managers, so a controller that legitimately co-owns a field loses it on the next apply. That trade-off is yours to make per component, which is why the default leaves conflicts fatal and visible.</p>
<p><code>server_side_apply</code> accepts <code>auto</code>, <code>true</code>, or <code>false</code>. Omitting it preserves the Helm default - server-side apply on install, and the prior release's method on upgrade - so a release that sets neither key behaves exactly as before.</p>
<p>Both settings resolve through the same path as the rest of the release lifecycle: stack type defaults, base-component inheritance, concrete component configuration, and command-line override. A release-wide value is the common case, and the per-phase <code>install</code> and <code>upgrade</code> blocks can override it when first install and later upgrades need different behavior. The configured values are validated before any chart download or cluster mutation.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Set the policy in a stack for steady-state behavior, or force a single recovery apply from the command line without editing configuration:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># One-off recovery: take ownership of the contested fields and continue.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos helm apply my-component </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod --force-conflicts</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Override the apply method for this run (a bare flag selects true).</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos helm apply my-component </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod --server-side-apply</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">false</span><br></div></code></pre></div></div>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/helm/apply#flags"><code>--force-conflicts</code> and <code>--server-side-apply</code></a> flags are available on <code>apply</code> and <code>deploy</code>, and take precedence over stack <code>release</code> configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-helm-force-conflicts#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/helm#release-lifecycle">native Helm release lifecycle</a> documentation for the full policy reference. If you hit a server-side apply scenario this does not cover, open an issue or discussion on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">GitHub</a> - we would like to hear about it.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[File deletion handling for scaffold/init --update]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion</guid>
            <pubDate>Thu, 01 Oct 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[Templates change over time: a file that made sense when you first generated your project gets]]></description>
            <content:encoded><![CDATA[<p>Templates change over time: a file that made sense when you first generated your project gets
renamed, split up, or just stops being relevant. Most codegen tools treat that evolution as
something only a brand-new scaffold run can fix — your existing project just keeps carrying the
leftover file forever. And if you'd deleted that file yourself to clean up, the next update used
to bring it right back, overwriting the choice you'd already made.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate --update</code></a> and
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init --update</code></a> compute a three-way merge for every file the template
still generates: what changed upstream, layered onto what you've customized locally. But that
merge only ever ran for files that exist on both sides. Two real gaps followed from that:</p>
<ul>
<li class="">When the template stopped generating a file, <code>--update</code> never noticed. The stale file just sat
there, untouched, on every future update, forever.</li>
<li class="">When you deleted a file yourself — because you didn't need it, or you'd replaced it with
something else — the next <code>--update</code> silently wrote it straight back, as if your deletion had
never happened.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><code>--update-strategy=rendered</code> now detects a file the template stopped generating and removes it,
but only when your copy still matches exactly what the template last produced. If you'd edited
that file since, <code>--update</code> doesn't guess: it surfaces an unresolved conflict instead, the same
way a genuine merge conflict does, so you decide whether to keep it or let it go.
<code>--update-strategy=tracked</code> can't offer this part safely — there's no reliable way to know which
files in your project's own git history actually belonged to the template versus anything else
that happened to live there.</p>
<p>Independently of strategy, <code>--update</code> also stops silently overwriting a deletion you made
yourself. Both <code>tracked</code> and <code>rendered</code> now check whether a file was previously generated before
recreating it, and leave your deletion in place if so. Pass <code>--recreate-deleted</code> to opt back into
the old always-recreate behavior — it's deliberately its own flag rather than folded into
<code>--force</code>, since <code>--force</code> already means "the template's version wins" for merge conflicts, and
tying file recreation to it would make "resolve conflicts manually" and "recreate what I deleted"
mutually exclusive. <code>--force</code> does, however, now resolve a deletion conflict the same way it
resolves any other: if the template removed a file you'd since edited, <code>--force</code> deletes it anyway
instead of leaving you stuck with a conflict only manual cleanup could clear.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># A file the template no longer generates is removed automatically on --update,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># as long as you haven't edited it -- otherwise you'll get a conflict to resolve.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --update-strategy</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">rendered</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Recreate a file you deleted instead of leaving the deletion in place.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --recreate-deleted</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Force past a deletion conflict -- the template's choice (delete it) wins.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --update-strategy</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">rendered </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--force</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-update-file-deletion#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate</code></a> and
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init</code></a> docs for the full flag reference. Have feedback on this
feature? <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a> or join the conversation in
the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">Cloud Posse community Slack</a>.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Use !include and other YAML functions in scaffold templates]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include</guid>
            <pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Two fields in the same scaffold template often need the exact same list of choices — a license]]></description>
            <content:encoded><![CDATA[<p>Two fields in the same scaffold template often need the exact same list of choices — a license
picker and a region list are both really just "options sourced from some small reference table."
Until now, that table had nowhere to live but inside <code>scaffold.yaml</code> itself, copied into every
field that needed it. And a value as simple as the current git branch, an environment variable, or
a random suffix had no path into a template at all, short of prompting the user for it by hand.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Small pieces of reference data — license choices, region codes, a naming convention lookup — show
up constantly in real scaffold templates, and they rarely stay confined to one field. A <code>select</code>
field needs them as <code>{label, value}</code> options; a file elsewhere in the same template needs the raw
table to look values up by key. Duplicating that table by hand, once per field that needs it, means
every future edit has to find and update every copy — and a missed one quietly drifts out of sync
with the rest. Beyond reference data, templates also commonly need small dynamic values — the
user's git branch or commit SHA, an environment variable, a random suffix for a resource name —
with no way to derive any of them automatically.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><code>scaffold.yaml</code> now resolves a set of <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml">Atmos YAML functions</a> — the same
explicit-tag mechanism stack manifests already use — anywhere it currently accepts a literal
value: <code>options:</code>, a <code>type: computed</code> field's <code>value:</code>, or a <code>matrix:</code> axis.</p>
<ul>
<li class=""><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/include"><code>!include</code></a>/<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/include.raw"><code>!include.raw</code></a> pulls in a
local or remote file, optionally reshaped with a YQ filter.</li>
<li class=""><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/env"><code>!env</code></a> reads an environment variable, <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/random"><code>!random</code></a>
generates a random number, and <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/cwd"><code>!cwd</code></a> reads the current working directory.</li>
<li class="">The <code>!git.*</code>/<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/repo-root"><code>!repo-root</code></a> family exposes the current git branch,
commit SHA, repository name, and more.</li>
<li class=""><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/literal"><code>!literal</code></a> preserves a value exactly as written, bypassing scaffold's
own template evaluation — useful when a value legitimately contains <code>{{ }}</code> and shouldn't be
treated as a template expression.</li>
</ul>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">scaffold.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> license</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> select</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!include</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"./lib/licenses.yaml '. | to_entries | map({\"label\": .value.full_name, \"value\": .key})'"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> license_lookup</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!include</span><span class="token plain"> ./lib/licenses.yaml</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> branch</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!git.branch</span></span><br></div></code></pre></div></div>
<p>A locally-included file that exists solely to be included is automatically excluded from generated
output, the same way <code>scaffold.yaml</code> itself is. <code>atmos scaffold validate</code> resolves everything too,
not just <code>generate</code>, so a missing file, a bad filter, or a malformed value is caught up front.</p>
<p>Not every YAML function is available here: anything that needs real stack, component, or backend
context (<code>!terraform.state</code>, <code>!store</code>, <code>!secret</code>, and similar) is rejected with a clear error
instead. A template's <code>scaffold.yaml</code> is often resolved just to show its name and description in
<code>atmos scaffold list</code> or the interactive picker — before a user has chosen or generated anything —
so only functions that are safe to run in that situation are supported.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/exec"><code>!exec</code></a> function is excluded for a different reason: it needs no stack
context at all, but <code>scaffold.yaml</code> is resolved for every template configured in <code>atmos.yaml</code> just
to populate the list and picker, not only the one a user actually generates — so allowing shell
execution there would let any configured template, including a shared or vendored one, run
arbitrary code merely by being listed.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add any of the functions above anywhere <code>options:</code>, a computed field's
<code>value:</code> (see <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields">Computed Fields</a>), or a matrix axis
currently accepts a literal value. See the full
<a href="https://github.com/cloudposse/atmos/tree/main/examples/scaffolding-yaml-functions" target="_blank" rel="noopener noreferrer" class=""><code>examples/scaffolding-yaml-functions</code></a>
example, or the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#loading-external-data-with-include-and-other-yaml-functions">Loading External Data with <code>!include</code> and Other YAML Functions</a>
section of the <code>atmos scaffold generate</code> docs.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-include#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#loading-external-data-with-include-and-other-yaml-functions"><code>atmos scaffold generate</code></a>
docs for the full reference, or <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with
feedback.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Component Mocks Now Fill Gaps Instead of Replacing Real State]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback</guid>
            <pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A local plan rarely depends on infrastructure that is entirely missing or entirely deployed. For example, the VPC exists, the database has not been created yet, and the cluster is somewhere in between. Until now, component mocks forced an all-or-nothing choice: with --use-mocks, every Terraform lookup returned its mock, even for components whose real state was sitting in the backend.]]></description>
            <content:encoded><![CDATA[<p>A local plan rarely depends on infrastructure that is entirely missing or entirely deployed. For example, the VPC exists, the database has not been created yet, and the cluster is somewhere in between. Until now, <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/mocks">component mocks</a> forced an all-or-nothing choice: with <code>--use-mocks</code>, every Terraform lookup returned its mock, even for components whose real state was sitting in the backend.</p>
<p>By default, <code>--use-mocks</code> now treats mocks as fallbacks. Real state wins whenever it exists, and a mock fills in only for a component that has not been provisioned or an output that is missing.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Mocks were designed to let a plan or <code>describe component</code> run before its dependencies exist. In practice, a stack is usually partly deployed. Turning mocks on replaced every <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/terraform.state"><code>!terraform.state</code></a> and <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/functions/yaml/terraform.output"><code>!terraform.output</code></a> lookup with literal values, so a plan against a half-built environment showed fake IDs for resources that already had real ones. The only alternative was to turn mocks off and fail on the components that were not there yet.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The behavior is configurable. In the new default <code>fallback</code> mode, each lookup resolves in this order:</p>
<ol>
<li class="">The real value, when the referenced component's state exists and declares the output.</li>
<li class="">The component's mock, when the state is not provisioned or the output is missing.</li>
<li class="">A YQ <code>//</code> default in the expression, if one is present.</li>
<li class="">The same result as without mocks: the not-provisioned error for a component that was never applied, or <code>null</code> for an output missing from applied state.</li>
</ol>
<p>Mocks never hide real problems. Credential, network, and backend failures still fail the command instead of quietly returning a mock value. As before, only <code>atmos terraform plan</code> and <code>atmos describe component</code> accept <code>--use-mocks</code>; every other Terraform subcommand, such as apply, deploy, and destroy, rejects it. Map outputs are merged: a mock fills keys that are missing from a real map output, while every value present in real state wins.</p>
<p>The other mode, <code>always</code>, keeps the previous behavior for lookups that must not depend on what is deployed, such as describing a component on a machine without cloud credentials. In <code>always</code> mode, <code>!terraform.state</code> and <code>!terraform.output</code> lookups resolve from mocks only and never initialize Terraform, authenticate, or read a backend for the components they reference. A plan still runs Terraform against the component being planned, with that component's own backend and provider credentials. Choose the mode per run with the flag, or set a project default with <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/components/terraform"><code>mocks.mode</code></a>, as shown below.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Declare mocks on the producer component as before:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">stacks/dev.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">vpc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">mocks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">vpc_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> vpc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">local</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">private_subnet_ids</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">subnet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">a</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> subnet</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">b</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">app</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">vars</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">vpc_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!terraform.state</span><span class="token plain"> vpc vpc_id</span></span><br></div></code></pre></div></div>
<p>Then pick the mode per run, or set a project default:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Real state where it exists, mocks for the gaps.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan app </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev --use-mocks</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Lookups use mocks only, with no backend reads or credentials for them.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># The plan itself still uses app's own backend and provider credentials.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan app </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev --use-mocks</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">always</span><br></div></code></pre></div></div>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">atmos.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">mocks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">mode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> always   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># fallback (default) | always</span></span><br></div></code></pre></div></div>
<p>The <code>mocks.mode</code> setting can also be set with <code>ATMOS_COMPONENTS_TERRAFORM_MOCKS_MODE</code>. Attach a mode to the flag with <code>=</code>, because <code>--use-mocks always</code> does not select one. An explicit mode passed with the flag, such as <code>--use-mocks=fallback</code> or <code>--use-mocks=always</code>, wins over the environment variable, which wins over <code>atmos.yaml</code>. A bare <code>--use-mocks</code> turns mocks on and keeps the configured mode.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="upgrading">Upgrading<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#upgrading" class="hash-link" aria-label="Direct link to Upgrading" title="Direct link to Upgrading" translate="no">​</a></h3>
<p>This changes what a bare <code>--use-mocks</code> does, so the new default is tied to <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/edition">config editions</a>. Projects pinned to an edition before <code>2026-10-01</code> keep the previous mocks-only behavior with no changes. Unpinned projects, and projects that move their edition forward, get the fallback behavior. To keep mocks-only regardless of edition, set <code>mocks.mode: always</code> or pass <code>--use-mocks=always</code>.</p>
<p>See <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/mocks#resolution-modes">resolution modes</a> for the full details.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terraform-component-mocks-fallback#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try the provider-free <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/examples/terraform-component-mocks">component mocks example</a>, which walks through both the fallback and <code>always</code> flows. Questions and feedback are welcome in <a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a> or the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">SweetOps Slack</a>.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Activate a PIM-eligible Azure role as part of your identity chain]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation</guid>
            <pubDate>Wed, 30 Sep 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[More and more Azure resource roles are handed out as PIM-eligible rather than standing: you hold]]></description>
            <content:encoded><![CDATA[<p>More and more Azure resource roles are handed out as PIM-eligible rather than standing: you hold
the role only after you activate it, for a time-boxed window, with a justification. That activation
is a multi-step REST dance against Azure Resource Manager - enumerate what you are eligible for,
file a self-activation request, then poll until it provisions - and there is no native <code>az</code> command
for activating an eligible Azure <em>resource</em> role. So every working session starts with hand-rolled
<code>az rest</code> calls or a third-party script before you can actually run anything.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Just-in-time access is the right default for privileged roles, but the activation step lands on the
operator every single session, outside the tool they actually came to use. You want to run a plan
against production; first you have to remember the role definition id, the scope, a justification,
and the sequence of REST calls to turn your eligibility into an active assignment - and redo it
when the window expires. Worse, nothing downstream benefits from a single place that performs the
elevation: the credential your tooling consumes is the same either way, so there is no natural seam
to hang "activate my role, then run" on. Atmos already modeled the two things this needs - becoming
something more privileged through <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/auth/identities#identity-chaining">identity chaining</a>,
and credential time-boxing - but Azure only had the <code>azure/subscription</code> identity. There was no way to
express the elevation at all.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>A new <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/auth/identities#pim-role-activation"><code>azure/pim-role</code></a> identity chains from an existing
Azure identity and, on authentication, runs the Azure Resource Manager PIM self-activation for a
role you are eligible for - scoped and time-boxed by config. Because the elevation lives in the
identity chain, every consumer inherits it with no extra wiring: <code>atmos terraform</code>, <code>atmos auth exec</code>, the AKS kubeconfig exec plugin, and MCP servers all just see the role active.</p>
<p>Unlike assuming a role, <code>azure/pim-role</code> mints no new credentials. Azure RBAC evaluates roles by
object id at request time, not as token claims, so the activation elevates the principal you already
authenticated as - server-side - and the identity hands back the parent credentials unchanged, now
carrying the active role. That is what lets it sit transparently anywhere in a chain.</p>
<p>It is also careful about not being noisy:</p>
<ul>
<li class=""><strong>It does not re-request on every command.</strong> If an active assignment already covers the scope, it
returns immediately instead of filing another request and tripping PIM throttling.</li>
<li class=""><strong>It resumes instead of duplicating.</strong> If a request for the same role and scope is already waiting
on an approver, a later run attaches to that pending request rather than starting a new one.</li>
<li class=""><strong>It refuses clearly when it cannot proceed.</strong> A missing eligibility is reported as "not eligible"
(this activates an eligibility, it does not grant one), distinct from an activation that failed.
In a non-interactive context with no justification, it fails fast and tells you how to supply one.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add a <code>azure/pim-role</code> identity that elevates from an identity you already have:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">auth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">identities</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">azure-dev</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure/subscription</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">via</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">provider</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">interactive</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">principal</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">subscription_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"00000000-0000-0000-0000-000000000000"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">prod-contributor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure/pim</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">role</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">via</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">identity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> azure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">dev          </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># elevate from who I already am</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">principal</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">role_definition_id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">scope</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"/subscriptions/00000000-0000-0000-0000-000000000000"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">duration</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"8h"</span><span class="token plain">               </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># converted to ISO-8601; Azure enforces the role's policy maximum</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">justification</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"planned change window"</span></span><br></div></code></pre></div></div>
<p>Then authenticate or run as usual - the role activates as part of the chain:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos auth login </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--identity</span><span class="token plain"> prod-contributor</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos auth </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">exec</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--identity</span><span class="token plain"> prod-contributor -- terraform plan</span><br></div></code></pre></div></div>
<p>Need a different reason for a specific run? Pass the <code>--justification</code> global flag (or set the
<code>ATMOS_AUTH_JUSTIFICATION</code> environment variable) - it overrides the configured default:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos auth </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">exec</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--identity</span><span class="token plain"> prod-contributor </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--justification</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"incident INC-123"</span><span class="token plain"> -- terraform apply</span><br></div></code></pre></div></div>
<p>In a non-interactive context (CI, a running MCP server) with no justification available at all, the
login fails fast and tells you to pass <code>--justification</code> or set <code>ATMOS_AUTH_JUSTIFICATION</code>. If a role
requires approval, the login bounds its wait and shows progress; a later invocation picks up the
pending request instead of starting over.</p>
<p>This covers Azure <em>resource</em> roles. Entra directory roles and PIM for Groups activate through
different APIs and will be separate identity kinds.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/azure-pim-role-activation#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>The <code>azure/pim-role</code> identity is part of the broader <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/auth">Atmos Auth</a> effort to
make least-privilege, just-in-time access something you configure once and forget. If you run PIM in
Azure, try it against an eligible role and let us know how it fits your workflow in
<a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>.</p>]]></content:encoded>
            <category>Feature</category>
            <category>Security</category>
        </item>
        <item>
            <title><![CDATA[Configurable merge-conflict threshold for --update]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag</guid>
            <pubDate>Wed, 30 Sep 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[Running --update against a generated project is supposed to save you from re-doing your own]]></description>
            <content:encoded><![CDATA[<p>Running <code>--update</code> against a generated project is supposed to save you from re-doing your own
customizations by hand. But once your local changes and the template's own changes overlap enough
— more than half of a file's lines, by the merge's own accounting — the merge doesn't hand you
conflict markers to work through. It refuses outright, with no way to say "I understand, show me
the conflict anyway."</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate --update</code></a> and
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init --update</code></a> compute a three-way merge for every existing file: what
changed in the template, layered onto what you've customized locally. If that merge would touch
more than 50% of a file's lines, it bails out entirely — no conflict markers, no partial result,
just a hard failure and the file left untouched. That 50% ceiling was hardcoded, with no flag to
raise it, even though a large conflict is often exactly the kind of thing a person wants surfaced
for manual review rather than blocked outright.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><code>--max-changes</code> on both commands controls that same conflict-percentage threshold. The default
stays <code>50</code> — no behavior change if you don't pass it. <code>0</code> disables the check entirely: the merge
always proceeds and writes conflict markers for you to resolve, instead of refusing the update.</p>
<p>One nuance worth understanding before you reach for a specific number: the change percentage this
is compared against isn't itself capped at 100. When your local edits and the template's changes
both diverge significantly from the common base, the computed percentage can climb well past
100% (200%+ in some cases). That means only <code>--max-changes=0</code> is a guaranteed "never fail on this"
setting — raising it to 100, 200, or higher only makes a hard failure progressively less likely,
it doesn't rule one out. If what you actually want is "always give me conflict markers, never a
hard failure," reach for <code>0</code>, not a large number.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Default behavior is unchanged: fails if a merge would touch more than 50% of a file.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Always get conflict markers instead of a hard failure.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --max-changes</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos init </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"> --max-changes</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Or configure it once via environment variable.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_SCAFFOLD_MAX_CHANGES</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> atmos scaffold generate my-template ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_INIT_MAX_CHANGES</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> atmos init </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--update</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-max-changes-flag#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate"><code>atmos scaffold generate</code></a> and
<a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/init"><code>atmos init</code></a> docs for the full flag reference. Have feedback on this
feature? <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a> or join the conversation in
the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">Cloud Posse community Slack</a>.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Automatic CLI Exception Reporting to Atmos Pro]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting</guid>
            <pubDate>Fri, 25 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Finding the stack, component, and team behind a failed infrastructure command can]]></description>
            <content:encoded><![CDATA[<p>Finding the stack, component, and team behind a failed infrastructure command can
require piecing together several CI logs. Atmos can now report CLI failures to
Atmos Pro with execution context and your existing metadata tags and labels.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Exception capture previously depended on configuring a separate Sentry destination.
Enabling Atmos Pro for a stack did not automatically send CLI exceptions to Pro,
leaving failures disconnected from the execution records already uploaded there.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/settings/pro#automatic-exception-reporting">automatic Pro exception reporter</a>
sends failures from GitHub Actions when the effective <code>settings.pro.enabled</code> is
<code>true</code>, using fresh GitHub OIDC credentials and the configured Pro base URL.
Separately configured Sentry destinations continue to receive events with the same
IDs and fingerprints.</p>
<p>Resolved <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/component-metadata">component metadata</a>, including
inherited stack defaults, becomes unprefixed Sentry tags. A <code>production</code> presence
tag becomes <code>production: "true"</code>; a <code>team: platform</code> label becomes
<code>team: "platform"</code>. Events also include stack/component identity and the execution
ID used by Pro uploads, with Atmos's existing masking applied.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Enable Pro in <code>atmos.yaml</code> for invocation-wide reporting, or use the existing
stack/component Pro setting:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_JJ7b">atmos.yaml</div><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">settings</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">pro</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span></span><br></div></code></pre></div></div>
<p>Grant the GitHub Actions workflow <code>id-token: write</code> permission:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> read</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> write</span></span><br></div></code></pre></div></div>
<p><strong>Upgrade behavior:</strong> existing stacks with <code>settings.pro.enabled: true</code> now send
CLI exceptions automatically in eligible GitHub Actions runs. To retain the
previous behavior while keeping other Pro features, explicitly opt out:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">settings</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">pro</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">errors</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">false</span></span><br></div></code></pre></div></div>
<p>The <code>ATMOS_PRO_ERRORS_ENABLED=false</code> environment override also disables reporting
and takes precedence over component settings. Edition pins do not suppress this
new behavior. Reporting failures preserve the command's exit code, with a
two-second delivery timeout and a shared two-second shutdown flush.</p>
<p>The CLI contract covers successful ingestion and authentication rejection; Pro's
provider verification and persisted-tag assertions are tracked in
<a href="https://github.com/cloudposse/atmos/issues/3219" target="_blank" rel="noopener noreferrer" class="">issue #3219</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-pro-exception-reporting#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/settings/pro#automatic-exception-reporting">reporting configuration and tag precedence</a>
for details, and <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with
feedback about exception reporting.</p>]]></content:encoded>
            <category>Feature</category>
            <category>Atmos Pro</category>
        </item>
        <item>
            <title><![CDATA[Consistent Toolchain Paths and Stable Version Declarations]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations</guid>
            <pubDate>Thu, 24 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[CI should install the artifacts reviewed and committed with a project. Ordinarily, Atmos verifies artifacts against the lockfile's recorded checksums, but it can still accept and record a new artifact when a tool or platform entry is missing.]]></description>
            <content:encoded><![CDATA[<p>CI should install the artifacts reviewed and committed with a project. Ordinarily, Atmos verifies artifacts against the lockfile's recorded checksums, but it can still accept and record a new artifact when a tool or platform entry is missing.</p>
<p>Enable <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#frozen-installs-in-ci"><code>toolchain.frozen_lock_file: true</code></a> in CI and security-sensitive environments to reject those missing entries and prevent lockfile writes. That way, you can prepare and review lockfile updates before running CI.</p>
<p>This update makes project configuration consistent across invocation directories and keeps automatic installs from changing declared dependencies. The same project paths and declarations apply whether a developer or CI runs the command.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Relative toolchain paths could follow the directory where you invoked Atmos. A command run from a component directory could therefore look for a different version manifest or use a different installation directory than the same command run from the project base.</p>
<p>Automatic installation also used the declaration-writing behavior of an explicit install command. Running an infrastructure command could leave a change in <code>.tool-versions</code>, even though you had not asked to change the project's dependencies.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#configuration-options">Toolchain paths</a> now resolve from the configured project base, and automatic installs leave declared versions unchanged.</p>
<table><thead><tr><th>Behavior</th><th>Before</th><th>Now</th></tr></thead><tbody><tr><td>Relative toolchain paths</td><td>Could resolve from the invocation directory.</td><td>Resolve from the configured project base, including the default <code>.tool-versions</code> path.</td></tr><tr><td>Automatic dependency installation</td><td>Could add entries to <code>.tool-versions</code>.</td><td>Installs the dependency without rewriting declarations.</td></tr><tr><td>Atmos version switching</td><td>Could use earlier toolchain settings instead of the active project configuration.</td><td>Uses the active configuration, including selected profiles and project lockfile settings.</td></tr><tr><td>Toolchain path environment overrides</td><td><code>ATMOS_TOOLCHAIN_FILE_PATH</code> and <code>ATMOS_TOOLCHAIN_INSTALL_PATH</code> were not applied.</td><td>Override configured paths for explicit installs, automatic dependencies, and Atmos version switching.</td></tr></tbody></table>
<p>For example, with <code>/work/infra</code> as the configured project base, Atmos reads <code>/work/infra/.tool-versions</code> even when invoked from <code>/work/infra/components/vpc</code>. A configured <code>install_path: .tools</code> resolves to <code>/work/infra/.tools</code> from either directory. The same rule applies to relative <code>versions_file</code> and <code>lock_file</code> settings.</p>
<p>Binaries still use shared XDG cache storage by default. When Atmos installs a version of itself outside a project, it now keeps installation metadata there too, unless you explicitly override the installation path.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Existing projects get the path and declaration fixes without adding configuration. Continue running your usual Atmos commands; when they need to install a tool automatically, the project's declarations remain unchanged. Use explicit <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/toolchain/install">toolchain management commands</a> when you intend to add or change those declarations.</p>
<p>Use the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#environment-variables">toolchain environment overrides</a> to select a different manifest or installation directory without editing project configuration. For example, <code>ATMOS_TOOLCHAIN_INSTALL_PATH=/shared/atmos-tools atmos toolchain install</code> installs binaries in the supplied directory even when invoked outside the project.</p>
<p>Automatic installs can still update <strong>resolved artifact metadata</strong> in the existing <code>toolchain.lock.yaml</code>: they record missing version or platform entries after successful installation, preserve matching entries, and fail on checksum mismatches. The distinction is between declaring a dependency and recording the artifact used to satisfy it. See <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/toolchain#automatic-installation-and-lockfiles">automatic installation and lockfiles</a> for details.</p>
<h3 class="anchor anchorTargetStickyNavbar_cA1_" id="compatibility">Compatibility<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#compatibility" class="hash-link" aria-label="Direct link to Compatibility" title="Direct link to Compatibility" translate="no">​</a></h3>
<p>If you relied on toolchain paths relative to the invocation directory, adjust them relative to the project base or use absolute paths. These behavior fixes apply to every <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/edition">config edition</a>; pinning an older edition does not restore the earlier path or declaration-writing behavior.</p>
<p>The existing lockfile defaults still depend on your edition. Editions before <code>2026-08-05</code> retain <code>use_lock_file: false</code>; set it explicitly to enable ordinary lockfile use. Frozen mode remains opt-in and requires verification regardless of that setting.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/toolchain-project-paths-and-declarations#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try running your existing commands from a project subdirectory and check that automatic installs leave <code>.tool-versions</code> unchanged. Share any unexpected behavior in <a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">GitHub Discussions</a>.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>Bug Fix</category>
        </item>
        <item>
            <title><![CDATA[Derive a scaffold field once with type: computed]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields</guid>
            <pubDate>Wed, 23 Sep 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[A scaffold template's spec.fields[] questionnaire is great at collecting answers, but not every]]></description>
            <content:encoded><![CDATA[<p>A scaffold template's <code>spec.fields[]</code> questionnaire is great at collecting answers, but not every
value a template needs is really an answer. Some values are just a function of other answers —
"the primary region, defaulting to the only region when there's just one" — and until now, every
file that needed that value had to re-derive it itself, with the same <code>{{ if .Config.primary_region_select }}...{{ end }}</code>
snippet copied into each one.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Templating and scaffolding tools all hit the same shape of problem eventually: a value the
generated output needs isn't something the user should be prompted for at all — it's derived from
answers they already gave. Ask for a list of regions, then only ask for a primary region when
there's more than one; when there's exactly one, it's the primary by definition, no prompt needed.
That derivation logic is simple once, but a scaffold template has no single place to put it. It
gets pasted into every file that references the value, and every copy has to independently stay
in sync with the same conditional. Miss one, or get the fallback logic subtly wrong in one file,
and that file quietly disagrees with the rest of the generated project.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>A new <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields"><code>type: computed</code></a> field declares a value
once — either derived from other answers via a <code>value:</code> Go-template expression, or a plain
literal (string, number, boolean, list, or map) used as-is — and is never itself prompted for or
settable with <code>--set</code>. A string is only treated as an expression when it actually contains a
template action; a plain string like <code>hello</code> is a literal too, same as any other type:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">fields</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> regions</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> multiselect</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">us</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">east</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> us</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">west</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">2</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> eu</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">west</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> primary_region_select</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> select</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">options</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> answers.regions</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">when</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"size(answers.regions) &gt; 1"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> primary_region</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"{{ ternary answers.primary_region_select (index answers.regions 0) (gt (len answers.regions) 1) }}"</span></span><br></div></code></pre></div></div>
<p><code>primary_region</code> derives its value exactly once, and <code>.Config.primary_region</code> is then usable
everywhere <code>.Config</code> is — file content, <code>target:</code> path templates, and <code>matrix:</code> axes — with no
per-file fallback logic to keep in sync. Computed fields evaluate in declaration order, after
every regular field's answer is already final, so a computed field can reference any regular
field regardless of where it's declared, and any <em>earlier</em>-declared computed field's own result.</p>
<p>A computed field's <code>value:</code> doesn't have to be an expression at all — a plain literal works too,
useful for a small hand-authored reference table shared across every file in the template:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> provider_version_pins</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> computed</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token key atrule">aws</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"~&gt; 5.0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token key atrule">azurerm</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"~&gt; 3.0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token key atrule">google</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"~&gt; 5.0"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span></span><br></div></code></pre></div></div>
<p><code>provider_version_pins</code> is stored exactly as written, with no template rendering, and is
reachable the same way as any other computed field: <code>.Config.provider_version_pins</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add a <code>type: computed</code> field to any existing <code>scaffold.yaml</code>, following the shape above, and
reference its name from <code>.Config</code> in any file the template generates:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate </span><span class="token operator" style="color:rgb(127, 219, 202)">&lt;</span><span class="token plain">template</span><span class="token operator" style="color:rgb(127, 219, 202)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(127, 219, 202)">&lt;</span><span class="token plain">target</span><span class="token operator" style="color:rgb(127, 219, 202)">&gt;</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">regions</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">us-east-1,us-west-2</span><br></div></code></pre></div></div>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields">Computed Fields</a> section of the
<code>atmos scaffold generate</code> docs for the full set of validation rules (<code>value:</code> is required on a
computed field and rejected on every other type; <code>required:</code>/<code>default:</code> are both rejected on a
computed field) and the ordering constraints that keep a computed field's dependencies resolvable.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-computed-fields#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#computed-fields"><code>atmos scaffold generate</code></a> docs for the
full reference, or <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with feedback.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Skip or duplicate a whole directory with glob spec.files[].path]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob</guid>
            <pubDate>Fri, 18 Sep 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[A scaffold template's spec.files[] entries have always matched one discovered file at a time —]]></description>
            <content:encoded><![CDATA[<p>A scaffold template's <code>spec.files[]</code> entries have always matched one discovered file at a time —
every file that needed gating or duplicating got its own entry, one <code>path:</code> per file. That's fine
for a handful of files. It breaks down the moment the thing you want to skip or duplicate is a
whole directory: a legacy docs tree gated behind an opt-in answer, or a <code>components/</code> tree that
needs to exist once per environment, region, or tenant. Either case meant repeating the same
<code>when:</code> or the same <code>matrix:</code> on every file inside the directory, one entry per file, kept in sync
by hand as the directory grew.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>The <code>matrix:</code> field already expands a single declared file into one generated file per selected value —
pick three environments, get three files, from one template file. But real templates aren't
single files; they're whole directories. A <code>components/</code> tree with a dozen resources that needs
to exist under every environment couldn't be matrixed as a unit — only file by file, one
<code>spec.files[]</code> entry per file, each with its own repeated <code>matrix:</code> and <code>target:</code>. Skipping a
directory recursively (docs that only ship when an answer opts in, a cloud-specific subtree that
only applies to one provider) had the same problem in miniature: one <code>when:</code>-gated entry per file,
duplicated across every file the directory contained.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#glob-paths-and-directory-level-matrix"><code>spec.files[].path</code></a>
field can now be a glob pattern instead of a literal path — <code>*</code>, <code>?</code>, <code>[...]</code>, <code>**</code> for any depth, and
<code>{a,b}</code> brace expansion — matched against every file the template discovers. One entry now covers
an entire directory:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"docs/legacy/**"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">when</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"answers.include_legacy_docs"</span></span><br></div></code></pre></div></div>
<p>The <code>when:</code> field still evaluates exactly as it does for a single file — it just now applies to every file
the glob matches, at any depth, recursively, with no per-file repetition. When more than one
entry's <code>path:</code> matches the same file, the <em>last</em> one declared wins, the same precedence
<code>.gitignore</code>/<code>CODEOWNERS</code> use: write broad patterns first, specific overrides after.</p>
<p>Combine a glob <code>path:</code> with <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#dynamic-file-generation"><code>matrix:</code></a>
and <code>target:</code> to duplicate an entire directory once per combination, the same way a single file
already could:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"components/**"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">target</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"environments/{{ .matrix.env }}/{{ .file.RelPath }}"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">matrix</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">dev</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> staging</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> production</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span></span><br></div></code></pre></div></div>
<p>Since a glob can match many files, <code>target:</code> needs to know <em>which</em> matched file an output came
from — <code>.file.RelPath</code> is that file's own path with the glob's literal prefix stripped (so
<code>components/vpc/main.tf</code> becomes <code>vpc/main.tf</code>), available in <code>target:</code> and the file's own content
alongside <code>.matrix.&lt;axis&gt;</code>. A <code>components/</code> directory with <code>vpc/main.tf</code> and <code>eks/main.tf</code>
produces six files across three environments — each preserving its own relative position under
every environment.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/examples/scaffolding-directory-matrix">scaffolding-directory-matrix example</a> is a minimal,
runnable template — a two-resource <code>components/</code> directory duplicated once per selected
environment:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token builtin class-name" style="color:rgb(255, 203, 139)">cd</span><span class="token plain"> examples/scaffolding-directory-matrix</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos scaffold generate example ./my-project </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">environments</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">dev,staging</span><br></div></code></pre></div></div>
<p>This generates four files: <code>environments/dev/vpc/main.tf</code>, <code>environments/dev/eks/main.tf</code>, and
the same pair under <code>staging/</code> — two full copies of <code>components/</code>, one per selected environment,
without listing <code>vpc/main.tf</code> and <code>eks/main.tf</code> individually in <code>scaffold.yaml</code>. Add a glob
<code>path:</code> to any <code>spec.files[]</code> entry in your own templates — with <code>when:</code> alone to skip a
directory, or with <code>matrix:</code> and <code>.file.RelPath</code> in <code>target:</code> to duplicate one.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scaffold-directory-glob#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/scaffold/generate#glob-paths-and-directory-level-matrix"><code>atmos scaffold generate</code></a>
docs for the full reference, or <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">open an issue</a> with
feedback.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Bring your own model: OpenRouter, DeepSeek, and Z.AI for Atmos AI]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers</guid>
            <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The AI model landscape moves faster than any single vendor's roadmap. A model that was the obvious]]></description>
            <content:encoded><![CDATA[<p>The AI model landscape moves faster than any single vendor's roadmap. A model that was the obvious
choice last quarter is often outclassed - or undercut on price by an order of magnitude - by one you
hadn't heard of this quarter. Locking your infrastructure assistant to one vendor's API means you
either overpay or miss out, and for teams outside the US, a US-only provider list can be a
non-starter entirely.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Atmos AI let you talk to your infrastructure through a fixed set of API providers. If you wanted to
try a cheaper or non-US model - route through an aggregator, run DeepSeek directly, or use a GLM
model from Z.AI - you were out of luck unless you were willing to point the generic OpenAI provider
at a hand-copied base URL and hope the defaults lined up. There was no first-class way to say "use
OpenRouter" and get a sensible model, API-key variable, and endpoint out of the box.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Atmos AI adds three OpenAI-compatible API providers you can select by name:</p>
<ul>
<li class=""><strong>OpenRouter</strong> (<code>openrouter</code>) - a router that fronts hundreds of models behind one API key. Switch
models by changing the <code>model</code> slug (<code>anthropic/claude-sonnet-4-5</code>, <code>openai/gpt-4o</code>,
<code>deepseek/deepseek-chat</code>, ...) without touching anything else.</li>
<li class=""><strong>DeepSeek</strong> (<code>deepseek</code>) - the DeepSeek API directly, including <code>deepseek-reasoner</code> for the
reasoning model. Low cost for a lot of everyday infrastructure questions.</li>
<li class=""><strong>Z.AI</strong> (<code>zai</code>) - Zhipu's GLM models over their OpenAI-compatible endpoint.</li>
</ul>
<p>Each one behaves like every other Atmos AI provider: set an API key with the <code>!env</code> function, and
optionally override the model, <code>base_url</code>, or token limits. Because they are OpenAI-compatible, they
work everywhere the existing providers do - <code>atmos ai ask</code>, <code>atmos ai chat</code>, and the <code>--ai</code> flag.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Add the provider under <code>ai.providers</code> in <code>atmos.yaml</code> and select it as the default:</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">ai</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">default_provider</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> openrouter</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">providers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">openrouter</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"deepseek/deepseek-chat"</span><span class="token plain">   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># any provider-prefixed slug</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">api_key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!env</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"OPENROUTER_API_KEY"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">deepseek</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"deepseek-chat"</span><span class="token plain">            </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># or "deepseek-reasoner"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">api_key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!env</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"DEEPSEEK_API_KEY"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain" style="display:inline-block"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">zai</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"glm-5.3"</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">api_key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token tag" style="color:rgb(127, 219, 202)">!env</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"ZAI_API_KEY"</span></span><br></div></code></pre></div></div>
<p>Then ask away:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token builtin class-name" style="color:rgb(255, 203, 139)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">OPENROUTER_API_KEY</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">sk-or-</span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai ask </span><span class="token string" style="color:rgb(173, 219, 103)">"What stacks and components do we have?"</span><br></div></code></pre></div></div>
<p>See the full list of options on the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/ai/providers">AI providers configuration page</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/more-ai-providers#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>The provider list will keep growing as the model landscape shifts. If there's an OpenAI-compatible
provider you want to see as a first-class name in Atmos, open an issue or a pull request on
<a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">cloudposse/atmos</a> - adding one is a small, well-templated change.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Use opencode as your Atmos AI provider]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider</guid>
            <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[If you already drive your infrastructure work through a terminal coding agent, paying for a]]></description>
            <content:encoded><![CDATA[<p>If you already drive your infrastructure work through a terminal coding agent, paying for a
separate AI API key just to ask Atmos a question is redundant. You've authenticated the agent
once, picked your model provider there, and you'd rather Atmos reuse that setup than make you
manage a second set of credentials.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Atmos AI could reuse a locally installed coding-agent CLI - Claude Code, OpenAI Codex, GitHub
Copilot - instead of an API key, but the popular open-source <a href="https://opencode.ai/" target="_blank" rel="noopener noreferrer" class="">opencode</a>
agent wasn't one of them. opencode users had to fall back to configuring a raw API provider,
which meant a second credential to manage and gave up opencode's own model selection and MCP
setup.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p>Atmos AI adds <strong>opencode</strong> as a CLI provider. Point Atmos at it and every <code>atmos ai</code> command runs
through your existing opencode installation and whichever model provider you've authenticated
there - no API key in <code>atmos.yaml</code>:</p>
<ul>
<li class="">Reuses your opencode auth and model configuration (<code>opencode auth login</code>).</li>
<li class=""><strong>Full MCP pass-through</strong>: MCP servers you declare in <code>atmos.yaml</code> are handed to opencode
automatically, with auth-requiring servers wrapped in <code>atmos auth exec</code> and the Atmos toolchain
on <code>PATH</code>. Atmos writes a temporary config and points opencode at it via <code>OPENCODE_CONFIG</code>, so
your own <code>opencode.json</code> is never touched.</li>
<li class="">Participates in auto-detection: with <code>ai.enabled: true</code> and no <code>default_provider</code>, Atmos finds
the <code>opencode</code> binary on your <code>PATH</code> and uses it.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<p>Select it as the default provider (or let auto-detection find it):</p>
<div class="language-yaml codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-yaml codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A codeBlockLinesWithNumbering_UQ30" style="counter-reset:line-count 0"><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token key atrule">ai</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 88, 116)">true</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">default_provider</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> opencode</span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">  </span><span class="token key atrule">providers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">    </span><span class="token key atrule">opencode</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># optional - opencode's provider/model slug; defaults to opencode's own default</span><span class="token plain"></span></span><br></div><div class="token-line codeLine_MYOh" style="color:#d6deeb"><span class="codeLineNumber_C7H_"></span><span class="codeLineContent_hnsy"><span class="token plain">      </span><span class="token key atrule">model</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(173, 219, 103)">"anthropic/claude-sonnet-4-5"</span></span><br></div></code></pre></div></div>
<p>Then ask away - opencode handles the model call and any MCP tools:</p>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai ask </span><span class="token string" style="color:rgb(173, 219, 103)">"Which components changed in the dev stack?"</span><br></div></code></pre></div></div>
<p>See the <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/ai/providers">AI providers configuration page</a> for the full CLI-provider
reference, including the MCP pass-through behavior.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/opencode-cli-provider#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>opencode joins Claude Code, OpenAI Codex, and GitHub Copilot as bring-your-own-subscription CLI
providers. If there's another local coding agent you'd like Atmos to drive, open an issue or a pull
request on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">cloudposse/atmos</a>.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Faster Vendoring with Concurrent Downloads]]></title>
            <link>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring</link>
            <guid>https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring</guid>
            <pubDate>Tue, 15 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Waiting for each component download to finish before the next begins adds up in]]></description>
            <content:encoded><![CDATA[<p>Waiting for each component download to finish before the next begins adds up in
large repositories. Atmos vendoring now prepares independent packages concurrently
and shows their progress together, while keeping destination writes in declaration
order.</p>
<div><div class="window_X9dN"><div class="titlebar_DN7h"><span class="dots_R2sg" aria-hidden="true"><i></i><i></i><i></i></span><span class="title_Dael">Concurrent vendoring and cleanup</span></div><pre class="screen__b5c noPreWrap_ImkX screenLoading_abuO"><span> </span></pre><div class="controls_eyLV"><button type="button" class="playButton_kD9r" aria-label="Pause cast"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M6 5H8V19H6V5ZM16 5H18V19H16V5Z"></path></svg></button><input aria-label="Cast position" type="range" min="0" max="0" step="0.01" value="0"><span>00:00.0<!-- --> / <!-- -->00:00.0</span></div></div><div class="castActions_M13G"><div class="container_zGFV"><div class="group_ncGU" role="group" aria-label="Share this demo"><button type="button" class="primary_hpkh" title="Copy a link to this demo" aria-live="polite"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg><span>Share</span></button><button type="button" class="caret_pPxC" aria-expanded="false" aria-label="More share options" title="More share options"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_P_nE" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div><div class="container_EXko"><button type="button" class="trigger_WxG7" aria-expanded="false" aria-label="Download cast"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg><span>Download</span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="icon_LbC3" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="6 9 12 15 18 9"></polyline></svg></button></div></div></div>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-problem">The Problem<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>A repository can contain many component sources, mixins, and targets. Fetching
these one at a time leaves the network idle between jobs and makes a large update
hard to follow. Overlapping destinations also mean downloads cannot simply copy
files whenever they finish.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="the-fix">The Fix<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#the-fix" class="hash-link" aria-label="Direct link to The Fix" title="Direct link to The Fix" translate="no">​</a></h2>
<p><a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/vendor/pull">Vendor pull</a> prepares up to four packages at once and
installs them in declaration order. Local sources wait for earlier writes before
being read. <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/vendor/vendor-update">Vendor update</a> checks upstream
versions concurrently and preserves the order and formatting of manifest edits.</p>
<p>The shared progress display shows active phases, download percentages when the total
size is known, retries, and completed results. A ready package is waiting for its turn
to install. The overall bar advances during downloads with known sizes and when
packages become ready, with equal weight for preparation and installation. The
completed count advances only after processing finishes. CI receives plain result
lines, and structured update reports stay on stdout.</p>
<p>The <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/vendor/vendor-clean">vendor clean</a> command displays relative paths
and preserves every lock entry when removing vendored files. Recorded versions,
checksums, and provenance remain available for the next pull. Fully cleaned packages
reinstall without drift warnings; partial deletion and modified files still trigger checks.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="how-to-use-it">How to Use It<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#how-to-use-it" class="hash-link" aria-label="Direct link to How to Use It" title="Direct link to How to Use It" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_W6UR theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_gU9i"><pre tabindex="0" class="prism-code language-shell codeBlock_dlrW thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_YA7A"><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos vendor pull --max-concurrency </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos vendor update </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--check</span><span class="token plain"> --max-concurrency </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><span class="token plain"></span><br></div><div class="token-line" style="color:#d6deeb"><span class="token plain">atmos vendor update </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--pull</span><span class="token plain"> --max-concurrency </span><span class="token number" style="color:rgb(247, 140, 108)">8</span><br></div></code></pre></div></div>
<p>Set <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/vendor#concurrency"><code>vendor.max_concurrency</code></a> in <code>atmos.yaml</code>
or use <code>ATMOS_VENDOR_MAX_CONCURRENCY</code>. Explicit flags take precedence over the
environment and configuration. Set one worker for serial execution.</p>
<p>Projects with an <a class="" href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/edition">edition pin</a> before <code>2026-09-15</code> keep
one worker by default. All editions receive the progress display, and explicit
concurrency settings override the pin.</p>
<h2 class="anchor anchorTargetStickyNavbar_cA1_" id="get-involved">Get Involved<a href="https://pr-3286.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/concurrent-vendoring#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Try the commands on your component catalog and share feedback in
<a href="https://github.com/cloudposse/atmos/discussions" target="_blank" rel="noopener noreferrer" class="">Atmos GitHub Discussions</a>.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
    </channel>
</rss>