A cap on a worker is not one rule. Depending on the type you hand it, it is either a question about the future or a question about the past.
We ran into this in shift-planner, our open-source scheduling library. A plan declares per-worker limits in a mapping, and the type of each value decides what gets counted.
An int means shift count. A timedelta means worked time. Nothing else gets accepted: the plan validates the whole mapping once at construction, raising TypeError for anything that is neither an int nor a timedelta, and ValueError for a negative value, compared against zero duration for a timedelta and against 0 for an int. Bad limits do not reach the planner.
The interesting part is what each type means once the planner unrolls patterns day by day across the horizon.
For an int limit, the test looks backward: skip the pattern if the worker's shift count has already reached the limit. You are reading history.
For a timedelta limit, the test looks forward: skip the pattern if the time already worked plus this pattern's length would exceed the limit. You are reading a future you have not committed to yet. A shift gets drawn only if it fits whole, with no partial shifts and no overshoot.
A missing limit draws everything.
The design choice that matters most is what a skip does. It does not end the walk. The planner keeps going along the horizon, and both tallies grow only when a shift is actually appended. So a long pattern that would cross the cap gets passed over, and a shorter pattern further along the horizon can still fit underneath that same cap. A worker is not retired by their first near-miss, they are just unavailable for the shifts that do not fit.
If a skip ended the walk, you would get a scheduler that quietly truncates people's availability based on pattern ordering instead of their actual capacity. Same data, worse plan, no error message.
One more decision worth stating plainly: the quantity summed is the pattern's declared wall-clock length, the same length every produced shift carries. Not the difference between two instants. That means a daylight-saving transition inside the horizon neither shortens nor stretches anyone's cap. An eight-hour shift counts as eight hours in March and in October. Contracts are written in declared hours, so we think the cap should be counted in declared hours too.
The broader lesson we keep relearning in systems that touch labour, money, or compliance: a limit is only half a specification. The other half is what happens when the limit is reached, stop, skip, or queue. Those three produce very different plans from identical inputs.
So when you model a per-person cap, it is worth being explicit about whether you are counting committed work or projected work, and about what made you pick.