Deleting a recurring task never sent a DELETE. markDeleted tombstoned
only the row it was handed, and master_id's CASCADE never fired because
nothing was actually deleted — so a series with any override left
LocalResource.isDeleted false, went to the upload phase, and ended
is_deleted = 1, is_dirty = 0 with a matching ETag: beyond every phase's
reach. Other clients kept the task; here it was gone. markDeleted now
tombstones the series whole, and a resource is read as deleted from its
master rather than from all of its rows, which also repairs the rows an
older version left in that state instead of leaving them stuck.
Deleting a single occurrence wrote no EXDATE. Removing the override does
not delete the occurrence, it un-overrides it — per RFC 5545 the
master's RRULE regenerates it as a plain instance, on the server and in
every other client. It was invisible locally only because the override
queries filter tombstones out. The exception is written onto the master
now and the override row is dropped, which is also what hides the
occurrence in a device-only list, where there is no tombstone to do it.
The value takes the shape the list already has: lib-recur parses the
whole EXDATE list or none of it, so a UTC date-time appended to a run of
DATEs would drop every exception the series had.
ResourceValidator gains the rule that would have named the old bug: a
body of overrides with no master describes instances of something that
is not in the resource.