TARGET PROJECT: scheduler — HANDOFF NOTE

Current authoritative configuration (after all updates; superseded values discarded):
```json
{
  "adjacency_allowed": false,
  "blackout_day": "2026-11-01",
  "cancelled_blocks": false,
  "capacity": 2,
  "cutoff_hour": 18,
  "cutoff_inclusive": false,
  "duplicate_policy": "last",
  "duration_max": 90,
  "lead_minutes": 60,
  "priority_high_first": true,
  "report_order": "input",
  "timezone": "UTC"
}
```

Fixed interface and algorithm contract:
- Implement `evaluate(payload) -> {selected:[id], blocked:[id], rejected:[id]}`.
- `payload` has `now` (offset-aware ISO datetime) and `appointments` with `id`, `start` (offset-aware ISO datetime), positive duration in minutes, integer `priority`, and `cancelled` boolean.
- Do not mutate input. Use `zoneinfo` or equivalent timezone conversion.
- Deduplicate by `id` preserving first insertion position, using the value selected by `duplicate_policy` (`first`/`last`); a retained row's original index is that value's actual original index.
- Stably process by priority: descending when `priority_high_first=true`, otherwise ascending; ties use original index.
- Use UTC elapsed minutes for start/end arithmetic, not local wall-clock minutes.
- Duration must be `>0` and `<=duration_max`.
- Start must be `>= now + lead_minutes`.
- Convert start to `timezone`; reject appointments whose local start day is `blackout_day`.
- Define cutoff on the start's local calendar day at `cutoff_hour:00` in that timezone. End must be `<=cutoff` when `cutoff_inclusive=true`, otherwise `<cutoff`.
- A cancelled appointment is rejected when `cancelled_blocks=false`; when true, it may reserve capacity and appears in `blocked` instead of `selected`.
- An appointment is accepted only if fewer than `capacity` already accepted intervals overlap it. This counts intersecting accepted intervals, not peak concurrency.
- When `adjacency_allowed=true`, intervals sharing only an endpoint do not overlap; when false, they do overlap.
- Sort each output list by `id` or retained original index according to `report_order` (current: `"input"` = original index).
- All invalid or conflicting ids go to `rejected`.

Exceptions / unresolved items:
- None recorded. No other project settings apply to scheduler.