Overview
A manager describes coverage in plain English. Hengam turns it into a checked weekly schedule — and when no valid schedule exists, it says which shift is uncovered and why instead of quietly producing something that only looks right.
Who it’s for. Managers of small cafés, restaurants and retail teams who build the weekly rota by hand, and the employees who need to see their own shifts.
My role. Solo — product, engineering, deployment.
The interesting parts
- The language model does exactly one job — free text to a structured constraint object. The schedule itself comes from deterministic code.
- Seeded generation (mulberry32): twenty variants scored by a validator; the same inputs always produce the same week.
- Per-user AI spend caps, a Firestore-backed rate limiter, server-side token checks on every endpoint, and a CSP verified against the real build.
The problem
Small-team scheduling is a constraint problem solved in spreadsheets. A manager juggles availability, time off, roles, target hours, minimum coverage per daypart and the rule that nobody closes at midnight and opens at 6 am. Changing one shift cascades, and a mistake surfaces when someone doesn't turn up. The hard part isn't producing a schedule — it's knowing when no valid schedule exists and saying which constraint is binding.
Architecture
Manager's plain-English rules
│
▼
Vercel function /api/generate-schedule
verify Firebase ID token · check monthly USD cap + daily call cap
│ Anthropic API: text → structured constraints (only job)
▼
runScheduler(employees, settings, weekStart, constraints) ← deterministic
× 20 seeded variants → validateSchedule() → keep fewest violations
│
▼
{ days: { shifts, emptySlots }, summary, issues, recommendations }
│
Cloud Firestore (per-workspace docs, rules next to the data)The key decision: the model doesn't write the schedule
Parsing a manager's sentence is a language problem, and the only place where a wrong answer is cheap and correctable. Everything after that is ordinary code that can be tested, reproduced and explained.
The scheduler walks the week and rejects a candidate for a slot if they're already assigned that day, unavailable or on time off, in the wrong role, overlap the slot by less than four hours, would break the minimum rest gap between a close and an open, or would pass their target hours.
Generate, then check
One pass can fill slots in an order that paints itself into a corner, so twenty seeded variants are generated and scored, keeping the one with the fewest violations. If every variant leaves coverage unfilled, that is a real answer: the week is over-constrained, and the uncovered slots are reported per day rather than dropped.
{
weekStart,
days: { monday: { date, shifts, emptySlots }, ... },
summary: [{ employee, role, scheduledHours, targetHours, difference }],
issues: [], recommendations: [],
}Security work nobody sees
- Content-Security-Policy built from what the app actually loads, plus HSTS, frame, referrer, permissions and opener policies — checked against the real build in a browser before shipping.
- Firestore rules enforce document ownership, keep collections non-listable where the ID is itself a secret, lock billing fields away from their owner, and only allow joining a workspace through a real invite.
- Every endpoint verifies a Firebase ID token server-side; admin routes check an allowlist.
- AI spend caps are checked before the request: one call can cost 25× another.
- Rate limiting lives in Firestore, not memory, because serverless instances recycle. It fails open, so a broken limiter can't lock a user out.
Tradeoffs
Firestore fits the access pattern — per-workspace document reads with real-time updates, and authorization that lives next to the data. Its rules can't count documents, so quota limits are enforced in application code, and the rules file says so where it applies.
Built & next
Built and working
- Plain-English coverage rules parsed into structured constraints
- Deterministic, reproducible schedule generation with explained gaps
- Availability, time off, roles and target hours per employee
- Team invites with a read-only employee view
- CSV, PNG and PDF export (lazy-loaded)
- English and Persian with RTL layout; light and dark themes
- 42 unit tests on the scheduler, shift-time maths across midnight, plans and export
Not built yet
- Hengam Teams — an Expo / React Native companion for employees (shift marketplace, swaps, time off) sharing the same Firebase project. In active development; not released.
- Screenshots of the explained-gap state in the repository documentation.
