in active developmentmy own product
skyopshub.inSkyOpsHub
Crew and fleet scheduling for Indian airlines.
An airline gives it routes, aircraft and crew. A planning agent picks who and what to use, OR-Tools builds the schedule, and a deterministic rulebook checks every duty against rule packs based on DGCA’s flight-duty limits. Nothing gets swapped quietly: when something is rejected, the schedule says which rule and why.
An agent picks the aircraft and crew
For the routes a planner selects, the planning agent chooses which aircraft and which crew to use. First it sizes the job, grounded in fleet, crew, route and utilisation context found by embedding search. Then it makes one structured-output call per aircraft or crew type, a few at a time, each against a JSON schema. If any call fails, the stage fails and names which ones. There’s no silent fallback.
If it picks fewer people than peak demand needs, it’s asked to reconsider once. If it holds its position, the gap ships as a warning instead of being patched over.
scope A320 captains: 11 chosen, coverage floor 12 RETURNED scope Agent holds at 11 · shortfall logged WARN
Blockers before building
Before anything is solved, a readiness report separates hard blockers from warnings. It checks crew capacity per category, route–aircraft compatibility (sampled on big scopes, and it says so), crew qualifications and duty pressure.
readiness 0 blockers · 6 of 14 routes sampled readiness 1 licence expires inside the window WARN
Route windows become rotations
Each route’s time windows are chained into aircraft rotations and crew pairings, with rest and continuity checked as the chains are built. An aircraft can’t be flown past its next maintenance check: any window that would take it there is ruled out for that aircraft.
chains VT-SKB held out · A-check due 03 Oct 14:20 EXCLUDED chains 42 route windows → aircraft rotations
OR-Tools does the arithmetic
The assignment itself is a constraint model solved with Google OR-Tools (CP-SAT). The language model is good at judgement calls like which crew to include, and the solver is good at fitting hundreds of flights together. Each does the part it’s good at.
solve OR-Tools · 42 / 42 route windows covered OK
The rulebook decides
Every duty is checked in plain code against rule packs based on DGCA’s flight-duty limits (CAR Section 7). For pilots and cabin crew that means minimum rest, flight duty periods, landings, night duties and rolling flight and duty caps from 7 to 365 days. Engineers and ground crew get their own configurable fatigue limits. Licences and medicals are checked against each duty date.
Hard limits block the schedule. Weekly rest raises a warning, as the backend does today. It’s a planning tool, not regulator-approved software.
validate Rest ≥ 12 h before every pilot duty PASS validate Licences valid on each duty date PASS
A job you can watch
Every run is a background job on Celery and Redis. It’s saved before it’s dispatched, reports live progress, and can be cancelled (which revokes the worker task) or retried. The finished schedule is stored with its warnings, so nobody has to rediscover them.
persist Schedule saved · 196 flights · 2 warnings DONE
The log lines are from the illustrative run on skyopshub.in, not a customer’s data.
What I built
- A FastAPI backend with JWT auth and tenant isolation through PostgreSQL row-level security.
- Agentic scope selection: structured outputs per type, embedding retrieval with a lexical fallback, and retries and truncation checks on provider calls.
- Scheduling on OR-Tools, followed by a deterministic DGCA duty-rule validator.
- Spreadsheet imports behind a quality gate. Ambiguous values, like “B737” (737 MAX 8 or 737-800?), are held for a person to decide instead of guessed, and an agent explains why each one is ambiguous.
- The Flutter operations app and the marketing site.
Stack
- Python
- FastAPI
- PostgreSQL
- Celery
- Redis
- OR-Tools
- OpenRouter
- Flutter
“The planner proposes. The rulebook decides.”