scheduling-engine

Notes

  • 2026-09-14 (session 103, list-first scheduling P1, entirely-api ff49e4d + 44968a4, entirely-portal 8cd19d6): services/slot_advisor.py is the pure engine (evaluate_day → per-stop green/amber/red with reasons, first leave, home ETA; suggest → insertion options honouring firm/time_flex/day_flex per ruling A2; defaults config.SCHED_* with C1 = 15 min site time). services/scheduling_service.py orchestrates: stops from daily_schedule.list_field_stops(include_tentative=True, light=True), tech_availability_blocks (migration 049), coordinates from services/geo_service.py (projects.geo_* migration 048, geocode_cached Redis 90 d, ensure_projects_geo write-back, TECH_BASES incl. env DUSTIN_HOME_ADDRESS), drive legs from services/drive_matrix.py (Routes computeRouteMatrix, 7-day pair cache by weekday+hour, one small call per departure-hour bucket, haversine estimate tier). Feasibility cached 10 min behind sched:feas:ver, bumped by supabase_write_service._bump_feasibility on every site-visit create/update/delete, project hold/cancel, block create/delete. API routes/portal_scheduling.py: GET /portal/scheduling/feasibility (≤21 days), POST /suggest, GET/POST/DELETE /blocks (owner/admin), GET /techs. Portal: FitBadge on every calendar card + DayView, “Drive” row, AvailabilityBlocksBar. Tentative visits (site_visits.tentative) count for capacity and are excluded from every field-facing surface by list_field_stops’ default. Live 9/14: Tue 9/15 Millersville 1:00 red (9 min late after Callaway), Wed 9/16 Woodbridge 2:00 + Solomons 3:00 red.
  • 2026-09-14 drive bands (Dustin ruling mid-session: calendar blocks correspond to drive times, not a flat hour): feasibility visits carry start/end = site time (15 min), site_minutes, and on the tech’s last stop leg_home + home_eta (entirely-api 01dcdec). CalendarPage week/two-week/day grids draw the card at site time (min 26 px) with a hatched fit-coloured drive-in band above and a drive-home band after the last stop; card top stays at the appointment start so drag-and-drop is unchanged; cards without engine data keep the one-hour block.
  • 2026-09-14 P5 (session 103): slot_advisor.suggest_day_fix — advisory same-day moves that make a RED tech-day feasible (iterative best-move over the red stops, tentative stops first on ties, equal-score steps allowed, no repeated configuration, ≤ 3 moves, never another day per A2); surfaced as fix {moves, fit_after, home_eta_after} on the feasibility payload; Route Board shows it and owner/admin Apply each move through POST /portal/calendar/drag-reschedule (audited) with the day-of/tomorrow inspector prompt. suggest() now takes the next quarter hour with a green cushion when the time is its own to pick. Return-visit demand: schedule_lists.ingest_return_visits (CRON 6:30 AM weekdays) puts needs_return_visit projects with no upcoming visit on a per-branch ‘return visits’ Week Board list as waiting rows; accept creates only a tentative Follow-Up visit; approve leaves a live project’s status alone. Not built by ruling: auto-approve (B2), branch wait-list replies (B4); automatic moves of existing appointments need a ruling. With this the list-first plan P0–P5 is complete.
  • 2026-09-14 P5 follow-up (entirely-api 25f5a1d, entirely-portal 4f526cd): the feasibility pass prices every pair among a day’s points plus the other inspector’s base (_matrix_for full=True) so reorders are judged on Google legs, not estimates; slot_advisor.suggest_reassign hands stops still red after the same-day fix to the OTHER inspector’s same day (Move.tech_to); POST /portal/scheduling/apply-move (owner/admin, audited) reschedules and reassigns in one write and is what the Route Board’s Apply calls. Live Wed 9/16 suggestion after this: Woodbridge → 10:00 AM, Solomons → 3:30 PM, day amber, home 5:53 PM.

Connections