1. Other than possibly adding a ?include_doc(s) query parameter that would indicate whether the modified doc needs to be returned, I would not see the HTTP API changing. On the back-end, I'd be open to anything from a hard-coded entry, configured Erlang handler or design doc similar to validate_doc.
2. I'm thinking there is already a different code path for replication since I'd think that replication would preserve the existing revision id. However, on my initial scan of couch_db:update_docs, I could not identify the code that was responsible for generating the new revision id. If done with a design doc approach, either the function would not be called on during replication, the design doc would define whether it was called during replication, or the function would be called but it could detect whether it was being called during replication. I haven't thought of a reason that you'd want to invoke during replication, so I'd be fine with any of them.
3. Instead of having the edit function directly modifying the document, it could return a document that is merged into the existing document. Any top-level key that is present in the return value from an doc-edit function would be inserted in the document replacing any existing value for that key in the document. However, if multiple edit functions add the same key value, the PUT would fail with a conflict error.