The way I understand it, the problem can be phrased in the following way.
Suppose that both Alice (address A) and Mallory (address M) want to buy an item from seller Steve. Alice and Mallory create a CoinJoin transaction (A,M) -> (S,M') where M' is another address controlled by Mallory. Alice signs the transaction as she sees that the correct amount is sent to Steve and expects to receive her service. However, Mallory may also later claim that
he sent the money to Steve and that
he should receive the service. From Steve's point of view, both claims are equally valid.
I believe that maaku's solution would work as long as the join requests are signed and kept by each party. One downside of this is that it might reduce the anonymity of the system since each user can later
prove the other user's intentions to a third party.
Reference the original offers in the join request, so that anyone considering signing the transaction can make sure that not just the correct outputs, but also the correct number of outputs are present. My Python implementation does this.
Another, simpler solution would be to make sure to never send funds to a public address in a CoinJoin transaction and for sellers to always use fresh receiving addresses.