Part 4 of 7 · Shift scheduler series ~5 min read

How a schedule reaches the team

The drafter built a week and the manager tapped Approve. Now the publish Lambda has to get each person their own shifts, on the right channel, at a reasonable time of day, with enough detail to act on. Get any of those wrong and the schedule is worse than a printed sheet on the wall: a 1am Slack ping, the whole team’s rota dumped on everyone, an invite to someone who left last month. And the schedule never reaches anyone at all until the manager approves it first. Four small guardrails sit between the approved draft and the schedule landing.

Key takeaways

  • Nothing publishes until the manager taps Approve on the full draft — Edit and Re-draft are the other two options.
  • Each person gets only their own shifts; the whole-team grid stays with the manager.
  • Quiet hours defer pings to the next reasonable hour; calendar invites go out per shift.
  • Slack DMs are the default; email is the fallback if no Slack ID is configured.
  • Every publish is logged so the next swap or re-draft knows the current state.

Four guardrails on every publish

Four guardrails between the approved draft and the published schedule A horizontal flow diagram. On the far left, an "Approved draft" box: the manager tapped Approve, so the publish piece has a frozen rota with each shift assigned to a named person. Four guardrail gates sit in a row to the right, each drawn as a vertical bar. Gate 1: Split per person — turns the whole-team grid into one private list per person, containing only that person's shifts for the week, with day, time, and role; the manager keeps the full grid, nobody else sees the whole rota. Gate 2: Resolve channel — looks up each person's Slack member ID from the staff sheet; if set, the schedule goes as a Slack DM; if blank, it falls back to the person's email address. Gate 3: Quiet hours — checks the local time against the rules-doc quiet-hours window (default 9pm to 7am); if outside reasonable hours, defers the dispatch to the next available minute via an EventBridge Scheduler one-off rule, so a schedule approved late at night lands first thing in the morning instead. Gate 4: Compose and invite — formats the message from the voice doc template, lists the person's shifts, attaches a calendar invite per shift, and adds a Request-swap button. After all four gates pass, each person's schedule ships via the Slack incoming webhook or via SES outbound for email. A note at the bottom: every publish is logged to DynamoDB so the next swap or re-draft starts from the right state. Approved draft manager tapped Approve; frozen rota Gate 1 Split per person one list each, own shifts only day, time, role manager keeps the full grid Gate 2 Resolve channel Slack ID set? DM the person if not, fall back to email Gate 3 Quiet hours local time in waking window? if not, defer to next morning Gate 4 Compose + invite voice template, list of shifts + calendar invite, swap button Publish — Slack DM (preferred) or SES outbound email (fallback) incoming webhook for Slack · SES SendRawEmail for inbox every publish logged to DDB ss-shifts — the next swap knows the state Every gate is a deterministic check — no model calls, no surprise ping at 1am on a Sunday.
Fig 4. Four guardrails between the approved draft and the published schedule. Split per person. Resolve the channel. Honor quiet hours. Compose with calendar invites. Then ship via Slack or email and log the publish so the next swap starts from the right state.

Approval comes first

Before any of the gates run, the full draft goes to the manager and nobody else. It arrives as a Slack message (or email) with the whole-team grid, the fairness summary from Part 3, any short-staffed flags, and three buttons: Approve, Edit, Re-draft. Approve freezes the rota and starts the publish. Edit opens the sheet so the manager can move a shift or fill a held one, then approve. Re-draft sends it back to the drafter — useful after the manager changes availability or a setting. Nothing reaches a single staff member until Approve is tapped. This is the heart of the system: the scheduler proposes, the manager decides.

Gate 1: split per person

The whole-team grid is the manager’s view, not the team’s. Gate 1 turns the approved rota into one private list per person, containing only that person’s shifts: the day, the start and end time, and the role. Nobody on the floor sees the full rota, which keeps the schedule from turning into a running comparison of who got what. The manager keeps the full grid; everyone else gets just their own week.

Gate 2: resolve the channel

For each person, the publish Lambda looks up their delivery preference from the staff sheet. If a Slack member ID is set, the schedule goes as a private Slack DM — it feels like a work message and carries action buttons. If no Slack ID is set, it falls back to the person’s email address. Email is the fallback so the new hire who isn’t on Slack yet still gets their shifts. Nobody falls through the cracks.

Gate 3: quiet hours

The draft usually runs Thursday afternoon, so an approval that follows right away is already in waking hours. But managers approve when they get a minute — sometimes that’s late at night. Gate 3 reads the rules doc’s quiet-hours setting (default 9pm to 7am, configurable per business). If the current local time is in the quiet window, the publish creates a one-off EventBridge Scheduler rule that fires at the next reasonable minute and exits without sending. The Scheduler re-invokes the publish Lambda with the same payload in the morning, where Gate 3 lets it through. A schedule approved at 11pm lands at 7am, not at 11pm.

Gate 4: compose, invite, then ship

The voice doc has the schedule message template: a short greeting, the person’s shifts for the week as a clean list, and a line about how to request a swap. The publish Lambda fills it in, attaches a calendar invite per shift (an .ics file or a Google Calendar event, depending on setup), and adds a “Request swap” button. For Slack, the message ships via the incoming webhook; the webhook URL lives in Secrets Manager. For email fallback, the same content is wrapped in a small HTML email, and the “Request swap” link hits a Function URL — the email equivalent of the Slack button.

Every publish — Slack or email — writes a row to ss-shifts in DynamoDB: who’s working what, this week, as published. When a swap comes in (Part 5) or the manager re-drafts, that table is the current truth the change is applied against.

Why the guardrails exist

None of these gates are exotic. They’re the kind of small care a thoughtful manager would take handing out the rota themselves — give each person just their own shifts, send it on a channel they actually check, don’t text at midnight, include the times and a way to flag a problem. Putting them in code as four small sequential gates, behind a manager approval that gates everything, makes the care part of the design rather than something you’re trusting a busy week to remember.

Next post: how a shift swap gets approved once the schedule is out — how a qualified replacement is found, how the swap is routed to the manager, and how the hours stay fair.

All posts