ical-recurrence: handle RECURRENCE-ID An overriding component appears as an **extra** occurrence beside the instance it is supposed to replace. Google and Outlook emit a `RECURRENCE-ID` for every "this event only" edit, so this fires on ordinary calendars: the edited instance shows up twice, once where it used to be and once where it now is. Two commits: the first is red, the second is the fix. ## Why it is wrong [§3.8.4.4](https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.4.4): > This property is used in conjunction with the "UID" and "SEQUENCE" properties to identify a specific instance of a recurring "VEVENT", "VTODO", or "VJOURNAL" calendar component. The property value is the original value of the "DTSTART" property of the recurrence instance. > The DATE-TIME value is set to the time when the original recurrence instance would occur; meaning that if the intent is to change a Friday meeting to Thursday, the DATE-TIME is still set to the original Friday meeting. The `RECURRENCE-ID` says where the instance used to be and the overriding component's own `DTSTART` says where it is now. Both cannot be in the recurrence set: the meeting moved, it did not happen twice. ## Matching Exact comparison of the property value against the generated instance's start, which is what the same section makes possible: > This property MUST have the same value type as the "DTSTART" property contained within the recurring component. Furthermore, this property MUST be specified as a date with local time if and only if the "DTSTART" property contained within the recurring component is specified as a date with local time. So a `RECURRENCE-ID` that matches none of them exactly is non-conforming input rather than a near miss to be resolved. `RANGE=THISANDFUTURE` is **not** handled here; it is the only part with real arithmetic and gets its own PR. ## The cases the spec does not define Three new fixable errors, each keeping what the calendar said rather than dropping it: | Error | Case | What it does | | --- | --- | --- | | `RecurrenceIdentifierUnmatched` | An override naming no instance the series generates | The override contributes its own instance | | `RecurrenceIdentifierDuplicate` | Two overrides of one instance at the same `SEQUENCE` | `Ord` picks the winner | | `RecurrenceMultipleSeries` | Two components of one UID with no `RECURRENCE-ID` between them | Both are expanded | A higher `SEQUENCE` wins outright — §3.8.7.4 says it "is monotonically incremented [...] each time the 'Organizer' makes a significant revision", so the higher one is the later revision. A tie contradicts that and the calendar does not say which to take, so `Ord` settles it rather than the order the components happen to appear in the file, following the colliding-observances precedent from #30. ## Tests A fixable error must halt a conforming run and succeed leniently, and these are the errors that only a *group* of components can have, so they need a whole calendar rather than the single component `test_resources/event/fixable` holds. Hence `test_resources/calendar/fixable`, with the same two tests per file. `scenarioDir` does not recur into subdirectories, so `test_resources/calendar` does not also pick them up. Each of those four goldens was derived by hand rather than generated, and each fixture's overriding component starts at a different time from the instance it replaces, so the golden shows which component won without the format having to name it. Unit tests pin *which* fixable error was emitted, which the scenario directory cannot say. **Every pre-existing `.occ` and `.res` golden is still byte-identical.**