So what seems to be happening is that the state of a ship is changing at different times. On my most recent test, when wait_lock reached 0, both the client and the server spent 8 sync_steps in the state NO_ROUTE, and then some either 3 (on the client) or 4 (on the server) sync_steps in the state ROUTING_2, before wait_lock became positive again. Since my desync detection code considers a convoys speed if (and only if) wait_lock reached (or was alread) zero, then this triggers the desync detection.
That is very interesting, thank you. The task now will be to track down how this divergence occurs, which I will not be able to do in any practical sense until the middle of next week at the earliest.
However, if it is of any help, some background to the phases of routing and some theoretical analysis as to what might be occurring: the reason that we have a ROUTING_2 state is because of the multi-threading algorithm: the idea is that the single threaded part of the routing algorithm transitions between ROUTING and ROUTING_2 (to do the preparatory work, if I recall correctly), the multi-threaded algorithm does the actual routefinding, transitioning between ROUTING_2 and ROUTE_JUST_FOUND (the single threaded algorithms knowing not to do anything to any convoy in the ROUTING_2 status), and the single threaded algorithms then dealing with the follow-up work on detecting the ROUTE_JUST_FOUND state. These three states replace the single ROUTING state from Standard to break apart the actual routefinding (which does not change any memory state other than the memory relating to the routing of that specific convoy) with the ancillary pre- and post-routefinding work, which does affect other memory states, and which must be single threaded.
In principle, there should be no distinction so far as the sync_steps are concerned between ROUTING_2 and ROUTE_JUST_FOUND, and the transition between ROUTE_JUST_FOUND and any other state should be handled in step, not sync_step. This is important because the multi-threaded routing algorithm runs in parallel with sync_step, but not step.
However, when the convoys cannot find a route, they will transition, not from ROUTING_2 to ROUTE_JUST_FOUND, but from ROUTING_2 to NO_ROUTE. The question, then, appears to be this: does sync_step do anything different if the status is NO_ROUTE than if it is ROUTING_2, or, at least, anything different that is network sync relevant? If so, then we perhaps need a new state of NO_ROUTE_2 or similar that is transitioned to NO_ROUTE in a step to ensure that it is deterministic.
I am not in a position here to check easily whether anything network sync relevant happens in sync_step if NO_ROUTE is the current status (there are UI things, but those should not alone be network sync relevant), but if anyone else could look into this, that might be very helpful.