"미리 본 걸 정확히 승인해 — 아니면 승인 자체를 못 해."
하나의 불변 plan, 두 state
Firelink 의 모든 mutation 은 같은 lifecycle 을 돌아. 요청이 capability 를 resolve 하고, preflight 를 통과하고, 불변 operation plan 이 되고, armed confirmation 뒤에서 기다리고, 배타적으로 실행되고, postcondition 을 검증하고, durable·audit 된 결과로 안착해. plan 이 그 심장이고, dry-run 과 apply 는 두 operation 이 아냐 — 그 하나의 plan 의 두 state 야. dry-run 이 정확한 plan 을 만들고 persist 해; UI 가 그 target·warning·기대 효과를 보여줘; apply 가 그 같은 plan 을, id 로, armed two-click 뒤에서 실행해. typed plan 은 canonical id 와 adapter-resolve 된 인자를 지녀 — 절대 raw command token 이 아니라.
apply 는 plan 을 실행해 — 절대 재구성 안 해
preview 를 신뢰할 수 있게 만드는 규칙이 여기 있어. backend 는 새 raw 값으로 operation 을 독립적으로 재구성하는 apply 요청을 절대 안 받아. apply 는 새 인자 집합이 아니라 plan id 를 받아. 부드러운 operation 을 preview 하고 payload 를 바꿔 다르고 거친 걸 apply 할 수 없어 — apply 가 payload 를 안 읽고, 저장된 plan 을 실행하니까. dry-run 에서 본 게 정확히, byte 단위로, 돌아가는 거야. preview 는 추정이 아냐; 실행되는 artifact 야.
바뀐 세계는 plan 을 무효화한다
preview 와 apply 사이에 세계가 움직이면? source state, registry revision, target 선택이 바뀌면, plan 이 무효화돼서 재생성·재프리뷰 되어야 해. 이게 전형적 time-of-check-to-time-of-use 간격을 일부러 닫은 거야. 한 현실에 대해 preview 하고 다른 현실에 대해 apply 할 수 없어. plan 은 지어진 세계에 핀으로 고정되고, 그 세계가 바뀌면 네 승인은 더는 적용 안 돼 — 다시 봐야 해. plan id 의 만료와 합쳐지면, apply 는 늘 신선하고, 안 바뀌고, 명시적으로 승인된 현실에 대한 거야.