Reference
On this page
Subcommands #
| Command | Does |
|---|---|
atago run | run specs and report results |
atago record | run a command once and write a spec from what it observed (--pty for interactive sessions) |
atago init | scaffold a spec (--template for browser, cli, db, grpc, http, mock, services, ssh; cli is the default) |
atago snapshot update | record or refresh golden files |
atago explain | describe what a spec does without running it, or what an ATG code means (atago explain ATG2201) |
atago doc | generate Markdown from specs, with fixtures and golden files inlined |
atago manifest | emit a stable JSON summary of specs for tooling |
atago list | show scenarios, tags, and artifacts |
atago completion | print a shell completion script |
atago version | print the atago version |
atago help | print usage (atago <command> --help for one command's flags) |
explain, doc, manifest, and list all load and validate the spec first — exit code 2 on a schema error — so any of them doubles as a lint step in CI.
Selecting scenarios #
Selection flags compose with any spec: --filter NAME (repeatable, and comma-separated for OR — --filter a,b or --filter a --filter b runs scenarios whose name contains a or b), --tag T and --skip-tag T (tags match exactly, not by substring — atago list shows the available tags), --parallel N, --fail-fast, and --rerun-failed. atago run --rerun-failed re-runs only the scenarios the previous run recorded as failed in .atago/last-failed.json, so the fix-and-recheck loop replays just the failures instead of the whole suite. While authoring, --verbose traces every command, capture, and assertion verdict — for passing scenarios too. Under --ci, a selection that matches no scenario fails the run (exit 3) rather than passing an empty suite. Flags may be written before, after, or between the paths — atago run ./specs --report json and atago run --report json ./specs are the same command — and -- ends flag parsing, so a path spelled like a flag can still be named. atago record is the exception: everything after its -- is the recorded program's own command line.
Snapshot testing #
snapshot matchers compare output against committed golden files; ANSI colors, temp paths, UUIDs, timestamps, ports, and CRLF are normalized so snapshots stay stable across machines. Record or refresh them with:
atago snapshot update spec.atago.yaml
For volatile patterns the built-ins do not cover — auto-increment IDs, request identifiers, epoch times — declare spec-wide scrub: rules that rewrite each regex match to a placeholder before the compare (applied after secrets: masking):
scrub:
- {pattern: 'id=\d+', placeholder: 'id=<ID>'}
See the scrub example.
Spec file keys #
Every key a spec file accepts, generated from the committed JSON Schema — the same document that powers editor completion, so this reference cannot drift from what the loader accepts. Indentation shows nesting; a type that links (like step) is documented in its own section rather than repeated inline.
All keys belong to spec format version 1 — the only format version so far;
version: "1" is the first line of every spec. The Since column is the atago release
that introduced the key (unreleased = merged to main, not yet in a tagged
release).
Top level #
The keys of a spec file document.
| Key | Type | Description | Since |
|---|---|---|---|
defaults | defaults | Spec-wide default fragments merged into every matching element at load time — authoring sugar to cut repetition. An explicitly authored value always wins; maps merge per key; a defaulted boolean cannot be turned off per element. | v0.1.0 |
permissions | object | The spec's security policy. | v0.1.0 |
network | object | Network egress policy for the spec. | v0.1.0 |
allow | array of [string number boolean] | Hosts that scenarios may contact; egress to any other host is a policy violation (exit 6). Enforced on http, grpc, ssh, and db egress, and on a cdp step's navigate: URLs — a page loaded from an allowed host may still fetch subresources from anywhere, which no navigate: action passes through (#486). | v0.1.0 |
runners | map of runner | Named runner configurations. cmd is the implicit default for run steps; http supplies a base_url for http steps; db a dsn for query steps; ssh, grpc, and browser configure their step types. | v0.1.0 |
scenarios | array of scenario | Required. The named behaviors under test. Each scenario runs in its own isolated temporary workdir. | v0.1.0 |
scrub | array of objects | Declarative output-normalization rules (#137): each replaces every regex match in captured output with a literal placeholder before a snapshot is compared or written. Where secrets masks known values, scrub normalizes volatile patterns the built-in normalizers do not cover (auto-increment IDs, request identifiers, custom timestamps). Rules apply in order, after secret masking and before the built-in ANSI/UUID/timestamp/port/path normalization. | v0.5.0 |
pattern | string | number | boolean | Required. A Go (RE2) regular expression; every match is replaced. Required. | v0.5.0 |
placeholder | string | number | boolean | Literal replacement text (no $1 expansion), e.g. "<ID>". Defaults to empty, which deletes matches. | v0.5.0 |
secrets | array of [string number boolean] | Environment variable names whose VALUES are masked as *** in output, reports, and snapshots. | v0.1.0 |
suite | object | Required. Groups the file's scenarios under a name, with optional suite-wide env, default step timeout, and one-time setup/teardown steps. | v0.1.0 |
description | string | number | boolean | Optional prose describing what the suite guarantees, rendered as Markdown under the suite heading by atago doc. Documentation only: it never affects execution, and ${name} references in it are not expanded. | v0.14.0 |
env | map of [string number boolean] | Environment exported to every scenario, setup, and teardown step (a scenario's own env wins per key). | v0.2.0 |
name | string | number | boolean | Required. The suite name shown in reports and generated docs. | v0.1.0 |
setup | array of step | Steps run ONCE before any scenario, in order, inside the ${suitedir} scratch dir. Allowed kinds: fixture, run, store, assert, and service (a suite-wide background process started at that point in the sequence). A failing setup step errors every scenario. | v0.2.0 |
teardown | array of step | Steps that always run after the last scenario, while suite services are still up (services stop last, LIFO). Failures are reported but never change the suite verdict. | v0.2.0 |
timeout | string | Suite-level default step timeout (#17): every run/http/query/grpc step without a more specific timeout (step > runner > defaults.run > suite) is bounded by this Go duration. "0" disables. When no level configures one, a built-in 60s default applies. | v0.3.0 |
version | 1 | 1 | Required. Spec format version. Write "1" (quoted); a bare 1 is accepted for convenience — the loader coerces it to "1". | v0.1.0 |
scenarios[*] #
| Key | Type | Description | Since |
|---|---|---|---|
description | string | number | boolean | Optional prose describing the behavior this scenario pins down and why it matters, rendered as Markdown under the scenario heading by atago doc. Documentation only: it never affects execution, and ${name} references in it are not expanded. | v0.14.0 |
env | map of [string number boolean] | Environment variables for every step in the scenario (a step's own env wins per key). | v0.1.0 |
expect_fail | object | Declares that this scenario documents a KNOWN bug (#395). Failing is reported as XFAIL and does NOT fail the run, so a reproduction can live in CI and execute on every commit. Passing is XPASS, which DOES fail the run (unless --allow-xpass): the fix landed, and the scenario has to be promoted into the suite that guards against a regression. An execution ERROR is still an error — expect_fail says the program gives the wrong answer, not that the spec cannot run. | v0.19.0 |
issue | string | number | boolean | Where the bug is tracked. Optional, but it is what makes an XPASS actionable — the message can name the issue to close. | v0.19.0 |
reason | string | number | boolean | Required. What is broken, in the author's words. Required: an expected failure with no stated reason is indistinguishable from a test somebody gave up on. | v0.19.0 |
forall | forall | Makes the scenario a template over GENERATED inputs (#656): one instance per generated row, each row's values seeded as ${name} variables. Property-based testing — "for every input of this shape, the CLI still holds up" — with the values derived from the spec's own seed, so they are the same on every machine and every run. | v0.23.0 |
matrix | array of object | Makes the scenario a template: one concrete instance per row, with each row's key/value pairs seeded as ${name} variables. | v0.1.0 |
mock_servers | array of mockServer | Declarative stub HTTP servers (#24): canned routes on an ephemeral loopback port, every request recorded for the mock: assertion target; ${<name>.url} / ${<name>.port} are seeded before steps run. | v0.3.0 |
name | string | number | boolean | Required. The scenario name shown in reports; with matrix:, ${var} placeholders make each instance's name unique. | v0.1.0 |
only | condition | Runs the scenario only when the condition holds. | v0.1.0 |
services | array of service | Background processes started before the steps and terminated (with their process group) when the scenario ends — peers the CLI under test talks to. | v0.1.0 |
skip | condition | Skips the scenario when the condition holds (os match, env var set, or probe command succeeding). | v0.1.0 |
steps | array of step | Required. The ordered actions: fixtures, commands, requests, assertions, captures. | v0.1.0 |
tags | array of [string number boolean] | Labels for run-time selection with --tag / --skip-tag. | v0.1.0 |
teardown | array of step | Steps that always run after steps — pass, fail, error, or interrupt — sharing the scenario's variable store. For external side effects the isolated workdir cannot undo. A teardown failure is reported but does not change the scenario's verdict. | v0.1.0 |
forall #
| Key | Type | Description | Since |
|---|---|---|---|
runs | integer | How many instances to generate (default 10). An upper bound rather than a promise: duplicate rows are dropped, so a generator over three choices expands to three instances. | v0.23.0 |
seed | integer | Picks the stream (default 0). Generation is deterministic, so the same spec produces the same inputs on every machine; changing the seed is how a spec asks for different ones. | v0.23.0 |
vars | map of generator | Required. Each variable name and the generator that produces its values; the values are seeded as ${name} for that instance. | v0.23.0 |
forall vars (generator) #
One forall variable's generator: a kind name as a scalar (s: ascii, the kind with its default range), a mapping narrowing that kind's range ({type: int, min: 1, max: 99}, where min/max are a string LENGTH for the string kinds), or a fixed set of choices ({one_of: [json, yaml]}).
| Key | Type | Description | Since |
|---|---|---|---|
examples | array of [string number boolean] | Values always tried, ahead of everything generated (#661): the input that broke the tool once, which is never a boundary of any generator. They are used as written, so one may sit outside this generator's own range, and they count toward runs. | v0.23.0 |
max | integer | Upper bound: the largest value for int, the longest length for a string kind. | v0.23.0 |
min | integer | Lower bound: the smallest value for int, the shortest length for a string kind. | v0.23.0 |
type | alpha | alphanumeric | ascii | bool | digits | int | unicode | Required. The generator kind: an integer, a boolean, or a string of digits/letters/alphanumerics/printable ASCII/non-ASCII letters. | v0.23.0 |
one_of | array of [string number boolean] | Required. The fixed set of values to pick from — a flag's accepted values, a set of formats. Rows are deduplicated, so a list of three choices expands to three instances however large runs is. | v0.23.0 |
steps[*] #
| Key | Type | Description | Since |
|---|---|---|---|
assert | assert | Checks externally observable behavior of the preceding step. Exactly one target family per assert block. | v0.1.0 |
cdp | cdp | Drives a headless Chrome through a browser runner with a declarative action list. | v0.1.0 |
fixture | fixture | Materializes an input file in the scenario workdir before the command runs. | v0.1.0 |
grpc | grpc | Calls a unary gRPC method through a grpc runner via server reflection. | v0.1.0 |
http | http | Issues an HTTP request through an http runner — as a peer of the CLI under test. | v0.1.0 |
mock_server | mockServer | Starts a suite-wide stub HTTP server at this point in the sequence (#24). Valid only inside suite.setup. | v0.3.0 |
pty | pty | Runs a command inside a real pseudo-terminal and drives it with a declarative expect/send session — for prompts, REPLs, and TUIs. Works on Linux, macOS, and Windows (ConPTY). | v0.2.0 |
query | query | Runs a SQL statement through a db runner; SELECT rows feed the rows: assert. | v0.1.0 |
run | run | Executes a command and captures its exit code, stdout, and stderr. | v0.1.0 |
service | service | Starts a suite-wide background process at this point in the sequence. Valid only inside suite.setup; the loader rejects it anywhere else. | v0.2.0 |
signal | signal | Sends a named POSIX signal to a managed service — the race-free alternative to kill hacks for graceful-shutdown tests. POSIX-only. | v0.3.0 |
store | store | Captures a value into the variable store; later steps reference it as ${name}. | v0.1.0 |
fixture #
| Key | Type | Description | Since |
|---|---|---|---|
base64 | string | number | boolean | Inline binary content, base64-encoded — carries exact bytes YAML text cannot. | v0.1.0 |
content | string | number | boolean | Inline text content for the file. Exactly one source (content/base64/from/symlink) is set. | v0.1.0 |
file | string | number | boolean | Required. The workdir-relative path to create. | v0.1.0 |
from | string | number | boolean | Copies an existing file (e.g. committed testdata), resolved relative to the spec file's directory. | v0.1.0 |
mode | string | number | boolean | Octal file mode (e.g. "0755") applied after writing. | v0.1.0 |
mtime | string | number | boolean | RFC3339 modification time applied after writing — pins timestamps for freshness/change-detection tests. | v0.1.0 |
symlink | string | number | boolean | Makes file a symbolic link to this target instead of a regular file. The target is written verbatim. | v0.1.0 |
run #
| Key | Type | Description | Since |
|---|---|---|---|
clear_env | boolean | Start the child from an empty environment instead of inheriting the host environment (#16). On Windows a small system-critical set (SystemRoot, SystemDrive, TEMP, TMP, PATHEXT) is always retained. | v0.3.0 |
command | string | number | boolean | Required. The program to run. Tokenized and exec'd directly unless shell: true. | v0.1.0 |
cwd | string | number | boolean | Working directory relative to the scenario workdir (default: the workdir itself). | v0.1.0 |
deterministic | deterministic | Re-runs the command and requires the declared observables to come back byte-identical (#398) — the same-input-same-output property that catches iteration order leaking into output. Assertions, store, snapshots, and changes: all still describe the FIRST run. Only meaningful for an effectively read-only command; not combinable with retry. | v0.19.0 |
env | map of [string number boolean] | Environment variables for the child process, layered over the scenario env; values get ${name} expansion. | v0.1.0 |
pass_env | array of [string number boolean] | Host variables copied into the cleared environment (#16). Requires clear_env: true; unset host variables are skipped. | v0.3.0 |
retry | retry | Re-runs the command until the until assertion passes — declarative polling for async behavior. The last attempt's result is what later steps observe. | v0.1.0 |
runner | string | number | boolean | Names a declared runner to execute through (e.g. an ssh runner runs the command remotely). Default: local execution. | v0.1.0 |
sandbox_home | boolean | Point the child's home and per-OS config/cache/data/state dirs at ${workdir}/.atago-home so a CLI touching ~/.config, ~/.cache, or %APPDATA% runs hermetically (#71). Precedence: step env > sandbox > pass_env > host; composes with clear_env. | v0.3.0 |
shell | boolean | Runs the command through the shell (/bin/sh on POSIX, cmd.exe on Windows) — for pipes, redirects, and builtins. | v0.1.0 |
stderr_to | string | number | boolean | Write the command's captured stderr to this workdir-relative file (create/truncate), without needing shell redirection. | v0.1.0 |
stdin | string | number | boolean | object | Standard input for the command (#18): a plain string (inline text), {file: path} (workdir-relative, path-confined), or {base64: data} for binary bytes. | v0.1.0 |
file | string | number | boolean | Required. Feeds the command's stdin from a workdir-relative file. | v0.3.0 |
base64 | string | number | boolean | Required. Feeds the command's stdin with exact binary bytes, base64-encoded. | v0.3.0 |
stdout_to | string | number | boolean | Write the command's captured stdout to this workdir-relative file (create/truncate), without needing shell redirection. | v0.1.0 |
timeout | string | Bounds this command as a Go duration. Precedence: step > runner > defaults.run > suite > built-in 60s. | v0.1.0 |
run deterministic #
Same-input-same-output check on a run step (#398). Nondeterministic-but-passing output — a column order that leaks map iteration, an unsorted listing, a JSON object whose keys move — satisfies every loose matcher on every run, so comparing one run's bytes against the next is the only cheap oracle. A mismatch fails the step with a unified diff between the runs.
| Key | Type | Description | Since |
|---|---|---|---|
compare | array of [string number boolean] | Which observables must match across runs (default [stdout, exit_code]). stderr is opt-in because progress output legitimately carries timings. | v0.19.0 |
runs | integer | How many times the command executes in total (default 2). Capped, because a large number is a benchmark rather than a test. | v0.19.0 |
pty #
Runs one command inside a real pseudo-terminal and drives it with a declarative expect/send session (#8) — for CLIs that branch on TTY-ness or present interactive prompts. Runs on Linux, macOS, and Windows (a ConPTY).
| Key | Type | Description | Since |
|---|---|---|---|
clear_env | boolean | Start the child from an empty environment instead of inheriting the host environment (#16). On Windows a small system-critical set (SystemRoot, SystemDrive, TEMP, TMP, PATHEXT) is always retained. | v0.3.0 |
cols | integer | Terminal width in columns (default 80). | v0.2.0 |
command | string | number | boolean | Required. The program to run inside a real pseudo-terminal (a ConPTY on Windows). The captured transcript becomes the step's stdout. | v0.2.0 |
cwd | string | number | boolean | Working directory relative to the scenario workdir. | v0.2.0 |
env | map of [string number boolean] | Environment variables for the pty child. The child gets TERM=xterm-256color by default; override here if needed. | v0.2.0 |
graphics | kitty | Makes the terminal answer like one that draws images with the kitty graphics protocol: the capability query is acknowledged, the cell-size query (CSI 16 t) reports 10x20-pixel cells, and every image the program transmits directly (t=d) is recorded for screen.images. Unset, neither query is answered, as on a terminal without image support, so a program that requires images can be tested both ways. | v0.24.0 |
pass_env | array of [string number boolean] | Host variables copied into the cleared environment (#16). Requires clear_env: true; unset host variables are skipped. | v0.3.0 |
rows | integer | Terminal height in rows (default 24). Also sizes the screen: assertion's emulator. | v0.2.0 |
sandbox_home | boolean | Point the pty child's home and per-OS config/cache/data/state dirs at ${workdir}/.atago-home so a CLI touching ~/.config, ~/.cache, or %APPDATA% runs hermetically (#71). | v0.3.0 |
session | array of objects | Ordered expect/send script. Each entry waits for the transcript to match (expect) or types into the terminal (send). Deliberately no branching. | v0.2.0 |
exec | string | number | boolean | object | Runs one command on the HOST while the program under test keeps running, so a session can test what a TUI does when the world changes underneath it — a commit made outside lazygit, a file another process creates, a line appended to a log a viewer is following. It blocks until the command exits, which is the point: after it, the change exists, so the expect_screen that follows is waiting on the program noticing rather than on a race. Runs in the scenario workdir with the same environment the pty child got, so sandbox_home / clear_env isolation still holds. A non-zero exit, a timeout, or a failure to start is an execution error — the command is scaffolding, not the subject under test. Its output never joins the transcript. Note that files it writes appear in a following changes: assert, so list or ignore: them. | v0.19.0 |
command | string | number | boolean | Required. The command to run on the host. | v0.19.0 |
shell | boolean | Runs the command through the shell, like run.shell. | v0.19.0 |
timeout | string | Go duration bounding this command (default 10s). The remaining session budget bounds it too, whichever is shorter. | v0.19.0 |
expect | string | number | boolean | Waits until the transcript (scanned after the previous match) matches this Go regular expression; a never-matching expect fails when the session timeout elapses. | v0.2.0 |
expect_screen | object | Waits until the CURRENT rendered terminal screen matches, using the same text/JSON/YAML matcher surface as screen: except snapshot:. stable_for requires the screen to keep matching continuously for that duration, which is safer than a blind sleep across Linux/macOS/Windows redraw timing. | v0.12.0 |
attrs | array of objects | Checks how text is DRAWN, not only what it says (#382): the error line is red, the selected row is reverse-video, --no-color really did leave the frame uncolored. Every entry must hold. An entry is position-free by default — it passes when at least one occurrence of its text has every one of its cells carrying the demanded styling — so a styling claim does not break each time the layout shifts; pin row when the position is the point. | v0.19.0 |
bg | string | number | boolean | Background color, same vocabulary as fg. | v0.19.0 |
blink | boolean | Whether the text blinks. | v0.19.0 |
bold | boolean | Whether the text is bold. false is a real claim ("must NOT be bold"), not the absence of one. | v0.19.0 |
fg | string | number | boolean | Foreground color: an ANSI name (red, bright-red), a 256-palette index (203), or default — the terminal's own color, which is how a --no-color contract becomes assertable. | v0.19.0 |
italic | boolean | Whether the text is italic. | v0.19.0 |
reverse | boolean | Whether the text is reverse-video, which is how most TUIs draw the selected row. | v0.19.0 |
row | integer | Restricts the search to this 1-based screen row, addressed the same way line addresses the text matchers. | v0.19.0 |
text | string | number | boolean | Required. The literal substring whose cells are checked. | v0.19.0 |
underline | boolean | Whether the text is underlined. | v0.19.0 |
contains | stringOrList | Requires every listed substring (a scalar counts as a one-element list) to be present on the rendered screen. | v0.12.0 |
count | integer | Exact number of times the contains substring or matches regexp next to it occurs on the rendered screen (#396). Occurrences are non-overlapping. | v0.19.0 |
empty | boolean | Asserts the rendered screen is empty (true) or non-empty (false). A screen carrying only whitespace counts as empty, which is what makes the check usable at all: a terminal pads every unwritten cell with spaces, so a blank screen is never zero bytes. | v0.12.0 |
equals | string | number | boolean | Requires the rendered screen to equal this string exactly (CRLF and one trailing newline are normalized). | v0.12.0 |
images | object | Checks the images on screen now, drawn through the kitty graphics protocol and recorded only by a pty step with graphics: kitty. An image counts from when it is transmitted until a delete command takes it off by id, number, id range or all (a hidden one is back after a=p). Deletes by screen position, z-index or animation frame are not followed and leave the set as it is. Every set field must hold. similar_to belongs to the post-step assert: a mid-session wait has no spec directory to resolve a baseline against. | v0.24.0 |
contains | array of objects | Images that must be among those on screen. An entry holds when at least one of them meets every constraint it sets; matching is by content, not position, because a program that loads images concurrently draws them in no fixed order. | v0.24.0 |
alpha | boolean | Whether the image carries transparency (any non-opaque pixel). | v0.24.0 |
height | integer | Exact pixel height. | v0.24.0 |
max_diff | number | Not supported in a mid-session wait, like similar_to. | v0.24.0 |
max_height | integer | Upper bound on the pixel height. | v0.24.0 |
max_width | integer | Upper bound on the pixel width. | v0.24.0 |
min_height | integer | Lower bound on the pixel height. | v0.24.0 |
min_width | integer | Lower bound on the pixel width. | v0.24.0 |
similar_to | string | number | boolean | Not supported in a mid-session wait (the loader refuses it): there is no spec directory to resolve a baseline against. Compare pixels in a post-step assert: screen:. | v0.24.0 |
width | integer | Exact pixel width. | v0.24.0 |
count | integer | Exact number of images on screen. | v0.24.0 |
min_count | integer | Minimum number of images on screen. | v0.24.0 |
json | jsonChecks | Parses the rendered screen as JSON and applies one JSONPath check, or a list of checks that must all hold. | v0.12.0 |
line | integer | 1-based line selector: applies the matcher to that single rendered row instead of the whole screen. | v0.12.0 |
matches | string | number | boolean | Requires the rendered screen to match this Go (RE2) regular expression. | v0.12.0 |
max_count | integer | Upper bound on how many times the contains substring or matches regexp occurs on the rendered screen (#396). | v0.19.0 |
min_count | integer | Lower bound on how many times the contains substring or matches regexp occurs on the rendered screen (#396). | v0.19.0 |
not_contains | stringOrList | Requires every listed substring to be absent from the rendered screen. | v0.12.0 |
not_equals | string | number | boolean | Requires the rendered screen NOT to equal this string. | v0.12.0 |
not_matches | string | number | boolean | Requires the rendered screen NOT to match this Go (RE2) regular expression. | v0.12.0 |
permutation_of | string | number | boolean | Waits until the rendered screen has the SAME LINES as this reference, in any order (#658). | v0.23.0 |
stable_for | string | Optional Go duration the rendered screen must keep matching continuously before the wait passes. | v0.12.0 |
subset_of | string | number | boolean | Waits until every LINE of the rendered screen appears in this reference (#658). | v0.23.0 |
timeout | string | Optional Go duration bounding only this wait; the enclosing pty timeout still caps the whole session. | v0.12.0 |
yaml | jsonChecks | Parses the rendered screen as YAML and applies one JSONPath check, or a list of checks that must all hold. | v0.12.0 |
resize | object | Changes the terminal size mid-session, delivered the way a real terminal delivers it (SIGWINCH on POSIX, a ConPTY notification on Windows), so a TUI's relayout is testable instead of being fixed at the size the step started with. The rendered screen follows: every later expect_screen, the final screen: assert, and its snapshot see each part of the transcript drawn at the size it was produced under. Settle the screen with an expect or expect_screen before and after a resize — output already in flight when it lands is attributed to the old size, exactly as on a real terminal. | v0.19.0 |
cols | integer | Required. New terminal width in columns. | v0.19.0 |
rows | integer | Required. New terminal height in rows. | v0.19.0 |
send | string | number | boolean | object | Writes to the terminal: a string verbatim ("" sends EOF/^D; ${name} expansion applies), {key: <name>} for a named key — enter, tab, shift-tab, esc, arrows, insert, f1-f12, ctrl-a..ctrl-z, alt-a..alt-z, modified arrows such as ctrl-left and shift-up, ctrl-space/ctrl-@, ctrl-[, ctrl-\, ctrl-], ctrl-^, ctrl-_, ctrl-hyphen/ctrl-minus — or {paste: <text>} to deliver the text as a bracketed paste. | v0.2.0 |
key | string | number | boolean | Named key: enter, tab, shift-tab (alias backtab), esc, space, backspace, delete, insert, arrows, home/end, pageup/pagedown, f1-f12, ctrl-a..ctrl-z, alt-a..alt-z, alt-enter, alt-backspace, modified arrows (ctrl-left, shift-up, …), ctrl-space/ctrl-@, ctrl-[, ctrl-\, ctrl-], ctrl-^, ctrl-_, ctrl-hyphen/ctrl-minus. | v0.3.0 |
mouse | object | Sends a mouse event as an xterm SGR (1006) report, for TUIs that accept clicks and scrolling. The step fails if the program has not enabled mouse tracking (ESC [?1000h, ESC [?1002h, or ESC [?1003h) together with SGR encoding (ESC [?1006h), because the bytes would otherwise mean nothing to it. Mutually exclusive with key, paste, and times. | v0.19.0 |
action | click | press | release | click (default) sends the press and its release in one write, the way a real click arrives; press and release send one half. A wheel button has no release, so click on one sends a single scroll notch and release is rejected. | v0.19.0 |
button | left | middle | right | wheel-up | wheel-down | Which button (default left). A wheel button sends one scroll notch. | v0.19.0 |
col | integer | Required. 1-based screen column of the cell to act on. | v0.19.0 |
mods | array of [string number boolean] | Modifier keys held during the event. | v0.19.0 |
row | integer | Required. 1-based screen row of the cell to act on. | v0.19.0 |
paste | string | number | boolean | Delivers the text as a BRACKETED PASTE, wrapped in the markers a terminal puts around pasted input, so a REPL or editor takes its paste path instead of its typing path. The step fails if the program has not enabled bracketed paste (ESC [?2004h), because the markers would otherwise arrive as ordinary characters. Mutually exclusive with key. | v0.19.0 |
times | integer | Presses the key this many times, as one terminal write — {key: left, times: 16} instead of sixteen identical session entries. Requires key; omit it (or 1) for a single press. | v0.19.0 |
shell | boolean | Runs the command through the shell, like run.shell. | v0.2.0 |
timeout | string | Go duration bounding the WHOLE session (default 30s). | v0.2.0 |
http #
| Key | Type | Description | Since |
|---|---|---|---|
body | string | number | boolean | Raw string payload sent verbatim (default Content-Type text/plain). The payload fields (json/body/body_file/form+files) are mutually exclusive. | v0.1.0 |
body_file | string | number | boolean | Workdir-relative file streamed as the raw request body (binary-safe) — for upload endpoints that take file content directly. | v0.1.0 |
body_to | string | number | boolean | Write the response body to this workdir-relative file, so file/image/pdf assertions can inspect a download (the http analog of run's stdout_to). | v0.1.0 |
files | array of objects | Files attached as multipart/form-data parts (the browser-style upload most web apps expect). | v0.1.0 |
content_type | string | number | boolean | Overrides the part's Content-Type (default: detected from content, falling back to application/octet-stream). | v0.1.0 |
field | string | number | boolean | Required. The multipart form field name the server reads the file from. | v0.1.0 |
path | string | number | boolean | Required. The workdir-relative file whose content becomes the part body. | v0.1.0 |
follow_redirects | boolean | Follow 3xx responses (default true). Set false to assert on the redirect itself: its status code and Location header. | v0.1.0 |
form | map of [string number boolean] | Form fields: application/x-www-form-urlencoded alone, multipart/form-data when files is also set. | v0.1.0 |
header | map of [string number boolean] | Request headers. | v0.1.0 |
json | any | v0.1.0 | |
method | string | number | boolean | Required. The HTTP method (GET, POST, ...). | v0.1.0 |
path | string | number | boolean | Request path appended to the runner's base_url; ${name} expansion applies. | v0.1.0 |
retry | retry | Re-issues the request until the until assertion passes — declarative polling for eventually-consistent endpoints. | v0.1.0 |
runner | string | number | boolean | Names the http runner declaring the base_url. With exactly one http runner declared it may be omitted. | v0.1.0 |
query #
| Key | Type | Description | Since |
|---|---|---|---|
runner | string | number | boolean | Required. Names the db runner declaring the dsn. | v0.1.0 |
sql | string | number | boolean | Required. The SQL statement. SELECT rows are captured as JSON for rows: asserts and store from.rows; other statements record their affected-row count. | v0.1.0 |
grpc #
| Key | Type | Description | Since |
|---|---|---|---|
header | map of [string number boolean] | Request metadata headers. | v0.1.0 |
json | any | v0.1.0 | |
method | string | number | boolean | Required. The unary method to call as "package.Service/Method"; the schema is resolved via server reflection (no compiled stubs). | v0.1.0 |
runner | string | number | boolean | Required. Names the grpc runner declaring the target server. | v0.1.0 |
cdp #
| Key | Type | Description | Since |
|---|---|---|---|
actions | array of objects | Required. Browser actions run in order against one session; the value captured by the last text/eval/attribute/title action feeds the value: assert and store from.value. | v0.1.0 |
attribute | object | Captures an element attribute value. | v0.1.0 |
name | string | number | boolean | Required. The attribute name to capture. | v0.1.0 |
selector | string | number | boolean | Required. The element to read. | v0.1.0 |
check | string | number | boolean | Ticks the checkbox matched by the selector. | v0.1.0 |
click | string | number | boolean | Clicks the element matched by the selector. | v0.1.0 |
download | object | Clicks to trigger a download and captures the file using the server-suggested filename; the captured value is the final filename. | v0.1.0 |
click | string | number | boolean | Required. The element to click to start the download. | v0.1.0 |
dir | string | number | boolean | Workdir-relative directory to save into (default: the workdir root). | v0.1.0 |
eval | string | number | boolean | Evaluates a JavaScript expression and captures the result as JSON. | v0.1.0 |
navigate | string | number | boolean | Loads a URL. | v0.1.0 |
press | object | Presses a single key on an element. | v0.1.0 |
key | string | number | boolean | Required. The key to press (e.g. "Enter", "Tab", or a printable character). | v0.1.0 |
selector | string | number | boolean | Required. The element to receive the key press. | v0.1.0 |
screenshot | object | Writes a PNG of the page (or one element) into the workdir for file/image assertions. | v0.1.0 |
path | string | number | boolean | Required. The workdir-relative PNG path to write. | v0.1.0 |
selector | string | number | boolean | Limits the screenshot to this element (default: the whole page). | v0.1.0 |
select | object | Chooses an <option> in a <select>. | v0.1.0 |
selector | string | number | boolean | Required. The <select> element. | v0.1.0 |
value | string | number | boolean | Required. The option value to choose. | v0.1.0 |
send_keys | object | Types text into an element. | v0.1.0 |
selector | string | number | boolean | Required. The element to type into. | v0.1.0 |
value | string | number | boolean | Required. The text to type. | v0.1.0 |
text | string | number | boolean | Captures the text of the element matched by the selector. | v0.1.0 |
title | boolean | Captures the page title. | v0.1.0 |
uncheck | string | number | boolean | Unticks the checkbox matched by the selector. | v0.1.0 |
upload | object | Sets a file on an <input type=file> — no scripted file dialogs. | v0.1.0 |
file | string | number | boolean | Required. The workdir-relative file to attach; must exist. | v0.1.0 |
selector | string | number | boolean | Required. The <input type=file> element. | v0.1.0 |
wait_hidden | string | number | boolean | Waits until the CSS selector is hidden or absent. | v0.1.0 |
wait_visible | string | number | boolean | Waits until the CSS selector is visible. | v0.1.0 |
runner | string | number | boolean | Required. Names the browser runner to drive. | v0.1.0 |
store #
| Key | Type | Description | Since |
|---|---|---|---|
from | object | Required. Where the value comes from — exactly one source. | v0.1.0 |
body | stream | Extracts from the preceding http step's response body via a json path or regex. | v0.1.0 |
file | file | Reads a generated file: a json path selects a value, or text: true captures the whole content. | v0.1.0 |
header | string | number | boolean | Captures an HTTP response header value by name. | v0.1.0 |
message | stream | Extracts from the preceding grpc step's response message via a json path. | v0.1.0 |
rows | stream | Extracts from the preceding query step's result rows (a JSON array) via a json path. | v0.1.0 |
stdout | stream | Extracts from the preceding step's stdout via a json path or matches regex, or captures the whole stream with trim. | v0.1.0 |
value | stream | Extracts from the last browser-captured value via a json path or regex. | v0.1.0 |
name | string | number | boolean | Required. The variable name; later steps reference the captured value as ${name}. | v0.1.0 |
signal #
Sends a named POSIX signal to a managed service's process group (#23) - the race-free alternative to kill/killall for graceful-shutdown tests. POSIX-only at runtime; Windows reports a clear execution error.
| Key | Type | Description | Since |
|---|---|---|---|
service | string | number | boolean | Required. A service declared in the scenario's services: list or started by a suite.setup service: step. | v0.3.0 |
signal | string | number | boolean | Required. TERM, INT, HUP, USR1, USR2, or KILL (optional SIG prefix accepted). | v0.3.0 |
wait | object | Block until the signaled process exits; a still-running process fails the step. | v0.3.0 |
timeout | string | Go duration bounding the wait (default 5s). | v0.3.0 |
service #
| Key | Type | Description | Since |
|---|---|---|---|
clear_env | boolean | Start the child from an empty environment instead of inheriting the host environment (#16). On Windows a small system-critical set (SystemRoot, SystemDrive, TEMP, TMP, PATHEXT) is always retained. | v0.3.0 |
command | string | number | boolean | Required. The program to run. Tokenized and exec'd directly unless shell: true. | v0.1.0 |
cwd | string | number | boolean | Working directory relative to the scenario workdir. | v0.1.0 |
env | map of [string number boolean] | Environment variables, layered over the scenario env, with ${name} expansion. | v0.1.0 |
max_log_bytes | integer | Maximum bytes of combined stdout/stderr atago retains for this service. The oldest bytes are dropped first and the retained log starts with a truncation notice, so readiness excerpts and preserved log artifacts (which only ever need the tail) stay bounded. Omit for the 8 MiB default. | v0.10.0 |
name | string | number | boolean | Required. Identifies the service in diagnostics and signal: steps; unique per scenario. | v0.1.0 |
pass_env | array of [string number boolean] | Host variables copied into the cleared environment (#16). Requires clear_env: true; unset host variables are skipped. | v0.3.0 |
ready | object | How to wait until the service accepts work before steps run. When omitted, steps start as soon as the process is spawned. | v0.1.0 |
delay | string | Simply waits this Go duration — a last resort when no observable readiness signal exists. | v0.1.0 |
file | string | number | boolean | Ready when this workdir-relative file exists and is non-empty — the canonical pattern for a server publishing its listen address. | v0.1.0 |
log | string | number | boolean | Ready when the service's combined stdout/stderr matches this regular expression. | v0.1.0 |
port | string | number | boolean | Ready when this TCP address (host:port) accepts a connection. | v0.1.0 |
store | string | number | boolean | Used with file: captures the ready file's trimmed content into ${<name>} so steps can reference a dynamic address or port. | v0.1.0 |
timeout | string | Bounds the readiness wait as a Go duration (default "5s"). | v0.1.0 |
shell | boolean | Runs the command through the POSIX shell (pipes, redirects, ${}). | v0.1.0 |
mock_server #
A declarative stub HTTP server (#24): routes match on exact method+path (query string excluded); an unmatched request answers 404 and is still recorded.
| Key | Type | Description | Since |
|---|---|---|---|
name | string | number | boolean | Required. Identifies the server: seeds ${<name>.url} / ${<name>.port} and is referenced by mock: asserts. Unique per scenario. | v0.3.0 |
routes | array of mockRoute | Canned responses, matched top-down on exact method+path (query string excluded; deliberately no patterns). An unmatched request answers 404 and is still recorded. | v0.3.0 |
mock_server routes[*] #
One canned response. At most one of json/body/body_file supplies the payload; status defaults to 200.
| Key | Type | Description | Since |
|---|---|---|---|
body | string | number | boolean | Inline text response body. Takes ${name} expansion when the scenario starts, like json. | v0.3.0 |
body_file | string | number | boolean | Spec-relative response body file, confined to the spec directory. Served verbatim, without ${name} expansion, so it may hold binary data. | v0.3.0 |
delay | string | Go duration to sleep before responding - for retry testing. | v0.3.0 |
header | map of [string number boolean] | Extra response headers to set. Values take ${name} expansion when the scenario starts, like json. | v0.3.0 |
json | any | Inline response document, marshaled with Content-Type: application/json. Its strings take ${name} expansion when the scenario starts, so a response can link back to the mock itself with ${.url}. | v0.3.0 |
method | string | number | boolean | Required. HTTP method to match (case-insensitive). | v0.3.0 |
path | string | number | boolean | Required. Exact request path to match (query string excluded). | v0.3.0 |
status | integer | Response status code (default 200). | v0.3.0 |
assert #
An assert sets one or more target families; each is an independent check and all must hold (e.g. exit_code + stdout + file in one block).
| Key | Type | Description | Since |
|---|---|---|---|
body | stream | Matches the HTTP response body of the preceding http step with the stream matchers. | v0.1.0 |
changes | changesAssert | Valid after a run/pty step: pins exactly which files that step created, modified, and deleted in the scenario workdir. Each set list is exhaustive in both directions. | v0.3.0 |
dir | dir | Black-box checks over a generated directory: existence, expected/forbidden children, entry counts, glob coverage, or a whole-tree snapshot manifest. | v0.1.0 |
duration | durationAssert | Bounds how long the preceding measurable step (run/http/query/grpc/pty) took, with Go-duration lt/lte/gt/gte bounds. | v0.3.0 |
exit_code | exitCode | Asserts the preceding command's exit status: a bare integer, {not: N}, or a documented set {in: [0, 2]}. | v0.1.0 |
file | file | Checks a generated file: existence, content substrings, byte-exact equality, JSONPath checks, executability, or a snapshot. | v0.1.0 |
grpc_status | integer | Asserts the numeric gRPC status code of the preceding grpc step (0 = OK). | v0.1.0 |
header | headerMatch | Matches one HTTP response header of the preceding http step by name. | v0.1.0 |
image | image | Inspects a generated image's decoded properties (format, dimensions, alpha) and optionally compares its pixels against a baseline. Every set field must hold. | v0.1.0 |
message | stream | Matches the preceding grpc step's response message (as JSON) with the stream matchers. | v0.1.0 |
mock | mockAssert | Asserts what the CLI under test actually sent to a declared mock server: request count, and header/body matchers on the last matching recorded request. | v0.3.0 |
pdf | Checks a generated PDF: page count, Info-dictionary metadata, and extracted text. Every set field must hold. | v0.1.0 | |
rows | stream | Matches the preceding query step's result rows, captured as a JSON array, with the stream matchers (json path/length, contains, ...). | v0.1.0 |
screen | screenAssert | The rendered terminal screen of the preceding pty step (#27), asserted as plain text and — with attrs — as colors and styling (#382). | v0.3.0 |
status | integer | Asserts the HTTP response status code of the preceding http step. | v0.1.0 |
stderr | stream | Matches the preceding step's captured standard error with exactly one stream matcher. | v0.1.0 |
stdout | stream | Matches the preceding step's captured standard output with exactly one stream matcher. | v0.1.0 |
value | stream | Matches the value captured by the last browser text/eval/attribute/title action with the stream matchers. | v0.1.0 |
stream matchers (stdout / stderr / body / rows / message / value / screen) #
| Key | Type | Description | Since |
|---|---|---|---|
contains | stringOrList | Requires every listed substring (a scalar counts as a one-element list) to be present. | v0.1.0 |
count | integer | Exact number of times the contains substring or matches regexp next to it occurs (#396). Occurrences are non-overlapping. Needs exactly one countable matcher; not combinable with equals/not_equals/empty/json/yaml/snapshot. | v0.19.0 |
empty | boolean | Asserts the stream is empty (true) or non-empty (false). A stream carrying only whitespace counts as empty, so a stray newline does not fail empty: true; empty: false therefore asserts the stream carried something legible, not that it carried bytes. | v0.1.0 |
equals | string | number | boolean | Requires the stream to equal this string exactly (CRLF and one trailing newline are normalized). | v0.1.0 |
json | jsonChecks | Parses the stream as JSON and applies one JSONPath check, or a list of checks that must all hold. | v0.1.0 |
line | integer | 1-based line selector: applies the matcher to that single line instead of the whole stream. Not itself a matcher; does not compose with json/snapshot. | v0.1.0 |
matches | string | number | boolean | Requires the stream to match this Go (RE2) regular expression. | v0.1.0 |
max_count | integer | Upper bound on how many times the contains substring or matches regexp occurs (#396). max_count: 0 reads as "never". | v0.19.0 |
min_count | integer | Lower bound on how many times the contains substring or matches regexp occurs (#396). | v0.19.0 |
not_contains | stringOrList | Requires every listed substring to be absent. | v0.1.0 |
not_equals | string | number | boolean | Requires the stream NOT to equal this string. | v0.1.0 |
not_matches | string | number | boolean | Requires the stream NOT to match this Go (RE2) regular expression. | v0.1.0 |
permutation_of | string | number | boolean | Requires the stream to have the SAME LINES as this reference, in any order (#658) — the relation to state when the content is the contract and the order is not. Lines are compared as a multiset, so a line printed twice needs two in the reference. The reference is usually a ${name} a store captured from an earlier run. | v0.23.0 |
snapshot | string | number | boolean | Compares the stream against a committed golden file (spec-relative path). Volatile details (ANSI, temp paths, UUIDs, timestamps, ports, CRLF) are normalized; refresh with atago snapshot update. | v0.1.0 |
subset_of | string | number | boolean | Requires every LINE of the stream to appear in this reference (#658) — the relation a filtering flag satisfies against the unfiltered run. Lines are compared as a multiset, so output that duplicated a line the reference has once is not a subset. The reference may have lines the stream does not. | v0.23.0 |
trim | boolean | Store-only (#158): capture the whole stream. trim: true strips surrounding whitespace; trim: false keeps bytes verbatim. | v0.6.0 |
yaml | jsonChecks | Parses the stream as YAML and applies one JSONPath check, or a list of checks that must all hold. | v0.1.0 |
contains / not_contains argument #
A contains/not_contains matcher argument: either a single substring, or a non-empty list of substrings that must all be present (contains) or all be absent (not_contains). The whole list counts as one matcher.
screen #
The rendered terminal screen of the preceding pty step (#27): the transcript replayed through a terminal emulator, asserted as plain text (line.n = screen row) and, with attrs, as colors and styling (#382). A screen assert may carry text matchers, attribute entries, or both — both must hold.
| Key | Type | Description | Since |
|---|---|---|---|
attrs | array of objects | Checks how text is DRAWN, not only what it says (#382): the error line is red, the selected row is reverse-video, --no-color really did leave the frame uncolored. Every entry must hold. An entry is position-free by default — it passes when at least one occurrence of its text has every one of its cells carrying the demanded styling — so a styling claim does not break each time the layout shifts; pin row when the position is the point. | v0.19.0 |
bg | string | number | boolean | Background color, same vocabulary as fg. | v0.19.0 |
blink | boolean | Whether the text blinks. | v0.19.0 |
bold | boolean | Whether the text is bold. false is a real claim ("must NOT be bold"), not the absence of one. | v0.19.0 |
fg | string | number | boolean | Foreground color: an ANSI name (red, bright-red), a 256-palette index (203), or default — the terminal's own color, which is how a --no-color contract becomes assertable. | v0.19.0 |
italic | boolean | Whether the text is italic. | v0.19.0 |
reverse | boolean | Whether the text is reverse-video, which is how most TUIs draw the selected row. | v0.19.0 |
row | integer | Restricts the search to this 1-based screen row, addressed the same way line addresses the text matchers. | v0.19.0 |
text | string | number | boolean | Required. The literal substring whose cells are checked. | v0.19.0 |
underline | boolean | Whether the text is underlined. | v0.19.0 |
contains | stringOrList | Requires every listed substring (a scalar counts as a one-element list) to be present. | v0.19.0 |
count | integer | Exact number of times the contains substring or matches regexp next to it occurs (#396). Occurrences are non-overlapping. Needs exactly one countable matcher; not combinable with equals/not_equals/empty/json/yaml/snapshot. | v0.19.0 |
empty | boolean | Asserts the stream is empty (true) or non-empty (false). A stream carrying only whitespace counts as empty, so a stray newline does not fail empty: true; empty: false therefore asserts the stream carried something legible, not that it carried bytes. | v0.19.0 |
equals | string | number | boolean | Requires the stream to equal this string exactly (CRLF and one trailing newline are normalized). | v0.19.0 |
images | object | Checks the images on screen now, drawn through the kitty graphics protocol and recorded only by a pty step with graphics: kitty. An image counts from when it is transmitted until a delete command takes it off by id, number, id range or all (a hidden one is back after a=p). Deletes by screen position, z-index or animation frame are not followed and leave the set as it is. Every set field must hold. | v0.24.0 |
contains | array of objects | Images that must be among those on screen. An entry holds when at least one of them meets every constraint it sets; matching is by content, not position, because a program that loads images concurrently draws them in no fixed order. | v0.24.0 |
alpha | boolean | Whether the image carries transparency (any non-opaque pixel). | v0.24.0 |
height | integer | Exact pixel height. | v0.24.0 |
max_diff | number | Largest normalized mean per-pixel difference for similar_to (default 0, an exact match). | v0.24.0 |
max_height | integer | Upper bound on the pixel height. | v0.24.0 |
max_width | integer | Upper bound on the pixel width. | v0.24.0 |
min_height | integer | Lower bound on the pixel height. | v0.24.0 |
min_width | integer | Lower bound on the pixel width. | v0.24.0 |
similar_to | string | number | boolean | Baseline image the drawn pixels are compared with, resolved like image.similar_to: next to the spec, then in the workdir. Same dimensions required. | v0.24.0 |
width | integer | Exact pixel width. | v0.24.0 |
count | integer | Exact number of images on screen. | v0.24.0 |
min_count | integer | Minimum number of images on screen. | v0.24.0 |
json | jsonChecks | Parses the stream as JSON and applies one JSONPath check, or a list of checks that must all hold. | v0.19.0 |
line | integer | 1-based line selector: applies the matcher to that single line instead of the whole stream. Not itself a matcher; does not compose with json/snapshot. | v0.19.0 |
matches | string | number | boolean | Requires the stream to match this Go (RE2) regular expression. | v0.19.0 |
max_count | integer | Upper bound on how many times the contains substring or matches regexp occurs (#396). max_count: 0 reads as "never". | v0.19.0 |
min_count | integer | Lower bound on how many times the contains substring or matches regexp occurs (#396). | v0.19.0 |
not_contains | stringOrList | Requires every listed substring to be absent. | v0.19.0 |
not_equals | string | number | boolean | Requires the stream NOT to equal this string. | v0.19.0 |
not_matches | string | number | boolean | Requires the stream NOT to match this Go (RE2) regular expression. | v0.19.0 |
permutation_of | string | number | boolean | Requires the rendered screen to have the SAME LINES as this reference, in any order (#658), compared as a multiset. | v0.23.0 |
snapshot | string | number | boolean | Compares the stream against a committed golden file (spec-relative path). Volatile details (ANSI, temp paths, UUIDs, timestamps, ports, CRLF) are normalized; refresh with atago snapshot update. | v0.19.0 |
subset_of | string | number | boolean | Requires every LINE of the rendered screen to appear in this reference (#658), compared as a multiset. | v0.23.0 |
trim | boolean | Store-only (#158): capture the whole stream. trim: true strips surrounding whitespace; trim: false keeps bytes verbatim. | v0.19.0 |
yaml | jsonChecks | Parses the stream as YAML and applies one JSONPath check, or a list of checks that must all hold. | v0.19.0 |
exit_code #
| Key | Type | Description | Since |
|---|---|---|---|
not | integer | Required. Asserts the exit code is anything but this value. | v0.1.0 |
in | array of integer | Required. The exit code must be one of these values (#19) — the contract shape of grep (0/1) or terraform plan -detailed-exitcode (0/2). | v0.3.0 |
file #
A file assertion: one content matcher and any of the size bounds, which compose ({exists: true, size: 0}, {min_size: 1, max_size: 4096}). At least one matcher is required here; the loader additionally rejects two content matchers on one assert (ATG2104).
| Key | Type | Description | Since |
|---|---|---|---|
contains | stringOrList | Requires every listed substring to be present in the file content. | v0.1.0 |
count | integer | Exact number of times the contains substring next to it occurs in the file (#396). Occurrences are non-overlapping. | v0.19.0 |
equals | string | number | boolean | Byte-exact content match against an inline literal (no CRLF/newline normalization). | v0.6.0 |
equals_file | string | number | boolean | Byte-exact content match against another workdir-confined file (round-trip/idempotence; no CRLF/newline normalization). | v0.6.0 |
executable | boolean | Asserts whether the file has an executable bit set (POSIX). A directory fails: every directory carries the execute bit, which means "can be entered", not "is a program". | v0.1.0 |
exists | boolean | Asserts the file exists (true) or is absent (false). A directory at that path is not a file: it fails either way and points at the dir: assertion. | v0.1.0 |
json | jsonChecks | Parses the file as JSON and applies one JSONPath check, or a list of checks that must all hold. | v0.1.0 |
max_count | integer | Upper bound on how many times the contains substring occurs in the file (#396). | v0.19.0 |
max_size | integer | Upper bound on the file length in bytes (#397) — the regression shape of a compression or bundling bug, where output size is the product. | v0.19.0 |
min_count | integer | Lower bound on how many times the contains substring occurs in the file (#396). | v0.19.0 |
min_size | integer | Lower bound on the file length in bytes (#397). min_size: 1 is the portable spelling of "non-empty". | v0.19.0 |
not_contains | stringOrList | Requires every listed substring to be absent from the file content. | v0.1.0 |
path | string | number | boolean | Required. The file under test, resolved against the scenario workdir when relative (confined to it). | v0.1.0 |
size | integer | Exact file length in bytes (#397). Composes with the content matchers and may also stand alone: size: 0 asserts a failed run left an empty file rather than a half-written one. Bytes are counted as written (no CRLF or trailing-newline normalization). | v0.19.0 |
snapshot | string | number | boolean | Compares the file content against a committed golden file, with the standard snapshot normalization. | v0.1.0 |
text | boolean | Store-only (#158): capture the whole file content verbatim instead of extracting a value via a json path. | v0.6.0 |
dir #
| Key | Type | Description | Since |
|---|---|---|---|
contains | array of [string number boolean] | Child paths (relative to path) that must exist; nested paths are allowed and confined to the directory. | v0.1.0 |
count | integer | Asserts the exact number of direct entries (files only under recursive: true). | v0.1.0 |
exists | boolean | Asserts the path exists and is a directory (false asserts it is absent). | v0.1.0 |
glob | string | number | boolean | Requires at least one entry to match this shell glob (basename match for patterns without /). | v0.1.0 |
ignore | array of [string number boolean] | Glob patterns excluded from the recursive walk and the snapshot manifest (*.log, .git/**). | v0.3.0 |
max_count | integer | Upper bound on the number of entries. | v0.1.0 |
min_count | integer | Lower bound on the number of entries. | v0.1.0 |
not_contains | array of [string number boolean] | Child paths (relative to path) that must NOT exist. | v0.1.0 |
path | string | number | boolean | Required. The directory under test, resolved against the scenario workdir when relative. | v0.1.0 |
recursive | boolean | Apply contains/not_contains/count/glob to the whole tree (#25): counts see files only; glob matches relative paths, or basenames for patterns without /. | v0.3.0 |
snapshot | string | number | boolean | Golden tree manifest (#25): sorted relative paths, one line per entry (dir/file sha256/link). Composes only with ignore; refresh with --update-snapshots. | v0.3.0 |
changes #
Pins the exact workdir delta of the immediately preceding run/pty step (#70): which files it created, modified, and deleted. Each set field is EXHAUSTIVE in both directions (every observed path must match an entry, every entry must match a path), so modified: [] asserts "modified nothing". An omitted field is unconstrained. Regular files and symlinks are tracked; directories are not (an empty directory is not a file the delta reasons about). A symlink is compared by the target it names, so planting one is a creation, retargeting it is a modification, and a dangling link is still visible. Entries are workdir-relative doublestar globs, always /-separated: a single * stays within one path segment while ** crosses / at any depth (site/** covers the whole tree, dist/**/*.css composes with a suffix). A backslash escapes a literal metacharacter (\[, \?, \*) — a\[1\].txt matches the file a[1].txt; the escape is portable because entries are always /-separated.
| Key | Type | Description | Since |
|---|---|---|---|
created | changesEntries | Exhaustive list of paths the step created: every observed creation must match an entry and vice versa. [] asserts "created nothing". | v0.3.0 |
deleted | changesEntries | Exhaustive list of paths the step deleted. [] asserts "deleted nothing". | v0.3.0 |
ignore | array of [string number boolean] | Workdir-relative doublestar globs whose matches are dropped from the observed delta before the categories are compared, for a path the program writes only sometimes (a state file, a cache in the sandboxed HOME). An ignored path neither counts as an unexpected change nor satisfies an entry, and an ignore glob that matches nothing is fine. | v0.16.0 |
modified | changesEntries | Exhaustive list of paths the step modified (content-based, not mtime; for a symlink, a change of target). [] asserts "modified nothing". | v0.3.0 |
changes entries #
A single workdir-relative path/glob, or a list of them (the empty list [] asserts the category changed nothing). Globs are doublestar and /-separated: * within a segment, ** across /; a backslash escapes a literal metacharacter.
duration #
Bounds the wall-clock time of the immediately preceding run/http/query/grpc/pty step (#31). At least one bound; lt/lte and gt/gte are mutually exclusive. Values are Go duration strings (2s, 100ms).
| Key | Type | Description | Since |
|---|---|---|---|
gt | string | Exclusive lower bound as a Go duration. | v0.3.0 |
gte | string | Inclusive lower bound as a Go duration. | v0.3.0 |
lt | string | Exclusive upper bound as a Go duration (e.g. "2s"). | v0.3.0 |
lte | string | Inclusive upper bound as a Go duration. | v0.3.0 |
image #
| Key | Type | Description | Since |
|---|---|---|---|
alpha | boolean | Asserts whether the image actually carries transparency (any non-opaque pixel), scanning decoded pixels. | v0.1.0 |
format | png | jpeg | gif | webp | bmp | tiff | avif | svg | Asserts the encoded format, detected from content: png, jpeg, gif, webp, bmp, tiff, avif, or svg. | v0.1.0 |
height | integer | Asserts the exact pixel height. | v0.1.0 |
max_diff | number | Maximum allowed normalized mean per-pixel difference (0..1) for similar_to. Defaults to 0 (exact); lossy formats need a small tolerance like 0.02. | v0.1.0 |
max_height | integer | Upper bound on the pixel height. | v0.1.0 |
max_width | integer | Upper bound on the pixel width. | v0.1.0 |
min_height | integer | Lower bound on the pixel height. | v0.1.0 |
min_width | integer | Lower bound on the pixel width. | v0.1.0 |
path | string | number | boolean | Required. The image file under test, resolved against the scenario workdir when relative. | v0.1.0 |
similar_to | string | number | boolean | Baseline image to compare decoded pixels against. A relative path resolves against the spec file's directory (a committed golden), falling back to the scenario workdir when no such file is there — which is how two images the run itself produced, such as the two ends of an encoder round trip, are compared. Both images must share dimensions. | v0.1.0 |
width | integer | Asserts the exact pixel width. | v0.1.0 |
pdf #
| Key | Type | Description | Since |
|---|---|---|---|
max_pages | integer | Upper bound on the page count. | v0.1.0 |
metadata | map of [string number boolean] | Maps an Info-dictionary field (title, author, subject, keywords, creator, producer; case-insensitive) to a substring its value must contain. The field is read whether the producer leaves the Info dictionary in the clear or packs it into a compressed object stream (PDF 1.5+). | v0.1.0 |
min_pages | integer | Lower bound on the page count. | v0.1.0 |
pages | integer | Asserts the exact page count. | v0.1.0 |
path | string | number | boolean | Required. The PDF under test, resolved against the scenario workdir when relative. | v0.1.0 |
text | stream | Applies the stream matchers to the text extracted from the PDF's content streams. | v0.1.0 |
mock #
Checks what the CLI under test sent to a mock server (#24): filter by path/method, pin the exact count (or require at least one match), and match header/body of the LAST matching request.
| Key | Type | Description | Since |
|---|---|---|---|
body | stream | Matches the body of the LAST matching recorded request with the stream matchers. | v0.3.0 |
count | integer | Asserts the exact number of matching requests (without it, at least one must exist). | v0.3.0 |
header | headerMatch | Matches a header of the LAST matching recorded request. | v0.3.0 |
method | string | number | boolean | Filters recorded requests by method (case-insensitive). | v0.3.0 |
name | string | number | boolean | Required. References a declared mock server by name. | v0.3.0 |
path | string | number | boolean | Filters recorded requests by exact path. | v0.3.0 |
query | headerMatch | Matches one query-string parameter of the LAST matching recorded request, by name, with the same equals/contains/matches as header. Routes match on the path alone, so this is where the parameters the client sent (a search term, a page cursor) are checked. A missing parameter reads as the empty string. | v0.24.0 |
header matcher #
| Key | Type | Description | Since |
|---|---|---|---|
contains | string | number | boolean | Requires the header value to contain this substring. | v0.1.0 |
equals | string | number | boolean | Requires the header value to equal this string exactly. | v0.1.0 |
matches | string | number | boolean | Regexp the header value must match - the natural shape for auth headers (^Bearer ). | v0.3.0 |
name | string | number | boolean | Required. The header name to check (case-insensitive). | v0.1.0 |
json / yaml checks #
A json/yaml node: path selects the value, and a matcher decides what is asserted about it. An assertion needs exactly one matcher and the loader rejects it otherwise (ATG2104); a store capture takes the path alone, because it extracts the value instead of judging it.
| Key | Type | Description | Since |
|---|---|---|---|
equals | any | Requires the selected value to equal this value, compared by JSON type (the string "true" does not equal the boolean true). Write null to assert the value is JSON null; omitting the key means no matcher was set. | v0.1.0 |
gt | number | Requires the selected numeric value to be greater than this bound. | v0.1.0 |
gte | number | Requires the selected numeric value to be at least this bound. | v0.1.0 |
length | integer | Asserts the selected array or string has exactly this length. | v0.1.0 |
lt | number | Requires the selected numeric value to be less than this bound. | v0.1.0 |
lte | number | Requires the selected numeric value to be at most this bound. | v0.1.0 |
matches | string | number | boolean | Requires the selected value (as a string) to match this Go regular expression. | v0.1.0 |
path | string | number | boolean | Required. JSONPath selecting the value under test (e.g. "$.items[0].name"). | v0.1.0 |
json / yaml check list #
A json/yaml matcher (#156): either a single check (mapping) or a non-empty list of checks that must all hold.
retry #
| Key | Type | Description | Since |
|---|---|---|---|
interval | string | Wait between attempts as a Go duration (e.g. "200ms"). Empty means no wait. | v0.1.0 |
times | integer | Required. Maximum number of attempts (>= 1). | v0.1.0 |
until | assert | Required. A single assertion polled after each attempt; the loop stops as soon as it passes. If it never passes, the step fails. | v0.1.0 |
skip / only conditions #
| Key | Type | Description | Since |
|---|---|---|---|
command | string | number | boolean | True when this probe command (run through the shell) exits 0. | v0.1.0 |
env | string | number | boolean | True when the named environment variable is non-empty. | v0.1.0 |
os | linux | darwin | windows | freebsd | openbsd | netbsd | Matches the host operating system as runtime.GOOS names it. Each BSD is named on its own: freebsd, openbsd and netbsd are separate hosts, not one family. | v0.1.0 |
runners.<name> #
A runner is discriminated by type: only the fields for that type are allowed, so cross-type fields are rejected and editors can narrow completion (#44).
| Key | Type | Description | Since |
|---|---|---|---|
type: cmd | |||
cwd | string | number | boolean | Working directory for commands run through this runner, relative to the scenario workdir. Beaten by a step's own cwd, and beats defaults.run.cwd. | v0.1.0 |
timeout | string | Default timeout for steps using this runner, as a Go duration. | v0.1.0 |
type | any | Selects the cmd runner: local command execution (the implicit default for run steps). | v0.1.0 |
type: http | |||
base_url | string | number | boolean | Base URL every http step's path is appended to. | v0.1.0 |
cwd | string | number | boolean | Unused for http runners; accepted for uniformity. | v0.1.0 |
timeout | string | Request timeout as a Go duration. | v0.1.0 |
type | any | Selects the http runner: HTTP requests from http steps. | v0.1.0 |
type: db | |||
cwd | string | number | boolean | Unused for db runners; accepted for uniformity. | v0.1.0 |
driver | sqlite | sqlite3 | postgres | postgresql | mysql | Names the database/sql driver explicitly (sqlite, postgres, or mysql), overriding scheme inference from the dsn. | v0.1.0 |
dsn | string | number | boolean | Required. Data source name, e.g. "sqlite:${workdir}/app.db", "postgres://user:pass@host/db", or "mysql://user:pass@host:3306/db". Pure-Go drivers are bundled. | v0.1.0 |
timeout | string | Statement timeout as a Go duration. | v0.1.0 |
type | any | Selects the db runner: SQL from query steps. | v0.1.0 |
type: ssh | |||
cwd | string | number | boolean | Remote working directory for commands. | v0.1.0 |
host | string | number | boolean | Required. Remote host, as host or host:port (default port 22). | v0.1.0 |
insecure_host_key | boolean | Must be set to true to connect without a known_hosts file, explicitly disabling host-key verification. | v0.1.0 |
key_file | string | number | boolean | Path to a private key for key authentication. | v0.1.0 |
known_hosts | string | number | boolean | known_hosts file verifying the host key (recommended). | v0.1.0 |
password | string | number | boolean | Password authentication (prefer key_file, and keep the value in an env var). | v0.1.0 |
timeout | string | Command timeout as a Go duration. | v0.1.0 |
type | any | Selects the ssh runner: running commands on a remote host. | v0.1.0 |
user | string | number | boolean | Required. Login user. | v0.1.0 |
type: grpc | |||
cwd | string | number | boolean | Unused for grpc runners; accepted for uniformity. | v0.1.0 |
target | string | number | boolean | Required. host:port of the gRPC server; the schema is resolved via server reflection. | v0.1.0 |
timeout | string | Call timeout as a Go duration. | v0.1.0 |
tls | boolean | Connect with TLS (default plaintext). | v0.1.0 |
type | any | Selects the grpc runner: unary gRPC calls from grpc steps. | v0.1.0 |
type: browser | |||
browser_args | array of [string number boolean] | Extra Chrome launch flags (bare names, no leading '--'), e.g. 'disable-gpu' or 'window-size=1280,720'. | v0.1.0 |
cwd | string | number | boolean | Unused for browser runners; accepted for uniformity. | v0.1.0 |
exec_path | string | number | boolean | Path to a specific Chrome/Chromium binary instead of the one discovered on PATH. | v0.1.0 |
headless | boolean | Run Chrome without a visible window (default true); set false to debug headed. | v0.1.0 |
timeout | string | Session timeout as a Go duration. | v0.1.0 |
type | any | Selects the browser runner: headless Chrome driven by cdp steps. | v0.1.0 |
defaults #
Spec-wide default fragments merged into every matching element at load time (#39). Authoring sugar only: an explicit value always wins, maps shallow-merge, and a boolean default is OR-ed in. Not a macro/include system.
| Key | Type | Description | Since |
|---|---|---|---|
run | object | Layered beneath every run step. command and retry are per-step and rejected here. The environment-shaping subset (env, clear_env, pass_env, sandbox_home) also layers onto pty steps, which share the same environment surface (#77); the run-only fields (runner, shell, cwd, timeout, stdin, redirects) stay per-step and never reach pty steps. | v0.1.0 |
clear_env | boolean | Start the child from an empty environment instead of inheriting the host environment (#16). On Windows a small system-critical set (SystemRoot, SystemDrive, TEMP, TMP, PATHEXT) is always retained. | v0.3.0 |
cwd | string | number | boolean | Default working directory for every run step. Precedence: step > runner > defaults.run. | v0.1.0 |
env | map of [string number boolean] | Default env map shallow-merged beneath every run step's own env. | v0.1.0 |
pass_env | array of [string number boolean] | Host variables copied into the cleared environment (#16). Requires clear_env: true; unset host variables are skipped. | v0.3.0 |
runner | string | number | boolean | Default runner name for every run step. | v0.1.0 |
sandbox_home | boolean | Point the child's home and per-OS config/cache/data/state dirs at ${workdir}/.atago-home so a CLI touching ~/.config, ~/.cache, or %APPDATA% runs hermetically (#71). Precedence: step env > sandbox > pass_env > host; composes with clear_env. | v0.3.0 |
shell | boolean | Default shell for every run step (an authored shell: false on a step cannot override a defaulted true). | v0.1.0 |
timeout | string | Default timeout for every run step (a step's own timeout wins). | v0.1.0 |
scenario | object | Scenario-level defaults: the env every scenario shares, and the gate every scenario is selected by. | v0.1.0 |
env | map of [string number boolean] | Env map shallow-merged beneath every scenario's own env. | v0.1.0 |
only | condition | Default selection gate: a scenario without its own only: runs only when this holds. Declaring a probe-first suite's gate once — only: {command: mytool --version} — is what keeps some scenarios in the file from being left ungated, which is how a suite ends up erroring on a machine without the tool instead of skipping. A scenario that states its own only: uses that one instead; the two are not combined. | v0.21.0 |
skip | condition | Default exclusion gate: a scenario without its own skip: is skipped when this holds. A scenario that states its own skip: uses that one instead; the two are not combined. | v0.21.0 |
service | object | Layered beneath every service. name and command identify a service and are rejected here; a whole ready probe is copied in when a service declares none. | v0.1.0 |
clear_env | boolean | Start the child from an empty environment instead of inheriting the host environment (#16). On Windows a small system-critical set (SystemRoot, SystemDrive, TEMP, TMP, PATHEXT) is always retained. | v0.3.0 |
cwd | string | number | boolean | Default working directory for every service. | v0.1.0 |
env | map of [string number boolean] | Default env map for every service. | v0.1.0 |
pass_env | array of [string number boolean] | Host variables copied into the cleared environment (#16). Requires clear_env: true; unset host variables are skipped. | v0.3.0 |
ready | object | Default readiness probe for every service (a service's own ready wins). | v0.1.0 |
delay | string | See service.ready.delay. | v0.1.0 |
file | string | number | boolean | See service.ready.file. | v0.1.0 |
log | string | number | boolean | See service.ready.log. | v0.1.0 |
port | string | number | boolean | See service.ready.port. | v0.1.0 |
store | string | number | boolean | See service.ready.store. | v0.1.0 |
timeout | string | See service.ready.timeout. | v0.1.0 |
shell | boolean | Default shell for every service. | v0.1.0 |
Directory manifest #
A directory of specs may carry an atago.project.yaml holding what belongs to the tree rather than to one file:
env:
MYTOOL_REGISTRY: "http://127.0.0.1:8080"
defaults:
run:
sandbox_home: true
fixtures_dir: testdata
It can also declare the binary under test:
subject:
name: mytool
artifact: bin/mytool
build:
command: "go build -o ${artifact} ."
cwd: ".."
profiles:
cover:
build:
command: "go build -cover -covermode=atomic -coverpkg=./... -o ${artifact} ."
cwd: ".."
env:
GOCOVERDIR: "${env:GOCOVERDIR}"
atago run builds it once per invocation, before any scenario, and prepends the artifact's directory to PATH. --profile NAME swaps in that profile's whole build block, cwd included, and layers its env. A failing build — or one that exits 0 without writing ${artifact} — is a run-level error and no scenario executes.
It is discovered by walking up from a spec to the nearest one, so atago run ./e2e and atago run ./e2e/one.atago.yaml resolve the same configuration. Precedence is host < project < suite < scenario < step for env, and a spec file's own defaults: beat the manifest's. fixtures_dir resolves against the manifest's directory and must exist at load time. atago explain prints the manifest that applied and the resolved fixtures directory. Its own schema is atago.project.schema.json.
| Variable | Is |
|---|---|
${workdir} | this scenario's isolated temp directory — the only one it owns |
${suitedir} | the suite's scratch directory, shared by suite.setup and every scenario |
${specdir} | the directory holding the spec file (read-only input) |
${fixtures} | the manifest's fixtures_dir (read-only input); unset when no manifest declares one |
All four are absolute, because a scenario runs somewhere other than where its spec lives.
Editor support (JSON Schema) #
A JSON Schema lives at schema/atago.schema.json. With the YAML language server you get completion and validation as you type — step types, every matcher, and the ${workdir} / ${env:NAME} / ${name} / $${...} expansion rules. atago init and atago record already emit this header as the first line of every generated spec, so scaffolded specs get completion out of the box. To add it to an existing spec, use the absolute URL (it resolves in any project, unlike a repo-relative path):
# yaml-language-server: $schema=https://raw.githubusercontent.com/nao1215/atago/main/schema/atago.schema.json
version: "1"
The report and manifest outputs have schemas too: report.schema.json and manifest.schema.json.
Platform support #
atago runs on Linux, macOS, and Windows, and CI tests all three: the unit suite on every OS, the self-hosted E2E suite on Linux and macOS, and on Windows both under the native cmd.exe and under a POSIX shell. Almost everything behaves identically. This section is the short list of what does not, and why.
skip: and only: gate on the host as Go names it, so besides linux, darwin and windows they accept freebsd, openbsd and netbsd. Each is a host of its own rather than one BSD family: what FreeBSD ps prints is not what OpenBSD ps prints, so a gate names the one it means. bsd is not a value, and neither is dragonfly, where atago does not build. atago ships no binary for the three it accepts; go install builds one, and a gate for a host you never run on simply never fires.
| Behavior | Linux / macOS | Windows |
|---|---|---|
shell: true | /bin/sh -c, resolved absolutely so the program under test cannot supply it | %SystemRoot%\System32\cmd.exe /S /C, resolved the same way. ATAGO_SHELL overrides on both |
signal: steps | delivers TERM, INT, HUP, USR1, USR2, KILL to the service's process group | not supported — Windows has no POSIX signals. Gate with skip: {os: windows} |
| cancel / timeout teardown | kills the whole process group | kills the whole process tree (taskkill /T) |
pty: steps and atago record --pty | a real pty | a ConPTY, which needs Windows 10 version 1809 or later. record --pty cannot auto-detect a password prompt there, because a ConPTY exposes no echo state — convert a secret send to ${env:...} by hand |
pty: {graphics: kitty} | the same pty | a ConPTY hosted by the Windows Terminal console host (OpenConsole, from Microsoft's MIT-licensed ConPTY package) instead of the one built into Windows, which drops the kitty graphics protocol on its way through. atago embeds it for amd64 and arm64 and writes it to %LOCALAPPDATA%\atago on first use; a build for any other architecture reports the step as unsupported on the platform |
file: {executable: ...} | the mode bits | the file extension against PATHEXT, which is what Windows uses to decide what it runs by name. There is no execute bit to read |
fixture: {mode: ...} | sets the permission bits | no effect — Windows has no POSIX permissions |
fixture: {symlink: ...} | always available | needs Developer Mode or an elevated process |
changes: | compares content, symlink target, kind, and permission bits | compares content, symlink target, and kind. Permissions are left out: Go synthesizes a mode from the read-only attribute, so including it would make one spec report a different delta per OS |
sandbox_home: true | redirects HOME and the XDG base directories | redirects USERPROFILE, APPDATA, LOCALAPPDATA, HOMEDRIVE, HOMEPATH |
clear_env: true | starts from an empty environment plus pass_env | the same, plus SystemRoot, SystemDrive, TEMP, TMP, and PATHEXT, without which a process cannot start at all |
How a command is split into argv #
Without shell: true, atago splits run.command itself. Both quotes group an argument and are stripped, and \" inside a double-quoted group is a literal quote that does not end the group — on every OS. Every other backslash differs: on Windows it stays literal, because it is a path separator there, so "C:\dir\x", "\\server\share" and "a\\b" arrive unchanged; on Linux and macOS the string goes through go-shellwords, whose escapes are C-style, so \t becomes a tab and C:\dir\x loses its backslashes. An argument that contains quotes or backslashes is therefore best written in single quotes, where nothing is an escape.
One consequence on Windows: a double-quoted path ending in a backslash ("C:\dir\") reads its closing quote as a literal one, so the argument is never closed and atago reports ATG4001 rather than guessing. Write that path in single quotes.
Choosing the shell #
shell: true runs the platform's own interpreter, so a command written for /bin/sh does not run under cmd.exe. Two ways out. Keep the command portable — echo and exit are builtins of both, and run.env:, run.stdin:, run.stdout_to: cover the variable prefixes and redirects a spec usually reaches for a shell to get. Or point atago at the shell you want:
ATAGO_SHELL='C:\Program Files\Git\bin\bash.exe' atago run ./e2e
ATAGO_SHELL takes an absolute path, or a name found on PATH, on either platform. atago picks the calling convention from the name: /S /C for cmd.exe, -c for anything else, which covers the bash that ships with Git for Windows and MSYS2 as well as PowerShell. One caveat when pairing a POSIX shell with Windows paths: ${workdir}, ${specdir}, and ${atago} expand to backslash paths, and a POSIX shell reads a backslash as an escape — so interpolate them into argv-form commands (shell: false) rather than into shell commands.
Shell completion #
atago completion <bash|zsh|fish|powershell> prints a completion script for your shell.
Exit codes #
| Code | Meaning |
|---|---|
0 | no unexpected failures — every scenario passed, was skipped, or was an XFAIL (an expect_fail: scenario that failed as declared) |
1 | one or more failed (including an XPASS: an expect_fail: scenario that passed, unless --allow-xpass) |
2 | spec error (YAML syntax or schema/semantic validation) |
3 | CLI-invocation error (unknown subcommand, bad flag, or no matching spec files) |
4 | execution error |
5 | internal error |
6 | security policy violation |
Ctrl-C/SIGTERM stops the run cleanly: in-flight processes, services, and sessions are torn down, partial results are reported, and the run exits 4.
Errors also carry a diagnostic code such as ATG2201, whose first digit is the exit code above — so ATG2xxx always exits 2. The codes are searchable and stable across rewordings of the message; Error codes lists what each one means, what to change, and which families carry codes today. Assertion failures carry none: exit 1 is a result, not an error. atago explain ATG2201 prints the same entry without a browser, and the JSON report carries the code as a code field on each failure so a dashboard can group by cause.