Skip to main content

Operators

Any unfamiliar word is in the glossary.

An operator pool is people as a constraint. A machine may be free and the work still wait, because nobody is there to load it.

operators:
- { id: setters, count: 2 }

and on a step:

resource: mill
process_seconds: { kind: constant, value: 300 }
operator:
pool: setters
attendance: { load: 1.0, unload: 1.0 }
This is the mechanism that finds phantom capacity

A plant whose tools look under-utilised and whose work is still late is usually short of people, not machines. Modelling operators is how that becomes visible instead of arguable.

The pool​

keytyperequiredwhat it means
idstringyesThe pool's name, referenced by a step's operator.
breaksarray of breaknoBreaks, meals and training slots. ⚠️ A person on a break is unavailable, not busy. Modelling it as work would make a short-staffed pool look more utilised when it is in fact less available, which are opposite readings of the same fab.
countintegeryesHow many people. ⚠️ Zero is refused: a pool nobody can be taken from stops every step that needs it for ever, which reads as a fab that produces nothing rather than as a model error.
travel_secondsdistnoHow long somebody takes to reach the next job. See walking time below. ⚠️ A declared zero is refused: omit the key instead.
roster_profilestringnoA profile id whose factor is how many of them are on shift. ⚠️⚠️ A headcount, not a multiplier, and refused if it is not a whole number or exceeds count. ⭐ This is deliberately not a break: a break is staggered so a pool never goes to lunch together, and a roster wants precisely what that prevents — everybody off at once, phase-locked to a cycle. ⭐⭐ A person finishes their task before going off — and so does a rostered-off machine, which is why the distinction is roster versus downtime rather than people versus machines.

Walking time​

The tools in a fab are tens of metres apart, so somebody who has just finished a job is not available for the next one at the same instant. travel_seconds says how long the walk takes.

⚠️⚠️ It is charged to the person, not to the lot — which is the whole difference between this and a step's transport_seconds. Transport moves the lot and occupies nothing else; a walk occupies the person on their way to whatever comes next. So a tool's own time per lot does not change when you add a walk, while the pool gets scarcer, and lots then wait longer for a person. Measured on tests/corpus/job-board.json with a 20–40 s walk: the bench's WaitingForLabour goes from 0.82% to 1.78%, the pool's peak_queue from 2 to 4, and the bench's own utilisation does not move at all.

⚠️⚠️ A walk is charged per attended phase, not per lot. The attended part of a phase comes first — the operator starts the job and leaves — so a step that attends load, process and unload seizes and releases a person three separate times, and they cross the fab three times. Measured: a pool reads 21.25% where the per-lot reading predicts 13.75%. ⭐ A phase with no duration takes nobody and therefore costs no walk, however large its attendance fraction.

⭐ It applies to every job the pool does, maintenance and repair included, because a technician walks too.

Why it is a duration and not a resource​

A hold on labour has a fixed duration and acquires nothing else — that is the property which makes seizing a machine and a person at the same time safe without any lock ordering, and it is documented in the engine's own labour module. A walk keeps it: the length is drawn the moment the person is let go, so the hold is still a known duration, and walking acquires nothing.

⚠️ A walk that had to reserve something — a corridor, a vehicle, an airlock — would break that argument and reintroduce the deadlock the design avoids by construction. Contended transport for people is not expressible this way, deliberately.

Shifts and breaks​

breaks take the pool out of service on a repeating cycle.

keytyperequiredwhat it means
namestringyesA label for this break, so several can be told apart.
every_secondsnumberyesSeconds between the starts of successive occurrences — a fixed grid, the same convention as calendar maintenance.
duration_secondsdistyesHow long the break lasts.
first_at_secondsnumberyesWhen the last person of the pool has their first one. ⚠️ Occurrences are staggered evenly across the pool: two operators with a first_at_seconds of 360 minutes go at 180 and 360. This is the only anti-synchronisation mechanism there is, and without it a pool goes to lunch together and the fab stops twice a day — while still looking like a plausible model.

Which phases need a person​

attendance says when during a step the operator is held: load, process, unload. Holding somebody for the whole of a long process when they are only needed to load and unload it will invent a shortage that is not there.

⚠️ The key is pool (singular) and it takes either one pool name or a list; a list means any of these will do. The values are fractions in [0, 1], not flags: 1.0 holds a person for the whole phase, 0.5 for half of it.

keytyperequiredwhat it means
poolpoolsyesWhich pool, or which pools are acceptable.
attendanceattendanceno, default a full hold⚠️ An object, not a number — { "load": …, "process": …, "unload": … }. A bare number is refused: invalid type: floating point, expected struct AttendanceIr. Fields below.

attendance​

What fraction of each phase the person is held for. ⚠️ Fractions in [0, 1], not switches: "process": 0.5 ties the operator up for half the processing time, not for every other job.

load: 1.0
process: 0.0
unload: 1.0

That is the common case — the operator loads the machine and unloads it, and walks away while it runs. ⚠️ Omitting attendance holds the person for the whole operation, which is a far stronger constraint than most models intend.

keytyperequiredwhat it means
loadnumbernoFraction of the load phase, in [0, 1].
processnumbernoFraction of the process phase, in [0, 1].
unloadnumbernoFraction of the unload phase, in [0, 1].

Pools for transport​

transport_operator requires a person for the move between steps, not for the step itself.

keytyperequiredwhat it means
poolpoolsyesWhich pool, or which pools are acceptable.
attendancenumberno, default 1.0Fraction of the delay a person is needed for, in (0, 1]. The attended part comes first — the operator starts the move and lets it finish.

Types used above​

pool​

Which pool a step draws its person from. Either one name:

pool: technicians

or a list, meaning any of these will do:

pool:
- technicians
- supervisors
note

The key is pool (singular) in both cases, whether you give it one name or several.

break​

A recurring interruption when the whole pool stops — lunch, a shift handover.

name: lunch
every_seconds: 28800.0
duration_seconds: { kind: constant, value: 1800.0 }
first_at_seconds: 14400.0
fieldmeaning
namewhat to call it, so it can be told apart in the output
every_secondshow often it comes round
duration_secondshow long it lasts, as a distribution
first_at_secondswhen the first one happens
warning

first_at_seconds is required, and it matters more than it looks. If every pool takes its break at the same moment, the whole plant stops twice a day — which may be exactly right, or may be an artefact of everybody defaulting to zero. Stagger them deliberately.