Operation Rules Guide
Operation rules define how Flow executes power control, firmware, ingestion, and bring-up operations. Each rule specifies a sequence of steps that determine component ordering, parallelism, verification, and retry behavior.
Table of Contents
Concepts
Rules and Operations
Each rule is bound to a single operation (e.g., power_on, power_off).
When an operation is triggered, Flow resolves the applicable rule for the target
rack and executes it.
Steps and Stages
A rule contains steps. Each step targets one component type and belongs to a numbered stage. Execution proceeds stage by stage in ascending order. Within a stage, all steps run in parallel.
Action Sequences
Each step defines three optional action lists:
pre_operation— runs before the main operation (e.g., Sleep to settle)main_operation— the primary work (PowerControl, FirmwareControl, or a verification action when used as the entire step’s purpose)post_operation— runs after the main operation (e.g., verify status)
All three phases execute inside a single child workflow per component. The
step-level timeout applies to the entire sequence (pre + main + post).
Rule Schema
Top-level fields
Rules are submitted as JSON via the --rule-file flag or embedded in a YAML
batch file.
Step fields
Retry policy fields
Duration format
All duration fields accept Go duration strings: "5s", "30s", "2m",
"1m30s", "10m", "1h".
Actions Reference
PowerControl
Executes a power operation (on/off/restart) for the component.
When used from the power workflow, the operation is inherited from the task
context — no parameters required. When used cross-workflow (e.g., firmware
power recycle, bring-up), specify operation explicitly:
FirmwareControl
Starts a firmware update and polls for completion (async start + poll pattern).
Calls FirmwareControl to initiate, then repeatedly calls
GetFirmwareStatus until all components complete or the poll timeout
expires.
BringUpControl
Opens the power-on gate for target components. No parameters required.
WaitBringUp
Polls GetBringUpStatus until all components reach the MachineCreated state
(ingestion complete). Used as a post-operation after BringUpControl.
VerifyPowerStatus
Polls the component until its power status matches expected_status. Typically
used in post_operation to confirm the result of PowerControl.
When used as main_operation, the step performs only verification (no power
command is sent). This is the pattern for forceful operation final-verification
stages.
VerifyReachability
Polls until all components of the specified types in the rack become reachable over the network. Used after powering on a powershelf to confirm downstream components have booted, or before bring-up to wait for all PMCs.
By default, a component type is considered reachable when the GetPowerStatus
API call succeeds. With require_all: true, every individual component within
the type must respond (i.e., the returned status map must contain all target
component IDs).
Sleep
Pauses execution for a fixed duration. Implemented as a durable workflow timer (survives worker restarts). Useful for hardware settle time.
GetPowerStatus
Queries the current power status of components and returns a status map.
InjectExpectation
Registers expected component configurations with their respective component manager services. Used as the initial ingestion stage in full bring-up rules, and as the sole action in ingestion-only rules.
The IngestRack gRPC API is a convenience wrapper that triggers a BringUp
workflow with an ingestion-only rule (all component types run
InjectExpectation in parallel).
No parameters or timeout overrides required — the action uses the step-level
timeout and the component Target to call the InjectExpectation activity.
Examples
Graceful power on
Powers components in dependency order (powershelf → nvswitch → compute) and verifies status at each stage before proceeding.
Graceful power off
Reverse dependency order (compute → nvswitch → powershelf). A Sleep in the
powershelf pre_operation allows downstream components to finish shutting down
before cutting power.
Forceful power on
Skips per-stage verification for maximum speed. All power commands are issued first; a dedicated final stage (4) verifies all component types simultaneously.
Ingestion only
Registers components with their respective component manager services without performing power or firmware
operations. All component types are ingested in parallel in a single stage.
The IngestRack gRPC API triggers this rule automatically.
Full bring-up (with ingestion)
A complete bring-up sequence that first ingests components, then powers on
powershelves, and finally brings up compute nodes. The BringUpRack gRPC API
uses this as its default rule.
Temporal Execution Model
This section describes what actually happens inside the Temporal executor when a rule is applied to a task.
1. Rule resolution
Before any workflow starts, the task manager resolves the applicable rule for the operation and rack using this priority order:
The resolved RuleDefinition is embedded in the ExecutionInfo passed to the
parent workflow. The workflow never queries the database.
2. Parent workflow — sequential stages
PowerControl (or FirmwareControl) is the parent Temporal workflow. It does
pure orchestration:
If any stage fails, the workflow stops and the task is marked failed. Stages do not roll back.
The parent workflow has no retry policy of its own (MaxAttempts = 1). Retries
are configured at the child workflow (step) level.
The parent workflow’s execution timeout is auto-calculated from the sum of each stage’s maximum step timeout, plus a 10 % overhead buffer. You do not set it directly.
3. Stage execution — parallel child workflows
Within a stage, each step spawns one GenericComponentStepWorkflow child
workflow. All child workflows in the stage are launched simultaneously and the
parent waits for all of them to finish before advancing.
Steps whose component type is not present in the rack are silently skipped.
The child workflow’s WorkflowExecutionTimeout is set to the step’s timeout
field (defaulting to 30 minutes if unset). This timeout covers the entire
pre + main + post sequence.
4. Child workflow — action sequence
GenericComponentStepWorkflow runs the three action phases in order:
The step’s retry policy applies to the entire child workflow. If any
action fails and retries are exhausted, the child workflow fails, which fails
the stage, which fails the parent workflow.
Activity options (timeout, retry) for individual Temporal activities are derived
from the step configuration via buildActivityOptions. If the step has no
retry block, a default of 3 attempts with 1 s initial interval and 2× backoff
is used.
5. Action dispatch
Each action name is looked up in a registry that maps it to an executor function:
Sleep is a workflow timer, not an activity. It survives worker restarts and
does not consume an activity slot.
6. Polling actions
VerifyPowerStatus and VerifyReachability are implemented as workflow-level
polling loops — not as single activities. Each iteration calls
GetPowerStatus (a Temporal activity) and then workflow.Sleep for
poll_interval before trying again. The loop exits when the condition is
satisfied or timeout is exceeded.
Because the sleep inside the loop is a durable workflow timer, the worker can restart mid-poll without losing progress.
VerifyReachability uses the same pattern but checks multiple component types,
remembering which types have already become reachable across iterations.
7. Cross-component verification
VerifyReachability needs to check component types other than the one the
current step targets (e.g., a powershelf step checking whether compute and
nvswitch are reachable). The parent workflow passes the full
map[ComponentType]Target to every child workflow, which in turn passes it to
every action executor. VerifyReachability uses this map to probe the correct
targets regardless of the step’s own component type.
Execution flow diagram
CLI Usage
Create a single rule
Load rules from a YAML batch file
Manage rules
YAML batch file format
Reference YAML
The file examples/operation-rules-example.yaml is the canonical loadable reference
covering all built-in rule patterns. Load it with: