| Runtime | Purpose | State |
|---|---|---|
| Module of the Pub Cloudflare Worker with D1 storage | Collect availability, generate assignments, record review state, and send schedule email | Active; the former n8n implementation is retired |
The Pub Scheduler handles the weekly staffing cycle: opening shifts, collecting tender availability, generating fair assignments, and emailing the schedule. It runs inside the Cloudflare Worker (worker.js) with data in D1—no external automation platform is involved.
Three n8n workflows handled this work in the past. They moved into the Worker in May and June 2026 and are no longer used. This page describes the current system.
Admin UI: https://pub.ihnyc-rc.org/admin/pub-schedule (Cloudflare Access required)
Weekly state flow
The scheduler follows a 4-step weekly cycle, visible as a progress bar in the admin UI:
flowchart LR SHIFTS["1. Create shifts"] --> FORM["2. Collect availability"] FORM --> ASSIGN["3. Generate and review assignments"] ASSIGN --> SEND["4. Approve and send"]
| Step | What happens | Triggered by |
|---|---|---|
| 1. Shifts | Operator creates or seeds shifts for the week (Wed/Thu/Fri/Sat). Shift templates provide one-click seeding. | Manual (admin UI) |
| 2. Form & Availability | Operator opens the Formbricks availability form. Tenders receive an email with their personalized link. Submissions stream in live. | Manual (admin opens form) |
| 3. Assignments | Operator clicks “Generate assignments”. The algorithm matches tenders to shifts based on availability, fairness history, and staff-needed counts. Operator reviews and can hold/release individual assignments. | Manual (admin generates) |
| 4. Send | Operator approves and sends the mass schedule email to all active tenders. The email includes shift times, assigned tenders, and ICS calendar links. | Manual (admin approves + sends) |
The weekly anchor is “week of” — the Sunday that starts the week, computed in America/New_York time.
Data model
All scheduler data lives in CONTENT_DB (Cloudflare D1).
| Table | Purpose |
|---|---|
pub_tenders | Tender roster: name, email, active status, bio, payment links, profile photo |
pub_shifts | Weekly shift slots: date/time range, staff needed, status (Draft/Approved/Sent) |
pub_shift_templates | Reusable presets (day + time + staff count) for one-click week seeding |
pub_assignments | Which tender works which shift. Supports hold/release gating |
pub_submissions | Formbricks availability responses, one row per response_id |
pub_schedule_runs | Weekly run state: formOpenedAt, generatedAt, sentAt, status |
pub_supervisors | Supervisor contacts shown in email templates |
pub_specials | Drink/food specials shown on the right bar TV |
pub_admin_audit | Audit log of every admin mutation |
pub_email_log | Record of every email sent |
Admin interface
This Week (/admin/pub-schedule)
The main operator view. A 4-step wizard showing the current week’s progress:
- Shifts panel — shows the week’s shifts with status badges. “Apply template” seeds from saved presets.
- Form & Availability panel — live feed of who has submitted, who is outstanding, and who said “not available this week”. Includes a “Remind outstanding” button to re-email non-responders.
- Assignments panel — after generating, shows the proposed schedule. Each assignment can be held (excluded from the email) or released. An “Edit assignments” button opens a detailed view.
- Send panel — preview the email, send to all active tenders, or send a full update if the schedule changes after the initial send. There is no separate “correction” recipient subset.
- Fairness brief — preview or send the operator-only fairness report to supervisors who opted in.
An Expert toggle shows all four panels at once.
Pub Setup (/admin/pub-setup)
Card grid for less-frequent operations:
Scheduling configuration and access defaults
- Tenders — add/edit/archive tenders, upload photos, set payment links
- Shifts — manage shift templates, edit individual shifts
- Email Templates — edit the HTML/text templates for schedule, availability, and update emails
- Supervisors — manage supervisor contacts shown in email footers
- Email Log — audit trail of every email the scheduler sent
- Stats — fill rate, opportunity-adjusted fairness, history, and per-tender assignment counts
- Raw Database — Tabulator grid for direct CRUD on all entities
Assignment algorithm
worker/lib/pub-scheduler/assign.js
- Takes the latest finished submission per tender (deduped by token, latest timestamp wins; finished beats unfinished)
- Tenders who said “not available” contribute zero slot keys — excluded from assignment
- Groups candidates by shift slot key
- Fisher-Yates shuffles candidates per slot
- Selects up to
staff_neededper shift - Fairness: opportunity-adjusted fill rate (assignments / weeks available) breaks ties so under-scheduled tenders get priority
Emails
Three email types, all with customizable HTML/text templates:
| Type | When | What |
|---|---|---|
| Availability | Step 2 (form opens) | Personalized Formbricks link per tender |
| Schedule | Step 4 (send) | Full weekly schedule with shift times, assigned tenders, ICS calendar links |
| Update | After initial send | Full current schedule plus the added/removed-assignment diff; sent to all active recipients with the normal subject prefixed by [UPDATE] |
Templates are editable at /admin/pub-setup > Email Templates. Each type has four styles (Branded, Classic, Clean, Minimal). The branded style uses the AVI + Affective Technologies joint logo with a navy/teal-green palette.
Emails are sent via the worker’s configured email provider. Every send is logged in pub_email_log with recipient, subject, timestamp, and send result.
The fairness brief is separate from the tender schedule. It summarizes assignment opportunity and outcomes for opted-in supervisors; it is not sent to tenders.
Shift templates
Reusable presets stored in pub_shift_templates. Each template specifies:
- Day of week (0=Sun through 6=Sat)
- Start and end time (HH:MM, supports overnight spans)
- Staff needed count
- Display label
“Apply to week” stamps a template into pub_shifts for a specific week, computing the correct date with America/New_York DST handling.
Managed via the Shifts page under Pub Setup, or the Templates tab in the Raw Database view.
Availability polling
The worker’s scheduled() cron polls Formbricks for new responses on a cadence (configurable, typically during the Fri-Sun availability window). Each response is normalized and upserted into pub_submissions keyed by response_id.
If a tender submits multiple times, all rows are stored. The checklist and assignment algorithm deduplicate at read time: latest finished submission per tender wins.
Specials
Drink and food specials managed at /admin/pub-specials. Each special has a title, price label, description, optional image, and an active toggle. Active specials appear in the right bar TV’s rotation on their linked shift days.
Weekly auto-remind
A cron job (scheduled()) sends a single reminder email per week to the operator listing outstanding tenders who haven’t submitted their availability form. The reminder fires once per pub_schedule_runs row (tracked by reminded_at).
Migration history
Migrations are in migrations/content/ and applied manually via wrangler d1 execute CONTENT_DB --remote --file=<path>. Key migrations:
| Migration | What |
|---|---|
| 0005 | pub_submissions table |
| 0006 | available + slots_offered columns on submissions |
| 0008 | pub_tenders + pub_shifts tables |
| 0009 | pub_admin_audit table |
| 0010 | FK indexes on pub_assignments |
| 0011 | pub_shift_templates table |
| 0012 | Sent-assignments snapshot for update-email diff |
| 0013 | Shift status rename (Created→Draft, Archived→Sent) |
| 0014 | Assignment hold/release columns |
| 0015 | Supervisor contacts table |
| 0016 | Email log table |
| 0017 | Pub specials table |
| 0018 | Schedule run reminded_at column |
| 0020 | Specials shift_ids column |
| 0021 | Tutorial versions table |
| 0052 | Email-log attachments |
| 0053 | Per-supervisor fairness-report delivery |
Related
- ihnyc-avi-pub-landing — the parent worker that hosts the scheduler
- admin — admin UI reference
- ops — deployment and secrets
