I knew I could count on you to give some constructive feedback.
Admittedly, yes, this is a potential problem. I decided that it is fairly unlikely to happen, so I didn't worry about it too much. However, it would be easy to solve by automatically having a separate queue for ACKs themselves, where the ID of the message being ACK'd would also be included in the ACK. That way we wouldn't have to potentially get rid of actual messages when trying to find an ACK. That being said...
This is a much better idea than mine! Wouldn't be too tough to implement, either. The interrupt handler would be responsible for going through the TX slots and (re)transmitting (un)ACK'd packets, meaning we don't have to use blocking TX functions. There are some implications I've been considering.
Using the approach outlined above, using slot S to send a command to some Wixel W that doesn't exist will result in slot S being used up permanently, without some additional measures to free up the slot later. Of course, as you say, it's perfectly reliable if we can guarantee that the device W exists.
However, blocking TX functions are kind of useful, because they allow me to determine right away if a message was ACK'd or not. So, there are clearly trade-offs here.