Skip to content

Ad-Hoc Execution

Operators can launch a procedural element (Phase, Operation, UnitProcedure) against an idle Unit without creating a Batch. This is the right path for equipment-oriented work (CIP/SIP cycles, calibration, sampling, equipment commissioning) where a full batch record would not apply.

Scope

Ad-hoc execution is supported at three levels:

Level Use case Permission Production-mode signature
Phase Single equipment action (open valve, take sample, run pump) operate not required
Operation Coordinated phase sequence (e.g. CIP cycle: pre-rinse + caustic + acid + final rinse) operate-lead not required
UnitProcedure Multi-operation equipment campaign (e.g. full SIP-CIP-leak-test sequence) operate-lead required

Procedure-level ad-hoc execution is not supported. A Procedure spans multiple units and is, by ISA-88 definition, what a Batch executes. Running a Procedure outside a Batch produces a "batch without a batch record", which breaks 21 CFR Part 11. Use dcs create batch for that.

Eligibility: the category field

OperationTemplate and UnitProcedureTemplate carry a spec.category field:

  • equipment — eligible for ad-hoc execution
  • process — recipe-bound, reachable only through a Batch (the default)

The default is process, so legacy templates and any newly authored templates are locked out of ad-hoc execution until an engineer explicitly opts in. Phase templates are not classified. Every PhaseTemplate is eligible for ad-hoc execution.

Launching ad-hoc execution

/hmiBatch in the sidebar (the Batch Execution view) → + New. The dropdown menu offers four entries: Batch, Phase, Operation, Unit Procedure. The Operation and Unit Procedure forms only list templates with category: equipment. The Phase form lists every PhaseTemplate.

HMI Batch Execution: the + New menu showing Batch, Phase, Operation, Unit Procedure entries

Pick a Unit. The picker shows every unit with its live state, and the gateway rejects a launch on anything but Idle (409). Pick a template, override any declared parameters, then Execute. Override rows are generated from the template's parameter interface, and there is no way to add one it doesn't declare. Ad-hoc executions appear in the same list as Batches, tagged with the matching Type chip. Filter by Phases, Operations or Unit Procedures to narrow the view.

Ad-hoc Phase launch form: Unit and Phase Template pickers, timeout field, and the template's four declared parameters rendered as override rows above Cancel and Execute

A CIP rinse launched ad-hoc on the idle fermenter: the four-entry menu (no Procedure, by design), pickers, overrides with the duration validator, and the run landing beside the batches with its Phase chip.

The Operation and Unit Procedure forms are the same shape: an equipment-only template picker, and (for Unit Procedure) the Reason field that production mode requires for the 21 CFR Part 11 signature.

Ad-hoc Operation launch form: equipment-only template picker with the process-category template absent, declared parameters as override rows

Ad-hoc Unit Procedure launch form: equipment-only template picker, parameter overrides, and the Reason field required in production for 21 CFR Part 11

# Ad-hoc Phase (operator role)
dcs execute phase \
  --unit reactor-1 \
  --template purge-with-N2 \
  --param duration_seconds=120 \
  -s houston-plant

# Ad-hoc Operation (lead-operator role; OperationTemplate must be category=equipment)
dcs execute operation \
  --unit reactor-1 \
  --template cip-cycle \
  --param caustic_temp_c=80 \
  -s houston-plant

# Ad-hoc UnitProcedure (lead-operator + production e-signature)
dcs execute unitprocedure \
  --unit reactor-1 \
  --template full-cleaning-cycle \
  --reason "weekly CIP per SOP-CL-014" \
  -s houston-plant
# Ad-hoc Phase
curl -X POST /api/v1/sites/houston-plant/units/reactor-1/execute-phase \
  -H "Content-Type: application/json" \
  -d '{"phaseTemplateRef":"purge-with-N2","parameterValues":[{"name":"duration_seconds","value":"120"}]}'

# Ad-hoc Operation
curl -X POST /api/v1/sites/houston-plant/units/reactor-1/execute-operation \
  -H "Content-Type: application/json" \
  -d '{"operationTemplateRef":"cip-cycle"}'

# Ad-hoc UnitProcedure (production mode requires reason + fresh OIDC token)
curl -X POST /api/v1/sites/houston-plant/units/reactor-1/execute-unitprocedure \
  -H "Content-Type: application/json" \
  -d '{"unitProcedureTemplateRef":"full-cleaning-cycle","reason":"weekly CIP per SOP-CL-014"}'

What gets created

The gateway instantiates the procedural tree as parentless resources (no OwnerReferences to a Batch). Children carry the parent's name in the usual OperationRef / UnitProcedureRef spec fields, plus these labels:

Label Purpose
dcs.io/adhoc=true Marks this as ad-hoc; powers the ?adhoc=true list filter
dcs.io/unit=<unit> The target Unit
dcs.io/phase-template / dcs.io/operation-template / dcs.io/unitprocedure-template Source template name

Reconcilers do not require a Batch owner. The same SFC chart-walking and unit state-mirroring run, just without a Batch context.

Commanding a running ad-hoc execution

A batch is commanded through its Batch resource, which forwards the command down the procedural tree it created (ISA-88 Clause 7.3.2). An ad-hoc run has no Batch, so its root is commanded by name: the Phase, Operation or UnitProcedure that the launch created at the top.

This matters most when a run stops on its own. Any chart error (a guard that cannot be evaluated, a step budget overrun, a failed step action) self-holds the element, and a held run needs an operator decision to leave that state:

Command Effect
Restart Resume the chart from where it held (see the caveat below)
Stop Run the stopping chart to a controlled Stopped
Abort Go to safe state immediately, ending at Aborted

Only the root accepts commands. The children of an ad-hoc Operation or UnitProcedure are refused with 400 Bad Request naming the root to command instead. Commanding a child would diverge it from the parent that is sequencing it, which is the same failure the Batch rule exists to prevent. Deleting a held run is not a recovery: it destroys the execution record and reaches no ISA-88 terminal state.

A restarting chart re-commands one step's posture

Restart replays the element's restartingChart, which is a single program for a phase that may have several steps. If the chart drives the equipment posture of one step, restarting into a different step can leave the run unable to satisfy its own transition condition. Where that applies, Stop or Abort and relaunch.

/hmiBatch in the sidebar → the run's row in the unified execution list. The Commands cell carries the commands valid for the current ISA-88 state, the same way a batch row does. Opening the run shows the same set in a Commands section on its detail.

HMI Batch Execution filtered to Phases: an ad-hoc post-CIP inspection running on the seed tank, its row carrying the Stop / Hold / Abort / Pause command bar a batch row would

Rows for the children of an ad-hoc Operation or Unit Procedure appear in the same list and carry an empty command cell by design. Command the root.

Irreversible commands (Stop, Abort) go through the reason ceremony that 21 CFR Part 11 §11.10(e) requires, exactly as they do for a batch.

# Restart a self-held ad-hoc phase
dcs command Phase adhoc-purge-with-n2-1754870400 Restart -s houston-plant

# Abort it instead (irreversible commands require --reason)
dcs command Phase adhoc-purge-with-n2-1754870400 Abort \
  --reason "N2 header pressure lost; ending the purge" \
  -s houston-plant

# Same for an ad-hoc Operation or UnitProcedure root
dcs command Operation adhoc-op-cip-cycle-1754870400 Stop \
  --reason "line needed for production" -s houston-plant

dcs get phases lists the live phases with their states. An ad-hoc root is the one with an empty OPERATION column. That column is the parent ref, so anything with a value there is a child and must be commanded through its owner.

curl -X POST /api/v1/sites/houston-plant/command \
  -H "Content-Type: application/json" \
  -d '{"kind":"Phase","name":"adhoc-purge-with-n2-1754870400","command":"Restart"}'

Valid kinds are Batch, Phase, Operation and UnitProcedure, the last three only when the named resource is an ad-hoc root. Unit and Procedure are never accepted. A name that does not exist returns 404 Not Found. The WebSocket command frame applies the identical rule (see SCADA Integration).

Audit trail

Every ad-hoc execution emits an AuditRecord with a category label so the audit-trail UI can filter:

  • dcs.io/audit-category=adhoc-phase
  • dcs.io/audit-category=adhoc-operation
  • dcs.io/audit-category=adhoc-unitprocedure

UnitProcedure ad-hoc in production mode carries an electronicSignature block with meaning: ad-hoc-execution, the signer's identity, the digest of the canonical UP spec, and the keystore key ID, meeting 21 CFR Part 11 §11.200.

The ceremony runs below, in production mode with real authentication, where the signed-in session is the signing credential:

The compliance anchor filmed for real: the required Reason, Execute, and the audit record's Electronic Signature card with meaning: ad-hoc-execution, signed by the authenticated supervisor.

Parameters are override-only

You pick an existing template and override its declared parameters at launch. The CLI/API will silently drop entries whose names are not in the template's Parameters interface. The template defines the schema. Ad-hoc execution is not a vehicle for new parameters. To add a new parameter, edit the template under change control.

Cleanup on partial failure

UnitProcedure instantiation creates child Operations and grandchild Phases. If any child Create fails partway through (missing PhaseTemplate, CM-role mismatch, etc.) the gateway deletes already-created children before returning the error. A failed launch therefore does not leak orphan resources.

Why Procedure-level is excluded

A full Procedure is, by definition, the executable form of a recipe. Running one ad-hoc has the same operational footprint as a batch but produces no Batch resource and therefore no BatchRecord. That is the wrong default for a 21 CFR Part 11 system. The regulator expects every product-affecting run to be tied to a batch record. If you find yourself wanting a one-off Procedure, the right answer is a one-off recipe approved through change control and run as a Batch.