ADR 0052: Only an operator command starts a batch, and a scheduled start time is a plan rather than a trigger¶
Status: Accepted Date: 2026-08-11 Issue: #1462
Context¶
A Batch may carry spec.scheduledStartTime. The batch operator read it as a
second way to start a batch. processPendingCommand waited while the time was in
the future, and once it had passed it reported a start with no command annotation
anywhere:
reconcile 0: phase="Allocating" state="" msg="Allocating equipment units"
reconcile 1: phase="Running" state="Running" msg="Units allocated, starting batch execution"
That was the whole mechanism. Naming a time set a vessel running, and no person at the plant was involved.
Three things in the product said the opposite. The MES create response set
awaitingOperatorStart: true unconditionally, so the one work order where the
claim was false asserted it as loudly as the ones where it was true.
docs/mes-integration.md said a dispatched batch waits "for an operator to issue
Start from the DCS. This is deliberate: an external system can queue work, and
only a person at the plant can set a vessel running." And
#864 recorded
scheduling as deferred until customer demand. Only docs/architecture.md
described the auto-start, in two places, and it described it as intended.
The MES work-order endpoint copies the field straight from the request, and
examples/mes-integration/work-order.json — the file the integration guide tells
integrators to POST — carries one. The shipped example was the case that broke
the documented rule.
This is the honesty gap of #1451 pointed the other way. That endpoint accepted work it would never run. This one ran work it had said it would not run without a person, which is the more expensive direction for a control system.
The gateway is one door onto the field, not the only one. Batch is a CRD, so
anyone with cluster credentials can apply one, and the fenced scheduling
controller creates batches carrying the field as well. Closing the MES door alone
would have left the sentence in the integration guide true of that door and false
of the product.
Decision¶
A dcs.io/command: Start is the only thing that starts a batch. The
scheduled-start branch is gone from processPendingCommand, so no code path
starts a batch that was not commanded, whichever door created it.
spec.scheduledStartTime is the planned start. It is recorded on the batch,
reported on GET /work-orders/{id}, and exported as the B2MML
ProductionRequest StartTime — which is what that element means in ISA-95: a
requested time, not an instruction. It gates nothing.
Because it gates nothing in either direction, it does not defer an operator's Start either. Start before the planned time starts the batch, at the moment the operator asks. The operator at the plant is the authority on when the vessel runs, and a field an external system wrote does not overrule them.
awaitingOperatorStart: true on the MES create response becomes true by
construction rather than by hope, which is what
#1450 added
the field to state.
Alternatives Considered¶
Refuse a scheduledStartTime at the MES door. Rejected. It fixes the door
this was found at and leaves the mechanism live behind every other one, so the
integration guide's sentence would still have been false for a hand-applied
Batch. It also refuses a field an ISA-95 work order naturally carries, which
costs integrators a real capability — recording when the order is planned for —
to close a hole that is not in the field.
Drop the field when copying to the Batch. Rejected for the same reason plus one more: silently discarding a value the caller sent is how #1451 happened. The MES would have gone on sending a planned time and gone on getting 201, with nothing recording it.
Keep the behaviour and report it truthfully — awaitingOperatorStart: false
when the field is set, and a paragraph in the guide saying a work order may carry
its own start time. Rejected by the founder on 2026-08-11. It is a coherent
product, but it makes remote-triggered start a supported capability of a control
system, and that is a capability to add deliberately with the interlocks and the
authorization story worked out, not one to inherit from a field that was only
ever meant to describe a plan.
Keep the future half as a gate, refusing or deferring a Start issued before the planned time. Rejected. It is the same defect wearing the opposite sign: an external system would decide when the plant may not run, and the operator holding the Start button would be overruled by a scheduling record. It also silently swallowed an Abort — a batch with a future planned time returned before the abort branch was reached, so cancelling a queued batch did nothing until its planned time arrived.
Consequences¶
A batch whose planned start has passed sits in Pending until someone starts it.
That is a behaviour change for any deployment relying on the auto-start, and the
change is the point. Nothing in the shipped surfaces relied on it: the schedule
front-ends were removed in
#480 and the
schedule dispatch mode was refused in
#1451, so the
MES work-order path was the only live door onto it.
A batch the fenced scheduling controller creates at an entry's planned time now waits for an operator like any other. A schedule still decides when the work appears; it no longer decides when the vessel runs.
Aborting a queued batch works whatever its planned start says.
Time-triggered start is not a product capability today. A deployment that wants one gets it as a designed feature with its own authorization, not as a side effect of a metadata field — #864 is where that revival belongs.