Currently, keystone does nothing to prevent an operator from running each step of the rolling migration process out of order. Theoretically, most migrations will fail if the table they're looking to drop does not exist, etc, but that might not always be the case.
The transition to rolling migrations introduces a few expectations between the migration repositories that we should be able to enforce rather easily, given that all 3 of the new migration repos should always contain the same number of migration steps).
1. All legacy migrations need to be run before any --expand migrations are allow to run.
2. The version number of the --expand repo must be greater than or equal to the version number of the --migrate repo.
3. The version number of the --migrate repo must be greater than or equal to the version number of the --contract repo.
I'd expect each command to abort with an error message if there are outstanding steps from the previous repository that had not been run. As a bit of a special case, db_sync --expand could (continue to) run the legacy repository automatically, but only if the legacy repository is guaranteed to be additive-only, as non-additive migrations should never be run by --expand (perhaps we should find the version number of the last non-additive migration and check the current state of the db will not result in that non-additive migration to be accidentally run.
Bug 1615020 and bug 1615024 also document new assertions regarding rolling upgrades.