Claude-skill-registry fm-residency-scheduling

Family Medicine Residency Excel-based scheduling. Use when working with academic year block schedule spreadsheets to assign faculty half-days (C, GME, DFM) and resident half-days, validate constraints, process FMIT/SAFP blocks, apply post-call rules, and meet AT coverage. Triggers: Block schedule, faculty scheduling, resident scheduling, half-day assignments, FMIT, clinic coverage, resident supervision, AY 25-26.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/fm-residency-scheduling" ~/.claude/skills/majiayu000-claude-skill-registry-fm-residency-scheduling && rm -rf "$T"
manifest: skills/data/fm-residency-scheduling/SKILL.md
source content

FM Residency Faculty & Resident Scheduling

Automates faculty and resident half-day assignments directly in Excel for Family Medicine residency block schedules.

Architecture Note

Excel is the communication layer to code. The Excel workbook serves as configuration input - the scheduling engine reads from it and has more detailed logic internally. Think of Excel as the "what" and the code as the "how."

Data Model

See:

docs/architecture/HALF_DAY_ASSIGNMENT_MODEL.md

The scheduling system uses persisted half-day assignments with actual dates (not block references). This enables:

  • Natural inter-block constraint handling (PCAT/DO carries to next block automatically)
  • FMIT spanning blocks without special logic
  • Leap year and year boundary handled by date arithmetic

Source Priority (when multiple sources want same slot):

  1. preload
    - FMIT, call, absences - NEVER overwritten
  2. manual
    - Explicit human override
  3. solver
    - Computed by CP-SAT
  4. template
    - Default from WeeklyPattern

C vs AT Distinction (CRITICAL)

This table is the foundation of all scheduling logic.

ActivityDefinitionPhysical CapacityAT Coverage
Resident CResident seeing patientsCounts (max 6)Creates demand
Faculty CFaculty seeing OWN patientsCounts (max 6)None
Faculty ATFaculty supervising residentsDoes NOT countProvides
PCATPost Call Attending TimeDoes NOT countProvides (= AT)
DODirect Observation-Auto-assigned after call

Key Rules:

  • PROC/VAS: +1.0 AT demand (dedicated 1:1 supervision)
  • SM: Closed loop - does NOT use AT resources, SM Faculty's SM does NOT provide AT

Terminology (CRITICAL - do not confuse):

  • PCAT = Post Call Attending Time (NOT Admin) - can precept, provides AT
  • DO = Direct Observation (NOT Day Off) - auto-assigned PM after call

Physical Capacity: Max 6 people doing clinical work (C) per half-day. AT does NOT count toward this limit because AT faculty are supervising, not generating their own patient load.

Order of Operations (Canonical)

Phase 1: PRELOAD (locked before solver)

  1. Absences (LV, HOL, DEP, TDY)
  2. FMIT - both faculty and resident
  3. FMIT Fri/Sat call - auto-assigned with FMIT
  4. C-I (inpatient follow-up clinic): PGY-1 Wed AM, PGY-2 Tue PM, PGY-3 Mon PM
  5. Night Float - full pattern including post-call
  6. aSM - Wednesday AM for SM faculty (SM Faculty)
  7. Conferences (SAFP, USAFP, LEC)
  8. Protected time (SIM, PI, MM)

Phase 2: SOLVER (computed)

  1. Assign Sun-Thu call (min-gap decay) → auto-generates PCAT baseline
  2. Solve outpatient (residents) - solver applies rotation patterns
  3. Calculate supervision demand (resident C creates demand)
  4. Assign faculty AT (to meet supervision demand)
  5. Assign faculty C (personal clinic, counts toward physical capacity)
  6. Fill admin time (GME/DFM/SM)

Academic Year Calendar Structure

All blocks start Thursday, end Wednesday (except Block 0 and 13).

Academic Year: July 1 - June 30

Block 0:  July 1 → First Thursday - 1 day
          Variable length (1-6 days)
          Purpose: Orientation, onboarding, calendar fudge factor

Blocks 1-12: Thursday → Wednesday
          Fixed 28 days each (56 half-day assignments per person)

Block 13: Thursday → June 30
          Variable length (28+ days)
          Purpose: Absorb end-of-year remainder

Example AY25-26:

  • July 1, 2025 = Tuesday
  • Block 0 = July 1-2 (2 days)
  • Block 1 = July 3-30 (28 days, Thu-Wed)
  • Block 13 absorbs remainder to reach June 30, 2026

Block 0 Logic:

  • New intern (no preceding year rotation): Assign FLX
  • Returning resident: Follow logic from preceding year rotation

Block 13 Logic:

  • Normal assignments (not just administrative)
  • Longer than standard 28 days to reach June 30

Build Order (Recommended Workflow)

  1. FMIT/FMET first - Lock inpatient faculty assignments
  2. Call assignments - Distribute call with equity tracking
  3. Everything else - Clinic, AT, admin time

Quick Reference

Priority Order: FMIT > AT (clinic) > Admin (GME/DFM)

Key Constraints:

  • Weekly clinic caps vary by faculty (0-4)
  • Post-call = PCAT (AM) + DO (PM)
  • FMIT weeks = no clinic, OFF Friday after
  • All residents/faculty have LEC on Wednesday PM
  • Mid-block rotation transitions occur at column 28 (start of week 3)

References

  • references/excel-structure.md
    - Complete workbook layout
  • references/faculty-roster.md
    - Faculty caps and admin types
  • references/residents-rotations.md
    - Resident rotations and codes
  • references/block10-context.md
    - Block N specific context

Block Template2 Structure (VERIFIED)

This is the working document for scheduling.

Row Layout

Row RangeContent
1Block number, Day names (THURS, FRI, SAT...)
2Day abbreviations (THU, FRI, SAT...)
3Date row with actual dates (2025-03-12, etc.)
4Staff Call (faculty name when on call)
5Resident Call
6Headers: TEMPLATE, ROLE, PROVIDER
9-13PGY-3 Residents (R3)
14-19PGY-2 Residents (R2)
20-25PGY-1 Residents (R1)
31-43Faculty (C19/FAC, ADJ/FAC, SPEC/PSY)
49-53TY/Float/SM
55-65Medical Students (USU, IPAP, MS)
67-68CP/BHC (Pharmacist, Behavioral Health)
71-82Metrics (Appointments, AT Needed, etc.)

Column Layout

ColumnContent
1First-half rotation (e.g., "FMC", "FMIT 2", "Remote_Site")
2Second-half rotation if different (e.g., "Peds NF")
3Template code (R1, R2, R3, C19, ADJ, etc.)
4Role (PGY 1, PGY 2, PGY 3, FAC, etc.)
5Provider name
6+Half-day slots (AM/PM pairs per day)

Date-to-Column Mapping (Block N: [Block Start] - [Block End], 2026)

Each date uses 2 columns: AM (even col), PM (odd col+1)

ColumnsDateDayWeek
6-7[Block Start]Thu1
8-9Mar 13Fri1
10-11Mar 14Sat1 (W)
12-13Mar 15Sun1 (W)
14-15Mar 16Mon2
16-17Mar 17Tue2
18-19Mar 18Wed2 (LEC col 19)
20-21Mar 19Thu2
22-23Mar 20Fri2
24-25Mar 21Sat2 (W)
26-27Mar 22Sun2 (W)
28-29Mar 23Mon3 ← MID-BLOCK TRANSITION
30-31Mar 24Tue3
32-33Mar 25Wed3 (LEC col 33)
34-35Mar 26Thu3
36-37Mar 27Fri3
38-39Mar 28Sat3 (W)
40-41Mar 29Sun3 (W)
42-43Mar 30Mon4
44-45Mar 31Tue4
46-47Apr 01Wed4 (LEC col 47)
48-49Apr 02Thu4
50-51Apr 03Fri4
52-53Apr 04Sat4 (W)
54-55Apr 05Sun4 (W)
56-57Apr 06Mon5
58-59Apr 07Tue5
60-61Apr 08Wed5 (LEC col 61)

Special Columns:

  • Weekend (W): 10-13, 24-27, 38-41, 52-55
  • LEC (Wednesday PM): 19, 33, 47, 61
  • Mid-block transition: Column 28 (Mar 23)

Faculty Section (Rows 31-43)

See "C vs AT Distinction" table above for foundational rules.

Weekly caps apply to C only (personal clinic), NOT to AT (supervision). If staffing is critical, faculty can do AT all day every day.

RowTemplateNameMin C/wkMax C/wkAdminNotes
31C19/FAC[APD]00GMEAPD, 100% admin
32C19/FAC[Faculty 1]24GME
33C19/FAC[Faculty 2]24GME
34C19/FAC[DFM Admin]11DFM90% DFM admin
35C19/FAC[Faculty 3]00GMEOUT Dec-Jun
36C19/FAC[Faculty 4]24GME
37C19/FACSM_FACULTY, Chelsea00SM/ATSports Med faculty, can also do AT
38C19/FAC[Faculty 5]22GME
39C19/FAC[Faculty 6]00GMEDEP (deployed)
40C19/FAC[Faculty 7]00GMEFMIT weeks
41ADJ/FAC[Adjunct 1]00GMEFMIT/Call only
42ADJ/FAC[Adjunct 2]00GMEFMIT/Call only
43SPEC/PSY[Spec Faculty]22GME

If MIN cannot be met: WARN - flag for PD review (e.g., conference week, faculty leave)

Faculty Weekly Target Distribution

Target half-days per week (10 total):

ActivityHalf-DaysNotes
C (Clinic)3Personal patients, has MIN/MAX caps
GME/DFM2-3Admin time
AT3-4Supervising residents (no cap)
PCAT/DO1If took call that week

If no call that week: Extra AT slot available

Full-day preference: Faculty prefer full-day C (AM+PM same day) when possible - increases chances of full-day GME and better work-life balance.

Resident Section (Rows 9-25)

PGY-3 (Rows 9-13)

RowNameBlock N Rotation
9[R3-1]Remote_Site (TDY)
10[R3-2]NF (Night Float)
11[R3-3]FMC
12[R3-4]FMIT 2
13[R3-5]NEURO → NF

PGY-2 (Rows 14-19)

RowNameBlock N Rotation
14[R2-1]FMIT 2
15[R2-2]SM (Sports Med)
16[R2-3]POCUS
17[R2-4]L&D Night Float
18[R2-5]Surg Exp
19[R2-6]Gyn Clinic

PGY-1 (Rows 20-25)

RowNameBlock N Rotation
20[R1-1]FMC
21[R1-2]Peds Ward → Peds NF
22[R1-3]Offsite_Hospital L&D
23[R1-4]Peds NF → Peds Ward
24[R1-5]PROC
25[R1-6]IM

Resident Rotation Scheduling (DETAILED)

Rotation Code Mapping

Rotation NamePrimary CodePatternNotes
Remote_SiteTDYTDY all dayOff-site, entire block
NF (Night Float)NFOFF (AM) / NF (PM)Works nights, minimal day
FMCC/CVC (AM) / CV or C (PM)High clinic load
FMIT / FMIT 2FMITFMIT all dayInpatient team
NEURONEURONEURO (AM) / C (PM)Elective + clinic
SM (Sports Med)SMSM (AM) / C (PM)Sports Med + clinic
POCUSUSUS (AM) / C (PM)Ultrasound + clinic
L&D Night FloatL&DL&D all dayLabor & Delivery nights
Surg ExpSURGSURG (AM) / C (PM)Surgery + clinic
Gyn ClinicGYNGYN (AM) / C (PM)Gynecology + clinic
Peds WardPedWPedW all dayPediatrics inpatient
Peds NFPedNFOFF (AM) / PedNF (PM)Peds night float
Offsite_Hospital L&DKAPKAP all dayOff-site L&D
PROCPRPR (AM) / C (PM)Procedures + clinic
IMIMIM all dayInternal Medicine ward

All Known Resident Schedule Codes

CodeFull NameUsage
CClinicFM Clinic precepting
C30Clinic 30-min30-min appointments
C40Clinic 40-min40-min appointments (intern)
C-IClinic-FMITFMIT resident clinic day
C-NNight Float ClinicThursday PM for oncoming NF
CCContinuity ClinicPanel patients
CVVirtual ClinicTelehealth
V1, V2Virtual 1/2Virtual clinic blocks
PRProceduresProcedure clinic
VAS, VasCVascularVascular procedures
ADMAdminAdministrative time
FLXFlexFlexible/catch-up time
FMITFM Inpatient TeamInpatient rotation
NFNight FloatNight shift
OFFDay OffPost-call or scheduled off
TDYTemporary DutyOff-site rotation (Remote_Site, etc.)
LVLeaveVacation/sick
HOLHolidayFederal holiday
HCHoliday CallOn-call on holiday
WWeekendSaturday/Sunday
LECLectureProtected didactics (Wed PM)
SIMSimulationSim lab
MMM&MMorbidity & Mortality conf
PIProcess ImprovementQI time
EPICEPIC TrainingEHR training
OrientOrientationNew rotation orientation
CodingCodingBilling/coding education
SMSports MedicineSM rotation
aSMAcademic Sports MedWednesday AM for sports rotation
HLCHouseless ClinicMonday PM for R2/R3
CLCContinuity LearningThursday PM, weeks 2 and 4
GYNGynecologyGYN clinic
NEURONeurologyNeuro elective
OPTHOphthalmologyOphth elective
ENTENTENT elective
UROUrologyUrology elective
PALPalliativePalliative care
ENDOEndocrinologyEndo elective
VAVA ClinicVA rotation
NBNNewborn NurseryNBN rotation
NICUNICUNeonatal ICU
KAPOffsite_Hospital L&DOff-site L&D (intern)
L&DLabor & DeliveryProgram L&D
LDNFL&D Night FloatR2 L&D nights
IMInternal MedicineIM ward
PedWPeds WardPediatrics inpatient
PedNFPeds Night FloatPeds nights
PedSPPeds SubspecialtyPeds specialty
SURGSurgerySurgery rotation
PARTNER_CLINICPartner_ClinicPartner_Clinic clinic

Remote_Site TDY Schedule (Off-site Rotation)

Off-island rotation to Remote_Site, remote location.

Pre-departure (Week 1):

  • 1st Thursday: Clinic (C)
  • 1st Friday: Clinic (C)
  • Weekend: Travel to Remote_Site

During Remote_Site (Weeks 2-4):

  • All slots: TDY

Return (Week 4/5):

  • Return Tuesday: Clinic (C) - 4th Tuesday of block
  • Less travel time needed than overseas location

Key Points:

  • Need clinic touchpoints before leaving and after returning
  • TDY = Temporary Duty (off-site, cannot be scheduled locally)
  • Wednesday PM still LEC if in town

Offsite_Hospital L&D Schedule (Intern - PGY-1)

Off-site rotation at Offsite_Hospital Medical Center.

DayAMPMNotes
MonKAPOFFTravel back from Offsite_Hospital
TueOFFOFFRecovery day
WedCLECContinuity clinic!
ThuKAPKAPOn-site
FriKAPKAPOn-site
SatKAPKAPOn-site
SunKAPKAPOn-site

CRITICAL Pattern Summary:

def get_offsite_hospital(day_of_week, is_am, is_last_wed):
    if is_last_wed:
        return "LEC" if is_am else "ADV"
    if day_of_week == 1:  # Monday
        return "KAP" if is_am else "OFF"
    elif day_of_week == 2:  # Tuesday
        return "OFF"  # Both AM and PM
    elif day_of_week == 3:  # Wednesday
        return "C" if is_am else "LEC"
    else:  # Thu-Sun
        return "KAP"

Key Points:

  • Mon PM = OFF (travel back)
  • Tue = OFF/OFF (recovery)
  • Wed AM = C (continuity clinic, NOT KAP!)
  • Thu-Sun = KAP/KAP

L&D Night Float Schedule (R2 - PGY-2)

Program Labor & Delivery night shift rotation.

DayAMPMNotes
MonOFFLDNFSleeping days, working nights
TueOFFLDNF
WedOFFLDNFNO Wed AM clinic!
ThuOFFLDNF
FriCOFFFRIDAY morning clinic!
SatWWWeekend
SunWWWeekend

CRITICAL: Friday clinic, NOT Wednesday!

def get_ldnf(day_of_week, is_am, is_last_wed):
    if is_last_wed:
        return "LEC" if is_am else "ADV"
    if day_of_week == 5:  # Friday
        return "C" if is_am else "OFF"  # FRIDAY clinic!
    elif day_of_week in (6, 7):  # Weekend
        return "W"
    else:  # Mon-Thu
        return "OFF" if is_am else "LDNF"

Key Points:

  • Friday AM = C (NOT Wednesday like other rotations!)
  • Mon-Thu = OFF/LDNF (sleeping days, working nights)
  • R2 rotation (not intern)

Night Float Timing (CORRECTED)

Night Float Schedule Structure:

  • Starts: Thursday
  • Ends: Wednesday (following week)
  • Post-call: Thursday after (inter-block day)

C-N Code (Night Float Clinic):

  • Thursday PM when oncoming to night float
  • Replaces C30 for night float resident
  • This preserves 2 weeks of continuity clinic
  • Marcy uses C-N as cue to drop C30s from templates
Night Float Week Example:
Thu (start): C-N in PM (oncoming clinic)
Fri-Wed: NF (working nights)
Thu (end): OFF (post-call inter-block)

Houseless Clinic (HLC)

Schedule:

  • Monday PM for R2s and R3s only
  • Every Monday of every rotation
  • One resident (PGY-2 or PGY-3) per slot
  • Staff coverage: twice a month/block (intermittent)

Orientation Blocks (Protected Time)

Procedures Orientation (Faculty_4):

  • Label: SIM (Simulation)
  • Faculty_4 needs procedures orientation time
  • Sometimes booked accidentally - he says "it's fine" but should be protected

MedsTo Orientation (Faculty_5):

  • Beginning of each block (timing varies)
  • Protected time for medication reconciliation training

CLC (Continuity Learning Curriculum)

Schedule:

  • Thursday PM, twice per block
  • 2nd Thursday and 4th Thursday (NOT back-to-back weeks)
  • NOT on 1st Thursday (beginning of block has problems)
Block Example:
Week 1 Thu PM: NOT CLC (too early)
Week 2 Thu PM: CLC ← First session
Week 3 Thu PM: NOT CLC (gap week)
Week 4 Thu PM: CLC ← Second session

Mid-Block Rotation Transitions

Some residents switch rotations mid-block (at column 28 / Mar 23):

# Check column 1 and 2 for rotation info
first_half = sheet.cell(row=row, column=1).value   # e.g., "Peds Ward"
second_half = sheet.cell(row=row, column=2).value  # e.g., "Peds NF"

MID_BLOCK_COL = 28  # Start of second half

def get_rotation(col, first_rot, second_rot):
    if second_rot and col >= MID_BLOCK_COL:
        return second_rot
    return first_rot

Intern Continuity Clinic (Wednesday AM) - CRITICAL

PGY-1 interns have protected Continuity Clinic on Wednesday mornings.

This is a HARD CONSTRAINT - interns must see their panel patients weekly regardless of rotation.

Wednesday AM for PGY-1:
- Most rotations → C (Continuity Clinic)
- FMC Block 1-6 → C60 (60-min appointments, new intern)
- FMC Block 7-13 → C40 (40-min appointments, experienced intern)
- ER → C30 (30-min appointments)

Exceptions (no Wed AM clinic):
- Night Float schedules (PedW night, NF) → PedW or OFF
- Off-site (Remote_Site, Offsite_Hospital) → TDY or KAP
- OB Intern → OB Cl
- Transitional → ADM

Senior residents (PGY-2/3) do NOT have this constraint - their Wed AM depends on rotation:

  • FMC (R3) → ADM
  • FMC (R2) → FLX
  • Procedures → SIM
  • Sports Med → aSM

Resident Clinic Caps and Flex Requirements

Clinic Half-Days per Week by PGY Level:

LevelClinic Half-Days/WeekNotes
PGY-11 (ideally)Protected for learning
PGY-22-3No more than 3
PGY-33-4Most clinic time

R2 on Sports Med: Maximum 3 clinics (not 4)

Flex Time Requirements:

LevelFLX Half-Days/Week
PGY-12 half-days
PGY-21 half-day
PGY-3 (FMC)At least 1 half-day

Rotation Fill Patterns

def fill_rotation(sheet, row, rotation, col):
    """Fill a single half-day slot based on rotation type"""
    is_am = (col % 2 == 0)  # Even columns are AM

    if rotation == 'Remote_Site':
        return 'TDY'
    elif rotation == 'NF':
        return 'OFF' if is_am else 'NF'
    elif rotation == 'FMC':
        return 'C' if is_am else ('CV' if col % 4 == 1 else 'C')
    elif rotation in ['FMIT', 'FMIT 2']:
        return 'FMIT'
    elif rotation == 'NEURO':
        return 'NEURO' if is_am else 'C'
    elif rotation == 'SM':
        return 'SM' if is_am else 'C'
    elif rotation == 'POCUS':
        return 'US' if is_am else 'C'
    elif rotation == 'L and D night float':
        return 'L&D'
    elif rotation == 'Surg Exp':
        return 'SURG' if is_am else 'C'
    elif rotation == 'Gyn Clinic':
        return 'GYN' if is_am else 'C'
    elif rotation == 'Peds Ward':
        return 'PedW'
    elif rotation == 'Peds NF':
        return 'OFF' if is_am else 'PedNF'
    elif rotation == 'Offsite_Hospital L and D':
        return 'KAP'
    elif rotation == 'PROC':
        return 'PR' if is_am else 'C'
    elif rotation == 'IM':
        return 'IM'
    else:
        return rotation  # Use as-is

Workflow

1. Load Block Template2 Sheet

from openpyxl import load_workbook
wb = load_workbook('schedule.xlsx')
sheet = wb['Block Template2']

2. Identify Pre-Blocked Slots

Never overwrite these codes:

CodeMeaningAction
FMITFM Inpatient TeamSkip
LVLeaveSkip
WWeekendSkip
PCPost-call markerKeep
PCATPost-Call AdminKeep
DODay OffKeep
LECLectureSkip
SIMSimulationSkip
SAFPState AFP confSkip
BLSBLS trainingSkip
USAFPUSAFP conferenceSkip
DEPDeployedSkip
aSMSports Med assistSkip
PIProcess ImprovementSkip
MM? / MMM&M conferenceSkip

3. Apply Call and Post-Call Rules

Call Schedule Structure

  • Row 4: Staff Call - contains faculty name at the date column when on call
  • Call is typically at the AM column (even col) of the call date

Call Back-to-Back Prevention (HARD CONSTRAINT)

Rule: Don't give faculty call on consecutive days - need gap days between call assignments.

BAD:  Faculty on call Mar 15 AND Mar 17 (only 1 day gap)
GOOD: Faculty on call Mar 15 AND Mar 19 (3 day gap)

Weekend Call Protection

Faculty with "W" (weekend) in their schedule row should NOT be assigned call that day.

  • This is a soft preference, not a hard constraint
  • Call row (Row 4) is separate from schedule row

FMIT Call Rules (CRITICAL)

FMIT Week Structure:

  • Starts: Friday
  • Ends: Thursday (following week)
  • PC (Post-Call/Day Off): Friday after FMIT ends

During FMIT week, faculty covers:

  • Friday night call (first night of FMIT)
  • Saturday night call (second night of FMIT)

FMIT faculty CANNOT be placed on call:

  • Sunday through Thursday during FMIT (they're on inpatient service)
  • The Saturday immediately AFTER their FMIT ends (need recovery)
Example: Faculty on FMIT Fri Mar 13 - Thu Mar 19:
  - Mar 13 (Fri)         ← FMIT starts, covers Fri call
  - Mar 14 (Sat)         ← FMIT covers Sat call
  - Mar 15-19 (Sun-Thu)  ← CANNOT be on call (on FMIT service)
  - Mar 20 (Fri)         ← PC (day off after FMIT)
  - Mar 21 (Sat)         ← CANNOT be placed on call (recovery)

FMIT Weekly Call Pattern Summary

FMIT week = Friday through Thursday
FMIT faculty covers:
  - Friday night call (FMIT start)
  - Saturday night call (FMIT weekend)
  - CANNOT be on call Sun-Thu (on service)
  - PC Friday after (day off)
  - CANNOT be on call Sat after (recovery)

Post-Call Rules (PCAT/DO)

Only applies to non-FMIT faculty:

If faculty on call Night N AND NOT on FMIT:
  Day N+1 AM = PCAT (Post-Call Admin Time)
  Day N+1 PM = DO (Day Off)

FMIT faculty do NOT get PCAT/DO - they continue FMIT coverage.

Post-FMIT Friday Off (PC)

After completing FMIT week, faculty gets Friday off:

FMIT ends Thursday -> Friday = PC (both AM and PM)
This Friday CANNOT have this faculty on call.

Call Assignment Pools

AUTO-ASSIGN Pool (Mon-Thu, Sun):

  • FACULTY_1
  • DFM_ADMIN
  • FACULTY_4
  • FACULTY_5
  • SM_FACULTY (when not on FMIT/PC)

MANUAL-ONLY Pool (assigned by hand, typically Sundays):

  • ADJUNCT_1
  • ADJUNCT_2
  • SPEC_FACULTY

SPECIAL RULES Faculty:

FacultyRule
FACULTY_7Week-on/week-off FMIT: No Sun-Thu call during "off" weeks; available after FMIT but not immediately
APDAvailable for call starting next week after post-FMIT (not same week)
FACULTY_2Only weeks 1-2 before FMIT week; no call in week immediately preceding FMIT

Sunday Call Equity Pool

Sundays are AUTO-ASSIGNABLE but tracked separately for equity. No single faculty member should be assigned every Sunday in a block.

Sunday equity tracking:
- Maintain separate sunday_counts vs weekday_counts
- When assigning Sunday, sort by sunday_counts (lowest first)
- Distribute evenly: aim for max 1 Sunday per faculty per block
- W (weekend) in faculty row does NOT block call assignment

Important: The "W" code in a faculty's schedule row indicates weekend/off, but call assignments (row 4) are separate. Faculty can be ON CALL on a day they have "W" in their schedule.

SM_FACULTY Sports Medicine Rules

SM_FACULTY is the Sports Medicine (SM) faculty. She does NOT do regular clinic (C).

CRITICAL: SM_FACULTY does SM REGARDLESS of whether residents are on SM rotation.

  • SM is SM_FACULTY's primary clinical work (not C like other faculty)
  • 2-4 SM half-days/week independent of residents
  • When residents ARE on SM, they must match SM Faculty's SM slots

Key Rules:

  1. SM requires SM_FACULTY: If a resident is scheduled SM, SM_FACULTY must ALSO be SM that slot
  2. No SM_FACULTY = No SM: If SM_FACULTY is blocked (FMIT, PC, LV, etc.), resident cannot do SM - change to C
  3. Weekly target: SM_FACULTY needs 2-4 SM slots per week (independent of residents)
  4. aSM is different: Academic Sports Med (aSM) = ultrasound teaching, not patient care; Wed AM preload
  5. FMIT blocks SM: When SM_FACULTY on FMIT, there can be NO SM that week
  6. Call eligible: SM_FACULTY takes call with normal PCAT/DO rules
  7. SM is closed loop: Does NOT use AT resources, SM Faculty's SM does NOT provide AT for other residents

SM_FACULTY Schedule Codes:

  • SM = Sports Medicine (with resident)
  • aSM = Academic Sports Med (teaching)
  • GME = Admin time (when not SM)
  • FMIT = Inpatient team (blocks SM)
  • PC = Post-FMIT Friday off

Workflow:

  1. Check if resident on SM rotation (column 1)
  2. Find slots where both resident has SM AND SM_FACULTY available
  3. Assign SM_FACULTY SM to match (3-4 per week)
  4. Change resident SM → C where SM_FACULTY unavailable

AT Supervision Ratios (ACGME REQUIREMENT - CANNOT VIOLATE)

This is a HARD CONSTRAINT - minimum supervision ratios required by ACGME.

Resident AT Demand:

PGY LevelAT Demand
PGY-1 (Intern)0.5 AT each
PGY-20.25 AT each
PGY-30.25 AT each

Non-Clinic Activities = +1 AT each:

  • PROC (Procedures)
  • VAS (Vascular)
  • SM (Sports Med) - covered by SM_FACULTY, not C faculty
  • Any specialty clinic requiring dedicated supervision

Calculation:

Total AT Needed = (PGY-1 count × 0.5) + (PGY-2 count × 0.25) + (PGY-3 count × 0.25) + (PROC/VAS count × 1.0)

ALWAYS ROUND UP - cannot have half a faculty member

Example:

2 PGY-1 in clinic = 1.0 AT
2 PGY-2 in clinic = 0.5 AT
1 PROC resident  = 1.0 AT
--------------------------
Total            = 2.5 AT → Round up to 3 AT minimum

Coverage codes that count as AT:

  • C (Clinic)
  • PCAT (Post-Call Admin Time) - CAN precept, counts as AT
  • CV (Virtual Clinic) - if precepting

Verification: Check rows 91-92 in Block Template2:

  • Row 91: "Total Attendings Needed" (calculated demand)
  • Row 92: "# Attendings Assigned" (current coverage)

Row 92 must be >= Row 91 for EVERY half-day slot.

Balancing AT: Coverage vs Demand

AT compliance is a two-way equation:

AT Coverage >= AT Demand

If coverage is short, you can EITHER:

  1. Increase coverage - Add faculty C (if available within caps)
  2. Decrease demand - Reduce resident clinic load

Reducing demand options:

  • Move resident from C → ADM/FLX (removes their AT weight)
  • Move resident from PROC → C (PROC = +1.0 AT, C = only 0.25-0.5 AT)
  • Reduce clinic load on days with limited faculty

Priority: AT compliance > weekly clinic caps > GME preferences

Faculty caps are preferences, not hard constraints. ACGME supervision IS a hard constraint. However, before going over caps, first check if reducing resident demand is feasible.

Example - Mar 13 PM with most faculty at USAFP:

  • Only 1 faculty available (Faculty_2)
  • 7 residents in clinic = ~2.0 AT demand
  • Solution: Move 2-3 residents from C → ADM for that slot
  • Result: Reduced demand to match available coverage

Rotation Templates as Guidelines

Rotation templates (column 1-2) are GUIDELINES, not strict requirements. The actual schedule may vary based on:

  • Faculty availability (e.g., no SM when SM_FACULTY unavailable)
  • AT demand needs
  • Call/post-call blocking
  • Conference/educational requirements

Reading FMIT Schedule

Check "FMIT Attending (2025-2026)" sheet for weekly assignments:

  • Column C: Week 1 attending
  • Column D: Week 2 attending
  • Column E: Week 3 attending
  • Column F: Week 4 attending

Example Block N:

WeekDatesFMIT Attending
1Mar 13-19Faculty_7
2Mar 20-26APD
3Mar 27-Apr 2Faculty_7
4Apr 3-9Faculty_2

4. Assign Faculty Clinic (C)

For each weekday half-day:

  1. Get available faculty (not blocked, under weekly cap)
  2. Sort by remaining weekly capacity (highest first)
  3. Assign top 2 faculty
  4. Mark cells with "C"

5. Fill Faculty Admin Time

For remaining empty faculty cells:

  • Most faculty → "GME"
  • DFM_Admin → "DFM"

6. Fill Resident Schedules

For each resident:

  1. Read rotation from column 1 (and column 2 if mid-block switch)
  2. For each column 6-61:
    • Skip if blocked (W, LV, LEC, etc.)
    • Apply rotation pattern based on AM/PM
    • Handle mid-block transitions at column 28
  3. Ensure LEC on all Wednesday PM slots (cols 19, 33, 47, 61)

7. Last Wednesday of Block Rules (CRITICAL)

Final Wednesday of block (Week 5) has special rules:

All Residents:

  • AM: Lecture (LEC) - NOT clinic
  • PM: Advising (ADV)

⚠️ Common Error: Scheduling morning clinic on last Wednesday - this is WRONG.

Last Wednesday Example ([Block End]):
  AM: LEC (Lecture) - protected
  PM: ADV (Advising)

8. Internal Medicine Last Wednesday Exception

Special Rule for IM rotation:

  • Final Wednesday of IM rotation → Tuesday PM clinic instead
  • Reason: Preserves continuity week count (otherwise only 3 weeks instead of 4)
  • "Inverted day" logic - Thursday starts new week in scheduling
  • Can be shorter (2 hours of patient care)
IM Resident Last Week Example:
  Tue PM: C (moved from Wed)
  Wed AM: LEC
  Wed PM: ADV

Complete Python Code Template

from openpyxl import load_workbook

# Faculty configuration (min_c = MIN clinic/wk, max_c = MAX clinic/wk)
# Weekly caps apply to C only, NOT AT (AT is unlimited)
FACULTY = {
    'APD': {'row': 31, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
    'Faculty_1': {'row': 32, 'min_c': 2, 'max_c': 4, 'admin': 'GME'},
    'Faculty_2': {'row': 33, 'min_c': 2, 'max_c': 4, 'admin': 'GME'},
    'DFM_Admin': {'row': 34, 'min_c': 1, 'max_c': 1, 'admin': 'DFM'},
    'Faculty_3': {'row': 35, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
    'Faculty_4': {'row': 36, 'min_c': 2, 'max_c': 4, 'admin': 'GME'},
    'SM_FACULTY': {'row': 37, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},  # SM only, no personal C
    'Faculty_5': {'row': 38, 'min_c': 2, 'max_c': 2, 'admin': 'GME'},
    'Faculty_6': {'row': 39, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
    'Faculty_7': {'row': 40, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
    'Adjunct_1': {'row': 41, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
    'Adjunct_2': {'row': 42, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
    'Spec_Faculty': {'row': 43, 'min_c': 2, 'max_c': 2, 'admin': 'GME'},
}

# Resident configuration
RESIDENTS = {
    9: {'name': 'R3_1', 'pgy': 3},
    10: {'name': 'R3_2', 'pgy': 3},
    11: {'name': 'R3_3', 'pgy': 3},
    12: {'name': 'R3_4', 'pgy': 3},
    13: {'name': 'You', 'pgy': 3},
    14: {'name': 'R2_1', 'pgy': 2},
    15: {'name': 'R2_2', 'pgy': 2},
    16: {'name': 'R2_3', 'pgy': 2},
    17: {'name': 'R2_4', 'pgy': 2},
    18: {'name': 'R2_5', 'pgy': 2},
    19: {'name': 'R2_6', 'pgy': 2},
    20: {'name': 'R1_1', 'pgy': 1},
    21: {'name': 'R1_2', 'pgy': 1},
    22: {'name': 'R1_3', 'pgy': 1},
    23: {'name': 'R1_4', 'pgy': 1},
    24: {'name': 'R1_5', 'pgy': 1},
    25: {'name': 'R1_6', 'pgy': 1},
}

# Rotation to code mapping (AM, PM)
# NOTE: These are DEFAULT patterns - special days override (Wed AM intern continuity, etc.)
ROTATION_CODES = {
    'Remote_Site': ('TDY', 'TDY'),           # Off-island, see Remote_Site TDY Schedule for details
    'NF': ('OFF', 'NF'),              # Night Float - starts Thu, ends Wed
    'FMC': ('C', 'C'),                # Family Medicine Clinic
    'FMIT': ('FMIT', 'FMIT'),         # FM Inpatient Team
    'FMIT 2': ('FMIT', 'FMIT'),       # FM Inpatient Team (2nd team)
    'NEURO': ('NEURO', 'C'),          # Neurology elective + clinic
    'SM': ('SM', 'C'),                # Sports Medicine + clinic
    'POCUS': ('US', 'C'),             # Point-of-care ultrasound + clinic
    'L and D night float': ('OFF', 'LDNF'),  # R2 L&D nights - Fri AM clinic!
    'Surg Exp': ('SURG', 'C'),        # Surgery experience + clinic
    'Gyn Clinic': ('GYN', 'C'),       # Gynecology + clinic
    'Peds Ward': ('PedW', 'PedW'),    # Pediatrics inpatient
    'Peds NF': ('OFF', 'PedNF'),      # Peds Night Float
    'Offsite_Hospital L and D': ('KAP', 'KAP'),  # Off-site L&D - see KAP schedule
    'PROC': ('PR', 'C'),              # Procedures + clinic
    'IM': ('IM', 'IM'),               # Internal Medicine ward
    'VA': ('VA', 'VA'),               # VA clinic rotation
    'ENDO': ('ENDO', 'C'),            # Endocrinology elective
    'Derm': ('DERM', 'C'),            # Dermatology elective
    'MSK': ('MSK', 'C'),              # Musculoskeletal elective
}

# Special columns
WEEKEND_COLS = {10, 11, 12, 13, 24, 25, 26, 27, 38, 39, 40, 41, 52, 53, 54, 55}
LEC_COLS = {19, 33, 47, 61}
MID_BLOCK_COL = 28

# Pre-blocked codes (never overwrite)
BLOCKED_CODES = {'FMIT', 'LV', 'W', 'PC', 'LEC', 'SIM', 'SAFP', 'BLS',
                 'PCAT', 'DO', 'USAFP', 'DEP', 'aSM', 'PI', 'MM?', 'MM',
                 'HOL', 'HC', 'TDY'}

def fill_resident_schedule(sheet, row):
    """Fill a single resident's schedule based on their rotation"""
    rot1 = sheet.cell(row=row, column=1).value
    rot2 = sheet.cell(row=row, column=2).value

    for col in range(6, 62):
        current = sheet.cell(row=row, column=col).value
        if current and str(current).strip().upper() in BLOCKED_CODES:
            continue

        if col in WEEKEND_COLS:
            sheet.cell(row=row, column=col).value = 'W'
            continue

        if col in LEC_COLS:
            sheet.cell(row=row, column=col).value = 'LEC'
            continue

        # Get rotation (handle mid-block switch)
        rotation = rot2 if (rot2 and col >= MID_BLOCK_COL) else rot1

        # Get AM/PM codes
        if rotation in ROTATION_CODES:
            am_code, pm_code = ROTATION_CODES[rotation]
            is_am = (col % 2 == 0)
            sheet.cell(row=row, column=col).value = am_code if is_am else pm_code
        else:
            sheet.cell(row=row, column=col).value = rotation

Manual Scheduling Workflow (Reference)

This is the typical workflow when scheduling by hand. The automated system should follow a similar approach:

Step 1: Load Non-Negotiables

  • Absences (LV)
  • FMIT assignments
  • Conferences (SAFP, USAFP)
  • Holidays (HOL)
  • Protected time (LEC, SIM)

Step 2: Assign Sun-Thu Call

  • Distribute call equitably across auto-assign pool
  • Track weekday and Sunday counts separately
  • Sunday = separate equity pool (max 1 per faculty per block)
  • Result: Generates PCAT for every working day AM (baseline 1 AT)

Step 3: Load Resident Templates

  • Apply rotation patterns from columns 1-2
  • These are GUIDELINES, not strict requirements
  • Adjust based on availability (e.g., no SM if SM_FACULTY blocked)

Step 4: Calculate AT Demand

  • See AT Supervision Ratios above
  • Check rows 91-92 for demand vs coverage

Step 5: Assign Faculty Clinic (C)

  • Prioritize slots with higher resident load
  • Prefer full-day C when possible (faculty preference)
    • Full-day C increases chances of getting full-day GME
    • Better work-life balance for faculty
  • Keep physical clinic ≤6 people

Step 6: Fill Admin Time

  • GME for most faculty
  • DFM for DFM_Admin
  • Fill remaining empty slots after C assignments

Physical Clinic Constraint

See "C vs AT Distinction" table for authoritative rules.

Max 6 people doing clinical work (C) per half-day slot. AT does NOT count toward this limit.

Why this matters: Staffing constraints limit how many patients can be seen. More than 6 physicians overwhelms support staff.


Validation Checklist

After completing assignments:

  • No faculty exceeds weekly C cap (MIN and MAX)
  • Post-call blocks (PCAT/DO) applied correctly
  • FMIT faculty have no C on FMIT days
  • No assignments on weekends (W columns)
  • LEC on all Wednesday PM slots (cols 19, 33, 47, 61)
  • PGY-1 interns have Continuity Clinic on Wednesday AM (C/C40/C60)
  • Resident rotations match column 1/2 rotation names
  • Mid-block transitions applied at column 28
  • Pre-blocked codes not overwritten
  • ACGME: AT coverage >= AT demand (Row 92 >= Row 91) for every slot
  • Physical clinic: ≤6 people doing clinical work per slot
  • Sunday call distributed (max 1 per faculty)
  • No back-to-back call (need gap days between assignments)
  • Night float starts Thursday, ends Wednesday, post-call Thursday
  • C-N used for oncoming night float (Thursday PM)
  • Last Wednesday: AM=LEC, PM=ADV (no morning clinic)
  • IM residents: Tuesday PM clinic instead of final Wednesday
  • R2 clinic caps: no more than 3 per week
  • HLC assigned Monday PM for R2/R3
  • CLC on 2nd and 4th Thursday PM (not back-to-back)

Common Issues

"Not enough coverage": Check if faculty are on leave (LV), USAFP, or DEP.

"Weekly cap exceeded": Recalculate weekly totals. Caps are PER WEEK, not total.

"Post-call conflict": If faculty on call Sunday, Monday AM/PM both blocked.

"Wrong rotation code": Check column 1 for rotation name, verify mapping exists.

"Mid-block not switching": Ensure column 2 has second rotation and col >= 28.


ROSETTA Stone Testing Approach

Ground truth file:

docs/scheduling/Block10_ROSETTA_CORRECT.xlsx

This file contains the CORRECT Block N schedule with all patterns applied correctly. Use it for TDD:

  1. Parse ROSETTA to get expected values
  2. Run expansion service to get actual values
  3. Compare cell-by-cell
  4. Fix until all tests pass

Test files:

  • backend/tests/scheduling/test_expansion_vs_rosetta.py
    - 24 parameterized tests
  • backend/app/utils/rosetta_parser.py
    - Parses ROSETTA xlsx

Run tests:

cd backend
pytest tests/scheduling/test_expansion_vs_rosetta.py -v

Key patterns verified by ROSETTA:

ResidentRotationKey Pattern
[R1-3]KAPMon PM=OFF, Tue=OFF/OFF, Wed AM=C
[R2-4]LDNFFri AM=C (not Wed!), Mon-Thu=OFF/LDNF
[R1-5]PROCWed AM=C (intern continuity)
[R1-6]IMWed AM=C, works weekends
You, JaeNEURO→NFMid-block transition at col 28
[R1-2]PedW→PedNFMid-block + intern continuity
[R1-4]PedNF→PedWReverse mid-block

Priority rules (highest first):

  1. Last Wednesday → LEC/ADV (cols 60-61)
  2. Wednesday PM → LEC (cols 19, 33, 47)
  3. Rotation-specific patterns (KAP, LDNF, NF)
  4. Intern Wed AM continuity (PGY-1 → C)
  5. Mid-block transitions (col 28+)
  6. Default rotation pattern

Edge Cases and Resolutions

Call/Post-Call Boundary Conditions

Edge CaseResolution
Call before FMITMin 3-day buffer; prefer no call week before FMIT
PCAT/DO inter-blockCarries over via actual dates (Wednesday call → Thursday PCAT in next block)
Call on last day of blockPCAT/DO applies to first day of next block automatically

FMIT Week Boundary Conditions

Edge CaseResolution
FMIT spanning blocksDate-based, not block-tied; solver handles naturally
Post-FMIT PC in next blockActual dates handle this (e.g., FMIT ends Thu → PC Fri may be in next block)
FMIT + Wednesday call prohibitionCannot take Sun-Thu call during FMIT week

SM/SM_FACULTY Dependencies

Edge CaseResolution
Multiple SM residents same slotMax 2 residents; SM_FACULTY supervises both; must match half-day
SM_FACULTY post-call + SM scheduledMedium constraint - avoid if possible, not a hard fail
SM_FACULTY on FMIT + SM residentsNo SM that week; convert resident SM → C

FMIT Clinic (C-I) by PGY Level

PGY LevelC-I DayNotes
PGY-1Wednesday AMHard constraint - intern continuity preserved
PGY-2Tuesday PMFMIT resident clinic day
PGY-3Monday PMFMIT resident clinic day

C-I is PRELOADED, not solved. These slots are locked before solver runs.

Inter-Block Continuity

ScenarioHandling
NF post-call inter-blockNF ends Wednesday → post-call Thursday = next block start
PCAT/DO from Wed callThursday AM/PM may be in next block
Rotation ending mid-weekDate arithmetic handles naturally

Physical Capacity

ConstraintLimit
Clinical work per half-dayMax 6 people (residents + faculty in C/CV/PR/VAS)
AT supervisionDoes NOT count toward physical limit

Holidays and Special Days

ScenarioResolution
CLC on holidaySkip; does NOT reschedule to another day
HLC on holidaySkip; does NOT reschedule
Intern continuity on holidaySkip; continuity does NOT reschedule

Resident Call System (Separate from Faculty)

Resident call is Chief-assigned and follows different rules:

  • L&D 24-hour call (Friday)
  • Night Float coverage
  • Weekend call patterns

Note: Resident call is tracked in

resident_call_preloads
table, separate from faculty call.


Related Documents

  • docs/architecture/HALF_DAY_ASSIGNMENT_MODEL.md
    - Data model specification
  • .claude/Scratchpad/session-104-half-day-model.md
    - Design session notes
  • references/faculty-roster.md
    - Faculty caps and constraints
  • references/residents-rotations.md
    - Resident rotation patterns