Files
manilabuiandClaude Opus 5.5 5e9cf917d0 Make calendar undo and redo save on Google (#2967)
* Add bumpEventSequenceUp, to re-send an older copy as a new revision

Google answers 409 to a VEVENT whose SEQUENCE is below the one it
holds. Undo and redo re-send a snapshot of the event, so each VEVENT
that differs from the copy last sent moves one past the higher of the
two and gets a fresh DTSTAMP. A VEVENT that matches apart from those
takes the last copy's, so guests see no revision of an occurrence
nobody changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Send a calendar undo or redo past the revision it replaces

Undo re-sent the pre-edit snapshot verbatim, SEQUENCE included, so
after a move Google held a higher SEQUENCE and answered 409. Redo had
the same problem after an undo, and so did undoing two edits to one
event in turn. Each window now records the ICS it last queued per
event, and undo and redo send their snapshot through
bumpEventSequenceUp against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Match occurrences by instant when bumping an older copy's SEQUENCE

Google stores a RECURRENCE-ID sent in UTC back in the event's zone, so
the same occurrence reads as 20261022T103000Z in the copy we sent and
TZID=America/Chicago:20261022T053000 in the row it returns. Compared
as text, an unchanged override found no counterpart and was bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Send an undo or redo at the SEQUENCE last sent rather than past it

Google accepts a tie and rejects only a lower SEQUENCE. A bump lifted an
override past the master + 1 that createRecurrenceException restarts it
from, so the next edit of that occurrence came back 409, which a live
re-edit after undo and redo confirmed. bumpEventSequenceUp becomes
matchEventSequence.

Whether non-Google guests apply an update at a tied SEQUENCE is untested;
plans/calendar-open-gaps.md records it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put back an override an undo leaves out, at its slot

Google keeps an override VEVENT that a PUT omits, so undoing the first
edit to one occurrence was accepted and changed nothing: a moved
occurrence stayed moved. The undo now sends that override back with the
master's content at its own slot, the occurrence as the series has it.
A slot the restored copy's EXDATEs cancel stays cancelled, so redoing
the cancellation of an overridden occurrence doesn't revive it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Record that an Outlook.com guest applies a tied update

Live: Google's update emails carried our SEQUENCE through with its own
fresh DTSTAMP, and an Outlook.com guest applied an undo and a re-edit
sent at the same SEQUENCE as the change before them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep only the untested part of the guest SEQUENCE note in the gaps doc

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Revert only the overrides the undone edit added, and only when the series is unchanged

Putting back every override the last copy had and the restored copy
lacked, matched by RECURRENCE-ID, misfired twice. A series move relabels
its overrides, so undoing one added an override at a slot the restored
series never generates. An occurrence response made after the undone
edit came back with the series' content and the old PARTSTAT.
revertAddedOverrides now runs on undo only, against the edit's own
before and after copies, and skips edits that changed the master. A
reverted all-day override takes the series' duration in days, so it
stays one day long on a DST change.

Adds specs that fail without redo's matchEventSequence call and without
undo recording what it queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Revert an override when the restored series still has its slot

Requiring the undone edit to have added the override, with the master
unchanged, missed two cases. Undoing a series edit after undoing an
occurrence edit left that occurrence with the newer title, because the
override the first undo sent was never rebuilt. And adding the first
guest to one occurrence writes ORGANIZER onto the master, so nothing was
reverted. revertAddedOverrides now takes the copy last sent, runs on
undo and redo, and rebuilds every override that copy has and the
restored one lacks, as long as the restored series still generates that
slot. A slot a series move relabelled, or an EXDATE cancelled, is
skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:37:27 -05:00
..