"You approve exactly what you previewed — or you don't get to approve at all."
One Immutable Plan, Two States
Every mutation in Firelink runs the same lifecycle: a request resolves a capability, passes preflight, becomes an immutable operation plan, waits behind an armed confirmation, executes exclusively, verifies its postconditions, and lands as a durable, audited result. The plan is the heart of it, and dry-run and apply are not two operations — they're two states of that one plan. Dry-run creates and persists the exact plan; the UI shows its targets, warnings, and expected effects; apply executes that same plan, by id, behind an armed two-click. A typed plan holds canonical ids and adapter-resolved arguments — never raw command tokens.
Apply Executes a Plan — It Never Rebuilds One
Here's the rule that makes preview trustworthy: the backend never accepts an apply request that independently rebuilds the operation from new raw values. Apply takes a plan id, not a fresh set of parameters. You cannot preview a gentle operation and then apply a different, harsher one by changing the payload — because apply doesn't read a payload, it executes the stored plan. What you saw in the dry-run is exactly, byte for byte, what runs. The preview isn't an estimate; it's the artifact that gets executed.
A Changed World Invalidates the Plan
What if the world moves between preview and apply? If the source state, the registry revision, or the target selection changes, the plan is invalidated and must be regenerated and re-previewed. This is the classic time-of-check-to-time-of-use gap, closed deliberately: you can't preview against one reality and apply against another. The plan is pinned to the world it was built in, and if that world shifts, your approval no longer applies — you have to look again. Combined with an expiry on the plan id, this means an apply is always against a fresh, unchanged, explicitly-approved reality.