Plan.digest()
SHA-256 of everything else in the saved plan, as JSON reads it back
Usage
Plan.digest()(keys sorted, values through str where JSON has no type).
The preconditions vouch for the world the actions and context were computed from; the digest binds the three together, so a saved plan edited in one place – an action dropped, a target_state or bookmark_commit changed – is refused at apply rather than applied under checks that describe another plan. It detects edits, it does not authenticate: whoever can write the file can recompute it.