# Scheduling and Booking

Phase 11 adds public and assisted booking for human and AI-agent assignees,
weekly availability, UTC exceptions, conflict-safe workflows, customer
confirmation mail, and a permission-scoped meeting dashboard.

## Time and availability contract

- UTC is authoritative for stored meeting, exception, booking, cancellation,
  reschedule, and history timestamps. The submitted IANA timezone and original
  local date/times are retained for customer-facing display and audit context.
- A local wall time must map to exactly one UTC instant. Fixed offsets, unknown
  zones, daylight-saving gaps, and daylight-saving folds are rejected.
- Bookings and reschedules require `start >= captured_now + 24 hours` by
  default. Equality is valid; `BOOKING_LEAD_HOURS` controls the interval.
- Weekly rules use ISO weekdays and same-day local half-open windows. A blocked
  exception wins over every rule; an available exception can fully contain and
  override a slot. Meetings crossing an assignee-local date boundary are not
  accepted by a weekly rule.

## Integrity and concurrency

- Booking, rescheduling, and cancellation serialize on the assignee registry
  row with `FOR UPDATE`, then recheck activity, availability, lead time, and
  conflicts inside the transaction.
- Conflicts use half-open intervals:
  `existing.start < requested.end && existing.end > requested.start`. Adjacent
  appointments are valid.
- A move between assignees locks both registry rows in ascending identifier
  order and retries bounded deadlocks. Meeting writes also use `lock_version`
  so stale workflow forms fail safely.
- Initial status, every later status transition, and every reschedule are
  append-only history rows. Scheduled meetings may complete, cancel, or become
  no-shows; those terminal states do not transition further.

## Human and AI registry

Human registry rows contain a restrictive foreign key to an active user. Phase
13 provider-owned agents expose the canonical `ai:<agent-slug>` reference used
by Scheduling. Existing meeting history and assignee display snapshots remain
unchanged when provider configuration or agent labels evolve.

## Access, notifications, and privacy

- Public `GET/POST /book` exposes active types and assignees and is throttled.
- `meetings.view` grants the paginated/filterable dashboard. Only
  `meetings.manage` exposes configuration, assisted booking, reschedule,
  cancel, completion/no-show, and confirmation controls.
- Customer confirmation uses the queued, after-commit
  `scheduling.meeting-booked` template and the existing redacted delivery
  tracker. Audit records exclude customer names, email, phone, and notes.
- The primary conflict index is
  `(schedulable_assignee_id, status, scheduled_start_at, scheduled_end_at)`.

## Operations

- Keep a notifications worker consuming the `notifications` queue and monitor
  the delivery console for the scheduling template.
- Run MySQL 8 CI before production certification. SQLite proves functional
  behavior but cannot certify `FOR UPDATE` serialization or deadlock retries.
- Treat meeting customer fields as personal data. Grant `meetings.view`
  narrowly and apply the platform retention policy to meeting records and
  their immutable histories together.
