Special step types for parallel execution, operator interaction, and shared hardware.

Corvus sequences support step types beyond standard plugin calls: background steps for parallel execution, user prompt steps for operator gates, pool groups for shared test stations, and resource locks for exclusive hardware access.

Background stepsUser promptPool groupsResource locks

Background steps

A background step runs a plugin action in a parallel thread while the main test sequence continues. Use them for environmental monitors, pre-charge tasks, data loggers, or any hardware control that should not block the main DUT test loop.

Lifecycle: spawn → stop / join

Every background task follows a three-step lifecycle, each controlled by a separate step in the sequence:

ModeWhat it does
spawnStarts the plugin action in a background thread and immediately continues.
stopSends an abort signal and waits up to 5 s for the task to finish cleanly.
joinWaits indefinitely for the task to finish on its own — no abort signal.

A sequence must have exactly one spawn per handle ID. Any background task still running when the sequence ends is aborted automatically.

JSON format

Spawn:

{
  "id":   "start_monitor",
  "name": "Start Power Monitor",
  "plugin":  "power_monitor",
  "action":  "record_continuous",
  "inputs":  { "channel": 1, "sample_rate_hz": 100 },
  "timeout_ms": 0,
  "background": {
    "mode": "spawn",
    "id":   "power_monitor_handle"
  }
}

timeout_ms: 0 means the background task runs until stopped or the sequence ends.

Stop:

{
  "id":   "stop_monitor",
  "name": "Stop Power Monitor",
  "plugin": "", "action": "", "inputs": {},
  "timeout_ms": 5000,
  "background": {
    "mode": "stop",
    "id":   "power_monitor_handle"
  }
}

Join (wait for natural completion):

{
  "id":   "wait_for_charge",
  "name": "Wait for Pre-Charge Complete",
  "plugin": "", "action": "", "inputs": {},
  "timeout_ms": 30000,
  "background": {
    "mode": "join",
    "id":   "precharge_handle",
    "propagate_failure": true
  }
}

Config fields

FieldRequiredDescription
modeYes"spawn", "stop", or "join"
idYesHandle ID — unique name for the task
propagate_failureNo (default false)join only: fail the step if the background task failed

Pass/fail accounting

ModeCounts toward sequence result?
spawnYes — passes if the worker starts successfully
stopNo — a stopped task is discarded from pass/fail totals
joinYes if propagate_failure: true; otherwise no

Sequence Editor

Open any step and scroll to Background Execution:

  1. Check Enable background execution.
  2. Select the Mode (Spawn, Stop, or Join).
  3. For spawn: the Handle ID is auto-populated as {step_id}_handle and is editable.
  4. For stop / join: a dropdown lists all declared spawn handles. If none exist yet, a free-text field is shown.
  5. For join: an optional Propagate failure checkbox appears.

Constraints

  • A background task cannot be spawned more than once per sequence run.
  • Background steps do not support retry — the task is stopped after the first attempt.
  • stop and join steps do not execute a plugin; plugin, action, and inputs are ignored.

User prompt steps

A user_prompt step pauses the sequence and displays a full-screen modal to the operator. The engine waits for the operator to press a button before continuing. No plugin is involved.

When to use

  • Visual inspections: "Verify all LEDs are green before continuing."
  • Manual assembly confirmations: "Confirm DUT is seated correctly."
  • Process gates: "Scan OK/NG after jig inspection."
  • Any step where a human decision is required mid-sequence.

JSON format

{
  "id": "visual_check",
  "name": "LED Visual Inspection",
  "timeout_ms": 60000,
  "on_pass": {},
  "on_fail": { "jump_to": "teardown" },
  "prompt": {
    "title": "Check LED Bar",
    "body": "All 8 LEDs must be lit green. Press PASS to continue or FAIL to abort.",
    "media": {
      "type": "image",
      "path": "led_bar_ok.png"
    },
    "buttons": [
      { "id": "pass", "label": "PASS", "color": "green", "key": "F1", "action": "pass" },
      { "id": "fail", "label": "FAIL", "color": "red",   "key": "F2", "action": "fail" }
    ],
    "button_layout": "right_first"
  }
}

Field reference

FieldRequiredDescription
titleYesLarge heading text shown to the operator
bodyNoInstruction paragraph below the title
mediaNoImage, GIF, or video shown above the text
buttonsYes1 to 4 buttons
button_layoutNoright_first (default) or left_first

Button actions

actionStep resultJump behavior
"pass"Step passesUses button jump_to if set; otherwise step-level on_pass.jump_to
"fail"Step failsUses button jump_to if set; otherwise step-level on_fail.jump_to
"abort"Job aborted immediatelyNo jump — job stops

Per-button jump_to allows fine-grained routing — for example a "Retry Step" button that fails the step but jumps back to a specific earlier step rather than the generic on_fail target.

Media

Place media files under config/assets/. Reference them by filename (or subdirectory path within assets/). Supported extensions: .png, .jpg, .jpeg, .gif, .mp4, .webm.

Keep video files short — 15 s or less recommended. Files over 50 MB produce a warning at sequence load time but are not rejected.

Keyboard shortcuts

Buttons with a key field can be triggered by keyboard. Values: "F1" through "F12", "Enter", single letters. Matched case-insensitively.

Validation rules (enforced at sequence load)

  • title must not be empty.
  • 1 to 4 buttons; all button IDs must be unique within the prompt.
  • At least one button must have action: "pass" — abort-only prompts are rejected.
  • Button jump_to targets must reference an existing step ID.
  • timeout_ms must be > 0.

Pool groups

Pool groups allow multiple parallel jobs to share a set of hardware stations where each station can only serve one job at a time. Every job must complete all steps in the group for its own DUT, but jobs are free to pick whichever station is currently unoccupied.

When to use

  • N functional test stations shared between M fixture slots, where M > N.
  • A single calibrator port that handles one DUT at a time across multiple parallel fixtures.
  • Any group of steps where hardware count is less than active job count and every job must run every step.

If every job has its own dedicated hardware, pool groups are unnecessary — use resource locks instead.

JSON format

Add "pool_group": "<name>" to each step that belongs to the pool:

{
  "id":   "station_rf",
  "name": "RF Calibration",
  "plugin": "rf_calibrator",
  "action": "calibrate",
  "inputs": { "band": "2.4GHz" },
  "timeout_ms": 15000,
  "validation": { "type": "numeric", "key": "power_dbm", "operator": ">=", "threshold": -10 },
  "pool_group": "calibration"
}

All steps sharing the same pool_group name form one pool. Steps must be contiguous in the sequence definition.

How it works

When a job reaches the first step of a pool group, it enters the pool loop. It scans pool steps in definition order and acquires the first unoccupied slot. If all slots are occupied, it waits until one is released, then retries. Each job completes all steps in the group before moving on.

A job running alone never blocks — all slots are free.

Example: 4 jobs, 3 stations

JIG1: [init] → [station_rf    ] → [station_audio ] → [station_optical] → [final_check]
JIG2: [init] → [station_audio ] → [station_optical] → [station_rf     ] → [final_check]
JIG3: [init] → [station_optical] → [station_rf    ] → [station_audio  ] → [final_check]
JIG4: [init] →  wait…            → [station_audio ] → [station_optical] → [final_check]

Validation rules

RuleError
Fewer than 2 steps in a groupPool group requires at least 2 steps
Steps are not contiguousAll steps in a pool group must be adjacent
Background step declares pool_groupBackground steps cannot participate in a pool group

Sequence Editor

Steps in a pool group show a purple label Pool: <group-name> above the first step and a purple left border on each step. A warning panel appears above the step list if a pool group consistency issue is detected — it updates in real time.

Resource locks

Resource locks let parallel jobs safely share hardware instruments. Declaring a lock on a step tells the engine: acquire exclusive access to this resource before executing; hold it until the step completes.

When to use

  • A single bench DMM shared between two fixture slots.
  • A power supply channel used by multiple jobs at different points in their sequences.
  • Any hardware resource that cannot safely handle concurrent access.

Lock modes

ModeBehavior
step (default)Acquire before the step, release automatically when it exits.
createAcquire and hold across subsequent steps until explicitly released.
releaseDrop session-held locks before this step runs.

JSON format

Step mode (default):

{
  "id":              "measure_dc_voltage",
  "locks":           ["dmm_bench"],
  "lock_timeout_ms": 8000
}

Multiple resources at once: "locks": ["dmm_bench", "psu_ch1"]

Create / release (hold across steps):

{ "id": "init_dmm",    "locks": ["dmm_bench"], "lock_mode": "create",  "lock_timeout_ms": 5000 }
// ... intermediate steps need no lock declaration ...
{ "id": "release_dmm", "locks": ["dmm_bench"], "lock_mode": "release" }

Field reference

FieldDefaultDescription
locks[]Resource names to acquire or release
lock_timeout_ms5000Maximum wait time in ms. Ignored in release mode.
lock_mode"step""step", "create", or "release"

Deadlock prevention

Corvus acquires all locks in alphabetical order regardless of the declaration order in the sequence. This prevents deadlocks when multiple jobs try to acquire the same set of resources simultaneously — both jobs will always attempt locks in the same order.

Behavior on timeout

If a lock cannot be acquired within lock_timeout_ms, the step produces an error outcome. The sequence's normal error handling applies. All session locks are cleared automatically at the end of every test cycle — no permit can leak across cycles.

Validation rules

RuleError
Lock name is blankLock name must not be empty
lock_timeout_ms == 0 in non-release modeMust be greater than 0
release step with no lock namesMust declare at least one lock name
create on an already-held nameDouble-create — add a release step first
release of a name never createdOrphan release — check for typos
create with no matching releaseUnclosed create — add a release step

Sequence Editor

Each step has a Resource Locks section. Check Manage resource locks to enable it, select a lock mode, type a name and press Enter. Autocomplete suggests names already used elsewhere in the sequence. An amber warning panel appears whenever a lock consistency issue is detected.

Step UIDs

Every step in a sequence carries a UID — a UUID4 generated at authoring time. The UID is the canonical internal reference for jump targets and background handle cross-references.

Why UIDs

The human-readable id field is convenient for authoring but fragile as a routing key:

  • Renaming a step id silently breaks all on_pass.jump_to / on_fail.jump_to references pointing at the old name.
  • Two steps can share the same id string by accident.

UIDs are stable and never change regardless of how the step's label evolves.

Jump resolution

The engine tries jump targets against the UID index first, then falls back to the id index. Existing sequences using "jump_to": "teardown" continue to work unchanged. New sequences authored in the editor store the UID in jump_to and are immune to renames.

Legacy migration

When the Sequence Editor opens a file without UID fields, it automatically assigns a fresh UUID4 to every step that lacks one and marks the sequence dirty. No manual migration is required — just save.