week-board

Notes

  • 2026-09-14 (session 103, list-first scheduling P2 slice 1; migration 050; entirely-api c6356db; entirely-portal 1059a81): /schedule-lists “Week Board” for scheduler/assistant/goa/owner/admin. Paste the week’s list → services/schedule_list_parser.py (regex only, position-independent; reads the published one-liner, Anna/Zoe’s Manassas email shape “2:00pm: Customer #581980 Name 112 Street, City, MD ZIP” under day lines, and Stephaney’s backlog shape “578589 4604 DAVIS AVENUE SUITLAND MD 20746 3/10 11:00 AM”; unreadable lines become error rows carrying the format) → services/schedule_lists.propose (geocode, Slot Advisor per row on its requested day, rows kept from colliding; branch roles propose against Darius only per ruling A4) → Accept / Counter (datetime) / Decline. Accept re-checks the exact time firm-flex; red → 409 not_feasible with options, “Book anyway” acknowledges (B6). Accept creates a minimal project (Intake, docs_pending, minted id, geo, owner name from the list) + a TENTATIVE site visit (site_visits.tentative=true) that reserves the window and is excluded from 6 AM/preview/SMS. Also: Add Appointment “Suggest times” chips on POST /portal/scheduling/suggest. Left for slice 2: email on-ramp, Jacob combined board, tentative release (B9), wait-list; P3 = documents + Gate 2 approval (tentative → Scheduled).
  • 2026-09-14 P2 slice 2 (session 103): email on-ramp services/schedule_list_inbox.py — operations@ mailbox polled every 30 min Mon–Fri 7–19 ET for mail from config.SCHEDULE_LIST_SENDERS (branch schedulers/OMs per reference_jes_gw_contacts; Stephaney excluded as triage-only); quoted history stripped, one parser, dedup on Gmail message id (schedule_lists.source_ref), propose Darius-only (A4), Gmail label Schedule/Processed, portal notification to owner/admin; frozen-template reply to the sender with the Week Board link exists but is OFF until SCHEDULE_LIST_EMAIL_REPLY=1 (Dustin’s call — new outbound channel). Tentative release (ruling B9): schedule_lists.release_expired at 8 PM ET — accepted rows whose visit date passed with the visit still tentative → visit Cancelled, project cancelled flag + comm log, row released, owner/admin notified; approved-meanwhile rows → final. Wait-list (ruling B4): schedule_lists.repropose_waiting hourly weekdays — waiting rows re-run against Darius, newly feasible → proposed + owner/admin notified, never the branch. Route Board /route-board (owner/admin): both techs, every branch, Mon–Sat runs with leave, drive-in, slack, fit, blocks, home ETA, week totals; tentative stops dashed. Calendar pencils tentative visits (dashed amber outline + TENTATIVE tag, ✎ in month cells). P2 complete; P3 = documents + readiness + Gate 2.
  • 2026-09-14 P3 core (session 103; migration 051): documents + Gate 2. services/schedule_list_docs.py — batch drop (≤40 files) staged under _intake_staging/schedule-lists/{list}/{batch}, processed on the WORKER (task_run_service): intake AUTO classifier → permit/plans Vision parse → match to an ACCEPTED row by the dup-check address scorer (≥ 0.80) or customer number → local_file_service.save_file into that row’s project → portal reparse_documents (the one parser rule) → readiness; unmatched files keep candidates for the correction grid (assign_file). compute_readiness: permit on file REQUIRED (B1), plans optional, scope-line count, production-day warning, guardrails on the readiness path (F4): Fairfax auto-reject + PG waterproofing-only = blockers; PWC/proprietary-form jurisdictions = owner-review warnings; map-county vs permit-jurisdiction disagreement = warning. Gate 2: schedule_lists.approve (owner/admin; blockers refuse unless the OWNER forces) → project Intake → Scheduled, docs_pending cleared when a permit is on file, visit tentative=false (now on field / 6 AM / preview), comm log, poster notified in the portal; reject → visit cancelled + project cancelled flag; approve_ready batches ready rows; pending_approval feeds the Action Center “Week Board” tab; nag_docs_pending CRON 6:45 PM ET (tomorrow’s stops without a permit → owner/admin + poster). Portal: drop zone + match grid, readiness chips, Approve / Approve anyway / Reject / Approve all ready / Re-check documents, ?list= deep link. Left: P4 Slack lane; branch confirmation on approval (email behind SCHEDULE_LIST_EMAIL_REPLY).
  • 2026-09-14 P4 Slack lane (session 103; client-lane ruling amendment F8 under list-first ruling A1): services/schedule_list_slack.py is routed from the ANNOUNCER app’s Socket Mode loop (announcer_app._dispatch, message events) — mapped {branch}-production channels only, top-level human posts, poster resolution (internal ids SLACK_USER_ID_DUSTIN/JACOB → owner/admin; SCHEDULE_LIST_SLACK_POSTERS member-id map; Slack profile email → SCHEDULE_LIST_SENDERS), the one parser (chatter ignored), dedup on channel:ts, create → propose → ✅ + in-thread f8_list_received (stop count, ET date range, unlikely.works Week Board link, list #) + f8_list_unreadable (poster’s lines + format); Gate 2 approval posts f8_list_confirmed (one line per stop) into the same thread via confirm_back (email lists: frozen confirmation email only with SCHEDULE_LIST_EMAIL_REPLY). Idle until the announcer Slack app has message.channels + message.groups events and history scopes (SCHEDULE_LIST_SLACK_SETUP.md). Nothing is booked from Slack.
  • 2026-09-14 VERIFIED end to end (session 103, triage account, date 10/6, fully cleaned up afterwards): paste → propose on real Routes legs → accept (project Intake + docs_pending + tentative visit; excluded by the 6 AM qualifier, seen by the engine) → generated permit PDF through the WORKER pipeline (classified PERMITS, Vision parse read the address + permit no., matched on customer # at 1.0, filed, portal reparse wrote permit_number/jurisdiction/owner/customer + UND-PUSH 6 / FND-BKFL scope lines) → readiness ready (warnings: no plans, production-day scope, Anne Arundel proprietary forms) → approve → project Scheduled, docs_pending false, tentative false, visible to the 6 AM qualifier, comm log “Scheduled via Week Board”. Also added: services/schedule_auto_move.py (automatic re-timing of TENTATIVE stops only; hourly; no-op until SCHED_AUTO_MOVE_TENTATIVE=1 — proposed ruling in plan §19) and scripts/slack_lane_check.py (probe of the announcer bot token’s list-lane scopes).