A comparison reads selected architectures in validated Forms. It reports changes in interpreted instances and facts, with uncertainty intact. It does not compare HCL text, Terraform actions alone, raw values, or Explorer layout.
One plan can provide three comparisons when its stages exist:
Recorded is reconstructed by reversing drift records and may be partial. Refreshed is the state the plan starts from; the plan does not record whether or how far refresh ran. The drift report is separate from the Reported drift comparison. It lists plan records, including changed attribute paths and an architectural consequence such as architectural, none_under_dialects, indeterminate, uncovered, or address_only. “No drift reported in this plan” says the plan JSON has no drift record; it does not prove every resource was refreshed or that live infrastructure is unchanged.
An input comparison, rootform run before.json --diff after.json, selects one stage from each separate input and writes a comparison Form with cross Differences. Its sides are Before and After; its Differences are never drift: they show how the two inputs differ and do not establish what drifted between the two exports. Plans default to Planned; state Forms select Recorded; saved single-input Forms use their saved default. Use --before-stage and --after-stage to choose other available stages. A comparison Form reopens alone with rootform run comparison.json; it cannot be a --diff operand.
A plan can propose to undo reported drift. The public restored-drift example uses synthetic plan JSON written by hand. It was not exported by Terraform or OpenTofu and describes no real infrastructure. An out-of-band change retargeted the KMS alias aws_kms_alias.app from aws_kms_key.primary to aws_kms_key.standby, and the plan retargets it to the primary key. The embedded AWS Rule aws.rule.kms-alias interprets the alias as a Contribution to the key it targets. From that directory:
rootform run plan.json --no-serve --color alwaysPlanned changes Refreshed -> Planned
Contributions 1 added, 1 removed Indeterminate closures 0
Contributions + aws_kms_alias.app contribution -> aws_kms_key.primary added - aws_kms_alias.app contribution -> aws_kms_key.standby removed
Reported drift Recorded -> Refreshed The export does not establish the refresh scope.
Drift entries 1 (1 architectural) Effect 1 fact added, 1 removed
~ aws_kms_alias.app changes the architecture; 2 fact changes
Net change Recorded -> Planned
No net architectural difference: the plan proposes to restore 2 drift fact changes.
Cancelled = aws_kms_alias.app removed by drift, planned for restoration contribution -> aws_kms_key.primary = aws_kms_alias.app added by drift, planned for removal contribution -> aws_kms_key.standbyReported drift removes the Contribution to the primary key and adds one to the standby key; Planned changes proposes the reverse. Net change compares Recorded with Planned directly, so the two fact changes cancel: it reports no net architectural difference and lists each cancelled fact with what drift did to it and what the plan proposes. The restoration is a proposal of the plan; nothing has been applied.
An instance Representation keeps its address across stages. A stable address can have changed Rule, Concept, implementation, or provider identity without being treated as removed and added. Indexed instances remain separate. Rootform does not infer that two differently addressed resources are the same physical cloud object because their labels, types, or remote IDs resemble each other.
A move reported in the plan can connect a previous address to its planned address. A reported replacement is replaced with its reason; deletion followed by creation can appear as recreated in the net view. A cross-input move needs the before side to contain the previous address. External endpoints have Form-local ordinals, so cross-input matching uses a recorded identity and Concept, never matching ordinal alone.
Adding a subnet may add one Representation and one network Context toward a VPC. They answer different questions: the instance exists, and it is placed in that network frame. Fact entries use added and removed; Representation entries can also report changed, moved, replaced, or recreated where the plan evidence supports them. A Context move is a removed old Context and an added new one, not a fact entry named moved.
A Terraform replacement can leave architectural meaning unchanged. Conversely, a changed Dialect Rule can change meaning even when infrastructure source stays the same. To isolate infrastructure edits, compare with the same Rootform binary and semantic selection. Comparability problems are explicit; Rootform does not silently turn incompatible semantics into an empty result.
An added fact is determined only when the Before side can prove its absence; a removed fact needs the same proof on the After side. An indeterminate closure, missing interpretation, withheld external identity, or uncomparable semantic selection can leave a conclusion indeterminate. The report counts unresolved closures separately on the Before and After sides, so readers can see where proof is missing. A comparable report may contain determined changes and indeterminate closure entries together. indeterminate means neither no change nor a failed comparison.
An invalid Form or a comparison with incompatible semantic selection reports a problem instead of claiming no change. Check comparable, problems, changed Representations and facts, and indeterminate before making a review decision. Status 0 means analysis completed; it does not mean the comparison is empty. Outputs and exit status is the status reference.
Text and Markdown summarize differences. The comparison Form retains both complete input Forms, selected stages, complete difference entries, and uncertainty. HTML opens the same Before, Differences, and After views as the local Explorer; it is self-contained and makes no network requests. See Compare two Forms for a runnable workflow, Plan inputs for plan evidence, and the Form contract for exact fields.