As a sort of half-way house between using lifecycle callbacks and sticking everything in the controller, I've just moved the main app I work on to a pub-sub event system, which is working out quite nicely. Controllers or mutator methods no longer handle post-action tasks, they just announce what they did, and any interested listeners can act upon the event announced. This way we can have a listener that handles (for example) all post-action emailing, and we can test the logic for that in isolation. Controller logic is cleaned up too, so testing them becomes easier. On the flip side we have to write more comprehensive integration tests to make sure our listeners are all hooked up correctly, but it seems like a pretty good trade-off so far.
Enjoyed the OP nonetheless, though - I didn't know about ActiveModel#previous_changes, that's pretty nifty to have.