Thanks Sergey! The way we've modernized other engines is something like:
Each login stores a "sync status" which is one of "new" (yet to be synced) or "normal" (syncing) and a "sync change counter" which is basically a flag to indicate if the item has changed (it's a counter so to handle the edge-case of the item changing during a sync - we record what the counter was at the start of a sync, and decrement at the end - if it's non-zero at that time, it changed during sync so is dirty). These are stored in the storage itself.
In general, sync itself maintains "sync status", but the store itself helps manage the change counter - each thing that touched a login increments it.
The sync modules then do not need to listen to the login manager observers (linked in comment 0), but instead it interacts with the store and these 2 new fields. "items to sync" are then something like "all items with status=="new" || counter > 0, etc.
Some extra work will be necessary to track "tombstones" correctly (a tombstone is a record on the server that indicates the item was deleted). Only items with a status=="normal" need a tombstone, but the fact the tombstone is needed (ie, in the time between the item being deleted and syncing) must be tracked somewhere. A tombstone really just needs to track the items GUID (and maybe the date of deletion, but maybe not)