Skip to main content

How to Build a Real Scheduling Solver in an Afternoon with AI

May 21, 2026
11 min read
Alex Radulovic

Stop losing margin to scheduling chaos. Use this step-by-step guide to build a custom AI-powered scheduling solver for your SMB using Claude Code and CP-SAT.

TLDR — Ask about this article
How to Build a Real Scheduling Solver in an Afternoon with AI

Scheduling problems eat margin at every SMB I work with. Multiple resources, skill matching, setup costs, dependencies, time windows — the math gets ugly fast, and the whiteboard your dispatcher uses today stops scaling around the point you wish it would.

This article is a wrench. Copy these prompts into Claude Code (or Cursor, or any agentic AI coding tool), drop in your scheduling spreadsheet, and start building a real solver in an afternoon. The answer in 2026 is straightforward: you point an AI coding agent at your real data and iterate.

Image 1

A few things to set up before you start.

Prep

Get your spreadsheet into clean CSVs. You need at minimum two tables: work items (one row per job/task/appointment) and resources (one row per person, machine, or room). The columns can be whatever you have today — the AI will help you normalize. Don't pre-clean. Don't restructure. Export what's in production.

A third optional table — current schedule — helps a lot. If you can dump what got assigned to whom over a recent week with start times, the AI can use it as a baseline for comparison and as a source of implicit constraints you forgot to mention.

Pick your tool. Claude Code (CLI) and Cursor (IDE) both work for this. Claude Code handles multi-file projects with subprocesses more cleanly, which matters once you have a JS layer talking to a Python solver. Cursor is better if you live in your editor and want tighter inline iteration. The prompts below work in either.

Set up a fresh working directory with your CSVs in it. Don't point this at your monorepo — give the agent its own playground.

Stage 1: Characterize the Problem

Paste this in. Attach your CSVs.

Code
I have a scheduling problem and want to build a working solver
iteratively. I'm attaching CSVs of my current data — please don't
write any solver code yet.

Your first job is to characterize the problem. Read the CSVs and
produce:

1. A normalized schema for work items and resources, with a note
   on each column about what you inferred and where you had to
   guess.

2. The constraints visible in the data: skills, dependencies,
   time windows, multi-resource coupling, setup/changeover,
   lag time, travel.

3. The objectives I appear to be optimizing for, ranked by how
   confident you are.

4. The problem size class: small (under 100 items), medium
   (100-500), or large.

5. A list of specific questions about the data before we move
   forward. Don't guess past the obvious — ask.

End with a clear recommendation on the starting tier: dispatch
rules, dispatch + CP-SAT with warm start, simulated annealing,
or decompose-and-recurse. Use this rule: dispatch for small/light-
constraint, dispatch+CP-SAT for medium, decomposition or SA for
large.

Be direct. If my data is missing something critical, say so.
If two constraints in the data appear to conflict, flag it.

What you should get back is a problem profile, a list of "I see these patterns but I'm not sure about these" questions, and a recommended starting tier. Answer the questions before moving on. Most of the time the AI catches data ambiguities you didn't know you had.

Image 2

Stage 2: Build the Dispatch Baseline

Once the profile is settled, this is the next prompt.

Code
Now build the dispatch-rule baseline. Requirements:

- JavaScript (Node.js). No Python in this layer.
- Read both CSVs into the normalized schema you defined.
- Implement a composite priority score for work items blending
  deadline urgency, business priority (if present), how
  constrained the item is (number of eligible resources), and
  expected duration. Weights configurable in a single object
  at the top of the file.
- For each item, assign to the best eligible resource using a
  composite resource score: earliest availability, skill match,
  estimated setup cost vs. the previous task on that resource,
  current load.
- Output the schedule as both a CSV (one row per assignment)
  and a console summary with: makespan, total tardiness,
  overtime hours, setup cost, and resource utilization.
- Include a --dry-run flag that prints the schedule without
  writing files.
- Console logging at each major step so I can see what the
  dispatcher is deciding and why.

Use the ATC rule (Apparent Tardiness Cost) as the default
urgency function. Keep the code under 400 lines, one file.
Only dependency: csv-parse.

The line cap matters. You want a baseline you can read in one sitting. If the AI starts adding abstractions — strategy classes, factory methods, adapter layers — push back. Tell it to inline everything and only break the file apart when you explicitly ask.

Image 3

Stage 3: Build the Validation Harness

This is the step most people skip and then regret.

Code
Before we improve the scheduler, build a validation harness
that lets me compare any two schedules quantitatively.

Requirements:

- A function evaluateSchedule(schedule, workItems, resources)
  that returns: makespan, weighted tardiness, total overtime,
  setup cost, utilization per resource, and a list of any
  constraint violations.
- A function compareSchedules(a, b) that prints a side-by-side
  diff of the metrics with percentage deltas.
- A loader that reads my current-schedule CSV (the third file
  I attached) and treats it as scheduleA.
- An entry-point script that runs the dispatch scheduler and
  immediately compares it to my current schedule.
- Highlight any constraint violations in the current schedule
  too. If my dispatcher is violating dependencies or skills
  today, I want to see that.

Output should be readable in a terminal. Color the deltas
with simple ANSI escapes (red for worse, green for better,
gray for unchanged). No chalk dependency.

Now you have a way to know whether each iteration actually helps. Run dispatch against current. If dispatch already beats current, you have an easy win and can ship the baseline. If it doesn't, the metrics tell you which factor — tardiness, setup cost, utilization — needs attention first.

Stage 4: Decide Whether You Need CP-SAT

Run the dispatch baseline against a week of real work. Then ask:

Code
Given these results — [paste the comparison output] — should
we move to CP-SAT, or are we leaving meaningful margin on the
dispatch baseline?

Consider:
- How close are the metric deltas to what dispatch can
  realistically improve via weight tuning vs. what would
  require a global optimizer?
- Are there constraint patterns in the data that dispatch is
  fundamentally bad at: heavy setup costs with grouping
  conflicts, multi-resource coupling, long dependency chains?
- What solve-time budget would CP-SAT need to be useful here?

Recommend one of: tune the dispatch weights and ship; add
local search on top of dispatch; move to CP-SAT with dispatch
warm start; decompose first. Argue your recommendation. Don't
hedge.

The "don't hedge" instruction matters. Frontier models will give you a wishy-washy four-options-with-tradeoffs answer if you let them. You want a call.

Image 4

Stage 5: Add CP-SAT When You Need It

When the call is CP-SAT, this is the prompt.

Code
Build the CP-SAT layer using Google OR-Tools Python.
Requirements:

- New file solver.py at the project root. Keep the JS
  dispatch baseline. Don't touch it.
- Read the same normalized CSVs the JS dispatcher uses. The
  schema is already agreed.
- Model with optional interval variables, no-overlap on each
  resource, precedence constraints for dependencies, skill
  constraints via eligible-resource filtering, setup costs
  via circuit constraints where applicable.
- Objective: weighted sum of tardiness, overtime, and setup
  cost. Weights match the dispatch composite.
- Accept the dispatch output as a hint via --warm-start
  path/to/dispatch.csv. Use AddHint() to seed the solver.
- Time budget configurable, default 10 seconds. Report the
  gap when the solver stops.
- Output the optimized schedule in the same CSV format the
  dispatcher uses so the validation harness can compare them
  directly.
- If the model is infeasible, use the assumptions API to
  identify which constraints conflict and print them.

Wrap the Python solver in a thin Node child_process call so
the JS layer can invoke it. Don't try to do everything in
one language.

Now the architecture is real. Two layers — dispatch in JS for interactive response and fallback, CP-SAT in Python for optimization — with a validation harness that compares any pair of schedules. That's the layered system, scaffolded.

Image 5

Stage 6: Iterate

The rest is iteration prompts. A few that come up:

Code
The solver is producing schedules that look optimal on the
metrics but my dispatcher says they're wrong. Help me find
what she knows that's not in my model. Run a session where
you ask me questions about specific assignments the solver
made vs. what she would have made. Goal: extract the implicit
constraint or objective into the model.
Code
Solve times are creeping up to 30+ seconds on real-size
problems. Profile the model: which constraints are taking
the most search time? Suggest decomposition strategies that
keep optimization quality but stay under 5 seconds.
Code
Build a minimal web UI that shows the schedule as a Gantt
chart, lets me drag an assignment to a different resource,
and re-runs the solver on the modified schedule using warm
starting. Keep the UI under 600 lines of React. SVG and
Tailwind only — no charting libraries.

Things That Will Go Wrong

A few patterns to watch for.

The AI will eventually suggest a genetic algorithm. Refuse. GA is rarely the right answer for SMB scheduling — it's slower than simulated annealing at single-objective optimization, harder to tune, and produces schedules that are difficult to debug. Its narrow useful niche is Pareto-front exploration, which you probably don't need.

The AI will eventually over-abstract. You'll get a class hierarchy with strategies and factories and adapters. Tell it to inline. The baseline you can read in a sitting is more valuable than the baseline you can extend, because you're not going to extend it — you're going to replace it with the CP-SAT layer.

The AI will suggest LLM-based scheduling at some point. There's a real research direction here, but it's not production-ready for SMB constraints as of this writing. Stick to the classical tiers.

The validation harness will reveal that your current schedule violates constraints you thought were hard. This is normal. Decide whether the violation is a real bug (the dispatcher was wrong) or a soft constraint masquerading as hard (she knows something you didn't encode). Either way, fix the model first, then re-run the comparison.

Adapting This to Your Stack

The cookbook above assumes a default stack — JS dispatcher, Python CP-SAT, CSV in and CSV out. That's a deliberate choice optimized for clarity, not because it's the right answer for every environment.

The article itself is a complete brief. If your stack is different, paste this entire piece into your AI coding agent and ask it to adapt the stages to your setup. A few variations worth asking for:

  • Language stack. "We're TypeScript everywhere. Rewrite the JS stages in TypeScript with strict types and Zod-validated schemas." Or: "We're Python end-to-end. Skip the JS dispatcher and build both layers in Python with FastAPI as the service boundary."
  • Existing infrastructure. "Adapt Stages 2 and 3 to read from our Postgres database instead of CSVs and write results back as records. The schema is [paste DDL]."
  • Data shape. "My actual CSV columns are [paste header row]. Rewrite Stage 1 to map directly to my schema and flag any data I should be capturing but currently am not."
  • Industry constraints. "We're a job-shop machining operation with heavy setup costs and multi-machine routings. Add the constraint patterns specific to that domain at each stage." Or: "We're a field-service company — replace setup-cost constraints with travel-time constraints and add a routing optimization step."
  • Solver choice. "We have a Gurobi license. Replace the CP-SAT stage with a MIP model using gurobipy, but keep the dispatch warm-start pattern."
  • Skip ahead. "We already shipped a dispatch baseline. Start at Stage 4 and help me decide whether we need CP-SAT given these metrics: [paste comparison output]."

You can also just ask the AI what comes next. After Stage 6, paste the whole article plus the state of your current code and ask for the next move — production hardening, exception handling, the dispatcher UI, the integration with your ERP. The architectural pattern — cheap interactive layer, expensive optimization layer, validation harness as the bridge — is what matters. The specific languages and file formats are just how this article happened to express it. Once the AI has the cookbook in context, it can map that pattern to whatever your environment actually is.

Image 6

Keywords

AI scheduling solverClaude Code tutorialSMB operations optimizationCP-SAT solverCursor IDE codingresource schedulingautomated dispatching

Related Articles

0:00/0:00