# preconditions

The step-level `preconditions` field declares tools that must already be on `PATH` for a step's work to be considered already satisfied — a lighter, inline, single-step-skip variant of the [`require`](/steps/type/require) step type's hard preconditions gate, for skipping one optional step rather than failing an entire run.

```yaml
steps:
  - name: install-stringer
    type: shell
    command: go install golang.org/x/tools/cmd/stringer@latest
    preconditions:
      tools: ["stringer"]
```

`go install` writes the binary to `GOBIN` (or `GOPATH/bin` if `GOBIN` is unset) — that directory must already be on `PATH` for `preconditions.tools` to find it on a later run. If it isn't, `stringer` never resolves and the step runs every time.

With no explicit [`when`](/steps#conditional-execution), declaring `preconditions` alone is enough — the step implicitly means `when: "!preconditions.success"` (**note the negation**: `preconditions.success` means the tool is already there, i.e. skip; the run-signal is its negation — the opposite implicit polarity from [`inputs`](/steps/inputs)'s `checksum.changed`, where the fact itself already means "needs to run"). Run this step when `stringer` is already installed and it's skipped; run it on a machine without `stringer` and it installs it.

`preconditions` is independent of `inputs`/`artifacts` and can be declared alongside them — a step with all three implicitly means `when: "checksum.changed || !preconditions.success"` (run if either signal says so).

## Fields

- **`tools`** — List of executable names resolved via Go's `exec.LookPath` — no shell involved, so there's no `which`-vs-`where` cross-platform mismatch to work around. `preconditions.success` is `true` iff every entry resolves without error.

## Facts exposed to `when`

- **`preconditions.success`** — `true` when every tool in `preconditions.tools` resolves on `PATH`. Reuses the same `success`/`failure` vocabulary the condition engine already uses for step lifecycle status, namespaced under `preconditions` — not a new, unrelated word for the same concept.

Power users can reference it directly, including negated, to combine it with anything else `when` already supports:

```yaml
steps:
  - name: install-stringer
    type: shell
    command: go install golang.org/x/tools/cmd/stringer@latest
    preconditions:
      tools: ["stringer"]
    when: "!preconditions.success"
```

## Storage

`preconditions.success` is always evaluated live (`exec.LookPath` at run time) — there's no persisted state, unlike `checksum.changed`.

:::note Custom commands
The same `preconditions` field works identically on [custom command](/cli/configuration/commands) steps.
:::
