RISE Framework Specification References: CAISHEN Spec 60 (State Machine Definition Format)
Spec ID: 70
Version: 1.1
Source: Extracted from py/stateloom/model.py, py/stateloom/parser.py
Implementation: Python (py/stateloom/, PyPI miadi-stateloom-engine), TypeScript (ts/src/, npm @miadi/stateloom-engine), MCP (mcp/src/server.ts)
Revised: 2026-09-20 — package directories renamed (py/smcraft/ → py/stateloom/); ERDF (Spec 80) named as the sibling document type
What SMDF Enables Users to Create: A declarative JSON schema where humans and LLM agents describe stateful workflows — hierarchical, parallel, timed — in a single file that feeds code generation, visual design, and runtime execution.
Desired Outcomes:
.smdf.json file and gets working state machine code in Python or TypeScriptRoot container holding settings, eventSources, and the state tree.
{
"settings": { "namespace": "...", "name": "...", "asynchronous": true },
"eventSources": [ { "name": "...", "feeder": "...", "events": [...] } ],
"state": { "name": "Root", "states": [...] }
}
A node in the state hierarchy tree. Properties:
| Field | Type | Description |
|---|---|---|
name |
string | Unique state identifier |
states |
StateDef[] | Child states (composite if non-empty) |
transitions |
TransitionDef[] | Event-triggered state changes |
onEntry |
ActionDef[] | Actions executed on state entry |
onExit |
ActionDef[] | Actions executed on state exit |
parallel |
ParallelDef | Orthogonal regions (parallel state) |
description |
string | Human-readable purpose |
notes |
string | Working notes about this state — what a person or an agent wrote down while discussing it, for whoever opens the document next. settings.notes carries the same for the whole diagram. Not part of the machine: parsers, engines and the code generator ignore it. MCP: get_notes, set_notes |
prompt |
string | What an agent is asked to resolve while the machine is in this state — the instruction it receives when the state is entered. description says what the state is, notes are the conversation about it, prompt is what to do in it. Engines and code generation ignore it; set_prompt writes it and generate_rispec shows it |
State Classification (derived):
states == [] AND parallel == Nonelen(states) > 0Final (convention)History (convention)parallel != None{ "id": "EvStart", "parameters": [{ "name": "data", "type": "string" }] }
{ "event": "EvStart", "nextState": "Running", "condition": "is_ready", "action": { "code": "self.initialize()" } }
Three forms:
{ "code": "self.do_something()" }{ "timerStart": { "timer": "TimerName", "duration": "1000" } }{ "timerStop": "TimerName" }{ "name": "RetryTimer", "event": "EvRetryTimeout" }
{ "states": [ { "name": "Region1", "states": [...] }, { "name": "Region2", "states": [...] } ] }
Groups events by their external interface (feeder). Each source generates a typed dispatch class.
Which validator you are holding matters. The format specifies fourteen rules; the two
engines do not carry the same subset. ✅ Implemented below means the Python engine,
which implements all fourteen. The TypeScript engine implements V001, V002, V003, V005,
V006, V007 and V013 — so V004, V008, V009, V010, V011, V012 and V014 are found by smcg
and not by validate() in JavaScript. The MCP server’s validate_definition runs its own
reference and uniqueness set whose rule ids are not these: its V001 is “no events
defined”, its V004 an unknown transition target, its V005 “root has no child state”. Read
the message, not the number.
| Rule | Description | Status (Python) |
|---|---|---|
| V001 | Exactly one root state | ✅ Implemented |
| V002 | Unique state names across entire tree | ✅ Implemented |
| V003 | Unique event IDs across all sources | ✅ Implemented |
| V004 | Timer IDs unique, no collision with event IDs | ✅ Implemented |
| V005 | Transition events reference defined event IDs | ✅ Implemented |
| V006 | Transition nextState references defined state names | ✅ Implemented |
| V007 | Final states have no outgoing transitions | ✅ Implemented |
| V008 | Final states have no children | ✅ Implemented |
| V009 | Composite states should designate an initial child | ✅ Implemented |
| V010 | Parallel states must have ≥2 regions, each with ≥1 child | ✅ Implemented |
| V011 | Parallel region transitions: nextState must be within same region or to parent exit | ✅ Implemented |
| V012 | Composite states must have ≥1 child | ✅ Implemented |
| V013 | At least one event source defined | ✅ Implemented |
| V014 | Timer references in actions must reference defined timer names | ✅ Implemented |
Canonical: .smdf.json (JSON)
Legacy: .fsm (XML, StateMachineDotNet-v1 namespace)
The parser auto-detects format by file extension and content inspection.
Desired Outcome: LLM agent creates a 13-state FDB Breakout Strategy definition via MCP tools
Current Reality: Agent has trading domain knowledge but no state machine structure
Natural Progression: Agent calls create_state_machine, iteratively add_state/add_event/add_transition, validate_definition catches errors, adjusts, exports .smdf.json
Resolution: Valid SMDF file ready for code generation and visual review
Desired Outcome: User creates nested state machine with parallel regions in the web designer
Current Reality: Web designer shows flat state view — drill-down landed (Spec 74). Double-click a composite state and the canvas shows only its children; a breadcrumb walks back up
Natural Progression: User creates parent state, drills into it, adds child states with transitions, navigates back; SMDF captures full hierarchy
Resolution: Hierarchical .smdf.json with composite and parallel states
.smdf.json describes one behaviour; a .erdf.json describes the data every behaviour acts on. The link is by name — settings.objects[].class names an entity, a guard’s <instance>.<field> names an attribute