feat(yaml): finite-state-machine entities via an fsms: block - #406
Conversation
Lower a top-level `fsms:` block, keyed by entity name, into an FSM entity: each state becomes one of the entity's events, its cardinality derived from the topology, and `FsmEntityBuilder` validates the topology at build time. The FSM constraint is builder-only; a hand-written one is rejected. Demonstrate it, and the AnyEvent decoder, in the instrumentation-build example: a Query FSM entity whose events are its states, printed through a callback exporter that decodes each event via AnyEvent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Declare FSM entities entirely under `fsms:` (their own doc/annotations plus states) instead of an empty `entities:` shell alongside a separate overlay; an entity declared as both is rejected. A state lists its outgoing transitions in `to:`, and the reserved target `exit` terminates the FSM, replacing the per-state `exit:` flag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the per-constraint flatten helpers and report each constraint result's error directly; an aggregate `Multiple` already renders its violations as a bullet list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # crates/instrumentation-build/example/src/main.rs
mbrobbel
left a comment
There was a problem hiding this comment.
Very nice. We should also write some user docs for this stuff.
Absolutely: #419 |
📝 WalkthroughWalkthroughChangesFSM schema and instrumentation example
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (1)
crates/yaml/src/lib.rs-86-95 (1)
86-95: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPreserve per-violation diagnostics in
crates/yaml/src/lib.rs:86-95
RefTargetError,RefTreeError, andFsmErrorall haveMultiplevariants, soe.to_string()turns several validation failures into one diagnostic entry here. Recurse through nested errors instead, matching the FSM-specific flattening inlower.rs, so each violation keeps its own location.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/yaml/src/lib.rs` around lines 86 - 95, Update the error handling around ref_target, ref_tree, and fsm in the report.results destructuring to recursively flatten Multiple error variants and emit each underlying violation separately through sink.error. Match the existing FSM-specific flattening behavior in lower.rs, preserving each diagnostic’s individual location instead of passing the aggregate error’s to_string() as one entry.
🧹 Nitpick comments (1)
crates/yaml/src/lower.rs (1)
148-176: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winNo explicit rejection of a state literally named
exit.
target.eq_ignore_ascii_case("exit")treats any transition target spelled "exit" as the reserved terminal marker, but a state key instates:can itself be named "exit" without any check. Such a state can never receive an incoming edge (anyto: [exit]elsewhere is always swallowed as the terminal marker), so it will only ever surface as a confusing "unreachable" diagnostic rather than a clear "state nameexitis reserved" error.🛡️ Proposed fix: reject reserved state name explicitly
for (state_name, state) in &spec.states { + if state_name.eq_ignore_ascii_case("exit") { + sink.error( + &format!("{path}.states.{state_name}"), + "state name `exit` is reserved for the exit transition", + None, + ); + complete = false; + continue; + } let Some(state_id) = ident(state_name, &path, sink) else {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/yaml/src/lower.rs` around lines 148 - 176, Reject any state key named "exit" case-insensitively during the state-processing loop before constructing its StateDecl, report a clear reserved-name validation error through sink, mark lowering incomplete, and skip that state. Keep the existing target handling in the transition loop unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Other comments:
In `@crates/yaml/src/lib.rs`:
- Around line 86-95: Update the error handling around ref_target, ref_tree, and
fsm in the report.results destructuring to recursively flatten Multiple error
variants and emit each underlying violation separately through sink.error. Match
the existing FSM-specific flattening behavior in lower.rs, preserving each
diagnostic’s individual location instead of passing the aggregate error’s
to_string() as one entry.
---
Nitpick comments:
In `@crates/yaml/src/lower.rs`:
- Around line 148-176: Reject any state key named "exit" case-insensitively
during the state-processing loop before constructing its StateDecl, report a
clear reserved-name validation error through sink, mark lowering incomplete, and
skip that state. Keep the existing target handling in the transition loop
unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 24497d1c-79c9-48c2-b86c-5fece033085b
⛔ Files ignored due to path filters (2)
Cargo.lockis excluded by!**/*.lock,!Cargo.lockcrates/instrumentation-build/example/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
crates/instrumentation-build/example/build.rscrates/instrumentation-build/example/model.yamlcrates/instrumentation-build/example/src/main.rscrates/yaml/Cargo.tomlcrates/yaml/src/ast.rscrates/yaml/src/lib.rscrates/yaml/src/lower.rscrates/yaml/tests/fsm.rs
|
/merge |
Description
Adds finite-state-machine entities to the YAML DSL.
A top-level
fsms:block declares an entity whose events represent transitions into its states, with cardinality derived from the topology and the FSM constraint validated at build time.Each state lists its outgoing transitions under
to:, terminating via the reservedexittarget.The instrumentation-build example models a
QueryFSM and decodes its events through theAnyEventcallback exporter.Related Issues
Part of #352.
🤖 Generated with Claude Code