Project 02 · featured

Shift Solver — ShiftStudio

The CP-SAT engine that outgrew the hackathon: a FastAPI scheduling service for the ZARA women's section where I work — in active daily use, cutting the day's role-chart planning from about two hours to minutes (operational, self-reported).

§ 01 Problem

Every morning someone has to decide, hour by hour, who stands where on the women's section floor: fitting room, welcome, sprinter, four zones. The chart has to respect Orquest shift times, half-hour breaks, blocking HR/training tasks, a five-level competency matrix, a buddy rule for people in their first month, and the store's physical topology of which roles can be merged. Done by hand this took the coordinator around two hours a day (self-reported) — and the rules lived in one person's head.

§ 02 Approach

  • The core model is the same shape as PUSULA's: binary variables x[p,r,t] with hard constraints (exact coverage from a discrete n→capacity table, one role per person per hour, availability including half-hour breaks and blocking HR/TR/ISG tasks, zero-competency ban) and a soft cost blending competency, rotation fatigue, fairness, and a development bonus (solver/cp_sat_scheduler.py, 1,847 lines).
  • The decisive engineering fix was sequential per-hour solving: the monolithic 12-hour model stalled at FEASIBLE with ~30 people on Railway's slow CPUs. solve_sequential() solves each hour as its own tiny model — provably OPTIMAL in milliseconds — while cross-hour rules (rotation limits, cumulative fairness, closing-hours stability) are injected from the already-solved history. Deterministic, ~1–2 s per day.
  • Floor reality is encoded as first-class constraints, each traceable to a dated decision in the code comments: five area teams with soft home-area pull, a hard buddy rule for 0–1-month staff with partner priority COACH > FT > CX, a dual-role topology of which cells one person may legally cover, and half-hour 'Name 1/2' notation with explicit bridge-handover warnings instead of silently faking coverage.
  • It ships as a small FastAPI service: POST /solve, a deterministic /solve/area-based v2 path, /health for Railway, CORS locked to the zaratraining.online frontend; output also exports to Excel in the store's existing paper-chart format, so adoption cost nothing.
  • Validation is built in rather than bolted on: a rule-based self-evaluator scores every chart 0–100 against the domain rules, and an offline backtest module compared solver output against 10 real historical charts — 63.8% personnel overlap, 78.8/100 average self-score.

§ 03 Outcome

This is the one project on this page that runs in production for real users — me and my section. Planning the women's section's roughly 10-hour day dropped from about two hours of manual work to minutes, and the system is still in use (operational, self-reported). Inside the repo, the regression baseline on the real June 2026 roster shows the quality score climbing from 83.6 to 89.2 and the objective cost falling from 2.50M to 527k across development phases.

2 h → min

daily planning time, women's section (operational, self-reported)

100

charts produced in production (Shift Organizer report, Jul 2026)

93.7

average quality score across those 100 charts (report screen)

63.8%

personnel overlap vs 10 real historical charts (parsers/backtest.py)

§ 04 For the record

§ 05 Evidence

evidenceShift Organizer report screen — 100 charts, avg 93.7, top 99.2, 98% optimal
evidencesolver/cp_sat_scheduler.py — 1,847-line CP-SAT model; solve_sequential redesign
evidenceapi/main.py — /solve, /solve/area-based; CORS for zaratraining.online
evidenceparsers/backtest.py + README — 63.8% personnel overlap, 10 real charts
evidencedocs/agents/BASELINE.md — quality 83.6 → 89.2, cost 2.50M → 527k
evidence.data/competency_2026_06_18.json — 39-person real competency snapshot
evidenceoutput/excel_writer.py — Excel export in the store's chart format
Shift Organizer report — 41 staff, 100 charts, 93.7 average score
fig. 1Shift Organizer report — 41 staff, 100 charts, 93.7 average score
Chart archive — dated production runs with per-chart scores
fig. 2Chart archive — dated production runs with per-chart scores