Skip to content

Update catalogs.md - #281

Merged
Andriy Knysh (aknysh) merged 1 commit into
masterfrom
johncblandii-patch-1
Dec 21, 2022
Merged

Andriy Knysh (aknysh) merged 1 commit into
masterfrom
johncblandii-patch-1

Conversation

@johncblandii

@johncblandii John C. Bland II (johncblandii) commented Dec 21, 2022 •

Copy link
Copy Markdown
Contributor

what

  • fix misspelling in docs

why

  • misspelling in docs

references

N/A

@aknysh Andriy Knysh (aknysh) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aknysh
Andriy Knysh (aknysh) merged commit d6fd12f into master Dec 21, 2022
@aknysh
Andriy Knysh (aknysh) deleted the johncblandii-patch-1 branch December 21, 2022 16:36
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Sep 2, 2026
…st, postcss-selector-parser)

Minor-version bumps only, compliant with dependabot.yml's major-bump
ignore policy:
- google.golang.org/grpc v1.82.1 -> v1.83.1 (GHSA-vp52-pcj8-j9qc,
  alert #279): HTTP/2 DATA frame fragmentation memory exhaustion.
- browserslist -> ^4.28.7 via pnpm override (GHSA-c83g-rgw3-j3cx /
  GHSA-73wf-gq98-2v4g, alerts #281/#282): unbounded cache growth and a
  crash via untrusted browserslist-stats.json.
- postcss-selector-parser@^6 -> ^6.1.3 via pnpm override
  (GHSA-w9m9-85wc-3x92, alert #280): uncontrolled AST recursion DoS.
  This is a separate transitive 6.x line from the 7.x one already
  pinned; both now carry overrides.

Regenerated NOTICE (go-licenses) and website/pnpm-lock.yaml
(pnpm install --lockfile-only) for the version bumps; verified
`pnpm run build` still succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Sep 2, 2026
- google.golang.org/grpc: bump v1.82.1 -> v1.83.2, fixing GHSA-vp52-pcj8-j9qc
  (heap memory exhaustion via HTTP/2 DATA frame fragmentation, <= 1.83.0).
- browserslist: pin transitive dependency to ^4.28.7 via pnpm.overrides,
  fixing GHSA-c83g-rgw3-j3cx (unbounded memory growth) and
  GHSA-73wf-gq98-2v4g (uncaught crash via untrusted stats file), both
  affecting <= 4.28.6.

Alert #280 (postcss-selector-parser) was already fixed on this branch by
an earlier commit; it stays "open" on GitHub only because it's scoped to
the default branch's dependency graph and will auto-close once this
branch merges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Sep 2, 2026
Add pnpm.overrides for browserslist (>=4.28.7) and
postcss-selector-parser (>=6.1.3) to pull in patched transitive
versions, resolving:

- GHSA (#282) Browserslist unbounded memory growth via distinct query
  results, leading to eventual OOM
- GHSA (#281) Browserslist uncaught crash / prototype write via
  untrusted browserslist-stats.json custom stats
- GHSA (#280) postcss-selector-parser denial of service through
  uncontrolled AST recursion

Both are patch-level bumps within their current major version, so
they're not blocked by dependabot.yml's semver-major ignore policy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Sep 2, 2026
Pin transitive browserslist and postcss-selector-parser via
pnpm.overrides to patched versions:

- browserslist <= 4.28.6 (GHSA alerts #281, #282, high) -> ^4.28.7
- postcss-selector-parser >=6.1.0 <6.1.3 (alert #280, low) -> ^6.1.3

Both are patch-level bumps within dependabot.yml's semver-major
ignore policy. The postcss-selector-parser override is scoped to the
^6 line only (via postcss-calc's ^6.0.11 request) so it doesn't touch
the separate, already-unaffected ^7.0.0 line used elsewhere.
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Sep 2, 2026
- golang.org/x/crypto v0.55.0 -> v0.56.0 (go get + go mod tidy), fixing two
  govulncheck alerts (GO-2026-6354, GO-2026-6355): a malicious SSH peer could
  deadlock a connection via crafted channel messages
  (golang.org/x/crypto/ssh). No direct callers in this repo beyond
  pkg/store/providers/github_actions_client.go; verified via go build and
  pkg/store/... tests.
- website pnpm override: fast-uri@^3 -> ^3.1.6 (patched; published 10 days
  ago, clears this repo's 7-day minimum-release-age cooldown).
- regenerate NOTICE to reflect the x/crypto bump.

qs (Dependabot #283/#284, patched at 6.16.0) is intentionally NOT bumped:
6.16.0 was published 4 days ago, still inside website/.npmrc's 7-day
minimum-release-age cooldown -- forcing it in via
minimumReleaseAgeExclude would defeat the cooldown's purpose. Will pick it
up once it clears.

browserslist (#281/#282) and postcss-selector-parser (#280) are already
fixed on this branch from an earlier commit; GitHub just hasn't re-scanned
yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Sep 4, 2026
…st, postcss-selector-parser)

Minor-version bumps only, compliant with dependabot.yml's major-bump
ignore policy:
- google.golang.org/grpc v1.82.1 -> v1.83.1 (GHSA-vp52-pcj8-j9qc,
  alert #279): HTTP/2 DATA frame fragmentation memory exhaustion.
- browserslist -> ^4.28.7 via pnpm override (GHSA-c83g-rgw3-j3cx /
  GHSA-73wf-gq98-2v4g, alerts #281/#282): unbounded cache growth and a
  crash via untrusted browserslist-stats.json.
- postcss-selector-parser@^6 -> ^6.1.3 via pnpm override
  (GHSA-w9m9-85wc-3x92, alert #280): uncontrolled AST recursion DoS.
  This is a separate transitive 6.x line from the 7.x one already
  pinned; both now carry overrides.

Regenerated NOTICE (go-licenses) and website/pnpm-lock.yaml
(pnpm install --lockfile-only) for the version bumps; verified
`pnpm run build` still succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
zack-is-cool pushed a commit to zack-is-cool/atmos that referenced this pull request Sep 8, 2026
…loudposse#2987)

* docs: document GOAWAY provider-registry retry scenario

Extend the component retry docs with the HTTP/2 GOAWAY provider-registry
failure mode and cross-link with the terraform cache docs, so users know
`retry:` (not the registry cache) is what recovers a batch of components
that all hit a transient registry connectivity blip at once.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: rewrite GOAWAY retry docs in ASD-STE100 style

Simplify the new provider-registry-failure prose from the previous
commit into short, active, single-idea sentences per ASD-STE100
conventions, with no loss of information.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: add retry-policy mixin use-case, fix stale mixins link

- Add a "Mixins for Retry Policies" use-case to howto/mixins.mdx,
  showing how overrides.retry shares one retry policy across
  components that don't have a common base component.
- Fix howto/mixins.mdx's "Learn Design Pattern" link: it pointed to
  /design-patterns/component-catalog/with-mixins, which was
  repurposed into the Component Archetypes page and explicitly says
  "This is NOT Mixins". Point it at the actual mixins design-pattern
  page instead.
- Document `retry` as a supported overrides.* field in
  component-overrides.mdx (already implemented in
  internal/exec/stack_processor_process_stacks_helpers_overrides.go
  but undocumented).
- Cross-link retry.mdx <-> howto/mixins.mdx and add a reciprocal link
  from the mixins design-pattern page back to component retry and
  component overrides.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test: cover overrides.retry end-to-end via the mixin-overrides fixture

Extend TestDescribeComponentWithOverridesSection and the
atmos-overrides-section fixture with a retry block, proving the exact
scenario documented for sharing a retry policy via a mixin: a
`terraform.overrides.retry` block in an imported file applies to
components imported after it (test3), and correctly does not apply
when imported after the component (test2) — matching the existing
file-scoping behavior already proven for `overrides.vars`.

This closes the gap between the unit-level merge-precedence tests in
stack_processor_merge_test.go (which prove overrides wins in
isolation) and an end-to-end proof that overrides.retry flows through
real import resolution the same way overrides.vars does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(fix): qualify retry batch-recovery claim in retry.mdx

"Any of the three lets the whole batch recover on its own" overstated
the guarantee. Each subprocess retry loop is independent and bounded
by its own max_attempts/max_elapsed_time (already documented in "How
it works"), so a component whose registry outage outlasts its own
budget still fails even with retry configured. Clarify that recovery
is per-component and budget-bound, and that a long enough outage can
still fail part or all of a batch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: wire overrides.provision into component overrides merge

`provision` is a real per-component section (workdir/target delivery
settings for helmfile/kubernetes/helm/terraform/packer components) but
was never extracted by processComponentOverrides, so overrides.provision
silently no-op'd instead of erroring or applying. Wire it in the same
way retry was: extract it (gated by supportsSourceProvision, matching
the merge's existing gate), add a dedicated sentinel error for a
malformed value, and merge it in as the highest-precedence layer
(global -> base -> component -> overrides), consistent with every
other overrides.* field.

Document `provision` in the "What You Can Override" list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(test): widen shrunk kubernetesReadyTimeout to stop CI flake

TestManager_WaitKubernetesReady_RetriesUntilReady failed on the
windows CI shard: "missing call(s) to *emulator.MockRuntime.Exec" for
the retry loop's second attempt. Root cause is test timing, not a
product bug — waitKubernetesReady correctly bounds by deadline, but
the test's 50ms shrunk kubernetesReadyTimeout has to survive two real
gomock-backed attempts, and the failing run took 0.31s total (300ms+
for a single mocked attempt is plausible under CI scheduler
contention), so the deadline expired before the loop's second
iteration ever ran.

Widen the shrunk timeout to 2s. Poll interval stays at 1ms, so a
passing run still finishes in low single-digit milliseconds — this
only adds headroom against CI jitter, not requirement laxity. Verified
with `go test -run TestManager_WaitKubernetesReady_RetriesUntilReady
-count=5`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(test): retry copyRepoWithRetry on Windows file-lock errors too

TestDescribeAffectedWithDependentsStackFilterYamlFunctions failed on
the windows CI shard: "The process cannot access the file because
another process has locked a portion of the file" reading a shared
fixture's terraform.tfstate mid-copy.

copyRepoWithRetry already retries when a source file vanishes mid-copy
(git background housekeeping racing the walk), but only checked
os.IsNotExist. It copies the live, shared repo tree, so on Windows
another concurrently running test can legitimately hold one of those
files open at the exact moment this copy walks it -- Windows enforces
mandatory file locking far more strictly than Unix, so the read fails
outright instead of racing cleanly. Same class of issue already
handled for `git worktree remove` in pkg/git/worktree.go via string
matching on the OS error text; apply the same idiom here.

Added TestIsTransientRepoCopyError covering both this file's known
transient causes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test: convert provision-overrides test to table-driven form

Addresses CodeRabbit review comment on PR cloudposse#2987: the three
provision-override scenarios (valid, non-map error, unsupported
component type) had repeated setup boilerplate. Consolidate into a
single table-driven test, matching the existing convention already
used by the very next function in this file
(TestProcessComponentInheritance).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test: use errors.Is for missing-file detection in copy-retry helper

Addresses CodeRabbit review comment on PR cloudposse#2987: os.IsNotExist does
not reliably unwrap wrapped errors (it predates errors.Is and only
special-cases raw *PathError/*LinkError/*SyscallError). Replace with
errors.Is(err, os.ErrNotExist) in isTransientRepoCopyError so a
wrapped missing-file error from a future otiai10/copy version (or any
other wrapping layer) is still classified as transient.

Converted TestIsTransientRepoCopyError to table-driven form per the
same review comment, and added a case covering a wrapped
os.ErrNotExist to guard against regressing back to os.IsNotExist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(test): classify closed-pipe errors as clean shutdown in session helper

TestRunSessionDefaultsNilOptions failed on Windows CI ("RunSession
with nil opts: exit status 1") well before its context timeout (5.8s
of a 10s budget), so the earlier 3s->10s timeout bump for this test
was treating the wrong symptom -- the child helper process itself was
exiting 1, not timing out.

runAsciicastSessionHelper (the test-binary-as-fake-shell used by
session tests) exited 1 on any stdin read error other than a literal
io.EOF. finishSession's ordinary teardown sends EOT then closes the
input pipe; under Windows CI load that race can surface as
io.ErrClosedPipe (or an "input/output error") instead of a plain
io.EOF, which the helper had never seen before this change. The
codebase already classifies exactly this error set as an expected
clean shutdown for the parent's stdout-read loop via
isExpectedSessionReadError (session.go) -- reuse it here instead of
duplicating a narrower, incorrect check.

Verified with `go test ./pkg/asciicast/... -run TestRunSession
-count=5`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat: support stack-root global retry, like metadata/hooks/provision

`retry:` was the only major component section without a stack-level
global default. metadata, hooks, generate, source, and provision all
support a stack-manifest-root block that cascades into every component
of that type, via a Global*Section layer threaded through
ComponentProcessorOptions and merged first (lowest precedence) in
mergeComponentConfigurations. retry never got the same treatment, so
the only ways to share a retry policy across a stack were per-
component, an abstract base component, or the overrides.retry mixin
trick.

Add a `retry:` stack-manifest-root block, following the exact pattern
already established for global metadata:
- Read and validate `config[retry]` in ProcessStackConfig (must be a
  map; unlike metadata, every RetryConfig field is meaningful at
  global scope, so no field allowlist is needed).
- Thread GlobalComponentRetry through ComponentProcessorOptions and
  wire it into all six built-in component-type constructions
  (terraform, helmfile, packer, ansible, kubernetes, helm).
- Merge it as the new lowest-precedence layer in the retry merge:
  global -> base component -> concrete component -> overrides.
- Also merge it into custom (non-built-in) component types in the
  builtInTypes passthrough loop, mirroring how global metadata is
  merged there.
- Add ErrInvalidGlobalRetrySection and register `retry` as a root
  property in the atmos/manifest and stacks/stack-config JSON schemas
  (reusing the existing #/definitions/retry).

Verified end-to-end with a live build: `atmos describe stacks` shows a
component with no local retry inheriting the global policy, and a
component with a local `max_attempts` override still inheriting
global's `conditions` list (deep-merge, not wholesale replacement).

Note: `atmos describe component`'s JSON/YAML output has its own fixed
key allowlist that has never included `retry` at all -- even a
component's own directly-set retry doesn't show there. Pre-existing,
unrelated to this change; the real execution path
(internal/exec/utils.go's ComponentRetrySection) reads the merged
config directly and is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: document stack-root global retry defaults

Document the new stack-manifest-root retry: block added in the
previous commit:

- retry.mdx: new "Stack-Level Defaults" section with the precedence
  order (stack-root -> base component -> concrete component ->
  overrides) and an example. Updated the "share a retry policy across
  a batch" list from three to four options, leading with the
  stack-root block since it's the simplest for the whole-stack case.
- howto/mixins.mdx: added a tip pointing at the stack-root option for
  readers who only need the retry policy on every component in a
  stack -- the mixin/overrides.retry trick documented there remains
  the right tool when the policy should apply to only part of a stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: add changelog post and roadmap entry for stack-level retry

Required by the pull-request skill for a minor-labeled feature PR.

- website/blog/2026-08-24-stack-level-retry-defaults.mdx: problem-first
  changelog post per the changelog skill's template (Problem/Fix/How to
  Use It/Get Involved), tagged enhancement, authored by osterman.
- website/src/data/roadmap.js: new shipped milestone under the CI/CD
  Simplification initiative (same initiative as the original
  component-level retry milestone), linked to the new changelog slug
  and the retry docs' new #stack-level-defaults anchor. Progress stays
  at 95% (20/21 shipped, same rounded value as before).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(fix): correct retry Inheritance example, document opt-out, broaden scope

Three fixes to the component retry docs found during a field-test pass
on the stack-level retry feature:

- The "## Inheritance" example used `metadata.component: base/network`
  to demonstrate config inheritance, but that key only selects which
  Terraform source a component uses -- it does not inherit config.
  `metadata.inherits: [base/network]` is the correct mechanism; verified
  live that the original example produced retry: null (no inheritance
  at all) while metadata.inherits correctly inherits the base policy.
  Also corrected the same example's claim that `conditions` "is
  appended to" the base under default settings -- default
  list_merge_strategy is replace, so conditions actually replaces
  unless the user opts into list_merge_strategy: append.
- Documented that retry merges as a deep merge like every other
  section, so `retry: {}` does NOT disable an inherited policy (verified
  live), and that `retry: !unset` is rejected outright (tracked
  separately in cloudposse#2994, a general Atmos gap affecting every typed
  section, not retry-specific). The supported opt-out is
  `max_attempts: 1`.
- Broadened the intro/scope language ahead of extending retry execution
  to Helmfile, Packer, and Ansible (previously terraform-only both in
  implementation and in how the docs read).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat: extend component retry to Helmfile, Packer, and Ansible

Component retry (retry.conditions matched against captured subprocess
output, with backoff) was wired up for terraform only, even though the
underlying mechanism was already fully generic -- a field-test pass
found retry.mdx documenting it as applying to "every component in the
stack" via the new stack-level defaults, while helmfile/kubernetes
components in that same stack silently got no protection at all.

Kubernetes and native Helm are out of scope here: they call Go SDKs
directly with no subprocess to capture/pattern-match, and would need a
different (err.Error()-based) retry mechanism. Tracked as a follow-up,
not implemented in this change.

- Export executeShellCommandWithRetry -> ExecuteShellCommandWithRetry
  in internal/exec, so pkg/component/ansible (a different package,
  already importing internal/exec for ExecuteShellCommand) can call it
  directly -- no new abstraction, just visibility.
- Harden it: a caller-supplied stdout/stderr capture option (e.g.
  helmfile's NodeHooks.After buffer) previously got silently replaced
  by retry's own capture buffer when both were configured (last
  ShellCommandOption wins). Now composed via io.MultiWriter so both
  receive the full output. Extracted the composition logic into
  composeRetryCaptureWriters, which also fixed a funlen lint finding.
- Wire Helmfile (internal/exec/helmfile.go), Packer
  (internal/exec/packer.go), and Ansible
  (pkg/component/ansible/executor.go) to the same wrapper terraform
  uses. Extracted each call site into its own small
  execute*CommandWithRetry function (matching the existing
  executeMainTerraformCommand pattern) so retry wiring is directly
  unit-testable without standing up each command's full
  stack-processing preamble or requiring a real binary.
- Bundle the shared (allArgsAndFlags, componentPath, envVars) trio into
  a new retryExecParams struct for the Helmfile/Packer wrappers,
  resolving an argument-limit lint finding; switched all three
  wrappers to a pointer atmosConfig param, resolving a gocritic
  hugeParam finding.
- Extended internal/exec's TestMain (and added one to
  pkg/component/ansible, which had none) so _ATMOS_TEST_EXIT_ONE can
  combine with _ATMOS_TEST_STDOUT/_ATMOS_TEST_STDERR to simulate a
  matching/non-matching transient failure -- lets retry-wiring tests
  use the test binary itself as a fake terraform/helmfile/packer/
  ansible-playbook command, no real binaries needed.
- Tests prove the wiring end-to-end through each real call chain (not
  just the shared helper in isolation): matching errors retry to
  max_attempts, non-matching errors fail fast on the first attempt
  (asserted via an invocation-count file), and helmfile's NodeHooks
  capture keeps receiving output when retry is also active.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: add changelog post and roadmap entry for retry's Helmfile/Packer/Ansible extension

Separate from the stack-level-retry-defaults post -- this is a
distinct capability (which component types retry works for at all,
not how it's scoped within a stack) and deserves its own changelog
entry per the roadmap skill's one-milestone-per-shipped-capability
convention.

Progress stays at 95% (21/22 shipped in the ci-cd initiative, same
rounded value as before).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: address CodeRabbit findings on retry PR

Honors settings.list_merge_strategy for retry.conditions merges (both
the built-in and custom component-type paths), asserts the configured
retry count in the Helmfile/Packer/Ansible matching-error tests instead
of only checking the final error, fails loudly instead of discarding
counter-file I/O errors in the shared test fixtures, adds trailing
periods to two comments, and corrects the retry-precedence wording in
both changelog posts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: scope stack-root retry claims to supported component types

Corrects four remaining "every component" claims (retry.mdx x2, the
retry blog post, and the mixins how-to) flagged by CodeRabbit's follow-up
review — they contradicted the doc's own "Supported component types"
section, which excludes native Kubernetes and native Helm.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: rewrite retry blog posts in plain, consistent language

Rewrites both retry changelog posts in short, active-voice sentences
(ASD-STE100 style) and bumps their dates to today's publish date
(filenames renamed to match).

Also fixes two framing problems flagged as AI-cliche/imprecise:
- stack-level-retry-defaults: drops the "it's not X, it's Y" contrast
  and the "hits all at once" framing in favor of the real point --
  every component in a stack shares a registry, so they all benefit
  from the same retry policy.
- retry-helmfile-packer-ansible: drops the "stack rarely runs one kind
  of component" framing in favor of the actual motivation -- CI is
  inherently flaky, retrying resolves it, and this pattern already
  works well for Terraform, so it now extends to the other component
  types for consistency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: rewrite stack-level-retry-defaults intro in natural prose

Replaces the choppy, fragment-heavy intro with full sentences that
open on the real pain (CI is flaky, retries fix it, this already works
well for Terraform) instead of a single contrived registry-blip
example, then lead into this post's actual news: define the retry
policy once at the stack level instead of per component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [autocommit] formatting fixes

* fix(ci): add missing shellescape override to stop flaky NOTICE diffs

al.essio.dev/pkg/shellescape is a vanity-import-path module that
go-licenses resolves non-deterministically -- some runs return its real
GitHub LICENSE URL, others return "Unknown". That flip-flop is exactly
what the existing REPO_OVERRIDES deterministic-URL mechanism in
generate-notice.sh was built to prevent, but this module was missing
from the list, so the "Review Dependency Licenses" CI check failed
whenever a run's committed NOTICE didn't match that run's resolution.

Adds the override and confirms two consecutive regenerations now
produce an identical NOTICE file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: differentiate the two retry blog post intros

Both posts' intros had converged on the same "CI is flaky, Atmos
already handles this for Terraform, now X" template, making two
distinct features read as copy-paste of each other. Gives each post
its own real hook instead:

- stack-level-retry-defaults: leads with the repetition/DRY problem
  (sharing one retry policy across a stack's components) -- what this
  post's feature actually changes.
- retry-helmfile-packer-ansible: leads with the coverage-gap problem
  (Terraform already recovered from transient errors, Helmfile/Packer/
  Ansible in the same pipeline didn't) -- what this post's feature
  actually changes.

Also trims each post's "The Problem" section where it had started
repeating the new intro's own examples verbatim.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate Dependabot alert cloudposse#278 (postcss-selector-parser DoS)

postcss-selector-parser < 6.1.3 and < 7.1.0 (< 7.1.3) allow uncontrolled
AST recursion in toString(), a low-severity DoS
(GHSA-w9m9-85wc-3x92 / CVE-2026-9358). Pins both major-version lines to
their patched releases (6.1.3, 7.1.3) via pnpm.overrides, since the
vulnerable package is only pulled in transitively.

39 other open CodeQL/Semgrep alerts on the repo were reviewed but none
match this repo's one established safe-fix pattern
(go/allocation-size-overflow), so per the security-remediate skill's
conservative rule they're left for manual review rather than guessed at.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate Dependabot alerts cloudposse#279, cloudposse#281, cloudposse#282

- google.golang.org/grpc: bump v1.82.1 -> v1.83.2, fixing GHSA-vp52-pcj8-j9qc
  (heap memory exhaustion via HTTP/2 DATA frame fragmentation, <= 1.83.0).
- browserslist: pin transitive dependency to ^4.28.7 via pnpm.overrides,
  fixing GHSA-c83g-rgw3-j3cx (unbounded memory growth) and
  GHSA-73wf-gq98-2v4g (uncaught crash via untrusted stats file), both
  affecting <= 4.28.6.

Alert cloudposse#280 (postcss-selector-parser) was already fixed on this branch by
an earlier commit; it stays "open" on GitHub only because it's scoped to
the default branch's dependency graph and will auto-close once this
branch merges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: atmos-pro[bot] <173522224+atmos-pro[bot]@users.noreply.github.com>
zack-is-cool pushed a commit to zack-is-cool/atmos that referenced this pull request Sep 8, 2026
…posse#2896)

* feat(migration): add Makefile, Justfile, and Taskfile migration guides

Extend the atmos-migration skill and docs to cover moving task-runner
orchestration (Make, Just, Task) to Atmos custom commands and workflows,
alongside the existing native-Terraform/Terraform-Workspaces coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(config): stop directory's own commands: inheriting unrelated .atmos.d subcommands

A directory's own inline atmos.yaml commands: entry named the same as a
command discovered from git-root .atmos.d (e.g. an unrelated outer
project's dev tooling) silently inherited that command's subcommand tree
and other subcommand-referencing fields such as default:. Treat a leaf
command with no commands: key as fully authoritative instead of merging it
field-by-field against the discovered default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(validate): stacks validation no longer requires name_template/name_pattern

createComponentStackMap derived a component's logical stack name via a
stricter, older code path that predated zero-config filename-based stack
naming (cloudposse#1934), so atmos validate stacks hard-failed on any repo that
terraform plan, list stacks, and describe component already resolved
stacks for fine, including this repo's own examples/native-terraform.
Reuse resolveStackName's precedence (manifest name > name_template >
name_pattern > filename) instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(migration): correct field-tested gaps in task-runner migration guides

Field-testing the new Makefile/Justfile/Taskfile migration references
against real fixtures and the real atmos binary surfaced several gaps:
from-native-terraform.md's Shape B recipe used a component name that
never resolved (component names must match the physical directory);
workflows.base_path has no default and needs to be called out; an
orphaned [private] Justfile recipe and Just's command-echo behavior
weren't addressed; from-taskfile.md overstated the need for `import:`
when atmos.d/.atmos.d is auto-discovered; and the migration docs sidebar
order contradicted the pages' own sidebar_position values.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(migration): address CodeRabbit review on PR cloudposse#2896

- Use the terraform component name (matching the physical terraform/
  directory) instead of the never-resolving infra in every single-directory
  Makefile/Justfile/Taskfile example, consistent with the from-native-terraform.md
  Shape B fix.
- Reserve type: atmos for native Atmos verbs only in from-taskfile.md's Shape A
  guidance; calling another custom command still needs type: shell.
- Fix from-justfile.md's Common Problems link fragment (verified against the
  actual github-slugger algorithm).
- Stop telling readers import: is required for auto-discovered atmos.d/.atmos.d
  files in from-makefile.md and website/docs/migration/taskfile.mdx.
- Document that workflows.base_path has no default in
  website/docs/migration/taskfile.mdx, matching the equivalent fix already
  applied to the agent-skill references.
- Normalize from-native-terraform.md's odd-space (3/5/7) list-continuation and
  nested-YAML indentation to even, matching the EditorConfig multiple-of-2 rule
  applied to the other reference files earlier in this branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat(commands): add hidden custom commands and --help=hidden topic

Custom commands can now set `hidden: true` to stay runnable (directly,
as a `default:` target, or from another command's steps) while dropping
out of `--help` listings, completions, and the AI `atmos_list_commands`
tool. This closes the gap the Just/Task/Make migration guides used to
call "no match", where a `[private]`/`internal: true` recipe or task
needed to be reusable across callers or invoked directly for debugging
rather than folded into a single caller's step.

Add a matching `--help=hidden` topic to reveal a command's hidden
subcommands on demand; the default-help hint only mentions it when a
command actually has one, to avoid cluttering the common case.

Refresh the affected migration guides (website + agent-skills mirrors)
to point at `hidden: true` instead of the old "no match" guidance, and
add previously-missing coverage for Task's `internal: true` flag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(links): exclude reproducible-builds.org from link check

The Check Markdown Links workflow failed on the SOURCE_DATE_EPOCH
citation in docs/prd/archive-step.md with "Connection refused". The
domain refuses connections from every network tested (CI, curl, and
WebFetch), not just this path or CI specifically — an upstream outage,
not a broken/moved link — so exclude it the same way other known-flaky
external docs are already handled in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(migration): address CodeRabbit review on PR cloudposse#2896

- lychee.toml: narrow the reproducible-builds.org exclude to the exact
  SOURCE_DATE_EPOCH path instead of the whole domain, so other links on
  that domain stay covered by the link check.
- justfile.mdx/makefile.mdx/taskfile.mdx (+ agent-skills mirrors): the
  "after" Terraform-apply examples ran `terraform apply terraform`,
  confusing the atmos verb with a component literally named "terraform"
  that didn't match the shown legacy `terraform/` directory layout.
  Rename the placeholder component to `infra` and add a one-line note
  on where it maps to under `components.terraform.base_path`.
- cmd_utils_test.go: document why the hidden-command tests' printf/
  redirection is cross-platform (Atmos's TaskTypeShell runs through the
  in-process mvdan/sh interpreter, not the host shell) rather than
  replacing it — flagged as a platform-specific-binary risk, but it
  isn't one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(io): make LinePrefixWriter's cross-node line batches atomic

TestExecuteTerraformConcurrentHooksUseNodeWriters was failing in CI
(reproduced locally under `go test -race -count=200`, ~30% failure
rate): concurrent nodes' hook output was interleaving mid-record
instead of staying grouped per node.

writeLine() acquired the shared writeMu once per line, but a single
Write() call can flush multiple buffered lines at once (e.g. a
\r-terminated segment held back by a prior Write, completed by the
next). Between the two per-line lock acquisitions for one node's
burst, another node's own burst could interleave into the shared
writer. Fixed by collecting a burst's complete lines up front and
writing them under one writeMu acquisition (writeLinesLocked), so a
whole burst lands as one contiguous block. Preserves the existing
partial-write-error retry behavior: a failed line and everything after
it, plus any trailing partial content, are restored to the buffer
for the next Write/Flush to retry.

Verified with `go test ./pkg/scheduler/adapters/... -race -count=500`
(0 failures, was reproducibly failing before) and the full pkg/io
suite, race detector, 5x.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate 7 npm Dependabot alerts in website/

Bump pnpm.overrides for transitively-pulled packages to their patched
versions, all within the semver-major bump the dependabot.yml ignore
policy blocks:

- js-yaml 3.15.0 -> 3.15.1, 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj,
  GHSA for the 4.x line): quadratic CPU consumption in !!omap
  resolution, fixes cloudposse#269, cloudposse#268.
- mermaid 11.16.0 -> 11.16.1: fixes cloudposse#267 (radar diagram DoS), cloudposse#266
  (config API prototype pollution), cloudposse#265 (CSS injection), cloudposse#264
  (Architecture diagram prototype pollution), cloudposse#263 (XY Chart
  infinite-loop DoS).

No open CodeQL alerts. Verified with `atmos lint --changed` (0
issues) and `npm run build` in website/ (succeeds, same pre-existing
unrelated broken-anchor warning as before this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(io): preserve unwritten suffix on partial LinePrefixWriter writes

Write and stdio.WriteString can return n > 0 with an error; the prior
code ignored n and restored the entire raw line on retry, so bytes the
underlying writer already accepted (including the prefix) could be
resent. A nil-error short write (n < len(payload)) also silently
dropped the unwritten tail. Track the encoded pending payload and
retry only its unwritten suffix, converting a nil-error short write
into io.ErrShortWrite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(website): repair broken pnpm lockfile and migration doc links

website-deploy-preview failed on pnpm install --frozen-lockfile because a
duplicate nanoid@^3.3.16 override (added independently by two commits and
merged from main) produced a duplicate YAML key in pnpm-lock.yaml. Also fix
5 new migration docs linking to the nonexistent /ai/agent-skills route
instead of /ai/skills, which broke the docusaurus build once the lockfile
issue was resolved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(commands): qualify that internal commands don't appear in help

Addresses CodeRabbit review comment: the `name` field description said
names unconditionally appear in `atmos help`, contradicting the `internal`
field's documented exclusion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(migration): fix stale no-parity claims for deps/freshness now shipped

cloudposse#2882 (already merged into this branch) added dependencies.commands/
dependencies.workflows and step-level inputs/artifacts, giving Atmos direct
parity with Task's deps:/sources:/generates: and Make's dependency ordering
and file-timestamp caching. This branch's own migration guides -- the
subject of this PR -- still declared those exact features unsupported
gaps, written before cloudposse#2882 landed.

- taskfile.mdx / from-taskfile.md: rewrite "parallel-by-default" and
  "sources/generates gap" sections to document dependencies.commands and
  inputs/artifacts as the direct matches, including the automatic dedup
  behavior a hand-built parallel step doesn't provide.
- makefile.mdx / from-makefile.md: document dependencies.commands for
  target chains with a shared prerequisite, and inputs/artifacts (with
  timestamp.changed for make's exact mtime semantics) for file-timestamp
  targets.
- SKILL.md: fix the same false claims in the top-level "Common Problems"
  summary agents read before the per-tool reference files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(migration): fix reviewer feedback on dependencies/freshness guidance

Local review comments:
- Recommend `atmos --help` (scriptable, direct) over the interactive
  `atmos help` (pages the same listing) for command discovery, across all
  three migration guides and their skill mirrors.
- Reframe makefile.mdx around "Atmos is the front door either way": a
  custom command can call `make <target>` as its one step, permanently if
  desired, rather than treating full migration into native `steps:` as the
  only end state.

CodeRabbit findings, verified against actual behavior before fixing:
- GNU Make's default is to build a target's prerequisites one at a time,
  in listed order -- `-j` is required for concurrency. `dependencies.commands`
  runs concurrently by default, so presenting it as Make's/Just's "direct
  match" changes behavior and can race prerequisites that were only ever
  sequential by accident. Ordered steps are now the default-preserving
  match; `dependencies.commands` is reserved for a shared prerequisite
  (dedup, independent of concurrency), genuine independence, or an
  explicit `-j` source. Fixed in SKILL.md, makefile.mdx, and
  from-makefile.md, including a corrected Shape B example showing how to
  keep `build` ordered ahead of `test` even under the concurrent
  scheduler.
- Confirmed in cmd/cmd_utils.go/internal/exec/workflow_utils.go that a
  skipped step just `continue`s the loop: inputs/artifacts freshness is
  evaluated and recorded per step, unlike Task's/Make's whole-recipe/task
  scope. Documented this across taskfile.mdx, from-makefile.md, and
  SKILL.md, with guidance to combine multiple commands into one step when
  a single freshness decision must gate all of them.
- Removed the stale "target chains become workflows" claim, which
  contradicted the dependencies.commands guidance it now sits next to;
  workflows are reserved for fixed, multi-step orchestration across more
  than one component.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(gitignore): ignore cached tools/gomodcheck binary

Mirrors the existing tools/lintroller/.lintroller entry for the
mage lint helper's cached build output.

* fix(docs): address CodeRabbit findings on atmos-migration skill docs

Corrects eight documentation-accuracy issues flagged by CodeRabbit on the
atmos-migration skill: prefer `type: atmos` over `type: shell` for calling
another custom command (preserves stack context and structured output);
map Make's `@` prefix to `show: { command: false }` instead of `output: none`
(which discards stdout/stderr entirely); describe `build-all`'s `-j4` loop as
sequential, not parallel; define a proper per-service `build-service` command
for the Shape C matrix example; document the `metadata.component` no-move
option for mapping a stack component onto an existing directory; scope
"target chains become workflows" guidance to ordered custom-command steps
instead; and fix the justfile.mdx examples to carry environment variables and
per-environment Terraform vars across build/test/deploy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(docs): address second round of CodeRabbit findings on migration docs

Makes the terraform-directory move optional (not required) in from-justfile.md,
makefile.mdx, and taskfile.mdx; corrects the mischaracterization of Just's
`{{ }}` interpolation as Go templates; clarifies that Task's `deps:` maps
directly to the concurrent-by-default `dependencies.commands` rather than
ordered steps; fixes a broken no-move `.tfvars` path example in justfile.mdx;
stops mapping a single `$(MAKE) -C dir` invocation to `matrix` (reserving it
for genuine `$(SUBDIRS)`-style loops); and documents that Atmos's
`internal: true` (Cobra Hidden) does not block direct invocation the way
Task's `internal: true` does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(docs): preserve per-environment tfvars contract in migration guides

Both the Justfile source (-var-file=envs/{{env}}.tfvars) and Makefile
source (-var-file=envs/$(ENV).tfvars) load per-environment Terraform
variables, but the migrated `atmos terraform apply infra -s <env>`
examples in from-justfile.md and makefile.mdx dropped that behavior --
`-s` only selects the stack, it doesn't load vars. Document the
per-stack `vars: !include` mapping for both the moved-directory and
no-move component layouts, matching the fix already applied to the
sibling justfile.mdx doc.

* fix(security): remediate 3 npm Dependabot alerts in website deps

Pin transitive browserslist and postcss-selector-parser via
pnpm.overrides to patched versions:

- browserslist <= 4.28.6 (GHSA alerts cloudposse#281, cloudposse#282, high) -> ^4.28.7
- postcss-selector-parser >=6.1.0 <6.1.3 (alert cloudposse#280, low) -> ^6.1.3

Both are patch-level bumps within dependabot.yml's semver-major
ignore policy. The postcss-selector-parser override is scoped to the
^6 line only (via postcss-calc's ^6.0.11 request) so it doesn't touch
the separate, already-unaffected ^7.0.0 line used elsewhere.

* fix(docs): add terminal periods to from-mise/from-aqua resource bullets

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
zack-is-cool pushed a commit to zack-is-cool/atmos that referenced this pull request Sep 8, 2026
…dposse#2878)

* docs(prd): correct stale status headers found during Terragrunt migration research

Checkpoint before syncing this branch with origin/main — these fixes were made
against an older snapshot and will likely need rework once current upstream
content is merged in.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(migration): add Terragrunt migration skill reference and correct stale PRD statuses

Adds the atmos-migration skill's Terragrunt reference (classic and Stacks
patterns, concept mapping, migration workflow), hands-on-validated against a
real Terragrunt Stacks example run end to end on the floci/aws emulator.
Corrects four PRD status headers that had gone stale relative to shipped
code, fixes pre-existing EditorConfig indentation violations the commit hook
surfaced in two of those files, and documents the mocks/--use-mocks feature
in the website Terragrunt migration guide as the direct equivalent of
mock_outputs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(docs): use clean !terraform.state syntax and fix EditorConfig indentation

CI caught two real issues in the new Terragrunt migration reference:
- Two examples used the legacy doubled-double-quote YQ escaping
  (!terraform.state x ".field // ""default""") instead of the clean current
  syntax (!terraform.state x .field // "default"), which scripts/check-
  terraform-example-syntax.sh flags outside its designated compatibility
  fixtures.
- The "Migration Workflow" numbered list used 3-space continuation
  indentation, not a multiple of the repo's 2-space EditorConfig setting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(docs): address CodeRabbit findings and field-test gaps on Terragrunt migration guide

Reconciles PRD status claims that contradicted themselves (dag-concurrent-execution.md
Phase 3 is only partially shipped, not fully; custom-hooks.md's relative "today" date),
completes the from-terragrunt.md 5-level merge listing, and fixes a hallucinated
`settings.terraform.provider_overrides` key found via hands-on field testing. Also
recommends `atmos list affected` over `atmos describe affected` for human-run migration
comparisons (table output vs. a wall of YAML), notes both diff committed trees only,
and updates the Change Tracking table to the current `dependencies.files`/`folders`
syntax instead of the legacy inline `kind: file`/`kind: folder` form.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(docs): resolve second CodeRabbit review round on Terragrunt migration guide

Step 3 of the migration workflow still mapped mock_outputs to the YQ // "default"
pattern, contradicting the mocks:/--use-mocks mapping documented a few paragraphs
earlier. Quotes the YQ default expressions for consistency with
atmos-yaml-functions/SKILL.md and atmos-components/SKILL.md.

dag-concurrent-execution.md had two more self-contradictions: the Subprocess
Execution section still described the os.Stdout race that Phase 1 already fixed
(terraform_plan_diff.go now captures via bytes.Buffer), and the Resolved Questions
section claimed cross-type dependency syntax was "solved by PR cloudposse#2193" — traced the
code and found pkg/scheduler/adapters/terraform.go explicitly skips any dependency
whose kind isn't "terraform", so the kind field is schema-parseable but not yet
consumed by the scheduler; corrected to match the already-accurate Phase 3 status.

terragrunt.mdx's list-affected example claimed to compare against main by default
without passing --ref; list affected has no --base flag (unlike describe affected),
so made the comparison explicit with --ref main instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore: trigger CI re-run

* fix(mocks): correct provenance rendering, error wording, and // default parity

A field test of --use-mocks found `describe component` silently rendering
empty output whenever a component's provenance path wasn't matched due to an
unnormalized lookup, a mock-output error that mislabeled the output name as a
component name, and a YQ `//` default that only rescued a missing key inside
a declared `mocks` map, not a component with no `mocks` section at all --
inconsistent with how `//` already rescues real state. Also cross-references
the mocks:/--use-mocks feature from the docs pages and skill most likely to
be read first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(snapshots): regenerate describe_component golden snapshots after provenance fix

The filterEmptySections fix (6c22503) corrected describe_component to stop
silently dropping real sections (backend, metadata, env, overrides) that lack
a stack-root section of the same name. CI caught the resulting golden
snapshot drift on both linux and macos; regenerated via
`-regenerate-snapshots` per CLAUDE.md, verified the diffs only add the
previously-hidden, now-correct content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(provenance): address CodeRabbit review on PR cloudposse#2878

Add periods to the rendering-constant comments (godot's inline-comment
scope missed these, but CLAUDE.md's comment convention still applies), and
cover the array-element provenance path (vars[0].foo) alongside the
already-tested dot-nested form. The trailing-period finding on
ErrTerraformMockOutputNotDeclared was already resolved by an earlier commit
in this PR — no change needed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(test): widen RunSession timeouts to fix Windows CI flake

Acceptance Tests (windows, shard 4/10) failed with ErrWaitTimeout in
TestRunSessionExecutesScriptedShellActions and
TestRunSessionAppliesDirectoryAndEnvironment: the write->echo->match round
trip against a spawned child (no PTY on Windows, unlike session_unix.go)
never completed within the 2s wait/3s context budget. Widened both to 8s/15s
across all four RunSession-based tests; no production code changed since
static review found no concrete pipe-wiring bug. Not reproduced locally (no
Windows environment available) — documented in docs/fixes/ per this repo's
convention for unconfirmed Windows-only CI fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(fixes): address CodeRabbit findings on PR cloudposse#2878

- Add a text language tag to the failure-output fenced block (markdownlint
  MD040).
- Correct the documented timeout values to match what actually shipped after
  merge-conflict resolution: 10s per wait (not 8s), and 25s outer context for
  TestRunSessionAppliesDirectoryAndEnvironment's two sequential waits (not
  15s) — the outer context must exceed the sum of sequential wait timeouts,
  not just one of them, per waitForOutput's ctx.Done()-vs-deadline-timer race.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(fixes): document CI exit-code test failure as a registry network flake

Acceptance Tests (linux, shard 9/10) failed
TestCLICommands/atmos_exit_code_should_be_same_as_command_exit_code_(2) with
"Expected exit code 2, got 1". The real cause was tofu init timing out
reaching registry.opentofu.org (context deadline exceeded) before any plan
could run -- confirmed the fixture has no registry-mirror config to regress,
and the sibling (0)/(1) exit-code cases in the same file passed. No code
change: there's nothing in this repo that fixes a transient outage on a
public third-party registry, and loosening the exit-code assertion would
mask a real CLI exit-code-propagation regression if one ever occurs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): don't fail test-required/k3s-required on a cancelled run

All five attached failure logs (Acceptance Tests linux/macos/windows,
[k3s] demo-helmfile, Build windows) traced to one event: workflow run
33394180592 on this PR was cancelled (confirmed via gh api), not failed. The
test/k3s matrix jobs were skipped as a result, but the -required gate jobs
(if: always()) still ran and misreported the cancellation as a hard failure
("expected 10 shard jobs, found 0" / "k3s matrix result was 'skipped'").

needs.test.result and needs.k3s.result both report "skipped" for a genuine
upstream failure and for a whole-run cancellation alike, so they can't
distinguish the two - cancelled() can, and is the fix. It's only valid in an
if:, not inside a run: script (caught by actionlint), so both gates get a
"Skip verification" step under if: cancelled() plus if: !cancelled() on
their existing check steps, leaving the fail-loudly-on-genuine-anomalies
logic untouched for real failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [autocommit] formatting fixes

* fix(ci): pin cuelang.org/go's NOTICE URL to a deterministic override

Review Dependency Licenses failed: NOTICE had "URL: Unknown" for
cuelang.org/go, but a fresh generate-notice.sh run resolved a real URL,
tripping the out-of-date check. Root cause was a race, not one bad run:
this branch's merge commit already had the correct URL, but a subsequent
[autocommit] formatting fixes commit (atmos-pro[bot]) regenerated NOTICE
under a network condition where go-licenses' live resolution for
cuelang.org/go failed, silently reverting it to "Unknown" and committing
that regression - exactly the oscillation scripts/generate-notice.sh's
REPO_OVERRIDES mechanism exists to prevent for modules go-licenses can't
resolve reliably, cuelang.org/go just wasn't in the list yet.

Added it (repo github.com/cue-lang/cue, no tag prefix, LICENSE path),
which reconstructs the exact URL CI itself resolved
(https://github.com/cue-lang/cue/blob/v0.16.1/LICENSE) from go.mod's
pinned v0.16.1 with no network dependency, and applied that one-line NOTICE
fix by hand: a local generate-notice.sh run silently produced a truncated
102-dependency report (vs. CI's 643) with 0 Apache-2.0/BSD licenses found,
consistent with this machine lacking a Linux-targeting C cross-compiler for
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 - so that broken local output was
discarded rather than committed, and the NOTICE line was hand-verified
against the override's own URL-construction formula and go.mod's version
instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): retry go install go-licenses on transient sum.golang.org failures

Review Dependency Licenses failed installing go-licenses@v1.6.0: a
mid-stream HTTP/2 reset (stream ID 1155; INTERNAL_ERROR) reading
sum.golang.org during go install's go.sum verification, unrelated to any
actual dependency problem and unrelated to the immediately preceding commit
on this branch (confirmed via gh api against head_sha 5ac5d97, which only
touched an unrelated NOTICE URL override).

Same failure class already fixed once for go mod download
(docs/fixes/2026-08-25-build-atmos-go-mod-download-retry.md, later ported to
magefiles/build.go's runGoModDownload) - just hit a different network call
(go install's dependency-graph resolution) in a different script. Wrapped
generate-notice.sh's bare go install in the same 3-attempt/15s-backoff until
loop, matching .github/actions/download-artifact-retry's convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(docs): replace a literal tab with spaces in a fix-log fenced block

Run pre-commit hooks failed atmos-validate-editorconfig: a fenced code block
in docs/fixes/2026-08-31-notice-go-licenses-install-retry.md quoted a Go
toolchain error message verbatim, including its original tab-indented
continuation line - violating this repo's *.md indent_style=space rule.
Replaced the literal tab with two spaces (matching indent_size=2), content
otherwise unchanged. Scanned every other 2026-08-31 fix-log doc added this
session for the same issue; none found.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(blog): bump terraform-component-mocks date to when its content changed

fix(mocks) commit 6c22503 edited this post's body (the // default
behavior clarification) on 2026-08-06, but the post kept displaying/sorting
under its original 2026-07-15 publish date since Docusaurus has no separate
date. Added an explicit date: frontmatter override for the edit date,
matching this repo's existing convention for date overrides (e.g.
2026-01-02-unified-task-runner.mdx).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate 4 Dependabot alerts

- google.golang.org/grpc v1.82.1 -> v1.83.1 (go get + go mod tidy),
  bumping compatible transitive deps
- website pnpm overrides: browserslist -> ^4.28.7, postcss-selector-parser
  (^6.0.11 and ^6.0.16 requesters) -> ^6.1.3
- regenerate NOTICE to reflect the grpc bump

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(fixes): document atmos_vendor_pull DNS-resolution CI flake

Acceptance Tests (macos, shard 4/10) failed with "tty did not match
pattern \"Vendored 3 components\"" because git itself could not resolve
github.com on the runner (OS-level resolver failure, corroborated by the
same job's Harden Runner network log) -- not a code regression. No code
change; re-running the job is expected to pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: address CodeRabbit review findings on mocks docs, fix-logs, and noticegen tests

- agent-skills/skills/atmos-migration/references/from-terragrunt.md: correct
  the mock_outputs -> mocks migration guidance -- Terragrunt scopes
  mock_outputs per dependency consumer, Atmos scopes mocks per producer
  component (shared by every consumer). Document that a shared mock value
  only works when every consumer agrees on it, and that a genuinely
  different per-consumer value needs its own producer component instance.
- docs/fixes/2026-08-31-notice-go-licenses-install-retry.md: the retry loop
  now lives in tools/noticegen/report.go's ensureGoLicenses (tested in
  tools/noticegen/report_test.go), not scripts/generate-notice.sh, which was
  deleted when the NOTICE generator was rewritten as a Go tool. Updated the
  title, Context, Changes, and Validation sections accordingly.
- docs/fixes/2026-08-31-required-check-gates-fail-on-cancelled-run.md:
  reworded "passing (all-steps-skipped) job" to "a passing job whose
  verification steps are skipped" -- the explicit Skip verification step
  still runs, so the job isn't literally all-skipped.
- tools/noticegen/report.go: extracted lookPathGoLicenses as a package-level
  var (previously a direct exec.LookPath call inside ensureGoLicenses) so
  tests can force the "not found" branch deterministically.
- tools/noticegen/report_test.go: both retry tests now inject
  lookPathGoLicenses to return exec.ErrNotFound, instead of relying on the
  real PATH not already containing go-licenses -- which it may, e.g. from a
  prior local run, silently skipping runGoInstall and making the retry
  assertions vacuous.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: address CodeRabbit findings on SKILL.md link casing and vendor-pull fix-log wording

- agent-skills/skills/atmos-migration/SKILL.md: lowercase the
  from-terragrunt.md reference link's display text to match the actual
  lowercase repo path and the style of the overview section's own link.
- docs/fixes/2026-09-02-vendor-pull-dns-resolution-flake.md: correct the
  fixture description -- tests/fixtures/scenarios/vendor/vendor.yaml
  exercises a local file:// source and a git::https:// source, not an OCI
  source or a separate plain-HTTPS source.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate 3 Dependabot/CodeQL alerts

- golang.org/x/crypto v0.55.0 -> v0.56.0 (go get + go mod tidy), fixing two
  govulncheck alerts (GO-2026-6354, GO-2026-6355): a malicious SSH peer could
  deadlock a connection via crafted channel messages
  (golang.org/x/crypto/ssh). No direct callers in this repo beyond
  pkg/store/providers/github_actions_client.go; verified via go build and
  pkg/store/... tests.
- website pnpm override: fast-uri@^3 -> ^3.1.6 (patched; published 10 days
  ago, clears this repo's 7-day minimum-release-age cooldown).
- regenerate NOTICE to reflect the x/crypto bump.

qs (Dependabot cloudposse#283/cloudposse#284, patched at 6.16.0) is intentionally NOT bumped:
6.16.0 was published 4 days ago, still inside website/.npmrc's 7-day
minimum-release-age cooldown -- forcing it in via
minimumReleaseAgeExclude would defeat the cooldown's purpose. Will pick it
up once it clears.

browserslist (cloudposse#281/cloudposse#282) and postcss-selector-parser (cloudposse#280) are already
fixed on this branch from an earlier commit; GitHub just hasn't re-scanned
yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): whitelist go.googlesource.com/go.dev/pkg.go.dev for CodeQL Go jobs

StepSecurity's blocked-call detections (analyzed via the stepsecurity MCP
server) showed the analyze and govulncheck jobs' harden-runner egress
policies blocking go.googlesource.com, go.dev, and pkg.go.dev during Go
module/toolchain resolution -- both are trusted Go project domains
(GOTOOLCHAIN auto-download and go-getter's git-host fallback path).
go.googlesource.com was already allowed for govulncheck but missing from
analyze; go.dev and pkg.go.dev were missing from both. Also removed a
duplicate storage.googleapis.com entry in analyze's list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: atmos-pro[bot] <173522224+atmos-pro[bot]@users.noreply.github.com>
Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>
zack-is-cool pushed a commit to zack-is-cool/atmos that referenced this pull request Sep 8, 2026
…osse#1908)

* feat: Add Terraform streaming UI with real-time plan/apply visualization

Implement a streaming TUI mode for terraform operations that displays plan/apply
changes in real-time with a structured tree view, colored badges, and detailed
attribute changes. When apply operations are destroy plans (only deletions), show
"Destroy" instead of "Apply" in completion messages. Uses colored dots (●) for
resource change indicators and color-coded keys for attribute changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: Add minimum 5s display duration for streaming UI progress

Ensures the progress bar is visible for at least 5 seconds even for
fast operations, allowing users to see and provide feedback on the UI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* refactor: move progress bar inline with activity on same line

Move the progress bar to be on the same line as the spinner and current
activity, similar to the vendoring UI. This creates a more compact display:

  ⣾ apply dev/myapp Creating aws_s3_bucket.test (1.2s)  ████████░░░ 3/5

Instead of having the progress bar on a separate line.

Also fix errorlint issue: use errors.Is(err, io.EOF) instead of ==.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: remove artificial minimum display duration

Remove the 5-second minimum display duration that was temporarily added
for testing. The inline progress bar layout now works correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: add .atmos-*.tfplan to .gitignore

Ignore temporary terraform plan files generated by atmos streaming UI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: update PRD and blog post with current UI design

Update examples to reflect the current implementation:
- Inline progress bar on same line as activity
- Colored dots (●) instead of +/-/~ symbols for resources
- Two-column attribute layout with color-coded keys
- Updated completion message format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review feedback for streaming UI

- Fix potential allocation overflow in executor.go by using explicit
  capacity variables for slice allocations
- Update PRD to list destroy instead of refresh in supported commands
- Fix executor_test.go to use destroy instead of refresh in test
- Use rune-aware truncation in init_model.go for multi-byte UTF-8
- Fix extractReferences in tree.go for module-qualified references
- Add trailing periods to inline comments in types.go per godot linter
- Convert tail recursion to iterative loop in parser.go
- Handle composite actions (replace) in tree.go for delete+create ops
- Add comprehensive test coverage for new functionality

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove explicit capacity allocation to prevent overflow

Use Go's built-in append growth strategy instead of pre-calculating
capacity. This eliminates the integer overflow concern flagged by
GitHub's security scanner when len(args) is near MaxInt.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: regenerate golden snapshots for streaming UI feature

Updated 9 golden snapshot files to include the new `ui` config section
and `--ui` flag in terraform command help output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use orange refresh icon for replace actions in streaming UI

- Changed replace action from pink dot (●) to orange refresh icon (↻)
- Added IconRefresh constant to theme icons
- Updated color comments to clarify each action's meaning:
  - Green dot (●) for create
  - Yellow dot (●) for update/change in place
  - Red dot (●) for delete
  - Orange refresh (↻) for replace/recreate
  - Cyan dot (●) for read

This better matches Terraform's native output which uses -/+ for
replace operations and ~ for in-place changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: show computed attributes as yellow (update) not red (delete)

When an attribute has Unknown=true (computed value that will be
"known after apply"), it should be styled as an update (yellow)
not a deletion (red), even though the After value is nil.

This fixes the display of attributes like content_base64sha256,
content_md5, etc. that are computed by the provider.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: implement line-by-line diff for multiline attribute changes

Instead of showing all old lines with - then all new lines with +,
now properly compares lines and only shows -/+ markers on lines
that actually differ. Unchanged lines are shown without markers.

This matches Terraform's native diff output style where:
- "Weather report: Stockholm" (unchanged) → no marker
- "0(-2) °C" vs "+1(-3) °C" (changed) → shows - and + lines
- "10 km" (unchanged) → no marker

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: group consecutive changed lines in diff output

When multiple consecutive lines differ, now groups all deleted lines (-)
together, then all added lines (+), instead of interleaving them.

Before (interleaved):
  -  _ /"".-.     0(-2) °C
  +  _ /"".-.     +1(-3) °C
  -    \_( ).     ↘ 8 km/h
  +    \_( ).     ↙ 13 km/h

After (grouped like native Terraform):
  -  _ /"".-.     0(-2) °C
  -    \_( ).     ↘ 8 km/h
  +  _ /"".-.     +1(-3) °C
  +    \_( ).     ↙ 13 km/h

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add comprehensive tests for diff behavior and color coding

- Add tests for renderMultilineDiff function:
  - Identical lines (no markers)
  - Single line changes
  - Consecutive changed lines are grouped (all - then all +)
  - Mixed unchanged and changed lines
  - Lines added/deleted at different positions
  - Different length before/after content
  - Empty before/after content

- Add tests for attribute change rendering:
  - New, deleted, and updated attributes
  - Computed/unknown values show "(known after apply)"
  - Sensitive values show "(sensitive)"
  - Multiple attributes with alignment
  - Boolean and numeric value formatting

- Add tests for helper functions:
  - valuesEqual for deep comparison
  - formatSimpleValue for value formatting
  - getRawStringValue for multiline detection
  - getContrastTextColor for accessibility

- Fix replace action symbol test to expect ● (dot) instead of ↻

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: show '# forces replacement' annotation for replace-triggering attributes

Parse the ReplacePaths field from Terraform's JSON plan output to identify
which attributes are causing a resource to be replaced rather than updated.
Display an orange "# forces replacement" annotation next to these attributes,
matching Terraform's native output behavior.

Changes:
- Add ForcesReplacement field to AttributeChange struct
- Parse ReplacePaths from terraform-json Change struct
- Render "# forces replacement" annotation in both single-line and multi-line modes
- Add comprehensive tests for forces replacement feature

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

* refactor: split tree.go into smaller focused modules

Split the 954-line tree.go file into smaller, focused modules to meet
the 600-line guideline:

- tree.go (30 lines): Type definitions only
- tree_builder.go (316 lines): Tree building logic
- tree_render.go (455 lines): Tree rendering logic
- tree_utils.go (171 lines): Utility functions

Also updates executor_test.go to handle CI/non-TTY environment
constraints for streaming UI tests.

Note: Pre-existing linter warnings were moved to the new files but
not addressed in this refactoring commit. Linter issues should be
fixed in a separate dedicated PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: regenerate golden snapshots for streaming UI feature

Update golden snapshot files to include:
- New terraform.ui.enabled config field in describe config output
- New --ui flag in terraform help output

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: remove tautological and stub tests from streaming UI

Remove tests that provided no value:
- TestShouldUseStreamingUI_ExplicitlyEnabled: Always asserts false due to CI
- TestShouldUseStreamingUI_EnabledWithFlag: Stub with no assertions
- TestShouldUseStreamingUI_EnabledWithConfig: Duplicate stub with no assertions
- TestExecuteOptions_Fields: Tautological struct field assignment test

These tests violated testing guidelines: "avoid tautological tests" and
"no coverage theater".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: improve streaming UI test coverage to ~70%

Add comprehensive tests for Bubble Tea UI components following the
pure reducer testing pattern - pushing logic behind interfaces and
testing Update() as pure reducers without mocking the framework.

Key changes:
- Add Clock interface for injectable time operations in tests
- Add ModelOption/InitModelOption patterns for dependency injection
- Create model_test.go with Update/View tests using mock clock
- Create init_model_test.go with line parsing and completion tests
- Create tree_builder_test.go for plan tree construction tests
- Create types_test.go for ResourceState/Phase String() methods
- Extend executor_test.go with helper function tests
- Extend parser_test.go with OutputsMessage and diagnostic tests
- Extend resource_test.go with edge case and activity tests
- Extend tree_test.go with badge rendering and countActions tests

Coverage improved from ~35% patch to ~70% package coverage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove accidentally committed tfplan file

Remove .atmos-destroy-*.tfplan file that was committed before the
gitignore pattern was added. The pattern already exists in .gitignore
to prevent future commits.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: mark streaming Terraform UI as shipped in roadmap

Update the roadmap to reflect that the Streaming Terraform UI feature
(PR cloudposse#1908) has been shipped in Q4 2025. Enhanced the description to
highlight dependency trees and line-by-line diffs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback for streaming UI

- Add ErrStdoutPipe and ErrCommandStart sentinel errors to errors.go
- Wrap stdout pipe error with ErrStdoutPipe in Execute()
- Wrap cmd.Start() errors with ErrCommandStart in Execute() and ExecuteInit()
- Wrap TUI run errors with ErrTUIRun in Execute() and ExecuteInit()
- Add perf.Track() to ResourceTracker.HandleMessage()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

* fix: wrap parser errors with static sentinel per CodeRabbit feedback

Add ErrParseTerraformOutput sentinel error and wrap all scanner and
JSON unmarshal errors in parser.go for proper error checking via
errors.Is(). The io.EOF sentinel is correctly left unwrapped per
idiomatic Go.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback (round 2)

- Remove .claude-plan.md from commit (internal planning doc)
- Add perf.Track to BuildDependencyTree in tree_builder.go
- Add perf.Track to RenderTree, GetChangeSummary, RenderChangeSummaryBadges
- Add perf.Track to ShouldUseStreamingUI in executor.go
- Replace hardcoded #FFFFFF with theme.ColorWhite
- Regenerate golden snapshot for describe_configuration test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: route terraform diagnostics through structured logger

Parse Terraform diagnostic messages from JSON streaming output and route
them through the Atmos logger based on their severity level:
- severity: "error" → logger.Error()
- severity: "warning" → logger.Warn()
- unknown/empty → logger.Info()

Structured key-value pairs include stack, component, detail, address,
and source file/line when available. This enables log aggregation and
respects ATMOS_LOG_LEVEL configuration while preserving the existing
styled TUI output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: improve streaming UI diagnostic display and badge summary

- Add badge summary to ExecutePlan output (was missing before)
- Skip tree rendering when no changes (just show NO CHANGES badge)
- Fix floating text artifacts by clearing all progress lines on completion
- Suppress Terraform stderr (use JSON diagnostics instead)
- Add -compact-warnings flag to reduce verbose warning output
- Make diagnostic log messages concise with pattern matching
- Remove redundant stack/component from diagnostic logs
- Move LogDiagnostics call to after TUI completes for cleaner output

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove duplicate error display in streaming UI

Error details were being shown twice - once inline in finalView() and
once via LogDiagnostics(). Now all diagnostic details are routed
through LogDiagnostics() for consistent display.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: restore failed resources display in streaming UI

Failed resources (apply errors with resource addresses) are different
from diagnostics. Diagnostics are config warnings/errors that go through
LogDiagnostics(), while failed resources show the specific resource
address that failed during apply.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback (round 3)

- Add cmd.Stdin for interactive terraform prompts
- Add perf.Track to NewModel, NewInitModel, NewParser, Next
- Add TTY validation in ConfirmApply and ConfirmDestroy
- Fix godot comment punctuation across all files
- Increase scanner buffer size to 1MB for large output
- Fix IsModule detection for resource nodes within modules
- Fix nested-module reference normalization in tree builder
- Wrap parser errors with static sentinel ErrParseTerraformOutput
- Use theme constants in tree_utils.go
- Add assertions to TestExtractDependencies_NestedModules

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: regenerate golden snapshots

Regenerate snapshots that were flagged in PR review:
- TestCLICommands_describe_component_with_relative_path
- TestCLICommands_describe_component_from_nested_dir_discovers_atmos.yaml_in_parent
- TestCLICommands_indentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address CodeRabbit review feedback (round 4)

- Route stderr to logger instead of discarding (captures backend errors, plugin failures)
- Add safe type assertions with comma-ok idiom to prevent panics
- Add perf.Track to InitModel.Init(), Model.LogDiagnostics(), and ResourceTracker methods
- Restore Code.Prefix in glamour style converter for test compatibility
- Add ErrStderrPipe and ErrUnexpectedModelType sentinel errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: regenerate golden snapshots and fix TTY exit code handling

- Regenerate snapshots for config output (added plugin_cache and ui.enabled fields)
- Update diagnostic pattern matching in tests (check failed, precondition failed)
- Fix bug in simulateTtyCommand: return wait error to capture exit codes

The TTY test framework was always returning nil for errors, causing exit code
verification to fail for commands that exit with non-zero status.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: use theme constants and add stdin TTY validation

- Replace hardcoded "#000000" with theme.ColorBlack constant
- Add ColorBlack to theme color constants for consistency
- Add IsTTYSupportForStdin() check to ConfirmApply and ConfirmDestroy
- Interactive prompts now validate both stdin and stdout are TTYs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: remove non-TTY diagnostic tests

Non-TTY tests don't make sense for the streaming UI feature since
the diagnostic formatting functionality only exists in TTY mode.
The TTY tests provide complete coverage for diagnostic streaming.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add perf.Track to ConfirmApply and ConfirmDestroy

Add performance tracking to public functions per coding guidelines.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: align tree continuation with tree connectors

The tree continuation character (│) for attribute lines was misaligned
with the tree connector (├/└) for resource lines by 1 character. This
was caused by attribute lines using 6 spaces of indentation while
resource lines only used 5 characters before the tree connector.

Changed all 5 attribute format strings from 6 to 5 spaces of indentation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add robust multi-line tree rendering with JSON support

Implement enhanced tree rendering for Terraform plan output:

- Add clean attribute indentation (remove noisy │ bars by default)
- Add RenderConfig struct with Compact, ShowAttributeBar, MaxLines options
- Add pretty-printed JSON for complex values (maps, arrays)
- Add isComplexValue and collapseIfNeeded utilities
- Add renderComplexAttributeChange for JSON diff rendering
- Add line-by-line semantic diff for multi-line values
- Update tests for new function signatures

Config options (atmos.yaml):
  settings.terraform.ui.compact: false    # Add blank lines between resources
  settings.terraform.ui.show_attribute_bar: true  # Show ┃ bar for attributes
  settings.terraform.ui.max_lines: 0      # 0 = show all (default)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: guard truncation against small terminal widths

Add safety check to prevent panic when maxLineWidth <= 3.
The slice operation line[:maxWidth-3] could cause a panic
if the terminal width is unexpectedly small.

Addresses CodeRabbit review comments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: regenerate golden snapshots after merge from main

Update golden snapshot files to reflect changes from the main branch merge:
- Add new TerraformUI fields (compact, show_attribute_bar, max_lines)
- Update Terraform help text output to match current version
- Fix minor formatting alignment in provenance output

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR cloudposse#1908 review feedback on streaming UI

- Fix CodeQL allocation-size-overflow in executor.go: replace
  make([]string, len(opts.Args)+1) with append, sizing the allocation
  from a single len() call instead of a sum.
- Raise the JSON scanner buffer limit in parser.go from 1MB to 10MB
  (with a smaller 64KB initial buffer) so large Terraform plan/state
  lines don't trip bufio.ErrTooLong and abort the streaming UI.
- Add perf.Track to the exported Model.Init/Update/View Bubble Tea
  entrypoints per repo convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: address second round of PR cloudposse#1908 review feedback

- cmd/terraform/workspace.go: pass the leaf *cobra.Command into
  ParseTerraformRunOptions in the workspace passthrough leaf, so --ui=false
  is correctly detected as an explicit override (UIFlagSet). Adds a
  regression test covering unset/--ui=true/--ui=false.
- internal/exec/terraform_streaming_ui.go: propagate the caller's context
  (via shellCommandContext) into the streaming executors instead of
  context.Background(), so cancellation/deadlines actually stop a running
  streaming Terraform process. Also skip streaming entirely when the
  component has retry conditions configured, since executeShellCommandWithRetry
  matches conditions against output captured via shellOpts, which the TUI
  executors never populate.
- pkg/terraform/ui/executor_args.go: recognize any non-flag positional
  argument as a saved planfile (Terraform allows arbitrary filenames), not
  just names ending in .tfplan, while still excluding Terraform
  config/vars-like extensions (.tf, .tf.json, .tfvars, .tfvars.json).
- pkg/terraform/ui/executor_outputs.go: pass the component's effective
  environment to `terraform output -json` instead of leaving cmd.Env nil,
  so it inherits credentials and TF_VAR_* instead of the Atmos process's
  own ambient environment.
- pkg/terraform/ui/tree_builder.go: guard against nil rc.Change in
  resourceChangeAction to prevent a panic before extractAttributeChanges'
  own nil check is reached.
- pkg/terraform/ui/tree_render.go: honor caller-provided styles in
  resolveRenderConfig instead of silently discarding them; use
  lipgloss.Width instead of len() for indent calculation so multi-byte
  tree-drawing characters don't throw off alignment.
- pkg/terraform/ui/executor_test.go, resource_test.go: use filepath.Join
  instead of hardcoded Unix paths; strengthen the concurrency test to
  assert all 50 concurrent writes land instead of just count > 0.
- docs/prd/terraform-streaming-ui.md, website/blog/2025-12-21-terraform-streaming-ui.mdx:
  correct the supported-command list (refresh is not supported, destroy
  was missing) and the "no external dependencies" claim.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore: gitignore the gomodcheck tool binary

tools/gomodcheck/.gomodcheck is a locally-compiled binary artifact,
same pattern as the already-gitignored tools/lintroller/.lintroller.
Current tooling (magefiles/mage_lint_gomodcheck.go) runs this tool via
`go run` rather than a cached binary, so this file is now stale, but
the pattern is still worth ignoring in case it's built locally.

* fix: regenerate stale atmos.yaml JSON Schema for TerraformUI

TestEmbeddedSchemaIsCurrent was failing in CI (linux + windows
acceptance test shards): the TerraformUI struct and components.terraform.ui
field added during the origin/main merge were never reflected in the
generated pkg/datafetcher/schema/atmos/config/1.0.json.

Regenerated via `go generate ./pkg/config/schema`.

* fix: isolate remaining pkg/telemetry tests from the shared cache root

TestCaptureCmdError (and 8 sibling tests) called cfg.LoadCache()/SaveCache()
directly without isolateTelemetryCache(t), so they read/wrote the real
shared <cache>/atmos/cache.yaml instead of a per-test temp directory.
On Windows CI this raced with other concurrently-running package test
binaries also touching that file, producing "cache write failed: ...
The process cannot access the file because it is being used by another
process" and, in one observed failure, cross-test mock argument
contamination from a stale installation ID.

isolateTelemetryCache(t) (added for a prior, related incident — see its
doc comment) already existed and is used by several tests in this file;
these 9 just hadn't been updated to call it.

* fix: exclude gitignored *.tfstate files from describe-affected repo copies

TestDescribeAffectedWithDependentsStackFilterYamlFunctions failed on
Windows CI: setupDescribeAffectedTest/setupDescribeAffectedTestWithFixture
copy the entire repository into a temp dir to compute a git-diff-based
affected-components comparison. That copy walked into another fixture's
terraform.tfstate.d/ while a concurrently-running acceptance test in a
different package binary had a live terraform apply in progress against
it, hitting "The process cannot access the file because another process
has locked a portion of the file."

*.tfstate/*.tfstate.* are already gitignored (not part of "the repository"
these tests diff) and are irrelevant to git-diff-based affected detection,
so exclude them from the copy in shouldSkipRepoCopyPath -- same pattern
already used there for node_modules/.terraform/build output. This avoids
the race entirely rather than retrying around it.

* fix: streaming UI Ctrl-C hang, duplicate-plan bug, destroy/refresh --ui no-ops, dead render config

Field-test pass on the streaming UI (--ui) found and fixed five defects:

- Ctrl-C during a streaming apply/init could hang atmos indefinitely with no
  terraform subprocess left to wait on. Model/InitModel now distinguish
  user-cancellation from normal completion and kill the subprocess on cancel.
- Redirected stdin caused a full plan to run twice (once via streaming, once
  via the plain fallback) before failing. Confirmation preconditions are now
  checked before the plan phase runs, not after.
- `destroy --ui` silently fell back to plain output: destroy.go never passed
  cmd to ParseTerraformRunOptions, so --ui was never detected as set, and
  isJSONSubCommand was missing "destroy" (which would have produced an
  invalid flag order once the flag did propagate). Both fixed; destroy now
  fully supports the streaming UI.
- `refresh --ui` is a genuine Terraform limitation (no -json output to
  stream); it now prints an explicit warning instead of silently falling
  back, and docs no longer claim it works.
- components.terraform.ui.{compact,show_attribute_bar,max_lines} were parsed
  and documented but never wired into tree rendering. Now wired via
  tfui.BuildRenderConfig.

Also corrects the resulting doc inconsistencies (refresh/destroy --ui docs,
a fictional completion-message example), removes the unused IconRefresh
constant, and adds a "Streaming UI" section to the atmos-terraform skill.

Adds a disposable local-only fixture (tests/fixtures/scenarios/streaming-ui-manual,
null_resource/local_file/time_sleep, no cloud credentials) used to reproduce
and verify each fix against a real terraform binary in a real pty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test: raise streaming UI patch coverage from 68.78% to ~92.5%

Codecov's patch check was failing (68.78% vs 85% target) on the new
Terraform streaming-UI feature. Adds real behavioral tests across
pkg/terraform/ui and internal/exec/terraform_streaming_ui.go, plus two
minimal DI seams in executor.go (execCommandContext, runTeaProgram) so
subprocess/tea.Program orchestration is unit-testable via the existing
self-re-exec-test-binary pattern. Also fixes 4 lint findings surfaced
by the resulting --new-from-rev=origin/main diff, including extracting
a repeated "ci" string literal in cmd/terraform/utils.go into a
constant. Two coverage ceilings (confirm.go's huh.Run(), executor.go's
TTY-gated Execute/ExecuteInit paths) are documented rather than chased
since they require a real terminal. See
docs/fixes/2026-08-25-terraform-streaming-ui-patch-coverage.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(test): strip ANSI in tree render assertions, retry Windows exe cleanup lock

TestRenderAttributeChanges_MultilineOnlyAddition/Deletion checked for literal
"+ line1"/"- line1" substrings, but resolveRenderConfig's default styled
Create/Delete symbols insert an ANSI reset between the marker and the text.
This passes wherever stdout isn't detected as color-capable (a plain local
shell) but fails wherever it is (GitHub Actions allocates a pty for shell
steps) -- reproduced locally by forcing color and confirmed the fix resolves
it. Strip ANSI before asserting, like existing ansi.Strip usage elsewhere.

TestYamlFuncTerraformOutput failed on the Windows acceptance job with
"TempDir RemoveAll cleanup: ... tofu.exe: The process cannot access the file
because it is being used by another process" -- a lingering handle on the
per-test isolated tofu.exe copy from isolateTerraformTestBinary. Retry the
removal with a short backoff before t.TempDir()'s own cleanup runs, matching
the copyRepoWithRetry/isTransientRepoCopyError pattern already used for the
same class of Windows file-lock flake in describe_affected_test.go.

* fix(test): scale pkg/perf recursion-tracking time ceilings to sleep count

TestRecursiveFunctionTracking and TestYAMLConfigProcessingRecursion failed
on the Windows acceptance job with total time just over their flat 2s/1s
ceilings (2.39s, 1.0007s). Both tests issue hundreds of very short
time.Sleep calls (100µs/50µs); Windows CI's timer-tick granularity under
load inflates each call to several milliseconds, which a fixed absolute
ceiling can't absorb regardless of environment. Scale the ceiling by the
actual sleep count instead, keeping enough headroom to still catch genuine
multiplicative counting-inflation bugs (the Count assertions already pin
that down exactly) without flaking on ordinary Windows timer coarseness.

* fix: reject --ui combined with --max-concurrency > 1 instead of racing terminals

Concurrently-scheduled components under --all/--affected/--components/--query
each independently dispatch through the streaming UI when --ui is enabled,
and the streaming executor has no output-writer redirection (unlike the
plain-output concurrent path, which suppresses spinners and redirects
workdir output for exactly this reason). Multiple components running
concurrently would each try to take over the same terminal with their own
full-screen TUI session, with nothing preventing or even warning about it.

Add tfui.WouldAttemptStreamingUI (requested + TTY/CI-capable, independent of
subcommand) and wire it into validateTerraformConcurrentExecution's existing
concurrent-run guardrails (alongside the identity and -auto-approve checks)
so this now fails fast with a clear error before any component runs, instead
of silently corrupting terminal output.

* fix: surface a clear error when a terraform JSON output line exceeds the parser buffer

Next() wrapped bufio.ErrTooLong the same as any other scanner error, giving
no indication of what actually happened when a single streamed line
exceeded the buffer. Detect it specifically and report the configured
buffer limit so the failure is actionable instead of opaque.

* docs: record the streaming UI /dev/tty fix and blog cast recording

Fix-log entry for the previous commit's pkg/terraform/ui/executor.go change
(Bubbletea crashed opening /dev/tty when output was TTY-capable but stdin
had no real controlling terminal) and the new --ui demo cast recording,
generated and validated against the fix.

* fix(ci): raise Windows Build job timeout to 60m for cold cache saves

Build (windows) hit its 45-minute job timeout mid-way through the trailing
actions/setup-go cache-save post-step, cancelling the job even though every
real build/test step had already succeeded (Build itself took 5m31s). This
cascaded: the per-OS Acceptance Tests gates and the k3s matrix job all
needs: Build (windows) and correctly failed loudly when it never completed.

This is the second occurrence of the same class of flake already documented
in this job's timeout comment (first killed at 30m10s, raised to 45m). A
large merge from main cold-started the Windows runner's Go cache, and 45
minutes wasn't enough headroom for that cache save this time. Raise to 60m.

* fix(ci): retry govulncheck on transient vuln.go.dev fetch failures

golang/govulncheck-action fetches the vuln.go.dev database itself with no
retry: a transient CDN 403 (observed: "HTTP GET
https://vuln.go.dev/index/modules.json.gz returned unexpected status: 403
Forbidden") fails the whole step on the first attempt, and since no SARIF
file is ever written, the always-run upload-sarif step then also fails
with "Invalid SARIF ... Unexpected end of JSON input".

Wrap the step in a 3-attempt retry, following the same continue-on-error +
if-outcome-chaining shape already established in
.github/actions/download-artifact-retry for the same class of problem
(actions/download-artifact not retrying connection-level failures either).
A genuine vulnerability finding still fails the job after exhausting
retries -- this only re-runs on failure, it doesn't change what counts as
one.

* Revert "fix(ci): retry govulncheck on transient vuln.go.dev fetch failures"

This reverts commit 036c38f.

* fix: streaming UI printed duplicate progress lines instead of redrawing in place

runTUIProgram passed the raw global iolib.UI writer to
tea.WithOutput(...). With secret masking enabled (the default), iolib.UI's
concrete type is *dynamicMaskedWriter, which has no Fd() method. Bubbletea
type-asserts its output writer for Fd() to tell a real terminal from a
pipe; failing that, it silently disables its own cursor-based
line-clearing, so every render tick appended a new line instead of
overwriting the previous frame (reported: ~25 duplicate "plan
<stack>/<component>" lines before the final summary).

Use iolib.MaskWriter(os.Stderr) instead, which returns *maskedWriter and
forwards Fd()/Read()/Close() to the real os.Stderr -- the same fix
already applied to the vendor-pull Bubbletea program in
internal/exec/vendor_model.go, which pkg/terraform/ui's executor never
picked up since it was written independently.

Also documents enabling the UI via `atmos config set
components.terraform.ui.enabled true` in the changelog post, and
regenerates the --ui demo cast (now shows correct per-frame cursor-up
sequences instead of the bug).

* fix(security): remediate Dependabot alert cloudposse#278 (postcss-selector-parser DoS)

postcss-selector-parser <7.1.3 allows denial of service through
uncontrolled AST recursion (GHSA, Dependabot alert cloudposse#278). Override to
^7.1.3 via pnpm.overrides, matching this repo's existing pattern for
transitive-dependency patch bumps. Resolved to 7.1.5 in the lockfile.

Verified: website build succeeds (npm run build).

* fix: streaming UI error count didn't include diagnostic-only failures

GetErrorCount() only counted resource-state errors while HasErrors()
(which gates whether the "failed" banner shows at all) also counted
error-severity diagnostics with no resource address. A failure with no
individually-errored resource -- e.g. Terraform's own -out= planfile
write failing -- tripped HasErrors() but GetErrorCount() stayed 0,
producing a confusing "Plan <stack>/<component> failed: 0 error(s)"
summary line.

Bring GetErrorCount() in sync with HasErrors()'s definition of a
failure. Extended TestResourceTracker_HandleDiagnostic with regression
coverage for the diagnostic-only case.

* fix: streaming UI plan/apply failed writing the planfile with a relative workdir

executePlanWithTempFile and generateTwoPhasePlanFile built the planfile's
-out=/apply-file argument as filepath.Join(opts.WorkingDir, filename), but
the terraform subprocess's cmd.Dir is already opts.WorkingDir. When
opts.WorkingDir is relative (it derives from atmosConfig.BasePath, which
unlike BasePathAbsolute isn't guaranteed absolute -- e.g. under --chdir),
the subprocess re-resolves that same relative string against its own cwd
(already opts.WorkingDir), doubling the path
(<workdir>/<workdir>/.atmos-plan-....tfplan) and failing with "no such
file or directory" on the final -out= write. Terraform ran successfully
for several seconds beforehand since refresh/read operations don't need
this doubled path, only the final write does -- and its diagnostic echoes
the raw (undoubled) argument string, which is why the error looked like a
normal path.

Reproduced deterministically by the user across repeated runs against the
same stack/component, ruling out the external-actor theory floated in the
prior diagnostic-only-error-count fix's follow-up notes.

Add planFilePath(), which joins then absolutizes via filepath.Abs (immune
to the subprocess's cwd either way), and use it at both call sites.
Verified the new regression test actually catches the bug: reverted
planFilePath to a bare filepath.Join locally, confirmed the test failed,
restored the fix.

* fix: offer profile-selection prompt when terraform hits a missing identity

atmos terraform apply (and helmfile/packer/native components) failed
auth setup before AuthManager.Authenticate() could ever run, so the
existing interactive "select a profile that defines this identity"
prompt was unreachable — callers only saw the flat "invalid auth
config: invalid identity config". Add a manager-independent entry
point to the existing fallback flow and wire it into the shared
setupTerraformAuth/createAndAuthenticateAuthManagerWithDeps seam so
every component type gets the same prompt atmos auth login already has.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): bump vulnerable Go dependencies flagged by CodeQL

Minor-version bumps only (compliant with dependabot.yml's major-bump
ignore policy): golang.org/x/crypto v0.54.0->v0.55.0 (GO-2026-6303),
golang.org/x/image v0.43.0->v0.45.0 (GO-2026-6222), golang.org/x/mod
v0.38.0->v0.40.0 (GO-2026-6180/6179), github.com/google/cel-go
v0.29.0->v0.30.0 (GO-2026-6094), go.opentelemetry.io/otel
v1.43.0->v1.44.0 (GO-2026-5158). Regenerated NOTICE via
scripts/generate-notice.sh for the updated license URLs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: TestPlanFilePath_AbsolutizesRelativeWorkingDir failed on Windows CI runners

The test built its expected path from filepath.EvalSymlinks(tmp), which
only accounts for macOS's /tmp -> /private/tmp symlink. On this Windows
runner GetCurrentDirectory reports the post-chdir cwd in its short
8.3-alias form (e.g. "RUNNER~1" for a "runneradmin" user profile), which
EvalSymlinks doesn't replicate, so the assertion compared a short-form
actual path against a long-form expected one.

planFilePath resolves via filepath.Abs, which on Windows joins against
the same GetCurrentDirectory value os.Getwd() exposes. Building the
expected path from os.Getwd() (called after the chdir) instead makes the
test match whatever form the OS reports, on any platform.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate 3 new Dependabot alerts (grpc-go, browserslist, postcss-selector-parser)

Minor-version bumps only, compliant with dependabot.yml's major-bump
ignore policy:
- google.golang.org/grpc v1.82.1 -> v1.83.1 (GHSA-vp52-pcj8-j9qc,
  alert cloudposse#279): HTTP/2 DATA frame fragmentation memory exhaustion.
- browserslist -> ^4.28.7 via pnpm override (GHSA-c83g-rgw3-j3cx /
  GHSA-73wf-gq98-2v4g, alerts cloudposse#281/cloudposse#282): unbounded cache growth and a
  crash via untrusted browserslist-stats.json.
- postcss-selector-parser@^6 -> ^6.1.3 via pnpm override
  (GHSA-w9m9-85wc-3x92, alert cloudposse#280): uncontrolled AST recursion DoS.
  This is a separate transitive 6.x line from the 7.x one already
  pinned; both now carry overrides.

Regenerated NOTICE (go-licenses) and website/pnpm-lock.yaml
(pnpm install --lockfile-only) for the version bumps; verified
`pnpm run build` still succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: clarify streaming UI dependency tree is resource-scoped, slow down deploy-ui cast

The streaming UI's dependency tree is built per-component from a single
plan file (pkg/terraform/ui/tree_builder.go), showing resource addresses
and actions — not a cross-component dependency graph. Reword to say
"resource dependency tree" to avoid the ambiguity.

Also drop the deploy-ui.cast blog embed to speed=0.3 (from the 0.6
default): the underlying recording only captures ~4.3s of real terminal
activity, so even the default slowdown plays too fast to follow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(terraform-ui): connect dependency-tree rails; add pkg/ui/tree gutter model

The streaming UI's dependency tree rendered with broken rails: attribute-diff
rows blanked the ancestor rails to spaces, nothing carried a rail from a node
down to its first child through those rows, and count/for_each resources were
flattened to the root because config-level dependencies (base addresses) never
matched their instance-keyed plan addresses in either direction.

Move the gutter geometry into pkg/ui/tree: Connector/ContentGutter/SpacerGutter
are pure functions of a node's Path, and Violations checks the invariant that
every box-drawing character has a rail or its parent's connector directly above
it. A 500-tree property test covers the model; tree_render asserts the real
renderer satisfies it in compact and non-compact modes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(terraform-ui): stop the completion frame erasing rows above the progress block

Bubbletea repositions to the top of the previous frame before writing the
done-state view, so the model's own cursor-up loop climbed 2+N rows above the
block, wiping the typed command, init/workspace lines and prior output, and
left the summary stranded with a blank tail. Erase to end of screen from the
frame top instead. CastPlayer's erase-below now drops the rows like a real
terminal rather than keeping blank scrollback, which is what made the player
auto-scroll to nothing at the end of a recording.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: record the streaming UI cast against a multi-resource fixture with real timing

Replace the single null_resource deploy-ui cast (0.65s of real activity, no
tree to show) with a dedicated demo/casts/fixtures/streaming-ui fixture: a
null_resource + time_sleep VPC (vpc, subnets, route table associations) with
genuine create/destroy delays, recorded through plan, apply and destroy in one
cast so all three --ui paths are shown.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(terraform-ui): harden streaming UI edge cases

- Truncate long attribute lines by rune, not byte, so multi-byte UTF-8 is
  never split mid-character.
- Strip instance keys on module segments too (module.x["k"].type.name) when
  resolving dependencies, and keep the trailing resource type/name of a
  nested-module reference instead of collapsing it to the module path.
- Don't mistake the value of a value-taking apply flag (-var, -lock-timeout,
  ...) for a trailing positional plan file.
- Reap a killed terraform process after a TUI failure so it isn't left a
  zombie with its pipes open, and mask terraform stderr before logging it,
  since the logger bypasses the masking writer when no log file is set.
- Pin CI=true in the dispatch tests so the streaming precondition gate is
  deterministic regardless of the runner's TTY.
- Schema: max_lines minimum 0; note destroy among the supported commands.
- Doc touch-ups (fenced block languages, skill path).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* refactor(list): derive tree spacer rails from pkg/ui/tree; check list trees are connected

The stacks/instances trees add spacer rows as marker-titled lipgloss child
nodes and then replaced each rendered marker row by counting its leading
spaces and emitting a single bar. That dropped every ancestor rail of a
nested spacer (the ones between a stack's components), breaking the gutter.

Replace it with pkg/ui/tree.SpacerFromConnectorRow, which keeps the ancestor
rails and turns the node's own connector into a rail. Teach Violations to
recognize lipgloss/tree's three-column enumerator arm alongside this
package's four-column segments, and assert the stacks, instances and
dependencies trees satisfy the connectivity invariant, with a regression
for the nested spacer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(terraform-ui): truncate by terminal-cell width, not rune count

lipgloss.Width(line) can exceed maxWidth while len([]rune(line)) is still
less than maxWidth-3 for wide (e.g. CJK) characters, which occupy two cells
per rune - slicing the rune slice at maxWidth-3 in that case indexes past
its end and panics. Switch makeTruncator to runewidth.Truncate/StringWidth,
already used elsewhere in this package for the same purpose.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(security): remediate 4 secrets-inherit CodeQL alerts in release workflows

Replace `secrets: inherit` on the reusable-workflow calls in test.yml,
nightlybuilds.yml, feature-release.yml (-> cloudposse/.github's
shared-go-auto-release.yml) and build.yml (-> shared-release-branches.yml)
with an explicit secrets map, following the principle of least privilege.

Traced the full transitive secret closure through both callees and their own
nested reusable-workflow calls (shared-go-auto-release.yml ->
shared-auto-release.yml, twice) to confirm nothing is missed:
BOT_GITHUB_APP_PRIVATE_KEY, GPG_PRIVATE_KEY, GPG_PRIVATE_KEY_PASSPHRASE for
the go-auto-release chain; BOT_GITHUB_APP_PRIVATE_KEY alone for
shared-release-branches.yml, which does not delegate further.

Alerts: cloudposse/atmos#5341, #5342, #5343, #5344

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): revert secrets-inherit narrowing that broke Tests/Feature release

The earlier security-remediate commit (f6fbd20) replaced `secrets: inherit`
with an explicit secrets map on the reusable-workflow calls in test.yml,
nightlybuilds.yml, feature-release.yml, and build.yml. Neither callee
(cloudposse/.github's shared-go-auto-release.yml or
shared-release-branches.yml) declares a `workflow_call.secrets:` schema --
only `inputs:` -- so GitHub rejects a named-secret map against them outright.

This surfaced on the PR as the "Tests" and "Feature release" workflows both
failing with startup_failure ("likely failed because of a workflow file
issue"), with zero jobs ever created -- the whole test suite silently stopped
running. build.yml has the identical shape but only triggers on
release/workflow_dispatch, so it hadn't failed yet on this PR, just hadn't
run at all.

Revert all four back to `secrets: inherit`, which is the only valid way to
pass secrets to a reusable workflow that doesn't declare named secret inputs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): gate the feature release job to same-repository pull requests

feature-release.yml runs on pull_request by design: it publishes a prerelease
binary for a PR a maintainer has labeled release/feature, so the shared
workflow checks out the PR head and signs/publishes with the release secrets.

Add an explicit same-repository guard on the job. pull_request already
withholds repository secrets from fork PRs, but this makes that boundary
explicit in the workflow itself and keeps a labeled fork PR from starting the
job at all instead of failing partway through with empty secrets. Document
why the trigger is pull_request and why the secrets can't be narrowed: the
callee declares no workflow_call.secrets schema, so an explicit secrets map
is rejected at startup (the earlier attempt was reverted in ec0cc09).

Addresses the CodeRabbit finding on the secrets: inherit line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* ci(test): cut Windows acceptance-shard budgets to 50m job / 40m step; drop shard-1 override

Measured over 378 successful Windows shard jobs across 38 main runs
(2026-08-19..09-02): whole job p50 14.8m / p95 27.4m / max 44.5m, and the
"Acceptance tests" step p50 6.8m / p95 18.8m / max 30.1m. 50/40 clears both
maxima with headroom.

The old 65/55 budgets bought nothing: the only Windows jobs that ever ran
past ~45m were ones whose steps had all finished and passed, after which the
runner's end-of-job results upload stalled indefinitely (the job's log blob
is never received), and in the same sample no job ever stalled after
"Complete job" and then recovered. A larger budget only decides how long a
dead job holds the run before failing it.

Drop the shard-1 override (65/60 on every platform): per-shard maxima are
linux 16.3m / macos 19.8m / windows 31.1m, all inside the flavor budgets,
and shard 3 -- not shard 1 -- is the slow one on Windows. Left in place, it
would have kept a hung Windows shard-1 job at the old budget.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
preview — 928b5fe4 Deployed Dec 21, 2022 by johncblandii
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants