Skip to content

Commit 70732ad

Browse files
ostermanclaude
andcommitted
docs(migration): fix stale no-parity claims for deps/freshness now shipped
#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 #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>
1 parent 9f8cb2b commit 70732ad

5 files changed

Lines changed: 243 additions & 125 deletions

File tree

agent-skills/skills/atmos-migration/SKILL.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,19 @@ These behaviors apply to every task runner. Check them before you open a referen
101101

102102
- **The default order can change.** Task runs `deps:` at the same time by default. Make and Just
103103
run dependencies one after another, unless the user adds a flag such as `make -j`. Atmos steps
104-
always run one after another, unless you put them inside a `parallel` or `matrix` step. Check
105-
the source tool's real default. Do not assume the step order stays the same when you move it to
106-
Atmos.
107-
- **Atmos has no file-freshness cache.** Task's `sources:`/`generates:` fields and non-`.PHONY`
108-
Make targets both skip work when a file has not changed. Atmos steps always run. The
109-
`require`/`assert` step type does not replace this. It only checks that a file exists. It does
110-
not check if the file is new. Tell the user this directly.
104+
always run one after another, unless you put them inside a `parallel` or `matrix` step. A named
105+
task/target/recipe dependency (Task's `deps:`, Make's `target: dep1 dep2`) maps to command-level
106+
`dependencies.commands`/`dependencies.workflows`, not to plain steps or a hand-built `parallel`
107+
step -- it runs concurrently by default and dedups a dependency shared by more than one caller
108+
to a single run, matching Task's/Make's own behavior. Check the source tool's real default. Do
109+
not assume the step order stays the same when you move it to Atmos.
110+
- **Freshness checks map to `inputs`/`artifacts`, not to plain steps.** Task's `sources:`/
111+
`generates:` fields and non-`.PHONY` Make targets both skip work when a file has not changed.
112+
Atmos's step-level `inputs.sources`/`artifacts.paths` fields are the direct match: with no
113+
explicit `when:`, declaring them implicitly means `when: checksum.changed`, and the step is
114+
skipped when nothing has changed since its last successful run. This does not carry over on its
115+
own -- add `inputs`/`artifacts` to the migrated step yourself. The `require`/`assert` step type
116+
does not replace this. It only checks that a file exists, not whether it is fresh.
111117
- **`workflows.base_path` needs to be set explicitly once the user has their own `atmos.yaml`.**
112118
A target chain becomes an Atmos workflow (Principle 7), but `atmos workflow <name>` fails with
113119
`'workflows.base_path' must be configured in 'atmos.yaml'` until you add it (for example,

agent-skills/skills/atmos-migration/references/from-makefile.md

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,19 @@ deploy: build test ## Plan and apply the given ENV (default: dev)
102102
**Steps:**
103103

104104
1. Turn `ENV ?= dev` into a command `flags:` entry with `default: "dev"`.
105-
2. Turn the target order (`deploy: build test`) into steps that run in the same order. In this
106-
example, the steps call the Shape A commands, one after the other.
107-
3. Check if the prerequisites are truly independent. In this example, `build` must finish before
108-
`test` runs, but nothing else depends on their order relative to each other. When two
109-
prerequisites do not depend on each other, use a `parallel` step with `needs:` instead of
110-
listing them one after the other. See [Shape C](#shape-c-recursive-or-parallel-make) for the
111-
general `parallel`/`matrix` pattern.
112-
4. Move the Terraform-specific line, `terraform apply -var-file=envs/$(ENV).tfvars`, to
105+
2. Turn the target list (`deploy: build test`) into command-level `dependencies.commands: [build,
106+
test]`. This is the direct match, not a workaround: it resolves through the same DAG scheduler
107+
as `parallel`/`matrix` `needs:`, runs concurrently by default -- `make` itself does not
108+
guarantee prerequisite order without `-j` either -- and dedups a dependency shared by more
109+
than one target to a single run, the same guarantee `make` already gives for free. Do not turn
110+
this into plain sequential steps unless one prerequisite genuinely must finish before another
111+
starts; if so, declare that dependency directly on the later one's own `dependencies.commands`
112+
instead of ordering a flat list.
113+
3. Move the Terraform-specific line, `terraform apply -var-file=envs/$(ENV).tfvars`, to
113114
[from-native-terraform.md Shape B](from-native-terraform.md#shape-b-single-dir-with--var-file-from-a-makefile).
114115
That guide shows how the Terraform side maps to stacks. Here, the line becomes a single
115116
`type: atmos` step, because `terraform apply` is a native Atmos verb.
116-
5. Turn `ifeq ($(ENV),prod)` conditionals into a Go template conditional inside a custom command:
117+
4. Turn `ifeq ($(ENV),prod)` conditionals into a Go template conditional inside a custom command:
117118
`{{ if eq .Flags.env "prod" }}...{{ end }}`. This is the same pattern used for `--verbose` and
118119
other boolean flags. Inside a workflow, use `when: !cel 'stack == "prod"'` on the step
119120
instead.
@@ -126,11 +127,9 @@ commands:
126127
- name: env
127128
shorthand: e
128129
default: "dev"
130+
dependencies:
131+
commands: [build, test]
129132
steps:
130-
- type: shell
131-
command: atmos build
132-
- type: shell
133-
command: atmos test
134133
- type: atmos
135134
command: terraform apply infra -s {{ .Flags.env }}
136135
```
@@ -182,10 +181,15 @@ commands:
182181

183182
Atmos steps have no tab requirement. Do not confuse `.PHONY` with a caching feature. If a
184183
Makefile target is not `.PHONY` and uses file timestamps to skip work when inputs have not
185-
changed, that caching behavior has no equivalent in Atmos. Atmos steps always run. Tell the user
186-
this directly. Do not imply that the behavior carries over. Task's `sources:`/`generates:`
187-
feature has the same problem. See
188-
[from-taskfile.md](from-taskfile.md#the-sourcesgenerates-gap) for more detail.
184+
changed, turn it into step `inputs.sources`/`artifacts.paths` -- with no explicit `when:`, that
185+
implicitly means `when: checksum.changed`, and the step is skipped when nothing has changed since
186+
its last successful run. It does not carry over automatically; tell the user to add
187+
`inputs`/`artifacts` to the migrated step themselves. Content hashing (the default) is a
188+
deliberate upgrade over Make's own mtime comparison -- a fresh `git clone`/CI checkout resets
189+
every file's mtime, which makes Make think everything changed even when it didn't; use
190+
`when: timestamp.changed` instead for Make's exact mtime semantics. Task's `sources:`/`generates:`
191+
feature maps to the same fields. See
192+
[from-taskfile.md](from-taskfile.md#sourcesgenerates-becomes-inputsartifacts) for more detail.
189193

190194
### Silent recipes and command echo
191195

@@ -210,16 +214,20 @@ language.
210214

211215
## What Not To Do
212216

213-
- Do not build file-timestamp or `.PHONY` caching as an Atmos feature. It does not exist. State
214-
this directly instead of dropping the behavior without comment.
217+
- Do not confuse `.PHONY` with a caching feature -- it is not one. Do not drop file-timestamp
218+
caching without adding the matching `inputs`/`artifacts` fields to the migrated step; it is a
219+
direct match, not a gap, but it does not carry over on its own.
220+
- Do not turn `target: dep1 dep2` into plain sequential steps, or into a hand-built `parallel`
221+
step, without first considering command-level `dependencies.commands` -- it runs concurrently
222+
by default and dedups a dependency shared by more than one target, the way `make` already does.
215223
- Do not turn every private or helper target into its own discoverable command by default. If the
216224
helper is called from only one recipe, put its logic in a step inside the command or workflow
217225
that needs it. If it needs to be called from more than one recipe, or invoked directly for
218226
debugging, make it a custom command with `internal: true` instead -- it stays runnable but is
219227
excluded from `atmos --help` listings and completion suggestions.
220228
- Do not treat "wrap `atmos` commands in the Makefile" as the final state. It is a valid bridge
221-
during early migration, as shown in Shape B, step 4. Leaf targets should become custom
222-
commands. Target chains should become workflows.
229+
during early migration. Leaf targets should become custom commands. Target chains should become
230+
workflows.
223231
- Do not invent `when:` conditions that check flag values on workflow steps. The `when:` field
224232
checks CEL context values, such as `stack`, `ci`, and `local`. Flag-based conditionals belong
225233
in the custom command's own Go templates.

agent-skills/skills/atmos-migration/references/from-taskfile.md

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ Terraform-specific steps.
1818
| `vars:`/`env:` and `sources:`/`generates:` | [Shape C](#shape-c-variables-and-up-to-date-checks) |
1919
| `includes:` (multi-file composition) | see [Common Problems](#includes-multi-file-composition) |
2020

21+
`deps:` maps to command-level `dependencies.commands` (concurrent by default, deduped -- the
22+
direct match). `sources:`/`generates:` maps to step-level `inputs.sources`/`artifacts.paths`
23+
(implicit `when: checksum.changed` -- the direct match). Neither is a gap; both need the user to
24+
add the matching field during migration, since neither carries over automatically.
25+
2126
## Shape A: Simple Tasks
2227

2328
**Before:**
@@ -82,28 +87,19 @@ tasks:
8287
- terraform -chdir=terraform apply -var-file=envs/dev.tfvars
8388
```
8489

85-
This is the most important problem in this migration. Task runs `deps:` at the same time by
86-
default. Atmos custom-command and workflow steps run one after another by default. This is the
87-
opposite default. If you turn `deps: [test, lint]` into two plain steps that run one after
88-
another, the command becomes slower. It also changes what happens when one task fails. To keep
89-
Task's default behavior, put the dependency tasks inside a `parallel` step:
90+
Task runs `deps:` at the same time by default. Atmos custom-command and workflow steps run one
91+
after another by default -- so a `deps:` entry is not a step and never becomes one. It maps to
92+
the command-level `dependencies.commands` field, which resolves through the same DAG scheduler as
93+
`parallel`/`matrix` `needs:` and runs concurrently by default -- matching Task's `deps:` behavior
94+
directly, not working around it with a hand-built `parallel` step:
9095

9196
```yaml
9297
commands:
9398
- name: deploy
9499
description: Plan and apply the given environment
100+
dependencies:
101+
commands: [test, lint]
95102
steps:
96-
- name: checks
97-
type: parallel
98-
fail:
99-
mode: wait_all
100-
steps:
101-
- name: test
102-
type: shell
103-
command: atmos test
104-
- name: lint
105-
type: shell
106-
command: atmos lint
107103
- type: atmos
108104
command: terraform apply infra -s dev
109105
```
@@ -113,9 +109,17 @@ task's Terraform code to `components/terraform/infra/` (the default
113109
`components.terraform.base_path` is `components/terraform`), then swap `infra` for whatever the
114110
user actually names the component.
115111

116-
If the Taskfile's `deps:` list needs its own internal order, add `needs:` to the steps inside the
117-
`parallel` block. Do not assume the tasks should run one after another just because that is
118-
Atmos's default for steps outside a `parallel` or `matrix` block.
112+
`dependencies.commands` also matches a behavior Task itself has that a hand-rolled `parallel`
113+
step does not: if two commands both depend on the same one -- for example both `test` and `lint`
114+
depending on `build` -- Atmos runs `build` exactly once and dedups it, the same as Task's own
115+
`deps:` graph. A `parallel` step calling `atmos build` from two different places would run it
116+
twice. If one dependency itself depends on another (`lint` depends on `build`, and `deploy`
117+
depends on `test` and `lint`), declare that directly on `lint`'s own `dependencies.commands` --
118+
the scheduler resolves the whole transitive graph itself, still deduping `build` to a single run.
119+
120+
Reach for a `parallel` step instead of `dependencies.commands` only for concurrency inside a
121+
single command's own steps, not between named commands -- for example, running several shell
122+
commands side by side that were never their own Task tasks to begin with.
119123

120124
## Shape C: Variables and Up-to-Date Checks
121125

@@ -141,19 +145,33 @@ tasks:
141145
`default: "dev"`. Task's Sprig `default` filter becomes the plain `default:` field.
142146
- Turn `env:` into an `env:` map. The two are almost identical.
143147

144-
### The `sources`/`generates` gap
148+
### `sources:`/`generates:` becomes `inputs`/`artifacts`
145149

146-
Task skips a task's `cmds:` when its `sources:` files match its `generates:` outputs. It checks
147-
this with a file hash. Atmos steps always run. There is no built-in check for whether a file is
148-
up to date. The `require`/`assert` step type does not fix this. It only checks that a file, tool,
149-
or directory exists. It does not compare hashes or timestamps.
150+
Task skips a task's `cmds:` when its `sources:` files match its `generates:` outputs, checked by
151+
default with a content hash (Task also supports `method: timestamp` for an mtime-based check).
152+
The step-level `inputs.sources` and `artifacts.paths` fields are the direct match, with the same
153+
checksum-by-default/timestamp-as-an-option choice:
150154

151-
If the user depends on `sources:`/`generates:` to skip a slow step, such as code generation, tell
152-
them plainly that this behavior does not carry over. Then offer two honest choices:
155+
```yaml
156+
commands:
157+
- name: build
158+
description: Compile the deployable artifact
159+
steps:
160+
- type: shell
161+
command: go build -o bin/handler ./cmd/handler
162+
inputs:
163+
sources: ["cmd/**/*.go"]
164+
artifacts:
165+
paths: ["bin/handler"]
166+
```
153167

154-
1. Accept that the step always runs. This is correct for most fast build steps.
155-
2. Add a hash or timestamp check inside the shell step itself. This is a script the user
156-
maintains. It is not a built-in Atmos feature.
168+
With no explicit `when:`, declaring `inputs`/`artifacts` on a step is enough -- it implicitly
169+
means `when: checksum.changed`, and the step is skipped when the hash of the matched source files
170+
matches the hash recorded after the last successful run. This does not carry over on its own --
171+
add `inputs`/`artifacts` to the migrated step yourself, matching the Taskfile's own
172+
`sources:`/`generates:` lists. The `require`/`assert` step type is a different, older step type --
173+
it only checks that a file, tool, or directory exists, not whether it is fresh, so it does not
174+
replace `inputs`/`artifacts`.
157175

158176
## Common Problems
159177

@@ -173,17 +191,20 @@ Pick the one that fits the content being split:
173191
"stacks/workflows"`) the first time the user's migration reaches a workflow, or `atmos
174192
workflow <name>` fails with `'workflows.base_path' must be configured in 'atmos.yaml'`.
175193

176-
### `sources`/`generates` has no built-in match
194+
### `sources`/`generates` maps to a different field than `steps`
177195

178-
See [Shape C](#the-sourcesgenerates-gap) above. This is the largest real gap in this migration.
179-
State it directly. Do not gloss over it.
196+
See [Shape C](#sourcesgenerates-becomes-inputsartifacts) above. It does not carry over
197+
automatically -- the user must add `inputs`/`artifacts` to the migrated step themselves. State
198+
that directly. Do not gloss over it, and do not claim it "just works" without the field.
180199

181200
## What Not To Do
182201

183-
- Do not drop `sources:`/`generates:` caching without comment. State the change directly. Let
184-
the user decide how, or whether, to replace it.
185-
- Do not turn `deps:` into plain sequential steps without warning the user about the change in
186-
default concurrency.
202+
- Do not drop `sources:`/`generates:` without adding the matching `inputs`/`artifacts` fields to
203+
the migrated step. It is a direct match, not a gap, but it does not carry over on its own.
204+
- Do not turn `deps:` into plain sequential steps, or into a hand-built `parallel` step, without
205+
first considering command-level `dependencies.commands` -- it is the direct match: concurrent
206+
by default, and it dedups a dependency shared by more than one command the same way Task's own
207+
`deps:` graph does.
187208
- Do not describe `require`/`assert` as a freshness or caching check. It only checks that
188209
something exists.
189210
- Do not turn every `internal: true` task into its own discoverable command by default. If it is

0 commit comments

Comments
 (0)