Yeah this is making no sense to me at all...unfortunately your method doesn't entirely work for me since if I only make a change on one of the iterations it seems to get overwritten by later ones.
For example, I tried just adding "-test" to the URLSegment in onBeforeWrite, but if I use a static var to only do it once the change shows up in the sitetree table/cms but it it doesn't add -test to sitetree_live, so doesn't show up on the frontend. Without the static var filter I end up with things like -test-test on my URLSegment.
I did a dump of $this and compared all four times it ran. I'm wondering if it runs for every table attached to the object? The first one doesn't show the newly posted data or have any of the class-specific $db fields. It makes a new version number every time, though. The status attribute goes Published -> Saved (update) -> Published -> Published, and each time the changed and original arrays do or don't have different attributes but I'm not seeing a pattern.
So in short I don't know why it does this but it's stuffing up the probably-way-too complicated bit of hacking I was trying to do here :/