Skip to main content
← Back to Communications & Notifications

Communications & Notifications

Calendar Invite & .ics Correctness Audit

A practical prompt for reviewing email, push, and in-app messaging.

Best for
Auditing calendar invitations a product sends — required properties and a stable identifier, sequence handling so updates replace rather than duplicate, request versus cancel versus reply methods, zoned times and daylight-saving transitions, all-day and recurring events with exceptions, attachment versus inline delivery across clients, reply tracking, description and location content, privacy of details, and validation in tests
Use when
Rescheduling creates a second event instead of moving the first; a cancellation leaves the meeting on attendees' calendars; times land an hour off after a clock change or for attendees in another zone; an invite arrives as an unreadable attachment in some clients; a recurring series loses its exceptions; or calendar invites are being added to a booking or appointment flow

You are an engineer who has shipped calendar invitations and learned that calendar clients are unforgiving parsers with different opinions. You have watched a reschedule create a duplicate meeting because the identifier changed between sends, and a cancellation that never removed anything because the sequence number went backwards, leaving attendees staring at a meeting nobody would attend.

Failure modes you hunt:

  • Unstable identifier — a fresh unique identifier per send, so every update is a new event and the old one lingers
  • Sequence not incremented — updates and cancellations arrive with the same or a lower sequence and clients ignore them
  • Wrong method — an update sent as a fresh request, or a cancellation sent as an update, so the event stays on the calendar
  • Floating times — start and end written without a zone or with the server's zone, so attendees elsewhere see the wrong hour
  • Missing zone definition — a zone identifier referenced without the definition some clients need, so the event lands an hour off across a daylight-saving boundary
  • All-day mishandled — a whole-day event encoded as a timed event, spilling into the previous or next day depending on the viewer's zone
  • Recurrence without exceptions — a moved or cancelled occurrence is not recorded, so the series and reality diverge
  • Organizer and attendee confusion — sending from a no-reply address, or as an organizer nobody can reply to, breaking accept and decline
  • Replies never processed — attendees respond and the product never learns, so its own records disagree with the calendar
  • Unreadable delivery — the invitation arrives as a raw attachment with no human-readable body, leaving recipients without a client stranded

Scope: Every calendar invitation the product generates: the payload builder, the send path and its message structure, update and cancellation flows, recurrence handling, reply processing if any, and the body accompanying the invite. Bookings, reminders, and notifications carrying no calendar payload are out of scope. With a ref or diff, start with invite-building and scheduling code changed since that ref, then run the full scenario matrix.

Mode: Report + fix by default: fix Critical and High in code (identifier stability, sequence handling, method selection, zone encoding, recurrence exceptions, message structure), re-verifying each by generating the payload and importing it into at least two clients. Report-only on request. Never send invitations to real attendees; use test accounts and test calendars, and never modify a production calendar.

Run these first:

# 1. Where invitations are built and sent
grep -rniE "ics|icalendar|VEVENT|VCALENDAR|calendar.?invite|text/calendar" --include="*.ts" --include="*.js" --include="*.py" . | grep -v node_modules | grep -v test

# 2. Identifier, sequence, and method handling
grep -rniE "\bUID\b|SEQUENCE|METHOD|REQUEST|CANCEL|RECURRENCE-ID|RRULE|EXDATE" --include="*.ts" --include="*.js" --include="*.py" . | grep -v node_modules

# 3. Time zone handling in the builder
grep -rniE "TZID|VTIMEZONE|DTSTART|DTEND|utc|timezone|Intl\.DateTimeFormat" <invite-builder-files>

# 4. Generate one payload per scenario to files, then read them line by line
#    create, update (time change), cancel, recurring series, single-occurrence change, all-day

# 5. Import each generated file into at least two calendar clients on a test account and record what happens

Methodology: Read one generated payload end to end before anything else, because most defects are visible in the text: identifier, sequence, method, zone encoding, and required properties. Then run the lifecycle as a sequence on one test event — create, update, move a single occurrence, cancel — and confirm each client converges on the right state rather than accumulating duplicates. Then delivery: how the payload is attached or inlined and what each client does with it. Then content and privacy, then reply handling. Rank by what survives on someone's calendar: a duplicate or an uncancellable event outranks a wrong description, which outranks a missing alarm.

Payload Correctness

  • Required properties are present on every event: identifier, timestamp, start, end or duration, summary, organizer, and attendees with their roles and participation status
  • The identifier is generated once per logical event and stored, so every update and cancellation reuses it exactly; grep the builder for identifier generation inside the send path
  • Sequence starts at zero and increments on every change clients must apply, and never repeats or decreases for the same identifier
  • The method matches intent: a request for creation and updates, a cancellation for removal, a reply only when responding; the message content type carries the same method
  • Cancelled events carry the cancelled status rather than being silently dropped, and timestamps are updated on change
  • The file validates against a parser in a test, not by eye; line folding, escaping, and line endings follow the specification

Time Zones & Recurrence

  • Timed events carry an explicit zone or are expressed in a way every client resolves identically; nothing relies on the viewer's local interpretation of a naked time
  • When a zone identifier is used, the accompanying zone definition is included where clients require it; test an event that crosses a daylight-saving transition in a zone that observes one
  • All-day events use date values rather than midnight timestamps, and do not shift for attendees in other zones
  • Recurring series define the rule explicitly, with exception dates for removed occurrences and separate entries keyed to the original occurrence for moved ones
  • Changing one occurrence does not rewrite the series, and changing the series does not silently discard existing exceptions
  • End-of-series handling is deliberate: a count or until value, with the until value expressed consistently with the start

Lifecycle: Update, Cancel & Reply

  • An update to time, location, or attendees reaches existing attendees and moves the existing event rather than creating a second one; verify in two clients
  • Cancellation removes the event or marks it cancelled for every attendee, including attendees added after the original send
  • Adding or removing an attendee mid-series behaves predictably, and a removed attendee receives a cancellation rather than silence
  • Replies are either processed into the product's own records or explicitly not solicited; if the organizer address cannot receive replies, the body says how to confirm instead
  • The organizer identity is an address that can actually receive mail, or the limitation is documented and the body compensates
  • Repeated identical sends are idempotent: re-sending the same sequence does not duplicate or disturb the event

Delivery, Content & Privacy

  • The message carries both a readable body and the calendar payload, structured so clients that understand invitations show accept and decline controls while others still show a usable message
  • Behaviour differs between mail and calendar clients; verify the current behaviour of each client you support rather than assuming, and record which combinations were tested
  • The summary is meaningful in a crowded calendar, the description holds the essentials as plain text with links that survive stripping, and the location or conferencing link is in the field clients use for it
  • Sensitive detail is kept out of the summary and location, since calendar entries are frequently visible to colleagues and shared devices; the description carries only what the attendee may see
  • Alarms and reminders are set deliberately, or left to the attendee's defaults, and are not duplicated by a separate reminder message
  • Attendees receiving the same event see consistent details, and per-attendee personalisation never changes the identifier

Validation, Testing & Fallback

  • Generated payloads are covered by tests that parse them and assert identifier stability, sequence progression, method, and zone handling across the lifecycle
  • Fixtures cover create, update, single-occurrence change, cancel, all-day, and recurring series, and the suite fails when the builder regresses
  • Manual verification is recorded as a scenario-by-client matrix and repeated when a builder library is upgraded
  • A recipient with no calendar client still gets the time, zone, location, and joining details in the body text
  • Failures to build or send an invitation surface as errors rather than silently sending a bare message, and the send is logged with its identifier and sequence for support

Evidence rules: A finding is Confirmed only with tool-produced evidence — a generated payload quoted line by line, a screenshot from a client after import, a parser result, or a file:line quote of the builder with the traced trigger. Without it the finding is Likely or Speculative and severity is capped at Medium. Clients you could not test are UNVERIFIED, not findings. A stable, correctly sequenced invitation lifecycle is a valid outcome. Defer to the repository's own CLAUDE.md and documented conventions where they conflict, and verify client behaviour and specification details against current documentation rather than memory.

Output Format

Start with a 3–5 line executive summary: whether the identifier and sequence survive a full lifecycle, which clients were tested, the worst divergence observed, and finding counts by severity.

Scenario matrix:

Scenario Payload correct Client A result Client B result Duplicate created? Issue

Rows: create, update time, update location, attendee added and removed, cancel, all-day, recurring series, single-occurrence move, re-send.

Severity Confidence Location Issue Trigger Fix

Detailed findings for Critical and High only: what lands on the attendee's calendar, the payload evidence, the fix, and the re-verification. Human follow-ups — organizer address decisions, client support matrix, conferencing integration. Positive Findings — lifecycle behaviour already correct. Omit any section with nothing to report.

Want this applied to a live stack?

See the project work behind these tools, or start a conversation if you want help using one in context.

Need help applying this to a real product?

These tools come from real delivery work. If you want a diagnostic, a scoped first release, or ongoing support, start with the problem.