Thanks for the good advise. Using dbglog(dbg_backtrace()) I found something interesting:
The bug seems to be that it triggers the IO_WIKIPAGE_WRITE when the revision is saved as well (which I assume is not the intention). The emails I received looked identical because the filename is not in the event data.
It seems to work as follows:
On page delete:
Triggers once as it saves the page to a revision file in attic, but not the second time as it doesn't write the page to a file in the wiki data structure - it is simply deleted. I haven't investigated whether this means that another signal is sent.
On page edit/creation:
Triggers twice (see diff under), once for the page write, and once more for the revision saving. However,
when the event triggers for the revision saving, $event->data is set with the revision number.
What was driving me mad was that I had a similar plugin that monitored certain namespaces for new pages and sent mails to a certain user group. However, this was only when the page was first created - not when the page was updated later. There is a crucial difference here since I both check if the file already exists via $INFO['exists'], and if $event->data is unset in this plugin. When it triggers the second time, the revision is already set so $event->data isn't empty anymore. Hence I will only get one mail.
$ diff wiki_dbg1.text wiki_dbg2.text
< io_writeWikiPage("##SNIP###/data/pages/falkortest.txt", "trigger twice test!", "falkortest") called at ##SNIP##/inc/common.php:772
> saveOldRevision("falkortest") called at ##SNIP##/inc/common.php:774
> io_writeWikiPage("##SNIP##/data/attic/falkortest.1188245754.txt.gz", "trigger twice test!", "falkortest", "1188245754") called at ##SNIP##/inc/common.php:810
On page creation you can distinguish the triggerers by the fact that when the event is the actual pagewrite you have $event->data unset and $INFO['exists'] == false. When it triggers for the revision saving, $event->data is no longer unset, but contains the revision number.
On page editing, $INFO['exists'] == 1, and $event->data is unset on the first save. When the revision is saving the latter is no longer unset, but contains the revision number.
On page delete $INFO['exists'] == 1, and $event->data is set, but only triggered once for the revision save - not for the page save.
Hope this helps. Sorry about the messy writeup - it's kind of late.
What just hit me was that maybe it would be more useful if the information that we have to look up in the $INFO variable was included in the IO_WIKIPAGE_WRITE event object instead? To me it seems more logical.
But is it so that the current page doesn't have a revision (since it's current?), and only gets a revision when it is put in the attic?