OK, yes, this “version” of the algorithm requires a perfect version-match. We -can- do better, and that better involves a design of a version-string specification and contract for up/down-compatibility. For example, the OCaml development team makes guarantees about up/down compatibility of AST versions – minor releases will not modify the AST type. Similar assumptions/guarantees could be made/used. Of course, this involves work, whereas what I suggested didn’t. For instance, if each end sends a hash + human-readable version-string, then the ends can compare hashes, and when they don’t match, print out the human-readable version-strings (which don’t need to be canonicalized, hence can be more human-readable) for the invoker to decide if they want to override.
I was making a -cheap- suggestion for how to improve Unison: a suggestion that didn’t involve a lot of design, but would catch many of the obvious error-cases. Specifically, when the two ends reject, it gives the human invoker a chance to have a look and decide if it’s OK to proceed. Which automatically means that the human is paying attention when/if things go awry.
 every step we make towards a structured version-string increases complexity and pushes towards just going with some RPC system/IDL-compiler that already has this sort of thing built-in, or is OCaml-version-independent. But that’s a much bigger cost than just exchanging/comparing version-hashes.
If one were going to do this, one would (of course) want to prepend a “protocol version” to the beginning of the stream, so that one could decide later to change to a different scheme. IIRC Thrift has something like this – you can version types, but you can also version the protocol framing. Might be worth looking at that to see how they did it.