Basically, the Zero's I2C stack is unable to communicate with some slave devices, but it works for others. Those same slave devices work with the Leonardo.
I used a logic analyzer and show the differences between the signals of the Leonardo and the Zero, reproduced here:
Does anyone have any suggestions? I've started poking around SERCOM.cpp but haven't made much headway.
There was a slave address conflict; the Zero has an on-board i2c chip - the debug controller chip has i2c lines going in, and uses 0x28 (7-bit address), the same as the device I was using.
I've shifted my device's address and everything is now working.