Run a workflow
This page is how you start a workflow and follow the execution it creates. There are two paths. orca run is the simple one: fire-and-forget a single-group, standalone submission. Multi-group and batched submissions drop down to orca submission submit. Both shapes are explained on Submissions.
orca run
orca run <workflow> --run-mode <PURE_SIM|DEVICE_SIM|LIVE> [options]
A run mode is required on every submission. There is no deployment-level fallback and no default. Omitting --run-mode exits 2.
| Run mode | What it means |
|---|---|
PURE_SIM | Every device runs against its in-process sim driver. No wire calls, so no device bridge is involved. Fast. |
DEVICE_SIM | The live driver runs, so the wire is exercised, and the device bridge binds a sim backend on the far end. No hardware moves. |
LIVE | The live driver runs against the real hardware backend. |
The submitted mode is combined with each device's topology sim_override at dispatch time. See sim hierarchy. The --sim flag on orca topology mount is a different thing and picks no mode: see the --sim flag on a mount.
LIVE and DEVICE_SIM cannot be in flight at the same time. Both dispatch over the same wire to the same on-prem device connections, so a DEVICE_SIM run would switch a device to its sim backend while a LIVE run drives it for real. Submitting one while the other is alive is refused, and the refusal names the execution to wait on. This holds across separate executions and regardless of --batch-mode. Two concurrent LIVE runs are fine, two concurrent DEVICE_SIM runs are fine, and PURE_SIM never reaches the wire so it runs alongside anything.
Quick patterns
# Fire-and-forget
orca run smc_assay --run-mode PURE_SIM
# Block until terminal, give up after 2 minutes
orca run smc_assay --run-mode PURE_SIM --wait --timeout 120
# Pass workflow variables
orca run smc_assay --run-mode DEVICE_SIM --vars plate_count=2,reagent_batch=L7
# LIVE submission against devices that declare a sim_override
orca run smc_assay --run-mode LIVE --confirm
Flags
| Flag | Required? | Effect |
|---|---|---|
--run-mode | yes | PURE_SIM / DEVICE_SIM / LIVE. No default. |
--wait | no | Block until the execution is terminal (completed / failed / aborted). |
--timeout <s> | with --wait | Seconds before the CLI gives up. Default 300. The execution keeps running; the timeout only stops the CLI waiting. Exit 50. |
--poll <s> | with --wait | Polling interval. Default 0.5. |
--vars KEY=VAL,KEY2=VAL2 | no | Per-submission variable overrides. See typing below. |
--profile <path> | no | Path to a JSON deployment profile, loaded into the execution before any thread ticks. Local-only; with --backend cloud exits 2. |
--confirm | LIVE plus sim_override | Acknowledges the LIVE_SUBMISSION_WITH_SIM_OVERRIDES_UNACKNOWLEDGED gate. Without it the runtime refuses the submission. |
--vars values are typed best-effort, in this order: true or false (case-insensitive) becomes a bool, then an integer parse, then a float parse, and anything left over stays a string. So plate_count=2 is an int, dry_run=TRUE is a bool, and reagent_batch=L7 is a string. An entry with no = exits 2.
--profile is validated locally first: the CLI resolves the path to an absolute one and checks it is a file, then hands the daemon the path. The daemon binds 127.0.0.1 only, so it reads the same filesystem the CLI sees.
Output
$ orca run smc_assay --run-mode PURE_SIM
submitted execution 7a3f1c0e (smc_assay)
With --wait the CLI polls until terminal, then prints a key-value summary:
execution 7a3f1c0e
workflow: smc_assay
status: completed
error:
The --json shapes differ between the two paths. Without --wait you get the ExecutionRecordDTO the submission returned. With --wait you get the ExecutionDetailDTO from the final poll, the same shape orca execution detail returns.
--wait exits 1 when the execution reaches a terminal state that is not completed, so a failed run is a failed command.
--wait and an error-paused thread
An action error whose failure policy is PAUSE parks the labware thread and waits for an operator. The execution never reaches a terminal state on its own, so a naive poll would burn the whole timeout.
--wait detects this and fails fast with exit 31, naming the thread, the device call it was inside, the error, and the recovery command:
error: execution 7a3f1c0e has an error-paused thread 22b9d1a4 (sample_plate)
while running device call 'shake': device did not respond.
Recover with `orca execution thread recover <exec-id> <thread-id>
<retry|retry-op|continue|abort-action|abort-method|abort-thread>`.
See Recover from errors for what each recovery decision does, and Failure policies for what makes a thread pause in the first place.
Pitfalls
--wait --timeout 60is not a kill switch. It abandons the CLI's poll loop; the execution keeps running. To actually stop one, useexecution stop.--run-mode LIVEagainst any device with a topologysim_overrideis refused unless--confirmis passed. The refusal lists the overriding devices, so you can see which ones the LIVE submission would route to a simulator.--profileis local-only. Passing it with--backend cloudexits 2 with a redirect; the cloud REST surface does not accept profile paths.--varsis per-submission. It overrides defaults declared on the workflow's variables. To set a default for every future submission, usevar set-global. The resolution order is on Variables.
orca submission submit: multi-group and batched
run is single-group and STANDALONE. For anything richer, use submission submit. What a labware group and a batch mode do to labware sharing is on Submissions.
# Multi-group: N samples per submission, each in its own group
orca submission submit smc_assay --run-mode PURE_SIM \
--groups-file groups.json
# Batched: join an in-flight accepting execution of the same workflow
orca submission submit smc_assay --run-mode PURE_SIM \
--groups-file groups.json --batch-mode JOIN_EXISTING
The workflow name is a positional argument, not --workflow. The group spec is a plain path via --groups-file, or inline JSON via --groups-json; there is no @file syntax. The content is a single group object or a JSON array of them.
| Flag | Effect |
|---|---|
<workflow> (positional) | The registered @orca.workflow template name. |
--run-mode | Same as run. Required. |
--groups-file <path> | Multi-group spec file: one group object or a list of them. |
--groups-json <json> | The same spec inline as a JSON string. Mutually exclusive with --groups-file. |
--batch-mode STANDALONE|JOIN_EXISTING | Default STANDALONE. Any other value exits 2. |
--vars KEY=VAL,... | Submission-scope variable overrides. Same typing rules as run. |
--operator-id <id> | Optional operator id recorded on the submission. |
--profile <name> | Optional deployment-profile name recorded for audit. This is not run --profile, which takes a file path. |
--confirm | Acknowledges the LIVE plus sim_override gate, same as run. |
A group looks like this:
{
"id": "batch-1",
"members": [
{"thread_template_name": "sample_plate", "acquisition": {"kind": "pool"}},
{"thread_template_name": "tip_rack",
"acquisition": {"kind": "location", "source_location": "stack_1",
"verify_barcode": "ABC123"}}
]
}
Submitting with zero groups is the same one-shot path orca run takes.
Confirmation
submission submit prompts before it submits, unlike orca run:
Submit workflow 'smc_assay' with 2 group(s), batch_mode=STANDALONE? [y/N]:
Pass --force (--yes, -y) to skip it. Declining exits 40, and so does a non-TTY stdin with no --force.
What it prints on acceptance
submission 4c1e77a2 accepted (execution=7a3f1c0e, groups=2)
3 thing(s) nobody has settled. Nothing is blocked; settle what this run depends on:
tip_rack_1: nothing has ever said what 'tip_rack_1' holds, which is not the same as empty
settle with: set-tip-state
ml_star: what 'ml_star' is carrying was known, then nobody was watching
settle with: confirm-mounted-tips
assay_plate_2: an action that has not finished did things to what 'assay_plate_2'
holds, and the record only hears about them when the action ends; settle the
action (retry, continue, or abort) rather than confirming or restating this
The unsettled list prints here because this is the moment somebody is looking. Nothing in it blocks the submission. The same list is available any time from orca state unsettled. See State.
Each line names the subject and why the record cannot answer for it. The second line is the verb that settles it, and which verb it names is the useful part:
confirm-*means the record is still the best value there is, and you are agreeing with it after a look. A rack that merely went unwatched getsconfirm-tip-state, not a demand that you retype ninety-six positions the record already has right.set-*means there is nothing worth agreeing with. Three cases produce it: nobody ever said, an aborted action threw away work so the record is short by an amount nothing can state, or the record holds nothing the confirm verb could back.- No second line at all means no verb answers, as on
assay_plate_2above. An unfinished action is holding operations: confirming would freeze a number that is behind, and stating one is worse, because the action's own operations get folded on top when it ends. Settle the action instead, with retry, continue or abort. See Recover from errors.
In --json, each row also carries subject_id. Labware verbs take a labware id and two live instances can share a name, so act on the id rather than re-looking-up the name.
List and detail
orca submission list # all submissions on this runtime
orca submission list --execution <exec-id> # one execution's submissions
orca submission detail <submission-id> # one submission
list prints id, execution, workflow, group count, status, batch mode and submission time. detail adds the operator id and the deployment profile. Both id arguments accept a prefix of 4 or more characters.
Pitfalls
JOIN_EXISTINGwith nothing live starts a fresh execution. It is not an error. The join only happens when an execution of the same workflow is already accepting; otherwise the submission boots its own.JOIN_EXISTINGis refused against an execution that is draining or stopping, and the refusal tells you to resubmit asSTANDALONEor wait for the current one to terminate.JOIN_EXISTINGis refused against a paused execution. A pause gates new work, and queuing behind it would hide the submission. Resume first.- One execution carries one run mode. Joining an execution submitted under a different
--run-modeis refused rather than mixing dispatch routes inside one run. - Groups are independent within a submission. Each group gets its own thread spawns. Groups whose labware templates declare
SHARED_ACROSS_GROUPSconverge on shared physical labware, so read the template flags before assuming isolation. See Submissions.
Run one method in isolation
To exercise a single method without its surrounding workflow (testing, recovery rehearsal), use method execute:
orca method execute <workflow> <method> \
--labware-start '{"sample_plate": "stack_1"}' \
--labware-end '{"sample_plate": "stack_2"}' \
--run-mode PURE_SIM [--vars '{"plate_count": 2}'] [--confirm]
The workflow and method names are positional. --labware-start and --labware-end are required JSON objects mapping labware name to location name. --run-mode is required, with the same modes and the same --confirm gate as run. Note that method execute --vars takes a JSON object, not the KEY=VAL form the other two verbs take. Works against both backends.
Track an execution
Once submitted, these read-only verbs follow its progress. Mid-flight inspection goes further: where every plate is, what is in it, and who is holding which device.
orca execution list # all executions
orca execution detail <id> # full snapshot
orca execution threads <id> # thread roll-up
orca execution thread detail <id> <thread-id> # one thread, all fields
execution list takes no filters. It lists every execution known to the loaded system as id, workflow, status, paused and error. Narrow it yourself with --json | jq if the count is large.
execution detail <id> prints workflow, status, paused, error and the three thread counts (total, active, completed). Every id argument accepts a prefix of 4 or more characters, and execution ids also accept last.
execution threads <id> prints id, name, status, completed-method count and the current method for each thread in the execution.
execution thread detail <id> <thread-id> prints the per-thread snapshot: name, status, current location, current method, current action, completed methods, and the pause fields (pause_reason, last_error, pause_message, pause_site, honoured_decisions, paused_device_command).
Status vocabulary
Both list and detail report the same value, and it comes from one of two vocabularies depending on where the row was read from.
While the runtime owns the execution, the status is its phase:
| Phase | Meaning |
|---|---|
accepting | Live, and open to further JOIN_EXISTING submissions. |
draining | close was called. No new submissions; running threads finish. |
stopping | A confirmed stop is unwinding the execution. Non-terminal; it becomes aborted once the task fully unwinds. |
completed | Every thread terminal, no crash and no abort. |
failed | A thread crashed with an unhandled error. |
aborted | Terminal via a confirmed stop, or via unload. |
An execution rehydrated from a terminal database row reports the flatter task state instead: running, completed, failed or aborted.
Pause is a latch, not a status
A pause never changes the status. A paused execution stays accepting or draining, so the status column alone reads as if the stop never landed. That is what the separate paused column is for:
id workflow status paused error
7a3f1c0e smc_assay accepting paused (operator), abort armed
paused (operator) means an operator set the latch; paused (system) means the runtime paused itself. abort armed means a second confirmed stop would abort the execution.
Error-paused threads are separate again. execution detail prints a block per error-paused thread under the summary, carrying the last error, the pause message, the action it stopped in, the pause site, the recovery decisions that site will honour, the device call it was inside, and the ready-to-run execution thread recover command line.
Stopping and closing
orca execution close <id> moves an execution from accepting to draining: no new JOIN_EXISTING submissions, live threads run to completion. It prompts unless you pass --force. Standalone submissions for the same workflow still boot a fresh execution afterwards.
orca execution stop <id> is a two-call confirmed abort. The first call pauses the execution and arms the abort, and is undone by orca execution resume. A second call with --confirm performs the abort. It is documented in full on Operator interventions.
Pitfalls
listreturns every execution, terminal ones included, with no server-side filters. Pipe--jsonthroughjqto narrow by status.thread detailreturns only the per-thread snapshot. Variables and reservations live on the parent; useexecution detail <id>for those.
See also
- Submissions: groups, batching and what each shape shares.
- Mid-flight inspection: where every plate is once the run is going.
- Operator interventions: pause, resume, close and stop.
- Recover from errors: what to do with a thread paused on an error.
- Sim hierarchy: how a run mode and a device
sim_overridecombine.