Hooks that listen only to the merge shape silently miss the replace shape
Designing a PATCH API that accepts multiple equivalent shapes (replace a field vs merge into it) and the downstream side effects of edits.
When a PATCH accepts both links: {...} (replace) and links_add: {...} (merge) for the same field, downstream side-effects that listen to additions must compute their diff against a pre-patch snapshot, not against the merge payload. Listening only to links_add causes silent skips: the UI usually sends replace, the public API often sends merge, and the side-effect (here: retroactive bucket reclaim and lazy IMAP backfill) fires on one but not the other. The symptom is that the data writes succeed and tests written against the API path pass, but the UI-driven happy path quietly drops the side-effect with no error anywhere. Fix: snapshot the field BEFORE applying any patch shape, apply the patch through whichever branch the input picked, then diff post-against-pre once. The diff is the source of truth for downstream hooks regardless of how the caller framed the request.
When a PATCH endpoint exposes both replace and merge shapes for the same field, write the downstream-effect diff once against a pre-patch snapshot. Never attach the side effect to one branch of the input shape and not the other.