When your approval step signs off on a draft, you are approving something that nobody will ever receive.
The draft is the input. The bytes the renderer produces are the output. Between them sits an adapter that adds a footer, a disclaimer, a formatting pass, whatever that channel requires. Approve the draft and you have approved everything except the part that actually goes out.
In exactsend, our open-source project, a proposal is rendered bytes and not a draft. The store resolves the renderer first, and if the adapter has no render function the proposal gets refused outright instead of falling back to raw text. Then the checks run in a fixed order: target, proposer, draft, attachments, window. An empty target is refused before any scheduling window gets examined, so the error a caller gets back names the first real problem rather than the last one.
The window check is where we chose strictness over convenience. It accepts two keys only, wants both to be real epoch milliseconds, wants the end to be later than the start, and refuses a span wider than the type allows, with its own error code instead of a silent clamp. A clamped window is worse than a rejected one, we think: the caller believes they asked for ten days, the system quietly scheduled two, and nobody finds out until the deadline moves.
What gets digested is the payload hash, the type, the target, the attachment hashes in the order given, and the window bounds. Not the draft. So the footer the adapter appended sits inside the approval, and reordering attachments produces a different digest, because order is part of what an attachment list means.
The second decision is that edits append. An edit or a reschedule creates revision n+1 with its own reason, digest and payload, freezes it, and leaves every earlier revision untouched. Reading the current state means reading the last revision. Nothing mutates in place, so there is no moment where an approved digest and its payload disagree, and when someone asks why the terms changed between Tuesday and Friday, the answer is a record rather than a reconstruction.
We have built enough document workflows and signing flows to know which failure actually hurts, and it is not a rejected request. It is the record that got quietly overwritten and now cannot be explained.
When you version an approved artifact, you either append revisions or mutate in place, and we would like to hear what finally pushed you one way.