In my opinion, the main source of replay instability comes from the timing model. I just dealt with this yesterday, in fact: I have a puzzle game running the Box2D physics engine, and had to rework some of my architecture to make it run deterministically each time the player restarts, since the gameplay is predicated on testing a configuration, resetting, and adjusting it.
Representing time in a game requires some way of slicing a time delta into discrete slices that you can run processing on it; so in the end every timing model comes down to "use a time delta directly within your simulation code" or "chunk it into frames and store or drop the remainders." The latter option is almost a given if you want your results to be stable and deterministic, it's just a matter of _when_ you apply the frames.
In my case, before the refactor I had each segment of the simulation(AI, spawners, physics, etc.) running independent frame chunks from the same dT. The problem is that in a loop that looks like:
ai.update(dT);
phys.update(dT);
spawns.update(dT);
If dT is large enough, then AI will run multiple times before ever hitting physics, causing some bizarre behaviors that may or may not be acceptable(in my case, not).
My solution was to change it to:
while (dT>fixdT)
{
ai.update(fixdT);
phys.update(fixdT);
spawns.update(fixdT);
dT-=fixdT;
}
This way, no one piece of code can race ahead of the others, but the option is still there to let some things run only every n frames(including fractional amounts).
Another thing I had to do for stability was a full reset on everything. Pre-refactor, I let Box2D stay "warm" and just removed all bodies, but this turned out to affect determinism - and proved worse for start times than a cold restart, anyway.