Preservation-Safe iCalendar Patcher #14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement a semantic, preservation-oriented iCalendar patcher that updates requested properties while strictly retaining unknown properties, parameters, alarms (
VALARM), custom timezone definitions (VTIMEZONE), multiline folded Unicode text, and unrelated overrides.Background & Technical Rationale
A catastrophic failure mode in calendar integration software is the "clean-serializer round-trip" antipattern. In naive implementations, an incoming CalDAV object (e.g., an RFC 5545
VCALENDARresource containing aVEVENTorVTODO) is parsed into a simplified internal domain struct (e.g.struct Task { title: String, due: Option<DateTime> }). When updating a property such as the title or status, the application re-serializes the struct through a clean generator.In real-world environments (such as Apple Calendar, Google Calendar, Thunderbird, or Nextcloud), CalDAV objects contain critical metadata that the local domain model does not natively represent:
X-APPLE-STRUCTURED-LOCATION,X-GOOGLE-CONFERENCE,X-MICROSOFT-CDO-*,X-MOZ-LASTACK.BEGIN:VALARM ... END:VALARMwith custom audio, display triggers, and snooze state.BEGIN:VTIMEZONE ... END:VTIMEZONEdefining local transition rules.TZID=Custom-Zone,ROLE=REQ-PARTICIPANT,PARTSTAT=ACCEPTED,RSVP=TRUE.VEVENT/VTODOblocks representing rescheduled or cancelled occurrences of a recurring series sharing the same UID.Deserializing through a clean builder strips all of this data, resulting in silent data loss, broken alarms, and destroyed calendar sync on user devices.
To eliminate this class of data corruption, Life establishes strict preservation invariants:
ical), modifying only the specific requested fields (such asSUMMARY,STATUS,DUE,DTSTART,CATEGORIES, or Life-specificX-LIFE-*tags).calendar_unsupported. Silent dropping of unmodeled fields is strictly prohibited.Exact Technical References & Specifications
Preservation Contract
From
docs/technical/calendar.md(lines 12–14):Conformance Gates
From
docs/technical/calendar.md(lines 49–51):Verification Matrix Scenario C09
From
docs/technical/verification.md(lines 43, 50):Illustrative Preservation Fixture
The following complex iCalendar object illustrates the preservation requirements:
Section Citations
docs/technical/calendar.md: Section Discovery and conditional writes (lines 12–14), Section Conformance gates (lines 49–51).docs/technical/verification.md: Section Calendar scenarios (lines 43, 50, Scenario C09).docs/technical/domain-model.md: Section Identity and relationships (lines 44–50).What to Build
1. Preservation-Safe iCalendar Patcher (
src/calendar/patcher.rs)ICalendarPatcheroperating directly on the parsed AST of the raw resource:ical::parser::ical::IcalParseror a dedicated lossless line/component AST) that preserves:VTIMEZONE,VEVENT,VTODO, etc.).VALARM).TZID,VALUE,X-*).X-*and standard RFC 5545 properties not modeled in domain types).VCALENDARmatchingUIDand optionalRECURRENCE-ID.\r\n) followed by a single space or horizontal tab.VALARMcomponents are identical.VTIMEZONEcomponents are identical.CalendarError::Unsupported("calendar_unsupported").2. Integration with Calendar Mutation Pipeline
life task updateorlife event updateis invoked:ICalendarPatcheragainst the raw bytes.PUTwithIf-Match: <etag>.icalendarbuilder used for new-item creation.3. Conformance Test Suite (
tests/calendar_patcher_tests.rs)VTIMEZONE,VALARM, customX-*properties, multiline folded UTF-8 description, and a detached override.SUMMARYof the master event to"Updated Focus Title".SUMMARYis updated.VALARMis present and unmodified.VTIMEZONEis present and unmodified.X-APPLE-STRUCTURED-LOCATIONandX-CUSTOM-VENDOR-PROPare present with all parameters intact.🚀) and em-dash (—) retains valid UTF-8 encoding and meaning.20300410T090000remains completely intact.CalendarError::Unsupported("calendar_unsupported")rather than generating a corrupted partial resource.Acceptance Criteria
SUMMARYorSTATUS) on a complex CalDAV object preserves all unknown vendor properties (X-*), parameters,VALARMcomponents, andVTIMEZONEblocks unchanged (Scenario C09).UID.calendar_unsupported(HTTP 422 / Exit code 2 or 5 depending on protocol mapping).calendar.mdand Scenario C09 fromverification.md.Notes
src/calendar/preservation.rs, a lossless line/component AST per this issue's allowance for "a dedicated lossless line/component AST"); PR #52 supplied the conformance evidence and reconciledcalendar_unsupportedto HTTP 422 / exit 2.Cargo.tomlauthors chore still pending (no dependency added).TZIDread-path classification) tracked separately.Blocked by
Issue #014: Preservation-Safe iCalendar Patcherto Preservation-Safe iCalendar PatcherDeferred chore:
Cargo.tomlauthors lineCargo.tomlcurrently readsauthors = ["Yuri Sasc <yuri@nokopia.com>"]. It should beauthors = ["Yuris Cakranegara <hello@yuris.dev>"].The
src/cli.rsauthor (which driveslife --help/--version) was already corrected in #11. TheCargo.tomlline was deliberately not changed in #11 becauseCargo.tomlis otherwise untouched by that slice, and an unrelated metadata line does not belong in its diff.Action: if this issue ends up editing
Cargo.toml(e.g. adding a parser dependency for the preservation AST), include the authors line in the same commit.Fallback: this issue may not need a
Cargo.tomlchange — its spec explicitly allows "a dedicated lossless line/component AST" instead ofical::parser::ical::IcalParser, and #11 already built exactly that. If no dependency is added here, carry the chore to the next issue that genuinely editsCargo.toml; #35 is the most likely (constant-time digest comparison may replace the deprecatedring::constant_time::verify_slices_are_equalwithsubtle).One line, no behaviour change. Do not open a standalone PR for it.