> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://rivet.chrisboulton.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://rivet.chrisboulton.com/_mcp/server.

# Plan, apply, and reports

> What plan predicts, how apply proceeds, and what each report status means.

The commands here use `--local`. Use `--target DESTINATION` instead to run them [over SSH](/operations/ssh).

## Plan and noop

`rivet plan BUNDLE --local` and `rivet apply BUNDLE --local --noop` inspect each resource and predict what would change. They make no changes and create no lock, journal, or temporary files. They don't run file validators or refresh apt indexes. Exec guards (`only_if`, `unless`) do run, so keep them read-only.

A plan is a snapshot, not a promise. If a prerequisite would change, its dependents are reported as `deferred`, because Rivet can't inspect a state that doesn't exist yet. Apt candidates can also be uncertain until the index is refreshed.

`rivet run --noop` first acquires and prepares the catalog, which can authenticate, write the artifact cache, and run custom fact scripts, and then predicts the same way.

## Apply

Before taking the host lock, apply checks the bundle against fresh host facts: integrity, platform, required capabilities, host identity, and reserved paths. It also checks that no two resources claim the same host object in incompatible ways, for example two SSH keys with conflicting permission policies on one `authorized_keys` file. A conflict stops the run before anything changes.

Resources then run one at a time, in dependency order. For each resource, Rivet:

1. Inspects the current state.
2. Records its intent, and any refreshes it will cause, in the journal.
3. Makes the change, within the resource's timeout.
4. Inspects again to verify the result.
5. Marks the work complete and delivers any refreshes that are ready.

Properties you didn't declare are left alone. A failed resource blocks its dependents; unrelated resources continue. Each resource commits on its own: there is no transaction across the host or across a module, and no automatic rollback.

`meta.noop = true` keeps a single resource read-only during a real apply. On a module it applies to every resource inside, and children can't turn it off. If a noop resource would change, its dependents are deferred, and an apply with deferred work doesn't report success.

## Report statuses

| Status         | Meaning                                                                                    |
| -------------- | ------------------------------------------------------------------------------------------ |
| `unchanged`    | Already in the desired state, or the work changed nothing managed.                         |
| `changed`      | A managed property was changed and verified.                                               |
| `would_change` | A noop or plan predicts a change.                                                          |
| `failed`       | Inspection, change, verification, or recovery failed.                                      |
| `blocked`      | A prerequisite failed, resources conflict, or earlier interrupted work can't be resumed.   |
| `deferred`     | A prerequisite's pending change makes a reliable check impossible now.                     |
| `skipped`      | Policy prevented work, for example a guard, or a refresh-only exec with no refresh queued. |

Text output prints one line per resource: the status, the resource ID, the changed properties in brackets, and an optional message. Shared actions, such as a systemd daemon reload, follow on `Action:` lines. For example:

```text
Changed file.unit [ensure]
Changed service.example [state]
Action: daemon_reload
```

A failed result may also say `(partial change)`, meaning the host may have been modified before the failure, or `(uncertain)`, meaning the outcome needs checking. Look at the [journal](/reference/recovery) rather than assuming a failed run changed nothing.

## JSON report

`--format json` prints the full report. Each resource result has `id`, `status`, `changed_properties`, `message`, `partial_change`, `uncertain`, `duration_ms`, `operation_timeout_ms`, and `source`. Successful commands that aren't sensitive also include their output, truncated if large.

The run itself has `run_id`, `catalog_digest`, `host_identity`, `noop`, start and end times, `resources`, `recovery`, counts of unresolved intents and refreshes, and `actions`.

`rivet run --format json` wraps the report in `run` and adds `provenance` (environment, release, and promotion generation), `cache` (`hits`, `downloads`, `downloaded_bytes`), and `offline`. With `--prepare-only`, it prints `prepared` and `catalog_digest` instead.