Basic lifecycle
An FSM puts lifecycle topology in the model. It declares an initial state, allowed transitions, and a reachable final state.
YAML model
quent: alpha
model: finite_state_machine
fsms:
Job:
states:
queued:
initial: true
to: [loading_input, restoring_checkpoint]
loading_input:
to: [running]
restoring_checkpoint:
to: [running]
running:
to: [completed]
completed: {}
The parser rejects missing initial states, unreachable states, invalid targets, and FSMs without a reachable final state. It also derives event cardinality from the topology.
Instrumentation API
Entering a state emits its generated event. The generated Rust API represents the current FSM state in the handle’s type. Each transition consumes that handle and returns a handle for the target state. Only transitions allowed from the current state are available as methods, so an invalid transition does not compile. This pattern is called typestate.
The comments after each call show how the handle’s type changes after every transition.
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#[allow(unused)]
mod instrumentation {
include!(concat!(env!("OUT_DIR"), "/finite_state_machine.rs"));
}
use instrumentation::{Context, FiniteStateMachine, Job, Noop};
#[rustfmt::skip]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let context = Context::<FiniteStateMachine>::try_new(Noop)?;
let _job = context
.observer::<Job>()
.handle() // FsmHandle<Job>
.queued() // FsmHandle<Job, job_state::Queued>
.loading_input() // FsmHandle<Job, job_state::LoadingInput>
.running() // FsmHandle<Job, job_state::Running>
.completed(); // FsmHandle<Job, job_state::Completed>
Ok(())
}
The generated handle exposes only the transitions allowed from its current state.