HANDOFF: scheduler

Scope: Only scheduler requirements are authoritative. Ignore all archived records for other projects. Updates 1–5 have been applied; superseded settings are discarded. No later update is available.

CURRENT SETTINGS
{
  "timezone": "UTC",
  "cutoff_hour": 18,
  "cutoff_inclusive": false,
  "lead_minutes": 60,
  "adjacency_allowed": false,
  "duration_max": 90,
  "duplicate_policy": "last",
  "cancelled_blocks": false,
  "blackout_day": "2026-11-01",
  "priority_high_first": true,
  "capacity": 2,
  "report_order": "input"
}

FIXED INTERFACE
Implement evaluate(payload) returning:
{selected: [id], blocked: [id], rejected: [id]}

payload contains:
- now: offset-aware ISO datetime.
- appointments: rows with id, start (offset-aware ISO datetime), duration (positive minutes), priority (integer), cancelled (boolean).
Do not mutate the payload. Use zoneinfo or equivalent timezone conversion.

PROCESSING CONTRACT WITH CURRENT CHOICES
1. Deduplicate by id, retaining the last value while preserving each id's first insertion position. The retained row's original index is its actual index in the original appointments array, not the first occurrence's index.
2. Process retained rows by priority descending; ties use retained original index.
3. Perform start/end and lead-time arithmetic in UTC elapsed minutes, never local wall minutes.
4. Reject invalid rows, including duration <=0 or >90, or start earlier than now +60 elapsed minutes. Equality at the lead boundary is permitted.
5. Convert start to UTC timezone for local-calendar rules. Reject appointments whose start's local date is 2026-11-01.
6. Define cutoff as 18:00 in the configured zone on start's local calendar day. End must be strictly earlier than cutoff; equality is rejected.
7. Cancelled appointments are rejected and do not reserve capacity. With the current cancelled_blocks=false setting, blocked is empty.
8. Accept an otherwise valid appointment only when fewer than 2 previously accepted intervals overlap it. Sharing only an endpoint counts as overlap because adjacency_allowed=false.
   IMPORTANT: Count distinct accepted intervals intersecting the candidate, not peak concurrency. A candidate intersecting two accepted intervals is rejected even if those two intervals do not overlap each other.
9. Accepted non-cancelled ids go to selected. All invalid/conflicting ids go to rejected.
10. Sort each output list by retained original index (report_order="input"), not priority processing order or first insertion position.

FIXED CONDITIONAL SEMANTICS TO PRESERVE IF UPDATED
- duplicate_policy="first" retains the first value; "last" retains the last. Always preserve first insertion position and use the retained value's actual original index.
- priority_high_first=false means ascending priority.
- cutoff_inclusive=true permits end equal to cutoff.
- adjacency_allowed=true means endpoint-only contact is not overlap.
- cancelled_blocks=true allows otherwise valid cancelled appointments to reserve capacity; accepted cancelled ids appear in blocked rather than selected. They remain subject to validation and capacity conflicts.
- report_order="id" sorts each output list by id; "input" sorts by retained original index.

UNRESOLVED / NOT SPECIFIED
- No current configuration key is unresolved.
- Error handling for malformed top-level payload/now, missing or unusable ids, and ordering heterogeneous id types is not specified.
- Do not invent extra defaults or import settings from other projects. Future target updates replace only explicitly supplied keys and leave other current choices unchanged.