<?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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog</link>
        <description>atmos Blog</description>
        <lastBuildDate>Tue, 30 Jun 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[Structured Diagnostics for Agentic Troubleshooting]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Human logs are useful while you are watching a command run, but they are not enough when you need to diagnose subprocess execution, CI failures, or agent runs after the fact.]]></description>
            <content:encoded><![CDATA[<p>Human logs are useful while you are watching a command run, but they are not enough when you need to diagnose subprocess execution, CI failures, or agent runs after the fact.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>When a run fails, the important question is usually not "what did the terminal print?" It is "what command ran, with which arguments, from which directory, for how long, and how did it exit?"</p>
<p>Humans can sometimes reconstruct that from logs. Agents and support tooling should not have to scrape terminal prose to find the root cause.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-change">The Change<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl#the-change" class="hash-link" aria-label="Direct link to The Change" title="Direct link to The Change" translate="no">​</a></h2>
<p>Atmos now supports an opt-in diagnostics stream: machine-readable JSONL events written to a file.</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">diagnostics</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> .atmos/diagnostics.jsonl</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">include_output</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></span></code></pre></div></div>
<p>The same settings can be controlled with environment variables:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_DIAGNOSTICS_ENABLED</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">true </span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_DIAGNOSTICS_FILE</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token plain">.atmos/diagnostics.jsonl </span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan vpc </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-dev</span><br></span></code></pre></div></div>
<p>Because the output is JSONL, it is easy to inspect with tools like <code>jq</code>:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">jq </span><span class="token string" style="color:rgb(173, 219, 103)">'select(.type == "process.exit")'</span><span class="token plain"> .atmos/diagnostics.jsonl</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="diagnostics-vs-logging">Diagnostics vs. Logging<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl#diagnostics-vs-logging" class="hash-link" aria-label="Direct link to Diagnostics vs. Logging" title="Direct link to Diagnostics vs. Logging" translate="no">​</a></h2>
<p>Logs are human-readable status and narrative output. They explain what Atmos is doing for someone watching the run.</p>
<p>Diagnostics are machine-readable event records for tooling, agents, and post-run inspection. They are designed to accelerate root-cause analysis by giving agents structured facts about subprocesses, exits, durations, cancellation, and failures without requiring them to parse terminal output.</p>
<p><code>diagnostics.include_output</code> can include masked subprocess stdout and stderr chunks, but it is disabled by default. Diagnostic output is masked before it is written.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Agentic troubleshooting gets faster.</strong> Agents can reason from structured events, identify the failing step, and move from diagnosis to remediation.</li>
<li class=""><strong>CI artifacts become more useful.</strong> Save the JSONL file with a failed job and inspect it after the terminal session is gone.</li>
<li class=""><strong>Logs stay for people.</strong> Diagnostics add a tooling-oriented layer without replacing human-readable logs.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/diagnostics-jsonl#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Enable diagnostics when you need a structured troubleshooting artifact, especially for CI and agent-driven workflows.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Profiles Can Now Import Shared Configuration]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/profile-imports</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/profile-imports</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Auth profiles and team defaults are often managed centrally, but workload or app repositories still need to use them. Until now, that usually meant copying profile YAML into each repository and keeping those copies in sync by hand.]]></description>
            <content:encoded><![CDATA[<p>Auth profiles and team defaults are often managed centrally, but workload or app repositories still need to use them. Until now, that usually meant copying profile YAML into each repository and keeping those copies in sync by hand.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/profile-imports#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Many teams keep authentication, identity, terminal, and CI defaults in a central infrastructure repository. That works well for governance, but it creates friction for workload or app repositories: every repo needs the same profile definitions, and every update has to be copied everywhere.</p>
<p>That duplication is especially painful for auth profiles. The central platform team owns the provider and identity shape, while each app repository only wants to consume the right profile and add a small local override.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-change">The Change<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/profile-imports#the-change" class="hash-link" aria-label="Direct link to The Change" title="Direct link to The Change" translate="no">​</a></h2>
<p>Profile config files can now use <code>import:</code>, including remote imports. That means a profile in a workload or app repository can pull shared configuration from a central repository and then override only the local pieces it owns.</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_lKDy">profiles/developer/atmos.yaml</div><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">import</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </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)">"github.com/my-org/platform-atmos//profiles/shared-auth.yaml?ref=v1.4.0"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain"></span><span class="token key atrule">logs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">level</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Debug</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain"></span><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">terminal</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">max_width</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)">140</span></span><br></span></code></pre></div></div>
<p>The imported file provides the baseline. Values in the local profile are merged over it, so the workload repository can keep its local preferences without forking the shared auth profile.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/profile-imports#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Centralize auth profiles.</strong> Platform teams can publish shared provider and identity configuration once.</li>
<li class=""><strong>Keep app repos small.</strong> Workload or app repositories can consume the shared profile instead of duplicating it.</li>
<li class=""><strong>Override locally when needed.</strong> Imported profile configuration is still just layered Atmos configuration, so local profile values win.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/profile-imports#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/profiles">profiles documentation</a> for profile discovery, activation, and merge behavior.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Check Workflow Prerequisites with require and assert]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/require-assert-step</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/require-assert-step</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Workflows often depend on tools, files, and directories being present before they run. Without a first-class check, those prerequisites tend to hide inside brittle shell snippets and ad hoc command checks.]]></description>
            <content:encoded><![CDATA[<p>Workflows often depend on tools, files, and directories being present before they run. Without a first-class check, those prerequisites tend to hide inside brittle shell snippets and ad hoc command checks.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/require-assert-step#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Shell checks work until they have to be portable, readable, and friendly. A workflow might need <code>vhs</code>, <code>ffmpeg</code>, a generated config file, and a local output directory before the real work starts. Encoding that as inline shell makes the workflow harder to scan and usually produces a poor error when something is missing.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-change">The Change<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/require-assert-step#the-change" class="hash-link" aria-label="Direct link to The Change" title="Direct link to The Change" translate="no">​</a></h2>
<p>Atmos now has a declarative <code>require</code> step type for workflow and custom command prerequisites. The <code>assert</code> step type is an alias for the same behavior.</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> require recording tools</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> require</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">tools</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> vhs</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> ffmpeg</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> ./Taskfile.yml</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">dirs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> ./demo</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">hint</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)">"on macOS run: brew install vhs ffmpeg"</span></span><br></span></code></pre></div></div>
<p>The step checks that tools are executable on <code>PATH</code>, files exist, and directories exist. If anything is missing, Atmos fails fast with one aggregated error and the remediation hint.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/require-assert-step#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Prerequisites are visible.</strong> The workflow declares what it needs before the work starts.</li>
<li class=""><strong>Errors are actionable.</strong> Missing tools and paths are reported together with a hint.</li>
<li class=""><strong>It is read-only.</strong> <code>require</code> never installs tools and never mutates <code>PATH</code>; use <code>dependencies.tools</code> when you want Atmos to manage tool installation.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/require-assert-step#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/require"><code>require</code> step reference</a> for all supported fields and examples.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Pace Terminal Output for Demos and Recordings]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terminal-speed</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terminal-speed</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Terminal demos and recordings are hard to follow when a command dumps hundreds of lines instantly. The output may be correct, but viewers cannot read it and recording tools capture a wall of text instead of a sequence.]]></description>
            <content:encoded><![CDATA[<p>Terminal demos and recordings are hard to follow when a command dumps hundreds of lines instantly. The output may be correct, but viewers cannot read it and recording tools capture a wall of text instead of a sequence.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terminal-speed#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Some Atmos commands produce rich, multi-line output that is useful in a real terminal but awkward in demos, docs, and recorded walkthroughs. When everything appears at once, important transitions are easy to miss.</p>
<p>That is especially noticeable with VHS recordings and scripted demos, where readable pacing matters as much as the final output.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-change">The Change<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terminal-speed#the-change" class="hash-link" aria-label="Direct link to The Change" title="Direct link to The Change" translate="no">​</a></h2>
<p>Atmos now supports <code>settings.terminal.speed</code>, also available as <code>ATMOS_TERMINAL_SPEED</code>, to pace terminal output by lines per second.</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">terminal</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">speed</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)">18</span></span><br></span></code></pre></div></div>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token assign-left variable" style="color:rgb(214, 222, 235)">ATMOS_TERMINAL_SPEED</span><span class="token operator" style="color:rgb(127, 219, 202)">=</span><span class="token number" style="color:rgb(247, 140, 108)">18</span><span class="token plain"> atmos describe stacks</span><br></span></code></pre></div></div>
<p>A value of <code>0</code> keeps the existing unlimited output behavior. That is the default, so normal interactive and CI usage does not change unless you opt in.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terminal-speed#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Better recordings.</strong> Output appears at a readable pace for demos, tutorials, and screengrabs.</li>
<li class=""><strong>No command rewrites.</strong> Keep the same Atmos command and control pacing through config or an environment variable.</li>
<li class=""><strong>Default behavior is unchanged.</strong> Unlimited output remains the default.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/terminal-speed#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/settings/terminal">terminal settings documentation</a> for the full terminal configuration reference.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Run Ordered Steps in Lifecycle Hooks]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ordered-steps-hooks</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ordered-steps-hooks</guid>
            <pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos lifecycle hooks can now run an ordered list of steps with kind: steps.]]></description>
            <content:encoded><![CDATA[<p>Atmos lifecycle hooks can now run an ordered list of steps with <code>kind: steps</code>.
Use it when one lifecycle event needs sequencing but the orchestration should
stay local to the component being operated on.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-changed">What Changed<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ordered-steps-hooks#what-changed" class="hash-link" aria-label="Direct link to What Changed" title="Direct link to What Changed" translate="no">​</a></h2>
<p><code>kind: step</code> runs one registered step type as a lifecycle hook. <code>kind: steps</code>
runs several registered step types in order:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">test-fixtures-up</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">before.terraform.test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> steps</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">on_failure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fail</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> aws</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">stack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fixtures</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> up</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform apply vpc </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s fixtures </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">auto</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">approve</span></span><br></span></code></pre></div></div>
<p>The hook envelope stays the same: <code>events</code>, <code>when</code>, <code>on_failure</code>, <code>retry</code>, and
<code>env</code> are still hook-level controls. The ordered payload lives under <code>with:</code>,
the same conventional payload key used by <code>kind: step</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ordered-steps-hooks#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>Some test fixtures need real lifecycle ordering. For example, a component test
may need a local AWS emulator before it can provision a VPC fixture, and the
fixture must be destroyed after the test even if the test fails.</p>
<p>Before <code>kind: steps</code>, the choices were awkward:</p>
<ul>
<li class="">split the lifecycle across multiple hooks and rely on unordered map iteration,</li>
<li class="">move component-specific fixture setup into a custom command or workflow, or</li>
<li class="">fall back to a helper script.</li>
</ul>
<p><code>kind: steps</code> keeps the fixture lifecycle declarative and component-local:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">test-fixtures-up</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">before.terraform.test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> steps</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">on_failure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fail</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> aws</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">stack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fixtures</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> up</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform apply vpc </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s fixtures </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">auto</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">approve</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">test-fixtures-down</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">after.terraform.test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> always</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> steps</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform destroy vpc </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s fixtures </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">auto</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">approve</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> aws</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">stack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fixtures</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> down</span></span><br></span></code></pre></div></div>
<p>Steps run in list order. If a step fails, Atmos stops the list and applies the
hook's <code>on_failure</code> policy. A hook-level <code>retry</code> retries the whole ordered list.</p>
<p>For Terraform tests, Atmos still owns the normal component preparation. It
generates the tested component varfile from the selected stack and can pass a
second generated test varfile from <code>test.vars</code>. Because <code>test.vars</code> resolves
after <code>before.terraform.test</code> hooks run, it can use <code>!terraform.state</code> to pass
fixture outputs into Terraform test without scripts.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="learn-more">Learn More<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/ordered-steps-hooks#learn-more" class="hash-link" aria-label="Direct link to Learn More" title="Direct link to Learn More" translate="no">​</a></h2>
<p>See the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/hooks#kind-steps-run-ordered-steps">Hooks reference</a> for the
full <code>kind: steps</code> syntax.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Flag-Aware Custom Commands and Dynamic Tables]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/flag-aware-custom-commands</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/flag-aware-custom-commands</guid>
            <pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Custom command steps can now read their own command-line flags through {{ .Flags. }}, and the table step templates its data, columns, and title per-step. Together they let you build flag-driven runbooks — rich, dynamic output assembled declaratively in atmos.yaml, no shell scripting required.]]></description>
            <content:encoded><![CDATA[<p>Custom command steps can now read their own command-line flags through <code>{{ .Flags.&lt;name&gt; }}</code>, and the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/table"><code>table</code></a> step templates its <code>data</code>, <code>columns</code>, and <code>title</code> per-step. Together they let you build flag-driven runbooks — rich, dynamic output assembled declaratively in <code>atmos.yaml</code>, no shell scripting required.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-changed">What Changed<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/flag-aware-custom-commands#what-changed" class="hash-link" aria-label="Direct link to What Changed" title="Direct link to What Changed" translate="no">​</a></h2>
<p>Two things came together:</p>
<ol>
<li class=""><strong>Flags are now template variables.</strong> When a custom command declares flags, each one is exposed to its steps as <code>{{ .Flags.&lt;name&gt; }}</code>. Step titles, shell commands, working directories, and <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/table"><code>table</code></a> data all resolve those values.</li>
<li class=""><strong><code>table</code> steps are fully templated.</strong> A <code>table</code> step's <code>title</code>, <code>columns</code>, and every cell in <code>data</code> are rendered as Go templates, so the table content can depend on flags and other <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type">step variables</a>.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/flag-aware-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>
<p>Here's a real example now shipping in <code>examples/demo-stacks</code> — a <code>platform</code> command group whose subcommands take a <code>--stack</code> flag and build their output from it:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> platform</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> Platform team shortcuts for stack discovery and day</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">two commands.</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> status</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> Show stacks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> components</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> and common next actions for one stack.</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> stack</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">shorthand</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> Target stack.</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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)">true</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> stage</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">title</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Components in </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"> .Flags.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 plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> shell</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos list components </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s </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"> .Flags.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 plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> table</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">title</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Platform commands</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">columns</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> task</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> command</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">data</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Show variables</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">                </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos list vars myapp </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s </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"> .Flags.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 plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">task</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> Plan affected</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">                </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos terraform plan </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">affected</span></span><br></span></code></pre></div></div>
<p>Run it like any other command:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos platform status </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-dev</span><br></span></code></pre></div></div>
<p>Every <code>{{ .Flags.stack }}</code> resolves to <code>plat-ue2-dev</code>, the stage titles and table rows fill in, and the shell steps run against the right stack.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/flag-aware-custom-commands#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Build your own shortcuts.</strong> Wrap your team's day-two operations — discovery, planning, secret access — into named commands that adapt to their flags.</li>
<li class=""><strong>Rich output without scripts.</strong> Tables, staged sections, and dynamic titles are declared in YAML, not hand-rolled in bash with <code>printf</code> and <code>column</code>.</li>
<li class=""><strong>Consistent across workflows and custom commands.</strong> The same step library powers both, so what you learn in one applies to the other.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/flag-aware-custom-commands#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type">workflow step types</a> and the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/table"><code>table</code> step</a> reference to start building. Share what you create with the <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">Atmos community</a>.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Visualize Component Dependencies as a Tree]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/list-dependencies</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/list-dependencies</guid>
            <pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The new atmos list dependencies command renders the dependency relationships between your components as a tree — showing, for every component, both what it depends on and what depends on it. It reads dependencies.components (preferred) and the legacy settings.depends_on, so the output stays consistent with atmos describe dependents.]]></description>
            <content:encoded><![CDATA[<p>The new <code>atmos list dependencies</code> command renders the dependency relationships between your components as a tree — showing, for every component, both what it <strong>depends on</strong> and what <strong>depends on it</strong>. It reads <code>dependencies.components</code> (preferred) and the legacy <code>settings.depends_on</code>, so the output stays consistent with <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/describe/dependents"><code>atmos describe dependents</code></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/list-dependencies#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>As infrastructure grows, the relationships between components matter as much as the components themselves. A <code>vpc</code> is a prerequisite for the <code>eks</code> cluster, which is a prerequisite for the <code>alb-controller</code>, and so on. Until now, answering "what would break if I change this?" or "what has to exist before I deploy that?" meant reading <code>atmos describe dependents</code> for one component at a time and stitching the picture together in your head.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-solution">The Solution<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/list-dependencies#the-solution" class="hash-link" aria-label="Direct link to The Solution" title="Direct link to The Solution" translate="no">​</a></h2>
<p><code>atmos list dependencies</code> gives you the whole graph at a glance, as a tree:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos list dependencies</span><br></span></code></pre></div></div>
<p>Tree output keeps stack context next to the component hierarchy, with component
type as secondary metadata on the right:</p>
<div class="language-text codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-text codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">Dependencies</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">Stack         Component                 Type</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">plat-ue2-dev  app-config                terraform</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">              ├──depends on ↓</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">              │  ├──▶ dynamodb-table</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">              │  └──▶ kms-key</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">              └──required by ↑</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">                 └──(none)</span><br></span></code></pre></div></div>
<p>Triangle markers show edge direction: <code>▶</code> means the component depends on that
child, and <code>◀</code> means that child depends on the selected component.</p>
<p>By default it walks every component and renders <strong>both directions</strong> — prerequisites and dependents. Scope it to a stack, focus on a single component, pick a direction, or emit structured data instead:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Limit to one stack</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos list dependencies </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--stack</span><span class="token plain"> plat-ue2-dev</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Focus on a single component in a stack</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos list dependencies vpc </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--stack</span><span class="token plain"> plat-ue2-dev</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Only what the component depends on (its prerequisites)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos list dependencies vpc </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--stack</span><span class="token plain"> plat-ue2-dev </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--direction</span><span class="token plain"> forward</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Only what depends on the component (its dependents)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos list dependencies vpc </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--stack</span><span class="token plain"> plat-ue2-dev </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--direction</span><span class="token plain"> reverse</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Machine-readable output for scripts and CI</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos list dependencies </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--format</span><span class="token plain"> json</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/list-dependencies#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>See blast radius before you change anything.</strong> <code>--direction reverse</code> answers "what depends on this?" so you know what a change could affect.</li>
<li class=""><strong>Understand deployment order.</strong> <code>--direction forward</code> shows prerequisites, so you know what must exist first.</li>
<li class=""><strong>Consistent with the rest of Atmos.</strong> Dependencies come from the same <code>dependencies.components</code> / <code>settings.depends_on</code> sources used by <code>atmos describe dependents</code>, so the tree never contradicts your CI change-detection.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/list-dependencies#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/list/dependencies"><code>atmos list dependencies</code></a> reference for all flags and output formats. As always, feedback and ideas are welcome in the <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">Atmos community</a>.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[AI Skills, Now Bundled in the Atmos Binary]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/offline-ai-skill-catalog</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/offline-ai-skill-catalog</guid>
            <pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The official Atmos agent skills are now embedded directly in the Atmos binary. atmos ai skill install  works fully offline — no network call, no Git clone — and atmos ai skill list shows a single merged view of every skill available to you alongside what's already installed.]]></description>
            <content:encoded><![CDATA[<p>The official Atmos <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/ai/agent-skills">agent skills</a> are now embedded directly in the Atmos binary. <code>atmos ai skill install &lt;name&gt;</code> works <strong>fully offline</strong> — no network call, no Git clone — and <code>atmos ai skill list</code> shows a single merged view of every skill available to you alongside what's already installed.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-changed">What Changed<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/offline-ai-skill-catalog#what-changed" class="hash-link" aria-label="Direct link to What Changed" title="Direct link to What Changed" translate="no">​</a></h2>
<p>Previously, installing an Atmos skill meant fetching it from a GitHub repository. But this also meant that the skills could be out of sync with the version of Atmos that you're running.</p>
<p>Now the complete catalog of official skills (and their reference files) ships inside the binary via an embedded filesystem. Two user-facing improvements fall out of that:</p>
<ul>
<li class=""><strong>Offline install by name.</strong> <code>atmos ai skill install atmos-terraform</code> copies the skill straight out of the binary — instant, deterministic, and air-gap friendly. Community skills can still be installed from any GitHub repository.</li>
<li class=""><strong>A unified available-vs-installed listing.</strong> <code>atmos ai skill list</code> merges the bundled catalog with what's installed locally, alphabetically sorted, with a status marker per skill (<code>●</code> installed, <code>○</code> available).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/offline-ai-skill-catalog#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_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Browse everything — bundled catalog plus anything you've installed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai skill list</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Show only what's installed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai skill list </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--installed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># More detail per skill</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai skill list </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--detailed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Install an official skill offline, by its bare name</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos ai skill </span><span class="token function" style="color:rgb(130, 170, 255)">install</span><span class="token plain"> atmos-terraform</span><br></span></code></pre></div></div>
<p>The <code>--installed</code> view is also bound to <code>ATMOS_AI_SKILL_INSTALLED</code> for scripting. Once a skill is installed, use it with any command via the global <code>--skill</code> flag (with <code>--ai</code>):</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform plan vpc </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> plat-ue2-prod </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--ai</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--skill</span><span class="token plain"> atmos-terraform</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-it-matters">Why It Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/offline-ai-skill-catalog#why-it-matters" class="hash-link" aria-label="Direct link to Why It Matters" title="Direct link to Why It Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Works anywhere.</strong> No network or Git access required to install the official skills — ideal for locked-down CI runners and air-gapped environments.</li>
<li class=""><strong>Discoverable.</strong> One command shows the full catalog and your install state together, instead of guessing what's available.</li>
<li class=""><strong>Versioned with Atmos.</strong> The bundled skills travel with the binary, so the catalog always matches the Atmos you're running.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/offline-ai-skill-catalog#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/ai/skill"><code>atmos ai skill</code></a> command reference and the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/ai/agent-skills">Agent Skills</a> guide. Skill ideas and contributions are welcome in the <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">Atmos community</a>.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Background Container Services in Atmos Workflows]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services</guid>
            <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos workflows can now start long-running container services in the background, wait for them to become healthy, and tear them down automatically. Bring up an emulator, database, or registry with background: true, gate the next step on its container health check, and stop it with a cancel step — no shell scripts, no background jobs, no orphaned containers.]]></description>
            <content:encoded><![CDATA[<p>Atmos workflows can now start long-running container services in the background, wait for them to become healthy, and tear them down automatically. Bring up an emulator, database, or registry with <code>background: true</code>, gate the next step on its container health check, and stop it with a <code>cancel</code> step — no shell scripts, no background jobs, no orphaned containers.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Plenty of workflows need a service running <em>alongside</em> the steps, not as a step. End-to-end tests need a cloud emulator. Integration steps need a database or a local registry. Until now, standing up that dependency meant leaving the workflow and dropping into shell:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token function" style="color:rgb(130, 170, 255)">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-d</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--name</span><span class="token plain"> emulator </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-p</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4566</span><span class="token plain">:4566 localstack/localstack</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token keyword" style="color:rgb(127, 219, 202)">until</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-sf</span><span class="token plain"> http://localhost:4566/_localstack/health</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(127, 219, 202)">do</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">sleep</span><span class="token plain"> </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"> </span><span class="token keyword" style="color:rgb(127, 219, 202)">done</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># ... run the real steps ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">docker</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> emulator</span><br></span></code></pre></div></div>
<p>That works until it doesn't:</p>
<ul>
<li class="">Readiness is a hand-rolled <code>until curl … sleep</code> loop that's different in every workflow.</li>
<li class="">A failed or interrupted run leaks the container — the cleanup line never runs.</li>
<li class="">The dependency is invisible in the workflow file; it lives in shell glue.</li>
<li class="">Local workflows and CI drift apart because each reinvents the same bring-up dance.</li>
</ul>
<p>A workflow runner should be able to say "start this service, wait until it's healthy, run my steps, then clean it up" as part of the workflow itself.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="whats-new">What's New<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#whats-new" class="hash-link" aria-label="Direct link to What's New" title="Direct link to What's New" translate="no">​</a></h2>
<p>A <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/container#background-services"><code>container</code></a> step with <code>background: true</code> starts a long-running service detached and lets the workflow continue. Three pieces work together:</p>
<ul>
<li class=""><strong><code>background: true</code></strong> on an <code>action: run</code> step starts the service detached.</li>
<li class=""><strong><code>healthcheck</code></strong> (under <code>with:</code>) gates readiness — Atmos blocks until the container is healthy before the next step runs.</li>
<li class=""><strong><a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/cancel"><code>cancel</code></a></strong> stops and removes the service; if you never cancel it, Atmos tears it down automatically when the workflow ends.</li>
</ul>
<p>There are also two readiness-gating control steps: <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/wait"><code>wait</code></a> blocks on named services, and <code>wait-all</code> blocks on every background service started so far.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="a-background-emulator">A Background Emulator<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#a-background-emulator" class="hash-link" aria-label="Direct link to A Background Emulator" title="Direct link to A Background Emulator" translate="no">​</a></h2>
<p>Start an emulator, run Terraform against it, then tear it down — all in one declarative workflow:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_lKDy">stacks/workflows/e2e.yaml</div><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">workflows</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">e2e</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> emulator</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> container</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> run</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">background</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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> localstack/localstack</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">ports</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">host</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)">4566</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">container</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)">4566</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">healthcheck</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">test</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 string" style="color:rgb(173, 219, 103)">"CMD"</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)">"curl"</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)">"-f"</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)">"http://localhost:4566/_localstack/health"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">interval</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 5s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">retries</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)">10</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">start_period</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 30s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> apply</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform apply vpc </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s dev</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> cancel</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">for</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span></code></pre></div></div>
<p>Atmos starts the emulator, blocks until its health check reports healthy, runs <code>apply</code>, then <code>cancel</code> stops and removes the container.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="readiness-reuses-the-health-check">Readiness Reuses the Health Check<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#readiness-reuses-the-health-check" class="hash-link" aria-label="Direct link to Readiness Reuses the Health Check" title="Direct link to Readiness Reuses the Health Check" translate="no">​</a></h2>
<p>The key idea is that readiness is not a new concept — it's the container's own <code>healthcheck</code>, the same shape you already use for <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components">container components</a>. When a background service declares a health check, Atmos blocks until it's healthy before continuing. No <code>sleep</code> guesses, no polling scripts.</p>
<p>For a service, "wait" means <em>until healthy</em>, never <em>until exit</em>. A long-running service never exits on its own, so its health check defines readiness. You can gate readiness implicitly (the step after a healthchecked background service waits automatically) or explicitly with a <code>wait</code> step:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><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"> wait</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">for</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">emulator</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span></span><br></span></code></pre></div></div>
<p>Start several services and wait for all of them at once with <code>wait-all</code>:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><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"> wait</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">all</span><br></span></code></pre></div></div>
<p>The ordinary <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps"><code>needs</code></a> field is unchanged — it expresses step ordering, while <code>wait</code>/<code>wait-all</code> express service readiness.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="teardown-you-dont-have-to-remember">Teardown You Don't Have to Remember<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#teardown-you-dont-have-to-remember" class="hash-link" aria-label="Direct link to Teardown You Don't Have to Remember" title="Direct link to Teardown You Don't Have to Remember" translate="no">​</a></h2>
<p>The most common shell-script bug is the cleanup line that never runs. Background services fix that by default: if you never <code>cancel</code> a service explicitly, Atmos tears down all background services when the workflow ends — <strong>including on failure</strong>. A crashed or short-circuited workflow does not leave orphaned containers behind.</p>
<p>Use <code>cancel</code> when you want to free a service early, before the rest of the workflow finishes, or simply to make teardown explicit in the file:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><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"> cancel</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">for</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>Background services turn "stand up a dependency for these steps" into a first-class, declarative part of the workflow:</p>
<ul>
<li class="">Readiness is the container health check, not a bespoke polling loop.</li>
<li class="">Teardown is automatic, so failed runs don't leak containers.</li>
<li class="">The dependency is visible in the workflow file, not hidden in shell glue.</li>
<li class="">The same workflow runs locally, in CI, or inside a larger runbook.</li>
</ul>
<p>This is especially useful for end-to-end tests against emulators, integration steps that need a database or registry, and any workflow where a service has to be up <em>while</em> the real work runs.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/background-container-services#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>For the full reference, see <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/container#background-services">background services</a> on the <code>container</code> step page, and the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/wait"><code>wait</code></a> and <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/cancel"><code>cancel</code></a> step types. Try it on a real end-to-end workflow and tell us where the readiness and teardown semantics feel right — or where you want more control.</p>]]></content:encoded>
            <category>Feature</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Run Terraform Tests Locally Against Cloud Emulators]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators</guid>
            <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Terraform's native testing framework (*.tftest.hcl) is great — until you hit a run block with]]></description>
            <content:encoded><![CDATA[<p>Terraform's native testing framework (<code>*.tftest.hcl</code>) is great — until you hit a <code>run</code> block with
<code>command = apply</code>. Those blocks create <strong>real</strong> infrastructure, so running them means a cloud account,
credentials, and spend. Atmos now lets you point <code>terraform test</code> at a <strong>local emulator</strong>, so the same
apply-backed tests run for free and hermetically on your laptop or in CI.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>A meaningful Terraform test applies resources and asserts on the result:</p>
<div class="language-hcl codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-hcl codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">run </span><span class="token string" style="color:rgb(173, 219, 103)">"provisions_resources_against_emulator"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token property" style="color:rgb(128, 203, 196)">command</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token plain"> apply</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token keyword" style="color:rgb(127, 219, 202)">assert</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token property" style="color:rgb(128, 203, 196)">condition</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token plain"> output.bucket_id </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 class="token string" style="color:rgb(173, 219, 103)">"atmos-demo-test"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token property" style="color:rgb(128, 203, 196)">error_message</span><span class="token plain"> </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)">"The S3 bucket was not created"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span></span><br></span></code></pre></div></div>
<p>Because <code>command = apply</code> provisions for real, this almost never runs locally. It needs cloud credentials,
it costs money, and it leaves residue you have to clean up. So the most valuable tests — the ones that
actually create infrastructure — rarely run until CI, against a real account.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-solution">The Solution<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators#the-solution" class="hash-link" aria-label="Direct link to The Solution" title="Direct link to The Solution" translate="no">​</a></h2>
<p>Atmos <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/emulator">emulators</a> provide a local, containerized stand-in for AWS (and GCP,
Azure, and more). Bind a component to an <code>aws/emulator</code> identity and Atmos wires the AWS provider —
endpoint, dummy credentials, path-style S3, skip-flags — into <strong>every</strong> Terraform run, including
<code>terraform test</code>. Your component code doesn't change between local and real cloud:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">test</span><span class="token plain"> app </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> fixtures</span><br></span></code></pre></div></div>
<p>That's it. The component hook starts the emulator, applies the fixture VPC, the <code>apply</code> run blocks create
the app resources against that VPC, and everything is torn down — no AWS account, no credentials, no
<code>providers.tf</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="lifecycle-hooks-bring-fixtures-up-and-down">Lifecycle Hooks Bring Fixtures Up and Down<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators#lifecycle-hooks-bring-fixtures-up-and-down" class="hash-link" aria-label="Direct link to Lifecycle Hooks Bring Fixtures Up and Down" title="Direct link to Lifecycle Hooks Bring Fixtures Up and Down" translate="no">​</a></h2>
<p>You don't even start the emulator or provision fixtures yourself. The component declares ordered lifecycle
hooks using <code>kind: steps</code>, bound to the new <code>before.terraform.test</code> and <code>after.terraform.test</code> events:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">test-fixtures-up</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> steps</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">on_failure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fail</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">events</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">before.terraform.test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> aws</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> up</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform apply vpc </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s fixtures </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">auto</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">approve</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">test-fixtures-down</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> steps</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">events</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">after.terraform.test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> always</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform destroy vpc </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s fixtures </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">auto</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">approve</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> emulator</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> aws</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">              </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> down</span></span><br></span></code></pre></div></div>
<p><code>atmos terraform test</code> fires these hooks around the run: the emulator comes up, the fixture VPC is applied,
the app test uses that VPC, and <code>when: always</code> guarantees teardown even when a test fails. The app component
owns its test fixture lifecycle without scripts or a custom command wrapper.</p>
<p>When a test needs fixture outputs, define them under the component's <code>test.vars</code>. Atmos resolves those
values after the setup hook, so <code>test.vars</code> can use <code>!terraform.state</code> to read the fixture VPC ID and pass
it into <code>.tftest.hcl</code> as a declared Terraform test variable.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="in-ci-test-summaries">In CI: Test Summaries<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators#in-ci-test-summaries" class="hash-link" aria-label="Direct link to In CI: Test Summaries" title="Direct link to In CI: Test Summaries" translate="no">​</a></h2>
<p><code>terraform test</code> now plugs into Atmos's native-CI reporting, the same path as <code>plan</code> and <code>apply</code>. Turn it
on in <code>atmos.yaml</code> — <code>ci.enabled</code> is the master switch:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">ci</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">summary</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span></code></pre></div></div>
<p>In a GitHub Actions job, a passing or failing <strong>step summary</strong> is then written to the job summary — per-run
pass / fail / skip results, with the failing assertions inlined — so you see what broke without digging
through logs.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<ul>
<li class=""><strong>Run the valuable tests locally.</strong> Apply-backed assertions — the ones that actually create
infrastructure — run on a laptop in seconds, for free.</li>
<li class=""><strong>Hermetic and repeatable.</strong> No shared account, no drift, no cleanup. Every run starts from a clean
sandbox.</li>
<li class=""><strong>Identical config everywhere.</strong> Because the emulator is bound through Atmos identity and provider
generation, the component is byte-for-byte the same locally and in CI.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/local-terraform-tests-with-emulators#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 <a href="https://github.com/cloudposse/atmos/tree/main/examples/terraform-tests" target="_blank" rel="noopener noreferrer" class=""><code>terraform-tests</code> example</a>,
read the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/emulator">emulator component reference</a>, and see the original
<a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators">emulators announcement</a> for the bigger picture. A container runtime (Docker
or Podman) is the only prerequisite.</p>]]></content:encoded>
            <category>Feature</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Container Components and Compositions]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components</guid>
            <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos now has a first-class container component kind. Where a type: container workflow step is a]]></description>
            <content:encoded><![CDATA[<p>Atmos now has a first-class <strong>container component kind</strong>. Where a <code>type: container</code> workflow step is a
procedural <code>docker run --rm</code>, a <code>components.container</code> entry is declarative, stack-scoped
infrastructure: one component is one service, with an image artifact Atmos builds/pushes/pulls and an
optional long-running named container you operate with <code>atmos container up/ps/logs/exec/restart/stop/rm/down</code>.
A new <code>compositions</code> section groups the components that make up a system.</p>
<p>This is the stack-scoped counterpart to the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-container-steps">native container steps</a> shipped
earlier. The step is ephemeral and workflow-scoped; the component is addressable infrastructure that
lives in your stacks alongside Terraform and Helmfile — same imports, inheritance, catalogs, deep-merge,
templating, and secrets.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="one-component-one-container">One Component, One Container<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components#one-component-one-container" class="hash-link" aria-label="Direct link to One Component, One Container" title="Direct link to One Component, One Container" translate="no">​</a></h2>
<p>Container components use <strong>first-class sections</strong> (<code>image</code>, <code>build</code>, <code>run</code>) — consistent with the
container step, NOT nested under <code>vars</code>:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">container</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">api</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">composition</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> storefront            </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># composition membership (optional)</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">alpine</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">NGINX_ENTRYPOINT_QUIET_LOGS</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)">"1"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">ports</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">host</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)">8080</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">container</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)">80</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">restart</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">policy</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> unless</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">stopped         </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Docker Compose-style restart policy</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">healthcheck</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">                      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># first-class health check</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">test</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 string" style="color:rgb(173, 219, 103)">"CMD-SHELL"</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)">"wget -q -O /dev/null http://localhost/ || exit 1"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">interval</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 30s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> 5s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">retries</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)">3</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">start_period</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 10s</span></span><br></span></code></pre></div></div>
<p><code>build</code>, <code>run.command</code>, <code>mounts</code>, <code>ports</code>, <code>user</code>, <code>restart</code>, and <code>healthcheck</code> all map onto the Docker
Compose shapes you already know. Inheritance (<code>metadata.inherits</code>), abstract base components, catalogs,
and deep-merge work exactly like every other component kind, so shared <code>run</code>/<code>build</code> defaults live in
one place.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="operated-by-labels-not-state-files">Operated by Labels, Not State Files<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components#operated-by-labels-not-state-files" class="hash-link" aria-label="Direct link to Operated by Labels, Not State Files" title="Direct link to Operated by Labels, Not State Files" translate="no">​</a></h2>
<p>Each instance is named and labeled from its canonical address <code>&lt;stack&gt;/container/&lt;component&gt;</code> (e.g.,
<code>atmos-dev-container-api</code>). Lifecycle commands discover the container by label — there are no local
state files to drift or lose:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container build api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># build the image from `build`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container up api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev         </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># create/start the long-running container (build-on-missing)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container list                  </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># all container components + running/health state</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container </span><span class="token function" style="color:rgb(130, 170, 255)">ps</span><span class="token operator" style="color:rgb(127, 219, 202)">|</span><span class="token plain">logs</span><span class="token operator" style="color:rgb(127, 219, 202)">|</span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">exec</span><span class="token plain"> api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container down api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> dev       </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># stop + rm</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="compositions">Compositions<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components#compositions" class="hash-link" aria-label="Direct link to Compositions" title="Direct link to Compositions" translate="no">​</a></h2>
<p>A composition groups components into a system. Declare membership on the component with the
<code>composition</code> field; the top-level <code>compositions</code> section declares the closed set of services:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">compositions</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">storefront</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> Storefront system</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">services</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">api</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> worker</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> database</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span></span><br></span></code></pre></div></div>
<p>Run <code>atmos composition validate storefront -s dev</code> to see which declared services are fulfilled vs. not
yet provided in a given stack — a closed contract for membership, open for fulfillment.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="try-it">Try It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/container-components#try-it" class="hash-link" aria-label="Direct link to Try It" title="Direct link to Try It" translate="no">​</a></h2>
<p>The new <code>examples/container-component</code> example defines an abstract base, a long-running web service with
a health check, and a built-from-Dockerfile worker — all under one composition:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span 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/container-component</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container up api </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos container list</span><br></span></code></pre></div></div>
<p>Container components are experimental while the surface settles. The goal is unchanged: run the same
declared system locally, in CI, and against real environments — without one-off shell scripts.</p>]]></content:encoded>
            <category>Feature</category>
            <category>Experimental</category>
        </item>
        <item>
            <title><![CDATA[Run Any Step Type as a Lifecycle Hook]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types</guid>
            <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos hooks can now run any workflow step type. A new kind: step hook bridges the]]></description>
            <content:encoded><![CDATA[<p>Atmos hooks can now run any workflow step type. A new <code>kind: step</code> hook bridges the
component lifecycle (before/after <code>terraform plan</code>, <code>apply</code>, <code>deploy</code>, <code>init</code>) to the same
step registry that powers workflows and custom commands — so a <code>container</code>, <code>toast</code>, <code>log</code>,
<code>markdown</code>, or <code>http</code> step you already use elsewhere runs identically as a hook.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-changed">What Changed<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types#what-changed" class="hash-link" aria-label="Direct link to What Changed" title="Direct link to What Changed" translate="no">​</a></h2>
<p>Until now, a hook's <code>kind</code> selected from a fixed list — <code>store</code>, <code>command</code>, <code>infracost</code>,
<code>checkov</code>, <code>kics</code>, <code>trivy</code>, <code>git</code>. Anything else meant falling back to <code>kind: command</code> and
hand-rolling a shell invocation. Every new capability meant a new hook kind.</p>
<p><code>kind: step</code> removes that ceiling. Instead of growing the hook-kind list one tool at a time,
hooks now reach the entire step library:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">notify-slack</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> step</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> http                 </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># any registered step type</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">after</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">on_failure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> warn           </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># warn | fail | ignore</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">retry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">max_attempts</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)">3</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">                      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># the step's own parameters</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">url</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">//hooks.slack.com/services/XXX</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">method</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> POST</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">body</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)">'{"text": "Deployed {{ .atmos_component }} to {{ .stack }}"}'</span></span><br></span></code></pre></div></div>
<p>The split is deliberate: the <strong>envelope</strong> (<code>kind</code>, <code>type</code>, <code>events</code>, <code>on_failure</code>, <code>retry</code>,
<code>env</code>) is what the hook runner interprets; <strong><code>with:</code></strong> is the step's own parameter block,
written exactly as you'd write it in a workflow. <code>on_failure</code> and <code>retry</code> are applied <em>around</em>
the step — the step never sees them.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>You already describe a lot of automation as steps. Reusing those steps as lifecycle hooks means
one mental model and zero duplication:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># Build and push an image after apply — retried, non-blocking.</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">publish-image</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> step</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> container</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">action</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> build</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">after</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">on_failure</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> warn</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">context</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> .</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">tags</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">example</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain">latest</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span></span><br></span></code></pre></div></div>
<p>Values inside <code>with:</code> are rendered with the standard hook template context and YAML functions,
so <code>{{ .atmos_component }}</code> and <code>!store ...</code> work there. Each step receives the usual <code>ATMOS_*</code>
environment (<code>ATMOS_STACK</code>, <code>ATMOS_COMPONENT</code>, <code>ATMOS_COMPONENT_PATH</code>, …) plus any <code>env:</code> you add.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="announce-success--or-failure">Announce Success — or Failure<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types#announce-success--or-failure" class="hash-link" aria-label="Direct link to Announce Success — or Failure" title="Direct link to Announce Success — or Failure" translate="no">​</a></h2>
<p>Hooks can now react to the operation's outcome. By default an <code>after-*</code> hook
runs only on success (so a <code>store</code> hook never writes outputs after a failed
apply), but <code>when: failure</code> / <code>when: always</code> opt in to failures — and the
outcome, component, and stack are all available to the step:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">announce</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> step</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> say                 </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># (or http/toast)</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">after</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">apply</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> always</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">message</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)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        The </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"> .atmos_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"> component in the </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"> .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 plain"> stack</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> if eq .status "failure" </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">failed</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"> else </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">deployed</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"> end </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></span></code></pre></div></div>
<p>The same values are exported as <code>ATMOS_HOOK_STATUS</code>, <code>ATMOS_HOOK_EXIT_CODE</code>,
<code>ATMOS_HOOK_ERROR</code>, <code>ATMOS_COMPONENT</code>, and <code>ATMOS_STACK</code> for steps that read the
environment. Under the hood, user hooks now fire on the failure path too — not
just on success — so "deployment failed" announcements actually reach you.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types#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>
<ol>
<li class="">Set <code>kind: step</code> and a <code>type:</code> naming any registered step type.</li>
<li class="">Put the step's parameters under <code>with:</code>.</li>
<li class="">Use <code>events:</code> to choose when it fires, <code>on_failure:</code> for warn/fail/ignore, and <code>retry:</code> to
wrap the step in Atmos's retry policy.</li>
</ol>
<p>A typo'd <code>type:</code> fails the preflight check <em>before</em> Terraform runs, so you find out immediately.
All step types are available, including interactive ones — whether an interactive step makes
sense on a (usually headless) lifecycle event is the step's responsibility, not the hook's.</p>
<div class="theme-admonition theme-admonition-note admonition_cbIa alert alert--secondary"><div class="admonitionHeading_squj"><span class="admonitionIcon_bxkr"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>The <code>http</code> step ships separately</div><div class="admonitionContent_HCPN"><p>The Slack example needs the <code>http</code> step type. If your build doesn't have it yet, use a
registered step type such as <code>container</code>, <code>toast</code>, <code>log</code>, or <code>markdown</code> — the bridge works with
every registered step type.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/hooks-step-types#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/hooks#kind-step-run-a-step-type">Hooks reference</a> for the full <code>kind: step</code>
documentation, and the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type">step types</a> reference for the available steps.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Introducing Emulators: Run Real Cloud Infrastructure Locally]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators</guid>
            <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A big challenge for infrastructure developers is how hard it is to iterate locally. Every]]></description>
            <content:encoded><![CDATA[<p>A big challenge for infrastructure developers is how hard it is to iterate locally. Every
<code>terraform apply</code> needs a real cloud account, costs real money, and touches a live environment. And in
many enterprises developers don't have permission to the cloud at all, so they can't iterate locally
even when they want to. Atmos emulators help with this: long-running, containerized stand-ins for AWS,
GCP, Azure, Kubernetes, Vault, and an OCI registry that you provision as ordinary Atmos components — so
you can run the <strong>full Atmos workflow (auth, secrets, vendoring, toolchain, and <code>terraform apply</code>) on
your laptop, with no cloud account and no credentials.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Iterating on a Terraform component is always coupled to something real:</p>
<ul>
<li class=""><strong>A real account.</strong> You can't <code>apply</code> without one, so even a small experiment starts with a
prerequisites checklist.</li>
<li class=""><strong>Real money and real risk.</strong> Standing resources up and tearing them down to test a change costs
money, and a mistake touches a live account.</li>
<li class=""><strong>Onboarding friction.</strong> A new contributor can't run the tutorial — or reproduce a bug — without
first being granted access to your infrastructure.</li>
<li class=""><strong>The enterprise permission wall.</strong> In many organizations developers simply aren't allowed near the
cloud environment. Local iteration isn't slow for them; it's impossible.</li>
</ul>
<p>Application developers reach for local databases, mock servers, and emulators to iterate without
touching production — but those conveniences never really existed the same way for infrastructure. That
was true until emulators became first class in Atmos.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-solution">The Solution<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#the-solution" class="hash-link" aria-label="Direct link to The Solution" title="Direct link to The Solution" translate="no">​</a></h2>
<p>An emulator is a new Atmos component kind. You declare it in a stack and drive it with the new
<code>atmos emulator</code> command:</p>
<div class="language-bash codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-bash codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator up aws </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain">              </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># start a local AWS emulator</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos terraform apply s3-bucket </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain">    </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># apply real Terraform against it — no AWS account</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator down aws </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain">            </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># stop it (state is kept by default)</span><br></span></code></pre></div></div>
<p>Atmos wires the emulator into the rest of the system automatically:</p>
<ul>
<li class=""><strong>No credentials.</strong> Atmos binds a local identity to the emulator and points both in-process cloud
calls and the Terraform provider at the emulator endpoint — so the same stacks that target real AWS
run unmodified against the emulator.</li>
<li class=""><strong>The same stacks everywhere.</strong> Because the emulator is bound through Atmos identity and provider
generation, your component code doesn't change between local and real cloud — laptop and CI run the
identical configuration.</li>
<li class=""><strong>Persistent by default.</strong> State survives <code>down</code>/<code>up</code>; <code>atmos emulator reset</code> wipes it, and
<code>--ephemeral</code> opts out per run.</li>
</ul>
<p>You can apply, destroy, and re-apply against a local cloud in seconds, without touching an account or
spending money — and without waiting on anyone to grant you access.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="supported-emulators">Supported Emulators<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#supported-emulators" class="hash-link" aria-label="Direct link to Supported Emulators" title="Direct link to Supported Emulators" translate="no">​</a></h2>
<p>Built-in drivers cover the major clouds and the backing services your stacks lean on. The <code>driver</code>
field on the component selects the image and target:</p>
<table><thead><tr><th>Service</th><th>Driver</th><th>Image</th><th>Notes</th></tr></thead><tbody><tr><td>AWS</td><td><code>floci/aws</code></td><td><code>floci/floci</code></td><td><strong>Default</strong> — free, MIT-licensed</td></tr><tr><td>AWS</td><td><code>ministack/aws</code></td><td><code>ministack/ministack</code></td><td>Alternative AWS emulator</td></tr><tr><td>AWS</td><td><code>localstack/aws</code></td><td><code>localstack/localstack:3</code></td><td>Opt-in / legacy</td></tr><tr><td>GCP</td><td><code>floci/gcp</code></td><td><code>floci/floci-gcp</code></td><td>Storage, Pub/Sub, Firestore, Bigtable, Datastore</td></tr><tr><td>Azure</td><td><code>floci/az</code></td><td><code>floci/floci-az</code></td><td>Blob / Storage</td></tr><tr><td>Kubernetes</td><td><code>k3s</code></td><td><code>rancher/k3s</code></td><td>A real single-node cluster</td></tr><tr><td>Vault / secrets</td><td><code>openbao</code></td><td><code>openbao/openbao</code></td><td><strong>Default</strong> — MPL open-source fork</td></tr><tr><td>Vault / secrets</td><td><code>vault</code></td><td><code>hashicorp/vault</code></td><td>Opt-in</td></tr><tr><td>OCI / Terraform registry</td><td><code>registry</code></td><td><code>registry:2</code></td><td>Vendoring + the registry cache</td></tr></tbody></table>
<p>The defaults are deliberately the free, open-source options (<code>floci/aws</code>, <code>openbao</code>) — reach for
<code>localstack/aws</code> or <code>vault</code> only when you specifically need them. See the
<a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/emulator#supported-drivers--targets">emulator component reference</a> for the full
matrix, ports, and per-driver details.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-emulators-are--and-arent">What Emulators Are — and Aren't<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#what-emulators-are--and-arent" class="hash-link" aria-label="Direct link to What Emulators Are — and Aren't" title="Direct link to What Emulators Are — and Aren't" translate="no">​</a></h2>
<p>Emulators are built for the <strong>happy path</strong> — the common control-plane API surface your stacks actually
exercise. That's where they shine, and for most local development and onboarding it's all you need. But
it's worth being clear about what they are not:</p>
<ul>
<li class=""><strong>They're not bug-for-bug clones of the real clouds.</strong> Edge cases, eventual-consistency quirks, and
the long tail of services and IAM nuances won't all be reproduced.</li>
<li class=""><strong>They're not a substitute for a real pre-prod environment.</strong> Validate against a real account before
production — emulators raise your confidence locally; they don't replace staging.</li>
<li class=""><strong>Fidelity varies by service.</strong> The more exotic the resource, the more likely you'll hit an
unimplemented corner.</li>
</ul>
<p>The point isn't 100% fidelity — it's being able to iterate quickly, for free, without cloud access, on
the work you do every day.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="try-it-the-advanced-quick-start">Try It: the Advanced Quick Start<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#try-it-the-advanced-quick-start" class="hash-link" aria-label="Direct link to Try It: the Advanced Quick Start" title="Direct link to Try It: the Advanced Quick Start" translate="no">​</a></h2>
<p>The <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/quick-start/advanced">advanced quick start</a> now deploys a <strong>real event-driven AWS backend</strong> — a KMS
key, an encrypted S3 bucket, a DynamoDB table, an SNS topic, an SQS queue, and an SSM Parameter Store
config — <strong>entirely on your laptop, start to finish, with no AWS account and no credentials.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="persistence-and-reset">Persistence and Reset<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#persistence-and-reset" class="hash-link" aria-label="Direct link to Persistence and Reset" title="Direct link to Persistence and Reset" translate="no">​</a></h2>
<p>Emulators persist their state by default. Atmos bind-mounts a per-instance host directory under the XDG
cache (<code>$XDG_CACHE_HOME/atmos/emulator/&lt;instance&gt;</code>) onto each emulator's data directory, so resources,
images, clusters, and secrets all survive a restart:</p>
<div class="language-bash codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-bash codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator up registry </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain">      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># start the registry</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain"></span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># ... push images ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator down registry </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain">    </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># stop it — state is kept</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator up registry </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain">      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># back up — your images are still there</span><br></span></code></pre></div></div>
<p>Use <code>reset</code> for a clean slate, or <code>--ephemeral</code> for a throwaway instance that never persists:</p>
<div class="language-bash codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-bash codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator reset registry </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--force</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos emulator up registry </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-s</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 139)">local</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">--ephemeral</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#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>
<ul>
<li class=""><code>atmos emulator up &lt;component&gt; -s &lt;stack&gt;</code> to start an emulator, then run your normal
<code>atmos terraform …</code> commands against it.</li>
<li class=""><code>atmos emulator ps</code>, <code>logs</code>, and <code>exec</code> to inspect a running emulator (it outlives the <code>atmos</code>
process and is rediscovered by label — no local state files).</li>
<li class=""><code>atmos emulator reset &lt;component&gt; -s &lt;stack&gt;</code> to wipe persisted state.</li>
<li class=""><code>--ephemeral</code> (or <code>ephemeral: true</code> on the component) for a throwaway instance.</li>
</ul>
<p>See the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/emulator">emulator component reference</a> and the
<a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/emulator/usage"><code>atmos emulator</code></a> command docs for details.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="whats-new">What's New<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/introducing-emulators#whats-new" class="hash-link" aria-label="Direct link to What's New" title="Direct link to What's New" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>atmos emulator list</code></strong> renders every emulator across your stacks in a clean table — a status dot, the image, and the container ID — so you can see what's running at a glance. Scope it to a single stack with <code>--stack</code>, or omit it to list everything. See the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/emulator/list"><code>atmos emulator list</code></a> reference.</li>
<li class=""><strong>Ready-to-run examples.</strong> Two new examples let you try emulators without assembling a stack yourself: <a href="https://github.com/cloudposse/atmos/tree/main/examples/emulator-aws" target="_blank" rel="noopener noreferrer" class=""><code>examples/emulator-aws</code></a> brings up a local AWS environment, and <a href="https://github.com/cloudposse/atmos/tree/main/examples/emulator-k8s" target="_blank" rel="noopener noreferrer" class=""><code>examples/emulator-k8s</code></a> brings up a local Kubernetes (k3s) cluster and deploys into it.</li>
</ul>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Plan-then-Deploy in CI: Planfile Storage and Automatic Drift Verification]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification</guid>
            <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos native CI now supports the full plan-then-deploy workflow with planfiles: atmos terraform plan --ci uploads the planfile to durable storage, and atmos terraform deploy --ci automatically downloads it, generates a fresh plan, reconciles it against the reviewed plan, and applies the fresh plan — only if they match, failing on drift by default.]]></description>
            <content:encoded><![CDATA[<p>Atmos native CI now supports the full plan-then-deploy workflow with planfiles: <code>atmos terraform plan --ci</code> uploads the planfile to durable storage, and <code>atmos terraform deploy --ci</code> automatically downloads it, generates a <strong>fresh</strong> plan, reconciles it against the reviewed plan, and applies the fresh plan — only if they match, failing on drift by default.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-changed">What Changed<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification#what-changed" class="hash-link" aria-label="Direct link to What Changed" title="Direct link to What Changed" translate="no">​</a></h2>
<p>Two pieces landed together:</p>
<ul>
<li class=""><strong>Planfile storage that works in GitHub Actions.</strong> The <code>github/artifacts</code> store talks to the GitHub Actions Artifacts API directly — including the runtime download path, so a planfile uploaded by a <strong>plan</strong> job can be consumed by a separate <strong>deploy</strong> job in the same run. (S3 and local stores work too.)</li>
<li class=""><strong>Automatic, configurable drift verification on <code>deploy</code>.</strong> When planfile storage is configured and you run <code>atmos terraform deploy --ci</code>, Atmos downloads the stored plan, generates a fresh plan against current state, compares them with a JSON-structural plan-diff, and:<!-- -->
<ul>
<li class=""><strong><code>fail</code></strong> (default under CI): blocks the deploy on drift,</li>
<li class=""><strong><code>warn</code></strong>: logs the drift but proceeds,</li>
<li class=""><strong><code>off</code></strong>: skips verification entirely.</li>
</ul>
</li>
<li class=""><strong>A defined answer for "no stored plan found."</strong> <code>verify</code> covers the case where a stored plan <em>exists</em> but differs. A companion <code>required</code> boolean covers whether a stored plan <strong>must</strong> exist to verify against — previously a silent fresh apply. It defaults to tracking <code>verify</code> strictness, so a fail-by-default CI deploy now fails loudly instead of quietly applying something unverified. That means a green <code>deploy</code> actually <em>proves</em> verification ran — no log-scraping required.</li>
</ul>
<p>Configure it once:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">planfiles</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">verify</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> fail        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># drift: stored plan exists but differs (fail | warn | off)</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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)">true</span><span class="token plain">      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># must a reviewed stored plan exist? (defaults to tracking `verify`)</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">priority</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">github</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">stores</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">github</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> github/artifacts</span></span><br></span></code></pre></div></div>
<p>Override per run with <code>--verify-plan</code> / <code>--verify-plan=false</code> (CLI beats config beats the CI default). And because CI is auto-detected (<code>CI</code> / <code>GITHUB_ACTIONS</code>), the <code>--ci</code> flag is optional in a real pipeline — <code>atmos terraform deploy mycomponent -s prod</code> behaves natively and exits non-zero on drift or a missing plan, so your workflow asserts via exit codes instead of grepping logs.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>The gap between "the plan you reviewed" and "what actually gets applied" is where infrastructure surprises live.</p>
<h3 class="anchor anchorTargetStickyNavbar_SsMH" id="reconcile-dont-replay">Reconcile, don't replay<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification#reconcile-dont-replay" class="hash-link" aria-label="Direct link to Reconcile, don't replay" title="Direct link to Reconcile, don't replay" translate="no">​</a></h3>
<p>By default <code>deploy</code> doesn't replay the stored planfile. It re-plans, checks the fresh plan against the one you reviewed, and applies the fresh plan only if they match.</p>
<p>Why not just apply the stored plan directly? A saved plan is brittle. It goes stale the moment the state changes — which, between a PR and its merge, it usually has. And while Terraform bakes the assumed role into the plan, the <strong>base credentials</strong> that authenticate (and assume that role) come from the apply environment, not the plan — so a plan built on the PR can fail to apply on merge. Re-planning at apply time uses the current state and the apply-time credentials; the diff still proves nothing changed since review.</p>
<p>Want byte-for-byte replay anyway? Use <code>deploy --from-plan</code> (or <code>apply --planfile</code>).</p>
<h3 class="anchor anchorTargetStickyNavbar_SsMH" id="why-a-naive-diff-doesnt-work">Why a naive diff doesn't work<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification#why-a-naive-diff-doesnt-work" class="hash-link" aria-label="Direct link to Why a naive diff doesn't work" title="Direct link to Why a naive diff doesn't work" translate="no">​</a></h3>
<p>A planfile is a frozen snapshot — but the plan it represents never really is. Between review and apply the details shift: values "known after apply," computed fields, hashes, ordering, timestamps. You adjust course as you go without changing <em>what</em> you set out to do. A byte-for-byte comparison can't tell the difference — it flags every shift as drift, so a plan that's still doing exactly what you reviewed gets rejected. Terraform's own saved-plan apply is stricter still: any movement in state lineage invalidates the snapshot outright, even when the substance hasn't changed.</p>
<p>That's the real-world problem: rigid checks make a plan go <em>invalid</em> long before it goes <em>wrong</em>. Useful verification needs <strong>wiggle room</strong> — tolerate the benign shifts, catch the real ones.</p>
<p>Atmos's verification is <strong>semantic</strong>, not naive. It parses both plans to JSON, normalizes the noise (sorted keys, masked secrets, computed-hash attributes, data-source reads), and compares what matters. So it flags a resource added, removed, or substantively changed — while letting the incidental variation slide. That's what makes plan-then-deploy practical.</p>
<p>Verification lives on <code>deploy</code>, not <code>apply</code>, by construction: <code>deploy</code> runs a discrete <code>plan</code> step as part of the command, capturing a fresh plan to diff the stored one against. <code>apply</code> doesn't capture that separate planfile — it applies a plan you pass, or plans-and-applies in one step like <code>terraform apply</code> — so it stays a thin, predictable wrapper.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification#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>Inside GitHub Actions, the <code>github/artifacts</code> store needs the runner's runtime credentials (<code>ACTIONS_RUNTIME_TOKEN</code> / <code>ACTIONS_RESULTS_URL</code>), which GitHub withholds from <code>run:</code> steps. Surface them once with the in-repo <a href="https://github.com/cloudposse/atmos/tree/main/actions/github-runtime" target="_blank" rel="noopener noreferrer" class=""><code>github-runtime</code></a> action:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">uses</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> cloudposse/atmos/actions/github</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">runtime@v1</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> env</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos terraform plan mycomponent </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s 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">ci    </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># uploads the planfile</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">run</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> atmos terraform deploy mycomponent </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">s 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">ci   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># downloads, verifies, applies</span></span><br></span></code></pre></div></div>
<p>See <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/ci/planfile-storage">Planfile Storage</a>, <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/components/terraform/planfiles#drift-verification">Planfile drift verification</a>, and <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/commands/terraform/deploy#automatic-plan-verification-in-ci"><code>atmos terraform deploy</code></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/native-ci-planfile-verification#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Planfile storage is evolving — try it in your pipelines and tell us how the <code>fail</code> / <code>warn</code> / <code>off</code> semantics fit your workflow. 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>
        </item>
        <item>
            <title><![CDATA[HTTP Step Type: Call HTTP Endpoints from Workflows and Custom Commands]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type</guid>
            <pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Workflows and custom commands now support a native http step type that performs an HTTP request — any verb, query-string parameters, headers, and a request body (raw or form/JSON) — with per-attempt timeouts and retries that compose with the existing retry webhook? It's an accepted alias.)]]></description>
            <content:encoded><![CDATA[<p>Workflows and custom commands now support a native <code>http</code> step type that performs an HTTP request — any verb, query-string parameters, headers, and a request body (raw or form/JSON) — with per-attempt timeouts and retries that compose with the existing <code>retry:</code> policy. No more shelling out to <code>curl</code>. (Prefer <code>type: webhook</code>? It's an accepted alias.)</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Calling an external endpoint from a workflow used to mean a <code>shell</code> step running <code>curl</code>:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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">type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> shell</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">command</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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token scalar string" style="color:rgb(173, 219, 103)">      curl -sf -X POST "https://ci.example.com/hook" \</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token scalar string" style="color:rgb(173, 219, 103)">        -H "Authorization: Bearer $TOKEN" \</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token scalar string" style="color:rgb(173, 219, 103)">        -d '{"status":"deployed"}'</span></span><br></span></code></pre></div></div>
<p>That works until it doesn't: <code>curl</code> isn't guaranteed to be on the box (especially on Windows), quoting and templating the payload is fiddly, and you get no first-class handling of timeouts or transient failures. Retrying a flaky <code>5xx</code> meant hand-rolling a bash loop.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-solution">The Solution<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type#the-solution" class="hash-link" aria-label="Direct link to The Solution" title="Direct link to The Solution" translate="no">​</a></h2>
<p>The <code>http</code> step makes HTTP a first-class citizen:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">workflows</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">notify</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> trigger</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> http</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">url</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)">"https://ci.example.com/hook/{{ .env.JOB_ID }}"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">method</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> POST</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">query</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">ref</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)">"{{ .env.GIT_SHA }}"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">headers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">Authorization</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)">"Bearer {{ .env.TOKEN }}"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">Content-Type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> application/json</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">body</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)">'{"status":"deployed"}'</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">status</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 number" style="color:rgb(247, 140, 108)">200</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)">201</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)">202</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)">204</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">response</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> /"status"\s</span><span class="token important">*:\s*"deployed"/</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">retry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">max_attempts</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)">5</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">backoff_strategy</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> exponential</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">initial_delay</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 1s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">max_delay</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 30s</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> report</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> info</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">content</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)">"Endpoint returned HTTP {{ .steps.trigger.metadata.status_code }}"</span></span><br></span></code></pre></div></div>
<p>Everything templates, including the URL, headers, query params, and body — so you can thread values from earlier steps or the environment straight into the request.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="sending-parameters">Sending Parameters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type#sending-parameters" class="hash-link" aria-label="Direct link to Sending Parameters" title="Direct link to Sending Parameters" translate="no">​</a></h2>
<p>You asked for full parameter support, and it's all here:</p>
<ul>
<li class=""><strong>Verb</strong> — <code>method:</code> accepts <code>GET</code> (default), <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code>, <code>HEAD</code>, and <code>OPTIONS</code>.</li>
<li class=""><strong>Query-string parameters</strong> — <code>query:</code> is a key-value map appended to the URL.</li>
<li class=""><strong>POST parameters</strong> — use <code>body:</code> for a raw payload, or <code>form:</code> for a key-value map. <code>form</code> is sent as <code>application/x-www-form-urlencoded</code> by default, or as a JSON object when you set a JSON <code>Content-Type</code>:</li>
</ul>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> notify</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">slack</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> http</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">url</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)">"{{ .env.SLACK_WEBHOOK_URL }}"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">method</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> POST</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">headers</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">Content-Type</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> application/json</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">form</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">text</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)">"Deployment complete"</span></span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="timeouts-and-retries-that-compose">Timeouts and Retries That Compose<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type#timeouts-and-retries-that-compose" class="hash-link" aria-label="Direct link to Timeouts and Retries That Compose" title="Direct link to Timeouts and Retries That Compose" translate="no">​</a></h2>
<p><code>timeout</code> is the per-attempt deadline. <code>retry</code> is the same retry block you already use on <code>shell</code> and <code>atmos</code> steps — and the http step is HTTP-aware about what's worth retrying:</p>
<ul>
<li class=""><strong>Retried by default:</strong> transport/network errors, <code>5xx</code>, and <code>429 Too Many Requests</code>.</li>
<li class=""><strong>Fail fast:</strong> other <code>4xx</code> responses (a <code>404</code> won't burn through five attempts).</li>
<li class=""><strong>Your call:</strong> <code>retry.conditions</code> regexes (matched against <code>"&lt;status&gt; &lt;body&gt;"</code>) let you retry additional cases, and <code>expect.status</code> / <code>expect.response</code> define exactly what counts as success.</li>
</ul>
<p>This makes the step a natural fit for polling, too — <code>GET</code> a health endpoint and retry until the body matches:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> health</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> http</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">url</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)">"{{ .env.HEALTH_URL }}"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">expect</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">status</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 number" style="color:rgb(247, 140, 108)">200</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">response</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain"> /"status"\s</span><span class="token important">*:\s*"(ok|healthy)"/</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">retry</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">max_attempts</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)">10</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">backoff_strategy</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> constant</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">initial_delay</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> 2s</span></span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="using-the-response">Using the Response<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type#using-the-response" class="hash-link" aria-label="Direct link to Using the Response" title="Direct link to Using the Response" translate="no">​</a></h2>
<p>The response body becomes the step's value, and useful details land in metadata:</p>
<ul>
<li class=""><code>{{ .steps.&lt;name&gt;.value }}</code> — the response body</li>
<li class=""><code>{{ .steps.&lt;name&gt;.metadata.status_code }}</code> — the numeric status code</li>
<li class=""><code>{{ .steps.&lt;name&gt;.metadata.status }}</code> — the status text (e.g., <code>200 OK</code>)</li>
<li class=""><code>{{ .steps.&lt;name&gt;.metadata.response_headers }}</code> — response headers</li>
</ul>
<p>Because it's a regular step type, <code>http</code> works in <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/commands">custom commands</a> exactly as it does in <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows">workflows</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/http-step-type#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows">workflow step types</a> and <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/cli/configuration/commands">custom commands</a> docs for the full reference, and the <code>examples/http-webhooks</code> example to try it out. Questions or ideas? Join us 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[Parallel and Matrix Steps for Atmos Workflows]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps</guid>
            <pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Atmos workflows can now run independent work concurrently with first-class parallel and matrix control steps. Add dependency-aware fan-out, readable grouped or live-prefixed output, and explicit failure behavior directly to your workflow YAML.]]></description>
            <content:encoded><![CDATA[<p>Atmos workflows can now run independent work concurrently with first-class <code>parallel</code> and <code>matrix</code> control steps. Add dependency-aware fan-out, readable grouped or live-prefixed output, and explicit failure behavior directly to your workflow YAML.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Workflows are where teams encode the operational knowledge that should not live in someone's shell history: run the checks, build the thing, deploy the dependencies, then summarize what happened.</p>
<p>Until now, those steps were sequential. That was easy to reason about, but it meant a workflow with four independent checks took the sum of all four runtimes. The usual workaround was to drop into shell scripts, background jobs, <code>wait</code>, temp files, and hand-rolled log prefixes. That works until it doesn't:</p>
<ul>
<li class="">Output from concurrent commands interleaves into unreadable logs.</li>
<li class="">Failure behavior is implicit and different in every script.</li>
<li class="">Dependency relationships are hidden in shell control flow.</li>
<li class="">Local workflows and CI matrices drift apart.</li>
</ul>
<p>Infrastructure automation should not force you to choose between "simple but slow" and "fast but fragile."</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="whats-new">What's New<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#whats-new" class="hash-link" aria-label="Direct link to What's New" title="Direct link to What's New" translate="no">​</a></h2>
<p>Atmos now supports two new workflow control step types:</p>
<ul>
<li class=""><strong><code>parallel</code></strong> runs sibling steps concurrently.</li>
<li class=""><strong><code>matrix</code></strong> expands literal axes and schedules the generated child steps.</li>
</ul>
<p>Both support:</p>
<ul>
<li class=""><strong><code>needs</code></strong> dependencies between sibling steps.</li>
<li class=""><strong><code>max_concurrency</code></strong> to bound parallelism.</li>
<li class=""><strong>Failure modes</strong>: <code>wait_all</code>, <code>fail_fast</code>, and <code>best_effort</code>.</li>
<li class=""><strong>Output modes</strong>: <code>grouped</code>, <code>prefixed</code>, and <code>none</code>.</li>
<li class=""><strong>Parent-owned summaries</strong> with success, failed, skipped, and canceled counts.</li>
</ul>
<p>This is built into the workflow engine, so the orchestration rules are visible in the workflow file instead of buried in shell glue.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="parallel-checks">Parallel Checks<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#parallel-checks" class="hash-link" aria-label="Direct link to Parallel Checks" title="Direct link to Parallel Checks" translate="no">​</a></h2>
<p>Run independent checks together, then run a dependent summary step only after both prerequisites succeed:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_lKDy">stacks/workflows/checks.yaml</div><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">workflows</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">checks</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> checks</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> parallel</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">max_concurrency</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)">4</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">fail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> wait_all</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">output</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> grouped</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">order</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> completion</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">show_summary</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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">prefix</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)">"{{ .step.name }}"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> lint</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> shell</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> make lint</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> test</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> shell</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> make test</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> summarize</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> shell</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">needs</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">lint</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> ./scripts/summary.sh</span></span><br></span></code></pre></div></div>
<p>The workflow is still declarative: <code>summarize</code> says what it needs, not how to poll for it. Atmos schedules everything else.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="matrix-fan-out">Matrix Fan-Out<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#matrix-fan-out" class="hash-link" aria-label="Direct link to Matrix Fan-Out" title="Direct link to Matrix Fan-Out" translate="no">​</a></h2>
<p>Use <code>matrix</code> when the same step should run across combinations:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockTitle_lKDy">stacks/workflows/test-matrix.yaml</div><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">workflows</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">test-matrix</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> test</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">matrix</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> matrix</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">max_concurrency</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)">3</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">output</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> grouped</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">order</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> definition</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">os</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">linux</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> darwin</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">          </span><span class="token key atrule">go</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 string" style="color:rgb(173, 219, 103)">"1.22"</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)">"1.23"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> test</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> shell</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">            </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> make test OS=</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"> .matrix.os </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"> GO_VERSION=</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"> .matrix.go </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></span></code></pre></div></div>
<p>That gives you CI-style fan-out without requiring the workflow to become a GitHub Actions-only construct. The same workflow can run locally, in CI, or inside a larger operational runbook.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="output-that-stays-readable">Output That Stays Readable<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#output-that-stays-readable" class="hash-link" aria-label="Direct link to Output That Stays Readable" title="Direct link to Output That Stays Readable" translate="no">​</a></h2>
<p>Concurrent output is only useful if humans can read it. The control step owns child output rendering:</p>
<ul>
<li class=""><code>grouped</code> captures child stdout/stderr and prints labeled blocks.</li>
<li class=""><code>prefixed</code> streams live output with complete-line prefixes.</li>
<li class=""><code>none</code> suppresses terminal output while still capturing metadata.</li>
</ul>
<p>For live logs:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">output</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> prefixed</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">prefix</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)">"{{ .step.name }}"</span></span><br></span></code></pre></div></div>
<p>Example output:</p>
<div class="language-text codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-text codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span class="token-line" style="color:#d6deeb"><span class="token plain">[lint] checking formatting</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">[test] running unit tests</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">[lint] passed</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">[test] passed</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">[checks] summary: 2 succeeded, 0 failed, 0 skipped, 0 canceled</span><br></span></code></pre></div></div>
<p>The summary uses the same Atmos UI formatter as other command output, so success, warning, and failure states are immediately visible.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="explicit-failure-semantics">Explicit Failure Semantics<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#explicit-failure-semantics" class="hash-link" aria-label="Direct link to Explicit Failure Semantics" title="Direct link to Explicit Failure Semantics" translate="no">​</a></h2>
<p>Parallel work needs a clear answer to "what happens when one branch fails?"</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">fail</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> wait_all        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># wait_all | fail_fast | best_effort</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">max_failures</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)">2</span><span class="token plain">       </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># 0 means unlimited</span></span><br></span></code></pre></div></div>
<ul>
<li class=""><strong><code>wait_all</code></strong> lets independent ready/running branches continue, skips dependents of failed children, and fails the parent after schedulable work settles.</li>
<li class=""><strong><code>fail_fast</code></strong> cancels pending and running siblings once the failure threshold is reached.</li>
<li class=""><strong><code>best_effort</code></strong> records failures and skips dependents, but lets the parent succeed unless the control step itself is invalid.</li>
</ul>
<p>That makes failure behavior reviewable. Operators can choose fast feedback for checks, complete collection for reports, or best-effort fan-out where partial success is still useful.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="guardrails-for-v1">Guardrails for v1<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#guardrails-for-v1" class="hash-link" aria-label="Direct link to Guardrails for v1" title="Direct link to Guardrails for v1" translate="no">​</a></h2>
<p>The first version intentionally allows only non-interactive child steps inside concurrent groups:</p>
<ul>
<li class=""><code>shell</code></li>
<li class=""><code>atmos</code></li>
<li class=""><code>sleep</code></li>
</ul>
<p>Interactive prompts, terminal-owning renderers, file editors, pagers, spinners, environment-mutating steps, and <code>exec</code> are kept outside concurrent groups for now. That boundary is deliberate: concurrent workflows should not start by letting multiple children fight over the same terminal.</p>
<p>You can still use rich UI steps before or after a <code>parallel</code> or <code>matrix</code> control step to frame the workflow, show tables, render markdown, or summarize the result.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>Parallel and matrix workflow steps make Atmos workflows feel like real orchestration instead of a sequential macro runner.</p>
<ul>
<li class="">Local runbooks get faster without becoming bash concurrency puzzles.</li>
<li class="">CI and local automation can share the same workflow definition.</li>
<li class="">Dependency relationships are visible as <code>needs</code>, not hidden in scripts.</li>
<li class="">Output remains readable by default.</li>
<li class="">Failure behavior is part of the contract.</li>
<li class="">Matrix fan-out is available anywhere Atmos runs, not only inside a CI provider.</li>
</ul>
<p>This is especially useful for validation workflows, multi-component smoke tests, cross-platform checks, reporting jobs, and any operational task where several independent branches can run safely at the same time.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="try-it">Try It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-steps#try-it" class="hash-link" aria-label="Direct link to Try It" title="Direct link to Try It" translate="no">​</a></h2>
<p>This PR includes a runnable example:</p>
<div class="language-shell codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-shell codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span 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/parallel-steps</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow checks </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> parallel</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow prefixed </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> parallel</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow matrix </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> parallel</span><br></span></code></pre></div></div>
<p>Start with validation and reporting workflows first. They usually have the safest fan-out shape: independent checks, obvious dependencies, and low risk if one branch fails.</p>
<p>For the full field reference, see the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/parallel"><code>parallel</code></a> and <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/workflows/steps/type/matrix"><code>matrix</code></a> step type documentation.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/parallel-matrix-workflow-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 the new control steps on real workflows and tell us where the v1 guardrails feel too strict or exactly right. We're especially interested in feedback on output modes, failure semantics, and which additional non-interactive step types should be allowed inside concurrent groups next.</p>]]></content:encoded>
            <category>Feature</category>
            <category>DX</category>
        </item>
        <item>
            <title><![CDATA[Atmos Can Finally Speak for Itself]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step</guid>
            <pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Workflows now have a say step type that speaks a message out loud using text-to-speech — an audible cue for when a long-running workflow finishes or needs your attention, even after you've switched to another window. When no speech engine is available (or you're in CI), it degrades gracefully and prints the message instead, so the same workflow works everywhere.]]></description>
            <content:encoded><![CDATA[<p>Workflows now have a <code>say</code> step type that speaks a message out loud using text-to-speech — an audible cue for when a long-running workflow finishes or needs your attention, even after you've switched to another window. When no speech engine is available (or you're in CI), it degrades gracefully and prints the message instead, so the same workflow works everywhere.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step#the-problem" class="hash-link" aria-label="Direct link to The Problem" title="Direct link to The Problem" translate="no">​</a></h2>
<p>Long-running workflows — a multi-stack <code>terraform apply</code>, a build pipeline, a vendoring sweep — often outlast your attention. You kick one off, switch to another window, and forget about it. The existing <code>alert</code> step rings the terminal bell, but a bell doesn't tell you <em>what</em> happened, and it's easy to miss.</p>
<p>People have worked around this by shelling out to <code>say</code> on macOS, but that breaks the moment a teammate runs the same workflow on Linux or in CI, where <code>say</code> doesn't exist.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-solution">The Solution<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step#the-solution" class="hash-link" aria-label="Direct link to The Solution" title="Direct link to The Solution" translate="no">​</a></h2>
<p>A new <code>say</code> step speaks its <code>content</code> aloud and works across platforms:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">workflows</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> apply</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> atmos</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> terraform apply vpc</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain" style="display:inline-block"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> notify</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> say</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">content</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)">"Deployment to {{ .steps.env.value }} is complete"</span></span><br></span></code></pre></div></div>
<p>Under the hood it detects an available speech engine per OS — macOS <code>say</code>, Linux <code>spd-say</code>/<code>espeak</code>/<code>espeak-ng</code>, and Windows PowerShell's <code>System.Speech</code> — so the same workflow runs unchanged on any machine.</p>
<h3 class="anchor anchorTargetStickyNavbar_SsMH" id="pick-a-voice-font-family-style">Pick a voice, font-family style<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step#pick-a-voice-font-family-style" class="hash-link" aria-label="Direct link to Pick a voice, font-family style" title="Direct link to Pick a voice, font-family style" translate="no">​</a></h3>
<p>Voice names are platform-specific, so <code>voice</code> is an <strong>ordered list</strong> — just like a CSS <code>font-family</code> stack. The first voice actually installed on the host wins; if none match, the engine's default is used:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> notify</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> say</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">content</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)">"Build finished"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">voice</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">Samantha</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> Microsoft Zira</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> en</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">   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># macOS, Windows, Linux</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">rate</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> normal                                </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># slow | normal | fast</span></span><br></span></code></pre></div></div>
<p>That single list resolves to Samantha on macOS, Zira on Windows, and <code>en-us</code> (espeak) on Linux — no per-OS branching required.</p>
<h3 class="anchor anchorTargetStickyNavbar_SsMH" id="graceful-degradation-you-control">Graceful degradation you control<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step#graceful-degradation-you-control" class="hash-link" aria-label="Direct link to Graceful degradation you control" title="Direct link to Graceful degradation you control" translate="no">​</a></h3>
<p><code>say</code> only makes sense on an interactive workstation, so it never fails a workflow and never hangs CI. The <code>print</code> field decides what happens when speech is (or isn't) available:</p>
<ul>
<li class=""><code>fallback</code> (default) — speak when possible; otherwise print the message as a Markdown blockquote so the information is never lost.</li>
<li class=""><code>always</code> — always print the blockquote <strong>and</strong> also speak when possible.</li>
<li class=""><code>never</code> — speak when possible; otherwise stay silent.</li>
</ul>
<p>In CI, or on any host with no speech engine, <code>say</code> automatically prints instead of speaking — so you can leave <code>say</code> steps in workflows that run both locally and in pipelines.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="try-it">Try It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step#try-it" class="hash-link" aria-label="Direct link to Try It" title="Direct link to Try It" translate="no">​</a></h2>
<p>A new <code>examples/say-something/</code> example demonstrates voices, rates, print policies, and composing <code>say</code> into a build pipeline:</p>
<div class="language-bash codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-bash codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B"><span 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/say-something</span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow notify </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> say        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># speak a completion message</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow voices </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> say        </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># cross-platform voice stack</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow print-modes </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> say   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># the three print policies</span><span class="token plain"></span><br></span><span class="token-line" style="color:#d6deeb"><span class="token plain">atmos workflow pipeline </span><span class="token parameter variable" style="color:rgb(214, 222, 235)">-f</span><span class="token plain"> say      </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># announce each build milestone</span><br></span></code></pre></div></div>
<p>The cross-platform plumbing lives in a reusable <code>pkg/say</code> package, mirroring the existing browser-opening abstraction, so other parts of Atmos can adopt audible notifications too.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/say-step#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Add a <code>say</code> step to the end of your slowest workflows and let Atmos tell you when it's done. Feedback and ideas are welcome on <a href="https://github.com/cloudposse/atmos" target="_blank" rel="noopener noreferrer" class="">GitHub</a>.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Introducing the Atmos Media Kit]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-media-kit</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-media-kit</guid>
            <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The official Atmos Media Kit is now available with downloadable logo assets, usage guidance, brand colors, and product language for teams writing about or building integrations with Atmos.]]></description>
            <content:encoded><![CDATA[<p>The official <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/media-kit">Atmos Media Kit</a> is now available with downloadable logo assets, usage guidance, brand colors, and product language for teams writing about or building integrations with Atmos.</p>
<div class="mediaKitBadgeGrid" aria-label="Atmos media kit logo previews"><a class="mediaKitBadgeCard" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/media-kit"><img src="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/img/atmos-logo-gradient-on-light.svg" alt="Atmos logo" class="themedComponent_W3rT themedComponent--light_ZcOx"><img src="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/img/atmos-logo-gradient.svg" alt="Atmos logo" class="themedComponent_W3rT themedComponent--dark_qFop"></a><a class="mediaKitBadgeCard" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/media-kit"><img src="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/img/powered-by-atmos-gradient-on-light.svg" alt="Powered by Atmos CI badge" class="themedComponent_W3rT themedComponent--light_ZcOx"><img src="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/img/powered-by-atmos-gradient.svg" alt="Powered by Atmos CI badge" class="themedComponent_W3rT themedComponent--dark_qFop"></a><a class="mediaKitBadgeCard" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/media-kit"><img src="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/img/atmos-ci-gradient-on-light.svg" alt="Atmos CI logo" class="themedComponent_W3rT themedComponent--light_ZcOx"><img src="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/img/atmos-ci-gradient.svg" alt="Atmos CI logo" class="themedComponent_W3rT themedComponent--dark_qFop"></a></div>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="whats-included">What's Included<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-media-kit#whats-included" class="hash-link" aria-label="Direct link to What's Included" title="Direct link to What's Included" translate="no">​</a></h2>
<p>The media kit gives contributors, partners, and community teams a single place to find approved Atmos brand assets:</p>
<ul>
<li class=""><strong>Logo downloads</strong> for the Atmos mark and wordmark, including light and dark variants.</li>
<li class=""><strong>Powered by Atmos badges</strong> for projects that want to show they run on Atmos.</li>
<li class=""><strong>Brand colors</strong> sourced from the official Atmos logo artwork, plus separate animated accent colors for the web treatment.</li>
<li class=""><strong>Usage guidelines</strong> for sizing, clear space, backgrounds, and preserving the original logo proportions.</li>
<li class=""><strong>Product facts and links</strong> for consistent references to Atmos, Cloud Posse, GitHub, and the community.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-media-kit#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>Atmos shows up in documentation sites, internal developer portals, integration pages, conference decks, and open source READMEs. Without a canonical source, teams either copied assets from the website or recreated them by hand.</p>
<p>The media kit makes those references easier to keep consistent. Use the provided files directly, keep the canonical green logo palette for brand-forward placements, and reserve the animated accent colors for the web treatment.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-the-assets">Get the Assets<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/atmos-media-kit#get-the-assets" class="hash-link" aria-label="Direct link to Get the Assets" title="Direct link to Get the Assets" translate="no">​</a></h2>
<p>Visit the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/media-kit">Atmos Media Kit</a> to browse the assets or download the complete ZIP.</p>]]></content:encoded>
            <category>Documentation</category>
        </item>
        <item>
            <title><![CDATA[Document Components with metadata.description]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/component-metadata-description</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/component-metadata-description</guid>
            <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Component metadata now supports an optional description field, so you can document what a component is for right next to its configuration. Atmos preserves description as component metadata — it does not change how the component is processed, planned, or applied.]]></description>
            <content:encoded><![CDATA[<p>Component <code>metadata</code> now supports an optional <code>description</code> field, so you can document what a component is for right next to its configuration. Atmos preserves <code>description</code> as component metadata — it does not change how the component is processed, planned, or applied.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="what-changed">What Changed<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/component-metadata-description#what-changed" class="hash-link" aria-label="Direct link to What Changed" title="Direct link to What Changed" translate="no">​</a></h2>
<p>You can now add a human-readable <code>description</code> to any component's <code>metadata</code> section:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">vpc-prod</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> vpc</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> </span><span class="token string" style="color:rgb(173, 219, 103)">"Production VPC with public and private subnets"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">environment</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> prod</span></span><br></span></code></pre></div></div>
<p>The Atmos manifest JSON schema was updated to allow the new field, so editors with schema support get auto-completion and validation for <code>metadata.description</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="why-this-matters">Why This Matters<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/component-metadata-description#why-this-matters" class="hash-link" aria-label="Direct link to Why This Matters" title="Direct link to Why This Matters" translate="no">​</a></h2>
<p>Stacks often define many components that point at the same Terraform root module with different configurations. A short <code>description</code> makes it obvious at a glance what each one is for, without forcing readers to reverse-engineer intent from variables. It keeps documentation co-located with the configuration it describes.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/component-metadata-description#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 <code>description</code> under any component's <code>metadata</code>:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">vpc-isolated</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">      </span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> vpc</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> </span><span class="token string" style="color:rgb(173, 219, 103)">"Isolated VPC without an internet gateway"</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">        </span><span class="token key atrule">vpc_cidr</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)">"10.1.0.0/16"</span></span><br></span></code></pre></div></div>
<p>The field is purely informational and additive — existing stacks are unaffected. See the <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/components/component-metadata">component metadata reference</a> for details.</p>]]></content:encoded>
            <category>Feature</category>
        </item>
        <item>
            <title><![CDATA[Scanner Findings as Inline PR Annotations and Code Scanning Alerts]]></title>
            <link>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning</link>
            <guid>https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning</guid>
            <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Security scanner hooks (Checkov, Trivy, KICS) can now surface findings as inline GitHub annotations on the pull request diff and upload them to GitHub Code Scanning (the Security tab) — natively, with no github/codeql-action step. Custom hooks running any SARIF-emitting tool get the same treatment by adding format: sarif.]]></description>
            <content:encoded><![CDATA[<p>Security scanner hooks (Checkov, Trivy, KICS) can now surface findings as <strong>inline GitHub annotations</strong> on the pull request diff and upload them to <strong>GitHub Code Scanning</strong> (the Security tab) — natively, with no <code>github/codeql-action</code> step. Custom hooks running any SARIF-emitting tool get the same treatment by adding <code>format: sarif</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-problem">The Problem<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning#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-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/hooks">Scanner hooks</a> already render a findings summary to the terminal, the job step summary, and the Atmos Pro run page. But the two richest GitHub surfaces were missing:</p>
<ul>
<li class=""><strong>Inline annotations</strong> — findings pinned to the exact file and line on the PR diff, where reviewers actually look.</li>
<li class=""><strong>Code Scanning alerts</strong> — tracked findings in the Security tab with an open → fixed lifecycle across runs.</li>
</ul>
<p>The findings were right there in the parsed SARIF; they just had nowhere to go.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="the-solution">The Solution<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning#the-solution" class="hash-link" aria-label="Direct link to The Solution" title="Direct link to The Solution" translate="no">​</a></h2>
<p>Three independent CI reporting outputs, all under <code>ci:</code> and gated by the <code>ci.enabled</code> master switch:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token key atrule">ci</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">summary</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 class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># markdown report in the job step summary  (default: on)</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">annotations</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 class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># inline ::error/::warning on the PR diff   (default: on)</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">results</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 class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># upload SARIF to GitHub Code Scanning      (default: off)</span></span><br></span></code></pre></div></div>
<ul>
<li class=""><strong><code>ci.annotations</code></strong> turns each finding into a GitHub <code>::error</code>/<code>::warning</code> annotation anchored at its file and line. This is the <strong>non-Code-Scanning</strong> path — it needs no GitHub Advanced Security, so it works on any repo.</li>
<li class=""><strong><code>ci.results</code></strong> uploads the raw SARIF to Code Scanning. Atmos derives the analysis category from the scan target automatically, so a <code>terraform plan</code> across many components tracks each as its own analysis instead of overwriting.</li>
</ul>
<p>Both are implemented as <strong>native CI provider capabilities</strong> (extending the same provider interface that already powers job summaries, check runs, and PR comments), not by shelling out to a third-party action. Everything is best-effort: a reporting failure never fails your hook or your plan, and outside CI it all no-ops.</p>
<p>Because both outputs can attach line-level feedback to a pull request, enable
both only when you want both the lightweight Actions annotation and the tracked
Code Scanning alert. If duplicate comments on the same lines are noisy for your
team, enable only one of <code>ci.annotations</code> or <code>ci.results</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_SsMH" id="built-in-and-custom-tools">Built-in and custom tools<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning#built-in-and-custom-tools" class="hash-link" aria-label="Direct link to Built-in and custom tools" title="Direct link to Built-in and custom tools" translate="no">​</a></h3>
<p>The three SARIF scanners — <code>checkov</code>, <code>trivy</code>, <code>kics</code> — participate automatically. Any <strong>custom</strong> tool does too, just by declaring <code>format: sarif</code>:</p>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">tfsec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">events</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">after</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">terraform</span><span class="token punctuation" style="color:rgb(199, 146, 234)">-</span><span class="token plain">plan</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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"> command</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> tfsec</span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">args</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 string" style="color:rgb(173, 219, 103)">"--format"</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)">"sarif"</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)">"--out"</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)">"$ATMOS_OUTPUT_FILE"</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)">"$ATMOS_COMPONENT_PATH"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"></span></span><br></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">    </span><span class="token key atrule">format</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> sarif</span></span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SsMH" id="permissions">Permissions<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning#permissions" class="hash-link" aria-label="Direct link to Permissions" title="Direct link to Permissions" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_E9g6 theme-code-block" style="--prism-color:#d6deeb;--prism-background-color:#011627"><div class="codeBlockContent_vFnX"><pre tabindex="0" class="prism-code language-yaml codeBlock_wj5q thin-scrollbar" style="color:#d6deeb;background-color:#011627"><code class="codeBlockLines_hX2B codeBlockLinesWithNumbering_VNBQ" style="counter-reset:line-count 0"><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><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></span><span class="token-line codeLine_DeN_" style="color:#d6deeb"><span class="codeLineNumber_xsMR"></span><span class="codeLineContent_j0xu"><span class="token plain">  </span><span class="token key atrule">security-events</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> write   </span><span class="token comment" style="color:rgb(99, 119, 119);font-style:italic"># only needed for ci.results (SARIF upload)</span></span><br></span></code></pre></div></div>
<p>Annotations and the summary need no special permissions and no paid add-on — they work on any repo. <code>ci.results</code> uploads to Code Scanning, which needs <code>security-events: write</code> and, on <strong>private repos</strong>, <strong>GitHub Advanced Security — a paid add-on that GitHub licenses per active committer</strong>. Code Scanning is free on public repos. That's exactly why annotations default on and <code>ci.results</code> defaults off — everyone gets inline feedback for free, and only GHAS subscribers opt into the Security-tab integration.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="how-to-use-it">How to Use It<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning#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>See <a class="" href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/stacks/hooks#ci-reporting">CI Reporting</a> in the hooks documentation for the full reference.</p>
<h2 class="anchor anchorTargetStickyNavbar_SsMH" id="get-involved">Get Involved<a href="https://pr-2288.atmos-docs.ue2.dev.plat.cloudposse.org/changelog/scanner-annotations-and-code-scanning#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved" translate="no">​</a></h2>
<p>Want findings routed somewhere else — GitLab security dashboards, threaded PR review comments? <a href="https://github.com/cloudposse/atmos/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a> or join us in the <a href="https://cloudposse.com/slack" target="_blank" rel="noopener noreferrer" class="">Atmos community</a>.</p>]]></content:encoded>
            <category>Enhancement</category>
            <category>Security</category>
        </item>
    </channel>
</rss>