guy_c:
Can someone recommend an approach of debouncing a switch attached to an i2c i/o expender's input?
Yep.
guy_c:
In my original scheme, a timer interrupt would, every ~5ms, interrupt the main program busy in some background work, and check that the (concerned) switch(s) had stabilized.
Fine so far.
guy_c:
In this scheme,
- an i2c interrupt notifies of a pushbutton press.
- corresponding i2c input interrupt is masked.
- that input is submitted to the timer interrupt ISR for debouncing (or added to debouncing queue if many concurrent such switches).
- once debounced that input is interrupt enabled again.
...
Unfortunately, it seems that within an ISR all calls to "wire" routines block the UNO.
As you would expect if you thought about it. 
Nope, it doesn't work that way.
You badly misunderstand the purpose of interrupts.
Interrupts are there to quickly service important things, so you have it wrong on two counts; what you propose to do - using I2C is not quick, and pushbuttons are never, repeat never important! Apparently the I2C routines themselves use some sort of interrupts as well.
guy_c:
The rationale is to have all the communication tasks -which in this case include the debouncing- done autonomously by an "independent channel" while the main program is occupied with processing tasks (as opposed to communications)
Yes, all fine, as long as you realise this independent channel is not performed within an interrupt.
What you are looking for is a RTOS or task scheduler, but you do not (should not) need to go to all that trouble of using a formal "packaged" one (if you understand how it works).
You have to write the main program (which is a loop - always a loop) so that it alternates between two tasks. You may not "busy wait" - if either or any task has to wait at any point, it needs to invoke the other task or if you are aware that it will take an inordinate amount of processing time, it will need to regularly invoke the other task.
In practice, this is generally fairly easy because you would have broken up your task into modules called from a main sequence. Simply put the debounce code into that sequence. It is most certainly true that any marginal efficiency that you might achieve by having the expander generate an interrupt, will be offset by the complexity of having that interrupt set a flag (semaphore) which will then be checked in due course by the main loop.
You already have an efficiency insofar as the I2C routine polls the whole byte from the expander each time it is called on the 1 ms (or better, 5 ms) "tick" and you debounce the whole input byte as one by comparing it to the "previous" version of the input byte, so all of your switches are debounced as one.
Just a reminder - pushbuttons are not an important priority at MCU speed. Your debounce should be counting at least three or four, 5 ms "ticks".