Clepsy enforces several ACGME-style guardrails, but it does not enforce every rule in the public product brief. The scheduling solver and the swap validator cover duty hours, days off, rest, streaks, and duty periods with real code. They do not model call frequency, moonlighting, or a maximum shift length. This page explains exactly what each layer checks, where the two layers differ, and where the gaps remain so you can describe the product accurately and plan around its limits.
Read this first: a successful swap or a deployed schedule is not proof of full ACGME compliance. Clepsy is a strong guardrail, not a compliance certification. Use the comparison table and the gap list below before telling anyone that a rule is "enforced."
The guardrails at a glance
The table compares the public expectation with what the code actually does. Where the two disagree, the "What Clepsy does" column reflects the implementation.
| Topic | What Clepsy does | Gap / caveat |
|---|---|---|
| 80-hour week | Solver enforces a hard 80-hour cap per ISO week and a hard rolling 28-day 320-hour cap. The swap engine checks one centered 28-day window with a 320-hour cap. | Not a true 4-week average, and the swap engine checks one window rather than every rolling window. The weekly cap is configurable from 40 to 120, and the 4-week cap is always four times that value. |
| 1 day off in 7 | Solver enforces one full calendar day off in every rolling 7-day window. The swap engine requires an unoccupied calendar day in a window of plus or minus 3 days around the shift. | No 4-week averaging. It counts a free calendar date, not a guaranteed continuous 24-hour gap. |
| Rest between shifts | A hard 10-hour minimum in both the solver and the swap engine. A 10-to-14-hour gap draws a soft solver penalty. | The public 8-hour minimum is not what Clepsy enforces; Clepsy is stricter by two hours. The 14-hour-after-24-hour-call rule is not implemented. |
| Maximum shift length | Not enforced. | There is no 12-hour cap. A configured 14-hour or 16-hour shift can pass if rest, 80-hour, and day-off rules are met. The only duration guardrail is a 5-hour minimum after blackout truncation. |
| In-house call frequency | Not modeled. There is no call entity and no monthly or per-block call cap. | Call-like shifts are ordinary assignments. They are constrained only by rest, 80-hour, day-off, and consecutive-night or consecutive-shift limits. |
| Moonlighting | Not modeled or counted. | Only scheduled assignments and mandatory duty periods count toward 80 hours. External moonlighting is invisible unless it is entered as duty or a blackout. |
| Consecutive nights | Solver default is 5 nights. The swap engine allows up to 6. | The limits differ by engine and are not the same constant. Reconcile them before quoting a single number. |
| Consecutive shifts / days | Solver default max consecutive days is 6, with a backend fallback of 7. The swap engine allows up to 6 consecutive shifts. | The 1-in-7 day-off rule effectively caps consecutive work at 6. Raising the solver slider above 6 does not allow a 7th consecutive day. |
| Night equity | A hidden hard rule keeps the busiest resident's night count within 1 of the least busy resident's count. | Not exposed in the UI. It can make a model infeasible without an obvious control to loosen. |
| Mandatory duty / blackouts | Mandatory duty blocks overlapping shifts and adds hours. Blackout actions prevent a shift or truncate it; a remnant under 5 hours becomes a hard block. | Non-blackout duty kinds are written as mandatory and count toward 80 hours. The UI has no toggle to change that. |
Two enforcement layers
Clepsy has two separate engines with different logic. The solver builds a schedule under hard constraints. The swap engine validates a proposed exchange. Their constants are close but not identical, and the server-side swap checks are narrower than the client-side checks.
Solver hard constraints and soft objectives
The CP-SAT solver treats the following as hard constraints. A schedule that violates any of them is not returned:
- One resident per position, coverage minimums and maximums, and intern / non-intern floors.
- One shift per resident per day and no duplicate position.
- 10-hour minimum rest between shifts.
- Maximum consecutive work days, with a default of 6 from the frontend.
- One day off in every rolling 7-day window.
- Maximum consecutive night shifts, default 5.
- Night equity, held to a spread of 1.
- 80 hours per ISO week and 320 hours per rolling 28 days.
- Strong off requests, blackout and duty periods, and pinned assignments.
Softer behavior is expressed through penalty weights. Only three weights are exposed in the UI: backward rotation, short turnaround (the 10-to-14-hour band), and post-night recovery. Other penalties exist in the objective, such as coverage shortfall, load imbalance, soft off requests, non-intern missing, group imbalance, prior night streaks, and permission mismatch, but they do not have visible controls.
Swap engine rules
The client-side swap engine runs an ordered rule list and stops at the first failure. The order is:
- Same assignment, then same user.
- Past shift.
- Excluded role (Orientation or Open).
- Draft versus final isolation.
- Overlapping request.
- PGY boundary.
- Off-service scope.
- Same-day overlap.
- 10-hour turnaround.
- 80-hour and 320-hour limits.
- One day off in the plus-or-minus-3-day window.
- Maximum 6 consecutive nights.
- Maximum 6 consecutive shifts.
- Block availability.
The swap engine's 80-hour check uses a centered 28-day window and a 320-hour cap. Its day-off check looks for one free calendar day in a 7-day window. Its rest check is a hard 10 hours. These are the main guardrails a resident will feel.
The server checks less
Server-side swap validation is deliberately narrower. It does not repeat the full client rule set. It does not enforce PGY boundary, off-service scope, consecutive nights, consecutive shifts, or block availability. Its turnaround check only considers published shifts. Do not claim server parity with the client engine; the client is the primary guardrail.
What to do when a rule blocks a swap
Most blocks are data problems, not permanent denials. Read the message, identify which rule fired, and choose the smallest fix.
| Message pattern | What it means | Best next step |
|---|---|---|
ACGME 80-hour violation |
The target shift would push the 28-day total past 320 hours. | Pick a later or earlier target, or drop another shift first. Changing availability will not fix an hours overage. |
ACGME day-off violation |
No free calendar day remains in the 7-day window around the target. | Choose a target near an existing day off. If the week is genuinely full, escalate to the chief instead of forcing it. |
Turnaround conflict (<10h) |
The gap to an adjacent shift is under 10 hours. | Pick a target that does not sit against another shift. Adjustment of block availability does not change rest math. |
ACGME consecutive nights violation |
The swap would create a night streak longer than 6. | Target a non-night shift, or break the streak with a day off before accepting. |
ACGME consecutive shifts violation |
The swap would create a work streak longer than 6 days. | Pick a target that leaves a gap, or free a day elsewhere in the streak. |
Use the Resident view to try a different target first. If the shift is the only coverage option, the Chief Resident can review the underlying assignments, and an Admin can confirm the solver parameters or correct bad data. Adjust block availability only when the message is about availability, not when it is about hours, rest, or streaks.
Compliance gaps to know
Not implemented as written in the public brief:
- 12-hour maximum shift length. There is no cap. Long shifts pass if other rules are met.
- 14 hours off after 24-hour call. A flat 10-hour minimum applies; the longer post-call rest rule does not exist.
- Moonlighting. External hours are neither modeled nor counted.
- In-house call frequency. No monthly or per-block call limit is tracked.
- True 4-week averaging. The solver uses weekly plus rolling 28-day caps, and the swap engine samples one centered window rather than every window.
- Manual fill rest mismatch. Manual fill and assignment accept an 8-hour rest gap, while the solver and swap engine require 10. An 8-to-10-hour manual turnaround can be saved and then make the next solve infeasible.
These gaps matter most when someone asks whether Clepsy "guarantees" ACGME compliance. It does not. It guarantees that the constraints listed above are honored by the solver, and that the client swap rules block the exchanges they cover. Everything outside that set depends on your program's own policies and data entry.
Questions to ask your program
Because call, moonlighting, and shift length are policy areas rather than solver features, confirm them with your program before relying on Clepsy as your compliance record.
- How does the program model in-house call, and where is its frequency limit tracked if not in Clepsy?
- Does moonlighting count toward duty hours, and how is it reported if it is not entered as duty or blackout?
- What is the program's maximum shift length, and is it enforced outside the scheduler?
- Does the program rely on 4-week averaging, and if so, who verifies it beyond the solver's weekly and 28-day caps?
- Who reviews a swap when the client rules block it but the resident believes the data is wrong?
Continue with shift swaps for the request flow and statuses, the admin guide for solver parameters and publishing, and the FAQ for common questions grouped by role.