A paint shop
Two models, identical except for one dispatch rule. The difference between them is sixty-three times the cycle time, and no capacity was added to get it.
Any unfamiliar word is in the glossary.
The plant
One paint booth. Cars arrive in two colours, red and black, at roughly one of each per hour. Painting a car takes 21 minutes. Changing the booth from one colour to the other takes 45 minutes — more than twice as long as the job itself.
That last sentence is the whole problem, and it is extremely common: a changeover that costs more than the work.
How the real world maps to the model
setup on a step is what ties a car to a colourA step names "setup": "red". The booth's changeovers say what it costs to go from one
named setup to another. Both halves are needed: a changeover table with no step naming a
setup costs nothing, and it took this repository a long time to notice that the first time.
How the model interacts
The only thing that differs is the rule the booth uses to pick the next car.
The two rules
First come, first served — paint whatever waited longest:
ranks:
- weights:
- - age
- 1.0
Same colours together, oldest first among equals:
ranks:
- weights:
- - setup_cost
- -1.0
- weights:
- - age
- 1.0
A rank is a score. setup_cost is the changeover this candidate would need on this
booth right now, so a negative weight means "prefer the car that needs no changeover".
The second rank only decides ties left by the first.
The model
Everything below is shared; swap entity_rule for the second rule to get the other run.
# yaml-language-server: $schema=https://clockwright.dev/schema/model.json
name: "paint shop, first come first served"
horizon_seconds: 2592000.0
warmup_seconds: 259200.0
resources:
- id: booth
servers: 1
changeovers:
- { from: red, to: black, seconds: 2700.0 }
- { from: black, to: red, seconds: 2700.0 }
entity_rule:
ranks:
- weights:
- - age
- 1.0
products:
- id: red_car
pieces: 1
steps:
- resource: booth
setup: red
process_seconds: { kind: constant, value: 1260 }
- id: black_car
pieces: 1
steps:
- resource: booth
setup: black
process_seconds: { kind: constant, value: 1260 }
releases:
- product: red_car
interarrival_seconds: { kind: exponential, mean: 3600.0 }
- product: black_car
interarrival_seconds: { kind: exponential, mean: 3600.0 }
What comes out
{
"released": 1424,
"completed": 986,
"cycle_seconds_mean": 493204.6,
"resources": {
"booth": {
"utilisation_pct": 100.0,
"mean_queue": 269.69,
"mean_wait_seconds": 491332.18,
"served": 986,
"state_pct": { "Processing": 48.0, "Setup": 52.0 }
}
}
}
{
"released": 1424,
"completed": 1423,
"cycle_seconds_mean": 7851.9,
"resources": {
"booth": {
"utilisation_pct": 95.92,
"mean_queue": 3.35,
"mean_wait_seconds": 6106.86,
"served": 1423,
"state_pct": { "Idle": 4.1, "Processing": 69.2, "Setup": 26.8 }
}
}
}
released against completed firstThe first model let 1,424 cars in and got 986 out. That is not a rounding difference — 438 cars are still in the queue when the run ends, and they are still arriving faster than the booth can clear them.
This is the single most important check in any results document, and it comes before every timing number. A plant that does not finish what it starts is not slow, it is failing, and its cycle time is not a measurement of anything stable — the longer you run it, the worse the number gets. The grouped model finishes 1,423 of 1,424, which is what a working plant looks like.
Each field is explained in Reading the results. The one to notice here is
state_pct, which is where the time went: the first booth spends 52% of its life in Setup
— occupied, unavailable, and producing nothing.
What it says
| cycle time | mean queue | changing colour | painting | |
|---|---|---|---|---|
| first come, first served | 137.00 h | 269.69 | 52.0% | 48.0% |
| same colours together | 2.18 h | 3.35 | 26.8% | 69.2% |
Same booth, same cars, same arrival rate, same 21-minute paint time. Under first come, first served the booth spends more than half its life changing colour, the queue runs away, and the shop is failing. Under colour grouping it paints 69% of the time and the queue is three cars.
Why first come, first served collapses
Alternate red, black, red, black and every single car costs a 45-minute changeover on top of 21 minutes of paint. That is 66 minutes of booth time per car against an arrival every 30 minutes. The booth cannot keep up, so the queue grows without bound and the only reason the number is 137 hours rather than infinity is that the run ends.
Grouping amortises one changeover over several cars. Nothing else changed.
Both rules are defensible on a whiteboard, and "serve the customer who waited longest" is the more intuitive of the two. One of them is a plant that works. Arithmetic on averages cannot separate them, because the averages are identical.