ReferenceControlArcReferenceStages

Stages

Arc stage reference for stage declaration, entry semantics, parallel monitoring, and inline sequences

Stage declaration

stage name {
    flow_statement
    flow_statement
    flow_statement
}

Stage bodies contain flow statements. All flow statements in a stage run concurrently while the stage is active.

Stage body syntax

stage pressurize {
    // Conditional transitions (propagate while truthy)
    pressure > 500 => next
    abort_btn => abort

    // Continuous flows
    true -> press_vlv_cmd    // once on entry
    sensor -> controller{}   // every sample
}

Conditional flows whose target is a stage or sequence name are transitions: next, named-stage jumps, or cross-sequence jumps.

Stage entry semantics

When entering a stage:

  1. Local variables (:=) reset to their initial values; stateful variables ($=) keep their state
  2. Continuous flows (->) start fresh
  3. Timing nodes (time.wait, time.interval) reset their countdowns
  4. Inline sub-sequences restart from their first item
  5. Channel triggers only react to new writes. Values written before stage activation are ignored

Aside from stateful variables ($=), a stage holds no state between entries.

Resumable controllers

Stage entry semantics enable a common pattern: a controller that parks in a yield stage and waits for a fresh command to re-enter:

sequence controller {
    stage run {
        // ... control logic ...
        stop_cmd => stop
    }
    stage stop {
        false -> valve_cmd
        time.wait{duration=250ms} => yield
    }
    stage yield {
        // Only a new write to start_cmd will re-enter run.
        start_cmd => run
    }
}

start_cmd => controller

Because channel triggers only react to new writes after stage entry, a held-truthy start_cmd won’t immediately re-enter run. The operator has to issue a fresh start.

Parallel monitoring

All flow statements in a stage run concurrently:

stage pressurize {
    // ALL of these run simultaneously
    ox_pt_1 > 600 => abort   // monitor
    fuel_pt_1 > 400 => abort // monitor
    abort_btn => abort       // listen
    pressure > 500 => next   // advance

    sensor -> controller{target=500} // control loop
}

Line order and priority

When multiple conditional transitions (=>) are truthy in the same cycle, the first one listed wins:

stage pressurize {
    // Safety conditions first (higher priority)
    ox_pt_1 > 600 => abort   // 1st priority
    fuel_pt_1 > 400 => abort // 2nd priority
    abort_btn => abort       // 3rd priority

    // Success condition after safety
    pressure > 500 => next // only if no abort
}

Between a stage and a sequence declared inside it, the line written first wins only when both lines jump to a top-level sequence or stage. A transition inside the inner sequence, such as => next, does not compete with the stage’s jump: it acts first, then the jump acts in the same cycle, so the stage the inner sequence entered stops at once.

For continuous flows (->), if multiple flows write to the same channel, last write wins based on line order.

Inline sequences in a stage

An anonymous sequence { ... } inside a stage runs its items in order while the stage’s parallel flows continue. The inline sequence restarts from its first item every time the parent stage is entered.

stage fire {
    chamber_temp > 2000 => abort
    sequence {
        true -> igniter_cmd
        time.wait{200ms}
        true -> ox_main_vlv_cmd
    }
}

Restrictions

Table

Restriction Description
Acyclic continuous flows Continuous flows (->) within a stage must be acyclic
Stage transitions can cycle Conditional transitions (=>) can form cycles