Conversation
A split gave the new series only the master's exceptions. Google keeps a deleted occurrence as a cancelled row and lists no exception for it (CalDAV can do the same with a cancelled override), so the occurrence came back in the new series; and Exchange lists the slot of every occurrence changed in Outlook among the exceptions, so a tail left those out while the truncate dropped them from the head. - `deletedSlots(master, rows)` names the slots the calendar shows nothing for: a master exception with no live row in its slot, and every cancelled row's slot. A live row is a changed occurrence, not a deletion. - The cut's new series gets exactly those slots from the cut on; a series written whole keeps its id and rows as before. - `firstOccurrenceFrom(master, from, rows)` reads the series with its deleted slots, so a copy is cut at its place in the series (decision 141): an occurrence changed in Outlook on the cut day is there, one Google deleted is not. Both carry dialogs read the copy's rows once, before the anchor, and hand them to the anchor and the plan. - `sameSlot` is the one slot rule (`expandAll` and the split use it). - Google's rows reach about a year ahead (decision 150: named, no question); the tutorial (en, de) and TODO.md say so. The tail's switch between all-day and timed, and a changed rule, are PR 3b. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The tutorial (en, de) promised deleted occurrences stay deleted in the new series. Aperio leaves them out, but not every calendar keeps that yet: Exchange writes no exceptions on create (140) and Google drops them on a series with a time of day (137). It now says so, beside the Google one-year limit. - The cut's comment said the truncate drops the head's rows after the cut. An all-day series on Google or CalDAV keeps them (the adapters skip that cleanup for days), so a changed all-day occurrence after the cut shows twice; said in the comment and in TODO.md, to be fixed at the truncate. - `sameSlot` asked the core for the series clock on every comparison, where `expandAll` used to ask once per series: a year of a daily series against sixty rows became thousands of WASM or FFI calls. `slotMatcher` answers once per series; `expandAll` and `deletedSlots` use it, and a test counts the calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth PR of the arc "exceptions when splitting a series" (decision 134): the new series leaves out what the calendar shows nothing for. Decisions 141 and 150.
Why
When a series was split, the new series (the tail) got only the master's exceptions. That was wrong in two ways:
STATUS:CANCELLEDoverride. On a split, those deleted occurrences came back in the new series, with an invitation.The carry had the same blind spot.
firstOccurrenceFromread only the master's exceptions, so it went wrong in two cases:The change
deletedSlots(master, rows)(shared/seriesSplit.ts) returns the slots the calendar shows nothing for:A live row means the occurrence was changed, not deleted. Each slot comes once and in order. An exception keeps its own spelling; a row's slot is written as the instant it names.
The split's new series gets exactly those slots from the cut on. A series written whole (no head) keeps its id and its rows, so nothing changes there.
firstOccurrenceFrom(master, from, rows)reads the series with its deleted slots, and anchors a copy by its place in the series (141).Both carry dialogs read the copy's rows once, before the anchor, and hand the same rows to the anchor and to the plan. A copy without a rule is not read.
sameSlot(shared/recurrence.ts) matches slots: exactly for a timed series, by day for a series of days.expandAlluses it, and so does the split, so the views and the split cannot drift apart.Not here:
Docs:
Checks
tscandeslintare clean.TZ=UTC.deletedSlots:UNTIL=20260824T075959Z).Review round
Two lenses (providers; logic): 5 confirmed, two pairs of them the same finding; 2 refuted. Fixed here:
sameSlotasked the core for the series clock on every comparison, whereexpandAllhad asked once per series. A year of a daily series against sixty rows became thousands of WASM or FFI calls.slotMatcheranswers once per series,expandAllanddeletedSlotsuse it, and a test counts the calls.Checks for the round:
tscandeslintclean;TZ=UTC;expandAllasking per comparison, anddeletedSlotsasking per comparison. The second one first stayed green, because the test madedeletedSlotscompare nothing; it now has exceptions and cancelled rows.Phone
No native change.
🤖 Generated with Claude Code