---
name: classprofiles-calendar
description: Operate a backend that declares the exact org.classprofiles.calendar/core@1 prototype contract. Use for listing, creating, updating, cancelling, and finding free time without branching on calendar vendor identity. Read the declared facets before using recurrence, RSVP, or reminders.
---

# Calendar — `org.classprofiles.calendar/core@1` (draft)

Use these instructions only after the backend declares the exact Calendar baseline. This draft has no official runner or passing receipts, so a declaration is not yet independently verified conformance. Operate within the selected contract and never infer support from a vendor name.

## Core verbs

| Verb | Does | Risk |
|---|---|---|
| `calendar.list_events` | Lists events overlapping `[range_start, range_end)`, optionally filtered by query. | Read-only |
| `calendar.get_event` | Fetches one event by opaque `event_id`. | Read-only |
| `calendar.create_event` | Creates an event and returns the resulting `Event`. | Writes; socially irreversible. Pass `request_key` so a re-issue cannot book twice |
| `calendar.update_event` | Applies a partial `patch`; omitted fields remain unchanged. | Writes; socially irreversible; idempotent on re-issue |
| `calendar.cancel_event` | Cancels an event and reports its terminal state. | Writes, destructive; idempotent on re-issue. Re-reading it later needs `calendar/tombstones@1` |
| `calendar.find_free_time` | Finds free slots of at least `duration_minutes` inside a window. | Read-only |

## Rules that prevent real mistakes

1. **Use RFC 3339 timestamps with an explicit offset.** Resolve natural-language dates in the user's timezone before calling a verb.
2. **Treat identifiers as opaque.** Capture `id` from results; never construct, parse, or guess it.
3. **Patch only changed fields.** Moving an event normally requires both `start` and `end`; changing a title must not touch location or time. Use explicit `null` only to clear nullable `timezone`, `location`, or `description_md`.
4. **Cancel is not delete.** A cancelled event remains available with `state: "cancelled"`.
5. **Durations are integer minutes.** Do not send ISO 8601 duration strings.
6. **Do not invent success.** Unknown identifiers fail with JSON-RPC `-32602`; a missing object is never fabricated.
7. **Confirm visible social effects.** Confirm invitations, attendee-visible updates, cancellations, and RSVP responses unless the user's exact instruction already authorizes that act.
8. **Keep list pagination stable.** Results are ordered by start instant, then opaque ID. Reuse the original range and query with each returned cursor.

## Worked calls

List one day in the user's timezone:

```json
calendar.list_events
{
  "range_start": "2026-02-26T00:00:00-05:00",
  "range_end": "2026-02-27T00:00:00-05:00"
}
```

Find and book a 45-minute slot:

```json
calendar.find_free_time
{
  "duration_minutes": 45,
  "window_start": "2026-02-26T12:00:00-05:00",
  "window_end": "2026-02-26T18:00:00-05:00",
  "max_results": 3
}
```

```json
calendar.create_event
{
  "title": "Leo / Priya",
  "start": "2026-02-26T13:00:00-05:00",
  "end": "2026-02-26T13:45:00-05:00",
  "request_key": "k_7ab3"
}
```

Adding `attendees` needs `calendar/invitations@1`. If the card does not declare it, say
so rather than creating the event silently without the invitation.

Move an event without replacing unrelated fields:

```json
calendar.update_event
{
  "event_id": "evt_a91x",
  "patch": {
    "start": "2026-02-26T16:00:00-05:00",
    "end": "2026-02-26T17:00:00-05:00"
  }
}
```

## Optional capabilities

Read the implementation's exact facet list; never assume these members:

- `calendar/recurrence@1` adds canonical `DAILY` or `WEEKLY` rules at event creation. `INTERVAL` is positive, `BYDAY` is weekly-only, `UNTIL` is an inclusive UTC cutoff, and positive `COUNT` includes the initial occurrence. Whole-series mutation is out of scope here. If the facet is absent, disclose that only the first occurrence can be created.
- `calendar/rsvp@1` adds `calendar.respond`. If absent, say that RSVP is unavailable through this contract.
- `calendar/reminders@1` adds `reminders_minutes`. If absent, creates use backend defaults and updates leave existing reminders unchanged.

Vendor members have their own semantic IDs and `degrades_to` declarations. Use one only when the implementation declares it and the user understands any loss. A projected implementation may separately expose raw upstream tools; those are outside this profile and require an explicit choice.

## Before a write

Check: Is the time explicit and offset-bearing? Are identifiers captured rather than guessed? Is the patch minimal? Does the request depend on an undeclared facet? Will another person see the effect? If any answer is unsafe or ambiguous, resolve it before dispatch.
