Skip to content

fix(ai): close DX gaps found in atmos ai field test - #2903

Merged
Andriy Knysh (aknysh) merged 14 commits into
mainfrom
osterman/field-test-ai-commands
Aug 10, 2026
Merged

Andriy Knysh (aknysh) merged 14 commits into
mainfrom
osterman/field-test-ai-commands

Conversation

@osterman

@osterman Erik Osterman (Cloud Posse) (osterman) commented Aug 7, 2026 •

Copy link
Copy Markdown
Member

what

  • Adds atmos ai skill update [name], a new command that compares each installed bundled skill's recorded version against the catalog embedded in the running binary and reinstalls only the ones that are actually outdated — closes the "no update command" gap the fixes below originally left deferred. See the blog post for the full story.
  • Enforces the compatibility.atmos version-compatibility gate for bundled and multi-skill Git package skill installs, not just single-skill Git clones (it was previously skipped entirely for those two paths).
  • Rejects unrecognized atmos ai skill install/uninstall/update --client values instead of silently no-op'ing, by extending pkg/flags's WithValidValues to work on string-slice flags generically (this also fixed a latent bug where WithValidValues was silently dead for every command that binds flags via BindFlagsToViper without calling the full Parse() pipeline).
  • Warns when --path is combined with --client/--scope/--global/--all-clients on skill install, since --path skips auto-distribution and those flags are otherwise silently ignored.
  • Gives the skill-registry-corruption error an actionable hint via the error-builder pattern instead of a bare wrapped JSON error.
  • Shows a skill's minimum required Atmos version in skill list --detailed, and flags when an installed skill has a newer catalog version available (using the same comparison update now acts on).
  • Fixes agent-skills/skills/atmos-ai/SKILL.md doc drift (it never documented skill install at all) and removes a phantom info subcommand from atmos ai skill --help.
  • Adds local-path/file:// support to skill source parsing and the downloader.
  • Documents --scope/--global precedence on skill install/uninstall/update.
  • Makes atmos ai exec/ask --session actually persist and resume conversations — previously a documented flag that was a complete no-op.
  • Resolves a session's Model from the constructed AI client instead of an independent config lookup, fixing sessions export/import for the default zero-config claude-code provider path (previously exported checkpoints for that path could never be re-imported).
  • Applies --mcp server filtering for CLI providers (claude-code/codex-cli/copilot-cli/gemini-cli) too — it was silently ignored, so all configured MCP servers were always passed through regardless of the flag.
  • Rejects invalid --format values on ai exec instead of silently falling back to text.
  • Behavior change: ai exec can now return exit code 2 for a genuine infrastructure-level tool failure (e.g. an unregistered tool) immediately, without waiting on the 25-iteration tool-call loop to exhaust as it did before.
  • Behavior change: sessions clean --older-than 0d now deletes all sessions immediately, distinguished from the flag not being passed at all (which still defaults to 30 days); negative durations are now a hard parse error instead of silently falling back to the default.
  • Remediates 7 open Dependabot alerts (2 high, 4 medium, 1 low) in transitive website dependencies: js-yaml (GHSA-5p4m-2wfm-xmqj, quadratic CPU consumption in !!omap resolution) and mermaid (5 advisories), via pnpm.overrides bumps within their existing major versions. No CodeQL alerts were open.
  • Fixes a flaky TestManager_ExportSession_WarnsOnUnimportableCheckpoint CI failure: the test asserted on raw ANSI-styled ui.Warning() output, which the formatter renders as two adjacent styled runs under CI=true — same visible text, different byte layout, so the test passed locally and failed in CI. Strips ANSI before asserting on content now.

why

  • These are all findings from a hands-on field test of the atmos ai command surface — reading the real implementation, hypothesizing plausible misuse an automated test wouldn't catch, and executing for real against isolated fixtures — rather than a spec change or feature request. Most are silent DX gaps (a flag that looks like it works but doesn't, an error with no way forward, a validation check that only applies on some of the paths that need it).
  • The two behavior changes exist because the current behavior actively undermines the documented contract: an exit code that's "practically unreachable" is useless to scripted consumers, and a duration flag that silently no-ops on 0d instead of doing what it says is a footgun in the other direction (a user who deliberately asks to delete everything gets nothing, silently).
  • atmos ai skill update exists because, once asked, leaving "no update command" as a documented gap wasn't the right call — bundled skills going stale after a binary upgrade is exactly the kind of silent drift this whole PR is about fixing elsewhere.
  • The security fix was picked up automatically after pushing this branch (GitHub reported the alerts against the default branch) and is bundled here per this repo's standing policy of fixing security alerts directly on the branch already in flight rather than opening a separate PR.

references

  • No tracked GitHub issues — everything here was discovered fresh during this field test and addressed directly in this PR, including the atmos ai skill update command that was initially scoped out and then built once asked for.

Fixes 16 findings from a hands-on field test of `atmos ai` commands:

skill install/uninstall:
- Enforce the compatibility.atmos version gate for bundled and
  multi-skill Git package installs, not just single-skill Git clones
- Reject unrecognized --client values instead of silently no-op'ing
  (extends pkg/flags WithValidValues to string-slice flags generically)
- Warn when --path is combined with --client/--scope/--global, since
  --path skips auto-distribution and those flags are otherwise ignored
- Give the registry-corruption error an actionable hint via the
  error-builder pattern
- Show a skill's minimum required Atmos version in `skill list --detailed`
  and flag when an installed skill has a newer catalog version available
- Fix `agent-skills/skills/atmos-ai/SKILL.md` doc drift (never documented
  `skill install`) and remove a phantom `info` subcommand from --help
- Add local-path/file:// support to skill source parsing and the
  downloader
- Document --scope/--global precedence

ask/exec/sessions/MCP:
- Make `exec`/`ask --session` actually persist and resume conversations
  (previously a complete no-op despite being a documented flag)
- Resolve a session's Model from the constructed AI client instead of a
  raw config lookup, fixing sessions export/import for the default
  zero-config claude-code provider path
- Apply --mcp server filtering for CLI providers too (was silently
  ignored, all configured servers were always passed through)
- Reject invalid --format values instead of silently falling back to text

Two intentional behavior changes:
- `ai exec` can now return exit code 2 for a genuine infrastructure-level
  tool failure (e.g. an unregistered tool) without waiting on the
  25-iteration tool-call loop to exhaust
- `sessions clean --older-than 0d` now deletes all sessions immediately,
  distinguished from the flag not being passed at all (which still
  defaults to 30 days); negative durations are now a hard parse error
  instead of silently falling back to the default

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bumps pnpm overrides for two transitive website dependencies to their
patched versions:
- js-yaml 3.15.0 -> 3.15.1, 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj,
  quadratic CPU consumption in !!omap resolution, high severity,
  alerts #268/#269)
- mermaid 11.16.0 -> 11.16.1 (GHSA-rhh3-jpg6-66xh, GHSA-c4c3-pg64-4m4v,
  GHSA-6x64-9x62-f2gx, GHSA-3rrr-jr9j-h3q3, GHSA-2v8p-3f2j-5mp7,
  alerts #263-#267)

Both are within-major-version patches (not blocked by dependabot.yml's
major-bump ignore policy). No CodeQL alerts were open. Verified with a
full `pnpm run build` in website/ — succeeds, no new broken links.
NOTICE is unchanged (no license-set change from these bumps).

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

atmos-pro Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@osterman Erik Osterman (Cloud Posse) (osterman) added the patch A minor, backward compatible change label Aug 7, 2026
@github-actions github-actions Bot added the size/l Large size PR label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

  • website/pnpm-lock.yaml

TestManager_ExportSession_WarnsOnUnimportableCheckpoint asserted on the
raw ui.Warning() output, including ANSI styling. Under CI=true (the
real GitHub Actions env, confirmed by reproducing locally with CI=true)
the formatter emits the message across two adjacent styled runs, which
split the literal substring "not be re-importable" the test was
checking for -- the visible text was identical, only the styling
boundary differed from a local run. Passed locally, failed in CI.

Strip ANSI codes and collapse whitespace before asserting on captured
UI output, so the test checks content, not rendering byte-layout.

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

codecov Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.26316% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.78%. Comparing base (4aec494) to head (235994b).

Files with missing lines Patch % Lines
cmd/ai/skill/update.go 75.86% 8 Missing and 6 partials ⚠️
cmd/ai/session_helpers.go 77.77% 7 Missing and 5 partials ⚠️
pkg/flags/standard.go 83.67% 6 Missing and 2 partials ⚠️
pkg/ai/skills/marketplace/installer.go 88.88% 2 Missing and 5 partials ⚠️
pkg/ai/session/sqlite.go 72.22% 4 Missing and 1 partial ⚠️
pkg/ai/skills/marketplace/update.go 88.88% 3 Missing and 2 partials ⚠️
errors/testing.go 57.14% 2 Missing and 1 partial ⚠️
pkg/ai/skills/marketplace/downloader.go 40.00% 2 Missing and 1 partial ⚠️
pkg/flags/types.go 0.00% 3 Missing ⚠️
cmd/ai/ask.go 84.61% 1 Missing and 1 partial ⚠️
... and 4 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2903      +/-   ##
==========================================
- Coverage   82.79%   82.78%   -0.01%     
==========================================
  Files        1863     1866       +3     
  Lines      180652   181025     +373     
==========================================
+ Hits       149568   149868     +300     
- Misses      23293    23341      +48     
- Partials     7791     7816      +25     
Flag Coverage Δ
unittests 82.78% <85.26%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cmd/ai/init.go 55.03% <100.00%> (+1.88%) ⬆️
cmd/ai/sessions.go 88.61% <100.00%> (+0.40%) ⬆️
cmd/ai/skill/clients.go 35.71% <100.00%> (ø)
cmd/ai/skill/install.go 86.30% <100.00%> (+3.25%) ⬆️
cmd/ai/skill/list.go 94.26% <100.00%> (+0.27%) ⬆️
cmd/ai/skill/uninstall.go 77.27% <100.00%> (+1.66%) ⬆️
pkg/ai/executor/executor.go 91.95% <100.00%> (+1.10%) ⬆️
pkg/ai/session/manager.go 64.39% <100.00%> (+0.52%) ⬆️
pkg/ai/skills/marketplace/errors.go 100.00% <ø> (ø)
pkg/ai/skills/marketplace/local_registry.go 86.00% <100.00%> (+0.73%) ⬆️
... and 17 more

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Closes the gap from finding #11 of the field-test fix pass: bundled skills
are static copies made at install time, so upgrading the atmos binary alone
never refreshed a skill you'd already installed, even when the new release
shipped improved skill content. `atmos ai skill list --detailed` already
surfaced an "update available" hint; nothing acted on it.

`atmos ai skill update [name]` compares each installed bundled skill's
recorded version against the catalog embedded in the running binary and
reinstalls only the ones that are actually outdated:

- With no <name>, updates every installed bundled skill that has a newer
  version available (a single confirmation, not one per skill). Skills
  already current are left untouched and reported separately, so it's safe
  to run repeatedly.
- An outdated skill is reinstalled the same way `install <name> --force`
  would install it, reusing all the same client-distribution/scope flags
  and logic (--client, --all-clients, --scope, --global, --path).
- Skills installed from GitHub aren't covered yet (no cheap way to check
  their upstream version without a fetch); update returns a clear error
  pointing at `install <source> --force` as the manual path for those.

Both `atmos ai skill list`'s "update available" indicator and the new
`update` command now share one `marketplace.SkillVersionOutdated` helper
instead of duplicating the comparison, so they can never disagree.

Docs, blog post, and roadmap updated; the `atmos ai skill --help` screengrab
regenerated (--filter'd to just that one cast). Relabeling this PR
patch -> minor since this is new user-visible functionality, which requires
both per this repo's release-doc policy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@osterman Erik Osterman (Cloud Posse) (osterman) added minor New features that do not break anything and removed patch A minor, backward compatible change labels Aug 7, 2026
Per the fix-log skill: one record each for the 16-finding atmos ai
field-test fix pass, the website Dependabot remediation, the flaky
CI test-assertion fix, and the new atmos ai skill update command.

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

mergify Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Important

Cloud Posse Engineering Team Review Required

This pull request modifies files that require Cloud Posse's review. Please be patient, and a core maintainer will review your changes.

To expedite this process, reach out to us on Slack in the #pr-reviews channel.

ValidateFlagValues only checked cmd.Flags().Changed(), so an invalid
value supplied purely via a bound environment variable (e.g.
ATMOS_AI_SKILL_CLIENT=bogus) silently bypassed validation -- a skill
install/update would report success while distributing to zero
clients. Now validates whenever Viper reports the value as actually
set (CLI or env), not just CLI-changed.

Found via a field-test pass on the atmos ai skill update /
pkg/flags changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
UpdateAllBundled/InstallAllBundled's batch path logged a warning per
failed skill but always returned nil, so a real per-skill failure
(e.g. a reinstall that can't remove the existing install) was
reported as overall success with exit code 0 -- the only signal was
an easily-missed log line, invisible entirely at logs.level: Error.

Adds a distinct outcomeRejected for expected compatibility/structure
skips (already covered by an existing test), keeping it separate
from genuine outcomeFailed so only real failures propagate an error.

Found via a field-test pass on the atmos ai skill update changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The anonymous-session branch (no --session given) still called
getModelFromConfig -- a raw config lookup with no default fallback --
instead of client.GetModel(), unlike the named-session branch fixed
in a prior commit. A zero-config claude-code chat with no --session
got a blank Model on its session record, breaking re-import (which
requires a non-empty Model).

Found via a field-test pass on the atmos ai session DX fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
prepareSession returned (nil, nil) identically for "no --session
given" and "--session given but ai.sessions.enabled is false",
so a user could run `exec --session foo` across multiple invocations
believing conversation context was being carried over, with nothing
persisted and no indication why -- only discoverable indirectly via
`sessions list` erroring "sessions are not enabled".

Found via a field-test pass on the atmos ai session DX fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two processes opening the same new session database at once (e.g.
concurrent `atmos ai exec --session <same-name>` invocations) could
crash with SQLITE_BUSY: PRAGMA journal_mode = WAL briefly needs
exclusive access to switch modes, and busy_timeout was set after it
in the pragma list, so a connection that lost the race had no busy
timeout in effect yet. Reorders busy_timeout first and adds a bounded
retry loop around pragma execution, since the pure-Go sqlite driver
(modernc.org/sqlite) doesn't consistently honor busy_timeout for that
specific one-time mode switch.

Found via a field-test pass on the atmos ai session DX fixes.

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

mergify Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

💥 This pull request now has conflicts. Could you fix it Erik Osterman (Cloud Posse) (@osterman)? 🙏

@mergify mergify Bot added the conflict This PR has conflicts label Aug 8, 2026
…i-commands

# Conflicts:
#	cmd/ai/skill/list.go
#	cmd/ai/skill/list_test.go
- github.com/go-git/go-git/v5: 5.19.1 -> 5.19.2, fixing a symlink
  traversal in worktree operations (GHSA-hc8v-wwc9-vgxm, high) and a
  path traversal in reference name handling (GHSA-qgq7-7hm3-q39j,
  medium). Alerts #270, #271.
- dompurify (website, transitive via pnpm override): 3.4.12 -> 3.4.13,
  fixing an IN_PLACE sanitization XSS via detached subtree hook
  removal (GHSA-55q2-fjhq-7xh7, medium). Alert #272.
- nanoid (website, transitive via pnpm override, both the direct
  override target and postcss's own dependency): 3.3.15/3.3.16 ->
  3.3.18, fixing two infinite-loop DoS issues with negative/zero size
  (GHSA-28wg-ghj8-5hjv, GHSA-2v37-7h3g-55p8, high). Alerts #273, #274.

Not fixed: #275/#276 (npm image-size <= 2.0.2, high) -- no patched
version exists yet upstream (first_patched_version is null on both
advisories); nothing to bump to.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@aknysh
Andriy Knysh (aknysh) added this pull request to the merge queue Aug 8, 2026
@atmos-pro

atmos-pro Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 8, 2026
@atmos-pro

atmos-pro Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

TestSetDescribeAffectedFlagValueInCliArgs_BaseResolution's "CI
auto-detect when enabled and no explicit base" subtest cleared
GITHUB_ACTIONS/CI but not GITHUB_EVENT_PATH. When this test suite
runs locally or under a regular pull_request CI trigger, no merge_group
event file is present, so resolveMergeGroupBase falls back to the
simulated GITHUB_BASE_REF the subtest sets, and the test passes.

But when this test actually runs inside a real merge_group-triggered
workflow (i.e. a PR going through the GitHub merge queue), the runner's
real GITHUB_EVENT_PATH points at a genuine merge_group event payload
with a real base_sha -- and resolveMergeGroupBase correctly prefers
that real payload data over GITHUB_BASE_REF, per its documented
fallback order. The subtest never accounted for this, so it fails with
an empty Ref field specifically -- and only -- in real merge-queue
runs, not locally or in regular PR CI. Reproduced locally by pointing
GITHUB_EVENT_PATH at a synthetic merge_group payload; fixed by
explicitly clearing GITHUB_EVENT_PATH so the subtest is hermetic
regardless of the ambient CI context it happens to execute in.

This was pre-existing on main (from PR #2395) and surfaced when PR
#2903 hit the merge queue for the first time; not a regression from
this branch's own changes, but fixed here directly per repo convention
for CI-blocking issues found on an open PR's branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman) added a commit that referenced this pull request Aug 8, 2026
…_EVENT_PATH

TestSetDescribeAffectedFlagValueInCliArgs_BaseResolution's merge_group
subtest only overrode GITHUB_ACTIONS/GITHUB_EVENT_NAME/GITHUB_BASE_REF,
so it depended on $GITHUB_EVENT_PATH being unset in the ambient
environment. That's true for local/regular CI runs, but not when the
test itself runs inside a real GitHub Actions merge_group job (i.e.
the merge queue): the real event payload's merge_group.base_sha then
takes precedence over the simulated GITHUB_BASE_REF, resolving to a
SHA instead of the "refs/remotes/origin/main" ref the test asserts.

This was failing the merge queue for this PR and, independently, for
#2900 and #2903 as well -- confirmed pre-existing and unrelated to any
of their changes. Fixed by explicitly forcing GITHUB_EVENT_PATH="" so
the subtest deterministically exercises the fallback path it's meant
to test, regardless of the real job's event context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 8, 2026
…i-commands

# Conflicts:
#	website/pnpm-lock.yaml
@aknysh
Andriy Knysh (aknysh) added this pull request to the merge queue Aug 10, 2026
@atmos-pro

atmos-pro Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

Merged via the queue into main with commit f31c1ec Aug 10, 2026
88 checks passed
@aknysh
Andriy Knysh (aknysh) deleted the osterman/field-test-ai-commands branch August 10, 2026 20:53
@atmos-pro

atmos-pro Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@mergify mergify Bot removed the needs-cloudposse Needs Cloud Posse assistance label Aug 10, 2026
@github-actions

Copy link
Copy Markdown

Warning

Release Documentation Required

This PR is labeled minor or major and requires documentation updates:

  • Changelog entry - Add a blog post in website/blog/YYYY-MM-DD-feature-name.mdx
  • Roadmap update - Update website/src/data/roadmap.js with the new milestone

Alternatively: If this change doesn't require release documentation, remove the minor or major label.

zack-is-cool pushed a commit to zack-is-cool/atmos that referenced this pull request Aug 10, 2026
…udposse#2880)

* fix(steps): resolve relative paths against step.WorkingDirectory

The archive, file, workdir, junit, and container build step handlers
resolved relative source/destination/path/glob/context fields via
template substitution only, then let filesystem calls resolve them
against the Atmos process's own cwd instead of step.WorkingDirectory.
This surfaced most visibly for `type: archive` hooks, since the hooks
engine correctly defaults working_directory to the component path but
the handler never read it back.

Add a shared BaseHandler.ResolveInWorkingDirectory helper that anchors
a relative resolved value to step.WorkingDirectory (falling back to
process cwd when unset, matching prior behavior), and apply it across
the five affected handlers. container_build.go additionally anchors
Dockerfile to the resolved Context rather than WorkingDirectory
directly, matching Docker's own convention.

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

* docs(fixes): add fix record for step WorkingDirectory bug

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

* test(runner/step): close coverage gaps in ResolveInWorkingDirectory and loadReport

Codecov flagged handler_base.go and junit.go below the 85% patch-coverage
threshold. Add a case that exercises the relative (non-template)
WorkingDirectory branch in resolveWorkingDirectory, and a junit test that
triggers a WorkingDirectory template-resolution error from inside
loadReport's per-pattern loop, distinct from the already-covered `files`
template error path.

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

* fix(steps): anchor container bake/cache paths and preserve workflow error context

A field test of the WorkingDirectory fix found the container_build.go handler
was only partially fixed: build.bake.file/bake.files and cache.from/cache.to
type: local src/dest still resolved against the Atmos process's own cwd
instead of step.WorkingDirectory, reproduced live as a silent build against
the wrong bake file. Both now route through ResolveInWorkingDirectory like
context/dockerfile already do.

Also root-caused and fixed a separate defect surfaced while verifying error
output: buildWorkflowStepError dual-wrapped step errors with fmt.Errorf before
building the final error, and cockroachdb/errors treats that Go 1.20
multi-error shape as an opaque leaf node, silently dropping any hints/context
a handler attached deeper in the chain. Switched to WithCause, which extracts
them eagerly.

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

* fix(steps): anchor container bake/cache paths and preserve workflow error context

Address CodeRabbit findings on PR cloudposse#2880:
- Remove the host-specific /tmp/atmos-field-test/ path from the fix doc,
  replacing it with a worktree-relative fixture reference.
- Split TestBuildWorkflowStepError and TestBuildWorkflowStepErrorPreservesInnerHintsAndContext
  out of workflow_utils_test.go into a focused workflow_step_error_test.go,
  keeping step-error tests co-located and out of the oversized (pre-existing)
  workflow_utils_test.go file.

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

* test(runner/step): close container_build.go coverage gaps from bake/cache anchoring

Codecov flagged container_build.go's patch coverage below 85% after the
bake/cache anchoring follow-up. Add regression tests for the three
previously-uncovered error branches: resolveBakeFiles propagating a
per-entry template failure, anchorCacheLocalPaths' own resolve failure, and
that failure propagating out through resolveBuildCache. handler_base.go's
remaining gap (os.Getwd/filepath.Abs failing inside resolveWorkingDirectory)
is left uncovered deliberately -- it requires corrupting the process's own
working directory to reach, has no DI seam, and matches this repo's existing
untested pattern for the identical os.Getwd/filepath.Abs fallback in
container_run.go.

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

* fix(docs): exclude reproducible-builds.org from link checking (CI connection refused)

CI's Check Markdown Links job failed on docs/prd/archive-step.md's citation
of the SOURCE_DATE_EPOCH origin -- the CI runner's outbound request was
refused while the page returns 200 OK outside CI. Follows the repo's
existing precedent for excluding CI-hostile hosts (docs.docker.com,
otelic.com, taskfile.dev, etc.) in lychee.toml.

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

* feat(hooks): anchor bare-relative working_directory to the component

An explicit working_directory: on a kind: step/kind: steps hook was always
resolved against the Atmos process's own cwd, regardless of shape. Following
docs/prd/base-path-resolution-semantics.md's existing Dot/Bare convention: a
dot-prefixed value (., .., ./x, ../x) keeps resolving against the process
cwd; a bare relative value (x, x/y) now resolves against the component's
own working directory instead -- the same directory ComponentPath(ctx)
already computes for the unset-default case, so this stays compatible with
provisioned working directories and metadata.component aliasing for free.
Workflows and custom commands are unaffected -- they have no "current
component" concept, so bare-relative values there still resolve against the
process cwd exactly as before.

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

* fix(security): remediate 7 open Dependabot alerts

Bump pnpm.overrides floors for transitive npm dependencies flagged by
Dependabot (all patch/minor bumps within the same major, not blocked by
dependabot.yml's major-version ignore policy):

- js-yaml 3.15.0 -> 3.15.1 (cloudposse#269, GHSA-5p4m-2wfm-xmqj: quadratic CPU
  consumption in !!omap resolution, high)
- js-yaml 4.3.0 -> 4.3.1 (cloudposse#268, same advisory, 4.x line, high)
- mermaid 11.16.0 -> 11.16.1 (cloudposse#267, cloudposse#266, cloudposse#265, cloudposse#264, cloudposse#263: five advisories
  ranging low-medium)

NOTICE unchanged (no license changes). Verified: pnpm install regenerated
website/pnpm-lock.yaml with the bumped versions resolved, atmos fix lint
(patch-scoped, no Go files touched) is a no-op, and cd website && npm run
build succeeds with no new broken links.

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

* docs(fixes): fix markdownlint MD018 in working-directory fix record

Address CodeRabbit finding on PR cloudposse#2880: a paragraph wrap left "cloudposse#2880):" at
the start of a line, which markdownlint's atx-heading rule (MD018) flags as
a heading missing a space after the hash. Rewrap so the line doesn't start
with a bare hash-prefixed token.

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

* fix(runner/step): close remaining working_directory anchoring gaps

Found by field-testing the working_directory fix against real hooks,
workflows, and a live docker build instead of just unit tests:

- workdir step: `source` (local relative path) now anchors to
  `working_directory` via a new `sourceprov.WithBaseDir` option,
  matching `path`'s existing anchoring instead of silently falling
  back to the process cwd.
- workflow-level `working_directory:` default now reaches extended
  step types (archive/file/junit/workdir/container), not just
  shell/exec/atmos steps, which silently ignored it before.
- step-level `working_directory` now expands a leading `~`, matching
  the tilde expansion --chdir already gets.
- Corrected agent-skills/atmos-steps and the workflow/hooks docs,
  which claimed a single blanket CWD-vs-base_path rule that doesn't
  match actual per-surface behavior.

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

* docs(steps): document handler-field and unset-hook working_directory contracts

Addresses CodeRabbit feedback on PR cloudposse#2880: state that relative handler
fields (source, destination, path, files, context) resolve against the
already-resolved working_directory, that a container Dockerfile
resolves relative to context rather than working_directory directly,
and that an unset kind: step hook working_directory (not just a bare
value) anchors to the component's own working directory.

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

* fix(runner/step): isolate CI base-resolution test from ambient GITHUB_EVENT_PATH

TestSetDescribeAffectedFlagValueInCliArgs_BaseResolution's merge_group
subtest only overrode GITHUB_ACTIONS/GITHUB_EVENT_NAME/GITHUB_BASE_REF,
so it depended on $GITHUB_EVENT_PATH being unset in the ambient
environment. That's true for local/regular CI runs, but not when the
test itself runs inside a real GitHub Actions merge_group job (i.e.
the merge queue): the real event payload's merge_group.base_sha then
takes precedence over the simulated GITHUB_BASE_REF, resolving to a
SHA instead of the "refs/remotes/origin/main" ref the test asserts.

This was failing the merge queue for this PR and, independently, for
cloudposse#2900 and cloudposse#2903 as well -- confirmed pre-existing and unrelated to any
of their changes. Fixed by explicitly forcing GITHUB_EVENT_PATH="" so
the subtest deterministically exercises the fallback path it's meant
to test, regardless of the real job's event context.

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

* fix(security): remediate 5 of 7 open Dependabot alerts

- github.com/go-git/go-git/v5: v5.19.1 -> v5.19.2, fixing GHSA-hc8v-wwc9-vgxm
  (symlink traversal in worktree operations) and the companion malicious
  reference-name advisory (cloudposse#270, cloudposse#271). Patch-level bump, no API changes.
- website: dompurify pnpm override ^3.4.12 -> ^3.4.13, fixing an IN_PLACE
  hook removal XSS (cloudposse#272).
- website: nanoid pnpm overrides bumped to ^3.3.17 (added a second
  selector, nanoid@^3.3.16, to also catch postcss's own nanoid range,
  which the existing nanoid@3.3.3 selector didn't match), fixing two
  indefinite-loop DoS advisories (cloudposse#273, cloudposse#274). The nanoid 5.x line was
  already patched by the existing override.
- NOTICE regenerated for the go-git/dompurify/nanoid version bumps.

Not fixed: image-size (cloudposse#275, cloudposse#276, both DoS via infinite loop) has no
patched release yet as of this commit -- GitHub's advisories list
"<= 2.0.2" as vulnerable with no first_patched_version. Left as-is
pending an upstream fix.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

These changes were released in v1.226.0-rc.4.

This branch was successfully deployed

1 active and 1 inactive deployments
preview — 235994be Deployed Aug 10, 2026 by github-actions[bot]
screengrabs — 235994be Deployed Aug 10, 2026 by osterman via build #1251
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features that do not break anything size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants