What a shift actually decides

A shift in DailyBrew is a name and two times. It takes about fifteen seconds to create one, which makes it easy to assume it is just a label.

It isn't. That one small object is the only thing in the product that knows what was supposed to happen. Attendance records what did happen; the shift is the other half of every comparison. Almost every number that looks wrong on a dashboard traces back to a shift that says something different from what the owner meant.

So here is the whole model, in one place.

One shift, many people

A shift belongs to the workspace, not to a person. You create Morning once — 07:00 → 15:00 — and assign it to the six people who work it.

That is the part worth internalising, because it cuts both ways. Fixing the morning start time fixes it for all six at once, which is the point. It also means editing a shift to suit one person quietly moves the other five. When one person's hours genuinely differ, they need their own shift, not an edit to a shared one.

An employee has at most one shift, and may have none at all. Both are legitimate states, and the difference between them is bigger than it looks — see below.

Three questions, one answer

The shift gets consulted in three different rooms, and it is the same answer in all three.

Is this person expected today? This is what the dashboard's absent count is built on. Absent is not headcount minus present — it is expected − present − on leave, and the shift is what supplies "expected". Nobody is absent from a day they don't work covers that calculation in full.

Was this punch late, or early? The check-in is compared against the shift's start, the check-out against its end.

How is the day drawn? The monthly grid and the exports use the shift to decide whether an empty day is an absence worth painting red or an off-day that should stay neutral.

Because it is one source feeding three consumers, the three can never disagree with each other. They can all be wrong together, if the shift is wrong — which is why the shift is the first place to look, not the last.

Late means late

There is no rounding and no quiet tolerance. A 09:00 start means a check-in at 09:01 is flagged late, and one at 09:00 is not.

Left-early works the same way from the other end: a check-out before the shift's end time is flagged, and one at or after it is not.

We would rather the flag be boringly literal than have owners guessing what threshold produced it. If a minute of slack matters in your restaurant, that is a conversation to have with a person, and a flag is a good enough prompt for it — a flag that silently forgave the first few minutes would just move the argument to a number nobody could see.

Nobody is flagged without a shift

An employee with no shift is never late and never leaves early. There is nothing to compare a punch to, and DailyBrew does not invent a schedule. Their times are still recorded in full — every scan, every export — they simply carry no flags.

This is the right setting for anyone whose hours are genuinely their own. It is also the accidental setting that explains most "why is nothing ever flagged?" questions: the shift was never assigned.

There is a second, deliberate version of the same thing. Setting an employee's attendance tracking to none removes them from the absent baseline and from flagging, even if they do have a shift. That is for the admin helper or the family member who works when they work. They can still check in, and their times are still recorded; they simply cannot be absent and cannot be late.

The same times, every day — unless you say otherwise

By default a shift means those times, every day of the week. Set 09:00 → 17:00 and it applies Monday through Sunday.

On Espresso, a shift can carry per-day rules instead: Friday 18:00 → 02:00, Saturday 10:00 → 18:00, and so on. The rule that catches people out is worth stating plainly:

Once a shift has any per-day rule, it is a complete schedule. The days you did not write a rule for are off-days — not "fall back to the default times". Add a single Monday rule and you have just declared Tuesday through Sunday off.

That is intentional. A per-day schedule that silently filled its own gaps could not express a Monday-only cleaner, and half-specified schedules produce wrong numbers quietly rather than loudly. If someone works five days, write five rules.

An end before a start means the night

There is no overnight checkbox. A shift runs past midnight when its end time is earlier than its start time — 18:00 → 02:00 is a night shift, 09:00 → 17:00 is not.

The night is filed as one row under the day it started, and a post-midnight scan closes it rather than opening a new day. Shifts that run past midnight has the details, including what the +1 marker means and how far past the end time a late check-out is still accepted.

Editing a shift rewrites the past

Change a shift today and past dates are re-read under the new schedule. Last month's Saturdays can stop being absences retroactively; a start time you move by half an hour can change which check-ins were late.

This is deliberate, and it is how closures have always worked here — there is no stored snapshot of "what the schedule was in March". The upside is that fixing a schedule you set up wrong repairs your history instead of leaving a scar in it. The tradeoff is real: if you restructure a shift, numbers you have already looked at can move. The shift editor says so on the per-day panel rather than letting you discover it later.

What a shift is not

A shift is the plan. It is never a claim about what happened, and it never blocks a fact from being recorded.

Turning up on an off-day still counts. Someone covering a sick colleague on their day off gets a normal present row, on the grid and in every export. An off-day suppresses an expectation, not a punch.

A closure never traps an open shift. Closures and approved leave stop someone starting a shift — you cannot clock in on a day the restaurant is shut. They deliberately do not stop someone finishing one. If a closure gets declared, or leave gets approved, while somebody is already clocked in, their check-out still goes through. Refusing it would strand the row open with no way for them to fix it themselves.

Hours that don't match the shift are still recordable. Somebody clocked in at 14:00 and did not leave until 11:04 the next morning, on a 09:00 → 17:00 shift. The shift says that shouldn't happen; the day says it did. Because the check-out reads earlier than the check-in, the entry form shows a +1 and files it on the following day — the roster does not get to overrule the record.


The short version: a shift is the only place DailyBrew stores an expectation. Attendance is what happened, and every flag, every absent count and every neutral grey square on the grid is the gap between the two. When a number surprises you, check the expectation first.