I have a theory about how great commander exp may differ and cause OOS's to occur.
When almost anything about a unit's properties has to be determined, it will make a call to getCommander(), because the presence of a usable commander can change most queried properties. If it finds a commander to use it will cache that commander's id and use it again (assuming it's still alive and still has command points and so on) on the next call (for the same unit).
For the AI this is fine, since the AI will make the same requests and so generate the same caching sequence on all machines.
However, suppose the human player has 2 great commanders nearby and does the following:
1) Check the attack odds for a unit against an enemy (right click and hold) but then NOT execute the attack. The determining of the odds will have cached a commander against the unit that didn't attack.
2) Move the unit to a nearby tile and then attack with it from there, which happens to be nearer the OTHER great commander (both still within command range though). This will continue to use the original GC, because its still valid and cached for that unit. However, on another machine when the final move and attack orders are pushed, the aborted odds-determination is never run, so the first commander is not cached. Thus the unit performs its search for best commander on the actual attack, when it is nearer the OTHER GC, so it picks that one instead.
There may be simpler scenarios, where an unsynchronised action (the odds check in this case) causes a commander to be cached, that also result in differences in outcome.
Anyway, I have modified the code to disable the caching for human units. We'll see if this helps. This change will be pushed to SVN later today.