Essentially, zkEVMs map EVM bytecode to equivalent zero-knowledge circuits (containing the logic of what is and isn't valid, i.e. double-spending) so that the entire execution can generate a proof. It is a lot of math.
My original confusion was more about how consistency of state can be maintained across completely opaque commitments of transaction validity.
Now that I think of it, it seems that my original concern was a matter more trivially dismissed than what you've explained - my doubts were more about how state could possibly be consistently yet opaquely maintained, but the blockchain state machine, and I gather the ZK commitments, are simply not opaque with respect to the VM state - the opaqueness provided by ZK commitments is opaqueness in the intermediate transaction state (essentially irrelevant information - perfect candidate to be optimised away). The problem of double-spending is never relevant within a single chain of block commitments, as any second-spending of resources is simply an invalid transaction and is rejected, given the (necessarily public) hitherto state reached by consensus (- the problem of which, is precisely what the blockchain mechanism was designed to solve).
Disclaimer: I have no idea what I'm talking about with respect to ZK, and may be completely wrong.
I have the same confusion as you, and I'm not sure I understood your last paragraph. The prover (person submitting a new transaction) can provide a ZKP that the transaction he is making is valid, and the blockchain will adopt it if indeed it's a valid proof. So then the onus of preventing the double-spend here effectively on the prover: he can only provide a valid ZKP for a valid transaction, and a double-spend would not be a valid transaction.
But then at the end of the day, seems like the underlying balance needs to be accessed somehow (otherwise how would the prover even know if he has enough funds avaliable?). I guess the prover (and only the prover) can do this because he presumably has access to the right private keys?