I finally understand what is going on!
The CTRL instruction is a nonsensical ST instruction that asserts both write enable /WE and output enable /OE. Without an expansion board, this would write a random byte at the specified location. The expansion board detects that both are asserted, prevents /WE from reaching the RAM chip, and interpret the provided address as special functions such as switching banks or twiddling SPI signals. All expansion boards so far (from Marcel's to Hans61's) compute /RAMWE = /WE or /OE in order to prevent writing anything during a CTRL instruction. Imagine now a CTRL instruction followed by an instruction that does not load from memory. This second instruction deasserts /OE on CLK1 via a single 74HCT139. Meanwhile /WE is de-asserted on CLK1 with a 74HCT32. Which one arrives faster is anyone's guess. Therefore the RAM can receive an asserted write enable signal during a short window after the raising clock. The RAM chip may or may not write. If it does, the write address is a mix of whatever address was presented during the CTRL instruction and whatever the following instruction causes the address bus to eventually become. For instance, for a sequence CTRL(X);LD(constant), this would write somewhere in page zero. Soon or later one is going to touch a byte that controls the video timing. In contrast, none of this happens when the CTRL instruction is followed by a memory load such as LD([vTmp]) because the latter also asserts /OE and therefore does not open a window for a spurious write enable.
To verify this theory, I returned to the initial ROM (the one with problem) and identified the only two CTRL instructions that are not followed by a memory load or another CTRL instruction. I just inserted a LD([vTmp]) after these two CTRL instruction and adjusted the timing constants to account for the additional instructions. The diff can be seen in the pull request
https://github.com/kervinck/gigatron-rom/pull/212
Result: the lost sync problems are gone, which I believe validates the theory.
Conclusion: CTRL() MUST ALWAYS BE FOLLOWED BY AN INSTRUCTION THAT LOADS SOMETHING FROM MEMORY (ASSERTS /OE)
In the end, this is the way the Gigatron works.