# rootform run

Analyze a plan or state, reopen a saved Form, or compare two inputs.

`run` compiles one plan or state export into a Form, opens a saved Form, or
compares two accepted inputs. It detects their kind from content. A plan or
state export is analyzed with the active Dialects; a saved Form is
validated and loaded without reinterpretation.

## Accepted inputs

| Input | Form |
| --- | --- |
| Plan JSON from `show -json` on a saved plan | Planned stage, available earlier stages, drift and internal comparisons |
| State JSON from `show -json` | One Recorded stage |
| Saved Form | Its saved stages, facts, closures, and evidence |
| `-` | One of those JSON forms on standard input |

With `--diff <input>`, each operand is a plan, a state, or a saved
single-input Form; a comparison Form reopens alone and is never a `--diff`
operand. At most one operand may read standard input. A configuration
directory and a binary saved plan are not analysis inputs. `--project <dir>`
selects project Dialect content; it does not read that directory as
infrastructure evidence. See [Choose an input](https://docs.rootform.dev/inputs/).

<!-- BEGIN GENERATED CLI: rootform run -->

## Usage

```text
rootform run <input> [--diff <input>] [options]
```

## Options

### Inputs and stages

| Flag | Type | Default | Description |
| --- | --- | --- | --- |
| ` --after-stage ` | ` string ` | ` "" ` | stage of the second input to compare: `planned\|refreshed\|recorded`; default: Planned for a plan, Recorded for a state |
| ` --before-stage ` | ` string ` | ` "" ` | stage of the first input to compare: `planned\|refreshed\|recorded`; default: Planned for a plan, Recorded for a state |
| ` --diff ` | ` string ` | ` "" ` | compare with a second `input`: a plan, a state, or a saved single-input Form |
| ` --stage ` | ` string ` | ` "" ` | stage the summary leads with, for one input: `planned\|refreshed\|recorded`; default: Planned for a plan, Recorded for a state |

### Output

| Flag | Type | Default | Description |
| --- | --- | --- | --- |
| ` --details ` | ` bool ` | ` false ` | add semantics, closure counts, and diagnostic codes; a Markdown summary and the summary beside the explorer then list every entry |
| ` --format ` | ` string ` | ` "" ` | format of standard output, or of a single -o file without a recognized extension: `text\|json\|markdown\|html`; default: text |
| ` -o, --output ` | ` stringArray ` | ` [] ` | write `file`; its extension selects the format: .json (the Form), .txt, .md, or .html; repeatable |

### Explorer

| Flag | Type | Default | Description |
| --- | --- | --- | --- |
| ` --no-browser ` | ` bool ` | ` false ` | serve the explorer without opening a browser |
| ` --no-serve ` | ` bool ` | ` false ` | exit once the outputs are written instead of serving the explorer |
| ` --port ` | ` int ` | ` 21717 ` | serve the explorer on loopback `port`; 0 picks a free port; default: 21717 |

### Rootform project

| Flag | Type | Default | Description |
| --- | --- | --- | --- |
| ` --dialect ` | ` stringArray ` | ` [] ` | use Dialect source `dir` for this command only; repeatable |
| ` --locked ` | ` bool ` | ` false ` | refuse to run unless rootform.lock is valid |
| ` --project ` | ` string ` | ` "" ` | read rootform.lock from project `dir`; paths stay relative to the working directory; default: the working directory |

### Advanced evidence settings

| Flag | Type | Default | Description |
| --- | --- | --- | --- |
| ` --diff-plan-file ` | ` string ` | ` "" ` | pair the --diff plan JSON with its saved plan `file`, as --plan-file does |
| ` --plan-complete ` | ` string ` | ` "" ` | declare the plan complete; the only `value` is attested |
| ` --plan-file ` | ` string ` | ` "" ` | pair the plan JSON with the saved plan `file` it was exported from, to enrich it; pairing compares version, timestamp, and configuration shape |
| ` --producer ` | ` string ` | ` "" ` | declare the tool that produced the input: `terraform\|opentofu` |
| ` --provider-map ` | ` stringArray ` | ` [] ` | map an observed provider to a binding, as `observed=binding`; repeatable |
| ` --require-enrichment ` | ` bool ` | ` false ` | refuse the input when its saved plan file does not pair with the plan JSON |

### Global options

| Flag | Type | Default | Description |
| --- | --- | --- | --- |
| ` -h, --help ` | ` bool ` | ` false ` | show how to use rootform run |
| ` --color ` | ` mode ` | ` auto ` | color human output: `auto\|always\|never`; default: auto |
| ` --no-pager ` | ` bool ` | ` false ` | print a long report in full instead of opening it in less |

<!-- END GENERATED CLI -->

## Examples

Analyze one plan, then save its Form:

<!-- docs-check:journey-run-save -->
```sh
rootform run plan.json --plan-file plan.tfplan --require-enrichment --no-serve -o analysis.json
```

The summary goes to standard output, and `Wrote analysis.json` to standard
error. The status is `0`; a saved plan that fails verification exits `3`
because of `--require-enrichment`.
The [quickstart](https://docs.rootform.dev/getting-started/quickstart/) reads the same summary
step by step.

Compare two plan exports and save a Markdown report:

<!-- docs-check:journey-run-compare -->
```sh
rootform run base/plan.json --plan-file base/plan.tfplan \
  --diff head/plan.json --diff-plan-file head/plan.tfplan \
  --no-serve -o comparison.md
```

The comparison summary goes to standard output. `comparison.md` contains a
Markdown review with bounded lists; `--details` includes every entry. See
[Review with Markdown](https://docs.rootform.dev/reference/outputs/#review-with-markdown).
Differences are not failures: the status is `0`.

`--stage` selects the reported stage of one input. With
`--diff`, use `--before-stage` and `--after-stage`; a missing or ambiguous
stage is refused with available choices. A plan defaults to `planned`;
a state defaults to `recorded`. The saved Form keeps every stage its input
supports, whatever stage the summary reports; `rootform check` chooses the
stage it evaluates on its own.

## Serve or export

By default, `run` starts a loopback-only server, opens a browser, and
stays in the foreground until interrupted. `--no-browser` leaves browser
launch to you. `--port 0` selects an available port. `--no-serve`
writes requested outputs and exits. The server analyzes once; it does not
watch files or replan. See [Explore a Form](https://docs.rootform.dev/guides/explore-architecture/).

See [Outputs and exit status](https://docs.rootform.dev/reference/outputs/#choose-an-output-file) for the
complete file-format, stream, collision, and write-failure contract.

## Project selection

Embedded Dialects work without a lock. `--locked` requires a valid
`rootform.lock` for the selected project. `--dialect` selects local
Dialect sources for this analysis; an override cannot be combined with
`--locked`. `run` does not select or evaluate Policies. Use
`rootform check` for a separate Policy gate.

`--plan-file` verifies and enriches the first plan; `--diff-plan-file`
does the same for the second. `--require-enrichment` refuses a failed
pairing. `--producer`, `--provider-map`, and
`--plan-complete=attested` record operator claims rather than
facts established by the plan. See [plan inputs](https://docs.rootform.dev/inputs/plans/).

## Exit status

Status `0` means the Form was produced or opened; `2` means the command was
used incorrectly; `3` means an input was refused, `rootform.lock` is invalid,
or a requested stage is unavailable; `4` means an input or output file, or the
explorer, failed. Architectural differences and reported drift do not by
themselves make the command fail. Read the exact
[output contract](https://docs.rootform.dev/reference/outputs/#analysis-exit-status).
