Over the last week, I've done quite a bit of work on bouyancy. It's an interesting problem, one that always appears simpler than it really is. The basic concept, that gravity (i.e. soy.bodies.fields.Monopole) effects bodies based on their mass minus their displacement, is deceptively concise.
First, you have to find a way to calculate all the displacements before you try to do gravity calculations. In this case, it required moving the gravity calculations into _commit() and adding action list population to _exert(). This allowed me to leave the displacement math in Bouyancy, storing the information in the bmass tag on effected bodies. However, this leaves yet another problem; all those tags are getting set, but nothing is out there unsetting them. Thankfully, we have Bouyancy._give() to the rescue, ready to clear up all those pesky tags in the cycles where Bouyancy is going to be repopulating them.
Unfortunately, our deceptively simple problem isn't done with us yet. We still don't have a good way to actually calculate displacement. As ODE doesn't have support for this, we have to write an approximation of sorts, and whichever way we do it, some cases are going to lose out. I settled on using pointDepth and a generalized Shape._radius() fuction to estimate how much of the shape of the body was within the Bouyancy field, but I welcome any input on improvements.