Replying to parts of all messages thus far.
>The obvious solution to this problem is to not create the problem in the first place.
Yes, that is a fair point. Not removing the checkpoints is one way of ensuring the consensus bug cannot be triggered. I'm agnostic about whether having checkpoints is also a reason to forgo consensus checks such as BIP30 (or my proposed alternative of checking the coinbase TXID for uniqueness and ensuring no future collision). Even though checkpoints essentially force your node to halt if something were invalid up until that point, I still think there is value in being able to verify that the rules were followed.
>Solution C could be to remove it, but restore the previous UTXO
Yes, as Sjors also pointed out, I do think it is best to be precise about which of the duplicates you're keeping. In fact, it's probably required to ensure the rolling UTXO set hash remains consistent.
>In the case of BIP30, one option could be to have a rule that says: if the 2014 checkpoint is missing, then enforce the BIP54 Consensus Cleanup nLockTime rule from genesis. BIP34 can then simply go away.
I'm afraid it's not that simple. If you wanted to fork off from some arbitrary point prior to the last checkpoint, you'd want to enforce the new consensus rules from that exact point (not from genesis), but that requires shipping the full node software with a hash for every possible block that could be forked off from. It's roughly 8MB of data so it's not impossible, and I even had this written up as an alternative solution, but I removed it in favor of the solution I ended up describing.
Cheers,
Ruben