I think the really interesting problem in this space is designing UIs and data structures that, on the one hand, capture as much user intent as possible, but, more importantly, make it easier for users to manage conflicts.

I.e., if there's a tricky conflict, the app need not resolve it at all. Rather, it should provide, by default, a nice way for the user to manage the resolution as part of the normal workflow.

Or, phrased another way, conflicts aren't conflicts. Parallel, "conflicting" edits are simply a state of affairs that is inherent to the process, and are still reflected in the data structure after merging all edits.

How this would actually look would probably vary from domain to domain. But my general philosophy on this stuff is that if complexity is real and potentially important to the user, the software should expose the complexity and enable the user to manage it, not force a simplification that hides something important.

For CRUD applications with real-time updates, I find that updating individual fields as opposed to overwriting the entire record works sufficiently well. It can be dome relatively cheaply using WebSockets. Would be overkill to use HTTP to update individual fields (as each request carries redundant headers) but WebSocket frames are very lightweight. I've built a serverless platform on this principle and it helps to simplify the frontend components.