Implicit State Derivation v2 Proposal β
Status: π‘ DRAFT
Supersedes: IMPLICIT_STATES_PROPOSAL.md (REJECTED)
Executive Summary β
This proposal demonstrates that implicit state derivation is fully achievable in EventFlow. The v1 proposal rejection was overly pessimistic - structural analysis of code can determine states with high confidence in virtually all cases.
Key Insight β
The v1 rejection's "calculator ambiguity" example is NOT ambiguous:
on :add (api) // No guards = can run anytime
on :subtract (api) // No guards = can run anytimeWithout guards, these handlers ARE independent. The code tells us this - there's no ambiguity to resolve. No guards + No emit = Single state machine.
Three Derivation Strategies β
- Event-Flow Derivation (95% confidence) - From
emit ... to @actor+ response handlers - Guard-Derived States (85% confidence) - From
? $variable existsand similar guards - Context-Derived States (80% confidence) - From mode variables with enumerated values
What's Actually Undecidable? β
Very little. The only truly undecidable cases are:
- Timeout events without explicit source or scheduling
- Race conditions in parallel responses (runtime concern, not derivation)
Machine mode markers are OPTIONAL - they document intent but don't change derivation. The algorithm derives states from code structure, not developer annotations.
Addressing the v1 Rejection β
The Original Rejection Reason β
The relationship between event handlers (whether they form a sequential process or are independent operations) is semantic information that cannot be inferred from code structure alone.
Example: Given two handlers
:checkoutand:payment_received, the CLI cannot determine if these are part of a sequential checkout process, or two independent operations (like a calculator with separate:add,:subtractoperations).
Why This Rejection is Overly Pessimistic β
The rejection treats all event handlers as equally ambiguous. In reality, structural and contextual signals exist that disambiguate most cases:
| Signal | What It Tells Us | Confidence |
|---|---|---|
emit :X to @Y + on :response from @Y | Sequential request/response | High |
(api) marker on handler | External entry point | High |
from @actor clause | Event origin is explicit | High |
Guards checking $variable exists | Handler depends on prior state | Medium |
| Context variable data flow | Handler depends on prior handler | Medium |
scenario: block grouping | Events are related | Medium |
The :checkout β :payment_received example is not ambiguous when we look at the full context:
on :checkout from @customer (api)
emit :request_payment to @payment // β This TELLS us we're waiting for payment
on :payment_received from @payment // β This is the RESPONSEThe emit ... to @payment followed by on ... from @payment is an explicit request/response pattern. This is not semantic guessing - it's structural analysis.
What About the Calculator Example? β
The v1 rejection used a calculator as an example of ambiguity:
machine: @calculator
on :add (api)
$result increases by $value
on :subtract (api)
$result decreases by $valueThis is NOT ambiguous. Here's why:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β FUNDAMENTAL RULE: No Guards = No Sequence = Independent Operations β
β β
β If handler B required handler A to run first, there would be a guard: β
β β
β on :subtract (api) β
β ? $add_was_called β This guard would ENFORCE sequence β
β $result decreases by $value β
β β
β Without such a guard, BOTH handlers can run at ANY time. β
β This means they ARE independent - not "might be" independent. β
β β
β RESULT: Single state (#ready) with self-loops β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββThe derivation is deterministic:
| Code Structure | Derivation Result |
|---|---|
Multiple (api) handlers, no guards | Single #ready state, all handlers loop back |
Multiple (api) handlers, with guards | Guards define the implicit states |
Handlers with emit/from patterns | Wait states from communication patterns |
EventFlow derives states from CODE, not developer intent. If a developer wants sequential behavior but doesn't write guards, that's either:
- A bug (guards should be added)
- Or they actually don't need sequencing
The Core Derivation Principle β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β THE STATE DERIVATION AXIOM β
β β
β A STATE exists when and only when: β
β β
β 1. A WAIT POINT exists (machine is waiting for an event) β
β - After emit to another actor β
β - After queued/async operation β
β - At machine start (initial state) β
β β
β 2. A GUARD PARTITION exists (behavior differs based on condition) β
β - ? $variable exists β
β - ? $mode is "value" β
β - ? machine is in #state β
β β
β 3. An EXPLICIT DECLARATION exists β
β - moves to #state β
β - machine mode marker β
β β
β If NONE of these exist β Single state with self-loops β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββCore Insight: State Types by Derivation Source β
States in EventFlow can be categorized by how they are derived:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STATE DERIVATION TAXONOMY β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 1. EVENT-FLOW DERIVED STATES β β
β β Source: emit/response patterns, cross-machine communication β β
β β Confidence: HIGH β β
β β Example: emit :pay to @payment β waiting β on :paid from @pay β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 2. GUARD-DERIVED STATES β β
β β Source: Conditional logic that partitions behavior β β
β β Confidence: MEDIUM β β
β β Example: ? $pending_op exists β "has pending operation" state β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 3. CONTEXT-DERIVED STATES β β
β β Source: Variable existence/value creating behavior modes β β
β β Confidence: MEDIUM β β
β β Example: $mode = "degrees" vs "radians" β different calc modes β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 4. DECLARED STATES (Explicit) β β
β β Source: Developer uses `moves to #state` or mode markers β β
β β Confidence: EXPLICIT β β
β β Example: order moves to #shipped β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 5. DEFAULT STATE (Stateless Operations) β β
β β Source: No state transitions, all handlers return to #ready β β
β β Confidence: HIGH (when declared with mode marker) β β
β β Example: machine: @calculator (stateless) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββAlgorithm 1: Event-Flow Derivation β
Overview β
Event-flow derivation identifies states from explicit communication patterns between machines or actors. This is the highest-confidence derivation method.
Algorithm β
ALGORITHM: EventFlowStateDerivation
INPUT:
- flow_file: Parsed EventFlow AST
OUTPUT:
- states: List of derived states with transitions
PROCEDURE:
1. IDENTIFY ENTRY POINTS
βββββββββββββββββββββ
For each handler H in flow_file:
If H has (api) marker:
Add H.event to entry_points
Mark as "can be triggered from #initial"
2. BUILD EMIT/RESPONSE GRAPH
ββββββββββββββββββββββββββ
For each handler H in flow_file:
For each emit E in H.actions:
Create edge: H.event β E.target_event
If E has "to @actor" clause:
expected_responses = FindHandlers(from: @actor)
For each R in expected_responses:
Create edge: E β R (response relationship)
Mark transition: "after E, waiting for R"
3. IDENTIFY WAIT POINTS
βββββββββββββββββββββ
For each emit E with expected_responses:
Create state: #after_{E.event} or #awaiting_{response.event}
Type: waiting
Waiting_for: expected_responses
4. IDENTIFY TERMINAL POINTS
βββββββββββββββββββββββββ
For each handler H:
If H has no outgoing edges (no emit, no further handlers expected):
Create state: #after_{H.event}
Type: terminal
Analyze for outcome (success/failure/neutral)
5. CONNECT TRANSITIONS
ββββββββββββββββββββ
For each state S:
incoming = events that lead to S
outgoing = events that exit S
Create transition map
RETURN states with full transition graphVisual Representation β
EMIT/RESPONSE PATTERN DETECTION
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β on :checkout from @customer (api) β
β validate cart β
β emit :request_payment to @payment βββββββββββ β
β β β β
β β β β
β βΌ β β
β βββββββββββββββββββββββ β β
β β WAIT POINT β β β
β β #awaiting_payment β β β
β β β β β
β β waiting_for: β β β
β β - :payment_successββββββββββββββββββββ€ β
β β - :payment_failed ββββββββββββββββββββ€ β
β ββββββββββββ€βββββββββββ β β
β β β β
β βββββββββββββββββ΄βββββββββββββββββ β β
β βΌ βΌ β β
β on :payment_success on :payment_failed β β
β from @payment from @payment β β
β β β β β
β βΌ βΌ β β
β ββββββββββββ ββββββββββββ β β
β β TERMINAL β β TERMINAL β β β
β β#confirmedβ β #failed β β β
β β success β β failure β β β
β ββββββββββββ ββββββββββββ β β
β β
β DERIVED BECAUSE: emit to @payment creates expectation β
β for response from @payment β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββPattern Catalog β
| Pattern | Detection | Wait State Created |
|---|---|---|
emit :X to @Y | Direct emit to actor | #awaiting_{X}_response |
emit async :X + handler exists | Async with callback | #processing_{X} |
(queued api) | Background processing | #queued_{event} |
reply 202 async for | Deferred response | #processing_{request} |
emit :X + on :X_success / on :X_failed | Success/failure pattern | #awaiting_{X}_result |
Algorithm 2: Guard-Derived States β
Overview β
Guard-derived states emerge when conditional logic partitions the machine's behavior based on runtime conditions. Guards that check for the existence or value of context variables effectively define implicit states.
Core Insight β
on :number (api)
? $pending_operation exists // β This guard IMPLIES a state
perform calculation
otherwise
$operand becomes $value // β Different behavior = different stateThe guard ? $pending_operation exists tells us:
- There's a state where
$pending_operationdoes NOT exist - There's a state where
$pending_operationDOES exist - These states have different behaviors for the same event
Algorithm β
ALGORITHM: GuardDerivedStateDetection
INPUT:
- flow_file: Parsed EventFlow AST
OUTPUT:
- implicit_states: States derived from guard conditions
- state_constraints: What context conditions define each state
PROCEDURE:
1. EXTRACT GUARD CONDITIONS
βββββββββββββββββββββββββ
guard_conditions = {}
For each handler H in flow_file:
For each guard G in H.guards:
condition = ParseCondition(G)
guard_conditions[H.event].add(condition)
2. IDENTIFY STATE-DEFINING GUARDS
βββββββββββββββββββββββββββββββ
State-defining guards are those that:
a) Check for variable EXISTENCE: "$X exists", "$X is empty"
b) Check for specific VALUES: "$mode is 'degrees'"
c) Check for STATE-LIKE values: "$status is 'pending'"
For each condition C in guard_conditions:
If C matches existence pattern:
Create state pair:
- #has_{variable}
- #no_{variable} (or #awaiting_{variable})
If C matches value pattern:
Create state: #{variable}_{value}
If C matches state-like pattern:
Create state: #{value}
3. BUILD STATE CONSTRAINT MAP
βββββββββββββββββββββββββββ
For each handler H:
active_states = []
For each guard G in H.guards:
state = MapGuardToState(G)
active_states.add(state)
handler_state_map[H] = active_states
4. DETECT STATE TRANSITIONS
βββββββββββββββββββββββββ
For each handler H:
pre_state = handler_state_map[H] // State required to enter
For each action A in H.actions:
If A modifies variable V:
If V is state-defining:
post_state = DeterminePostState(V, A)
Create transition: pre_state β post_state
5. VALIDATE STATE GRAPH
βββββββββββββββββββββ
Check for:
- Unreachable states
- Missing transitions
- Conflicting conditions
RETURN implicit_states, transitionsGuard Pattern Catalog β
| Guard Pattern | Implied States | Transition Trigger |
|---|---|---|
? $X exists | #has_X, #no_X | $X becomes value |
? $X is empty | #X_empty, #X_filled | $X adds item |
? $X is "value" | #X_value for each value | $X becomes "other" |
? $X is greater than N | #X_low, #X_high | $X increases/decreases |
otherwise after guards | Default/fallback state | - |
Example: Calculator with Pending Operation β
machine: @calculator
on :number (api)
? $pending_operation exists
perform $pending_operation with $operand and $value
$result becomes $calculation_result
$pending_operation becomes empty
$operand becomes empty
otherwise
$operand becomes $value
on :operation (api)
? $operand exists
$pending_operation becomes $operation_type
on :equals (api)
? $pending_operation exists
perform $pending_operation with $operand and $value
$result becomes $calculation_result
$pending_operation becomes emptyGuard Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GUARD CONDITION EXTRACTION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Handler: on :number β
β Guard 1: $pending_operation exists β State: #has_pending_op β
β Guard 2: otherwise β State: #no_pending_op β
β β
β Handler: on :operation β
β Guard 1: $operand exists β State: #has_operand β
β β
β Handler: on :equals β
β Guard 1: $pending_operation exists β State: #has_pending_op β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DERIVED IMPLICIT STATES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β #ready β
β constraints: $operand is empty AND $pending_operation is empty β
β description: Calculator ready for first number β
β β
β #has_operand β
β constraints: $operand exists AND $pending_operation is empty β
β description: First number entered, waiting for operation β
β β
β #has_pending_op β
β constraints: $pending_operation exists β
β description: Operation selected, waiting for second number β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββDerived State Machine:
GUARD-DERIVED STATE MACHINE
ββββββββββββββ
β #ready β $operand = empty
β β $pending_op = empty
βββββββ¬βββββββ
β
β :number (otherwise branch)
β $operand becomes $value
βΌ
ββββββββββββββββββ
β #has_operand β $operand exists
β β $pending_op = empty
βββββββ¬βββββββββββ
β
β :operation
β $pending_operation becomes $op_type
βΌ
ββββββββββββββββββ
β#has_pending_op β $pending_op exists
β β
βββββββ¬βββββββββββ
β
β :number (guard branch)
β or :equals
β $pending_op becomes empty
βΌ
ββββββββββββββ
β #ready β (back to start)
ββββββββββββββAlgorithm 3: Context-Derived States β
Overview β
Context-derived states are similar to guard-derived states but focus on mode variables - context variables that fundamentally change machine behavior without explicit guard checks in every handler.
Distinction from Guard-Derived β
| Aspect | Guard-Derived | Context-Derived |
|---|---|---|
| Detection | Explicit ? condition | Variable assignment patterns |
| Scope | Per-handler | Machine-wide behavior mode |
| Example | ? $cart is empty | $mode becomes "degrees" |
Algorithm β
ALGORITHM: ContextDerivedStateDetection
INPUT:
- flow_file: Parsed EventFlow AST
OUTPUT:
- mode_states: States representing operational modes
- mode_variables: Variables that define modes
PROCEDURE:
1. IDENTIFY MODE VARIABLES
ββββββββββββββββββββββββ
Criteria for mode variables:
a) Set to enumerated values: "degrees", "radians", "scientific"
b) Checked in multiple handlers
c) Changes machine behavior globally
mode_candidates = {}
For each handler H:
For each action A:
If A is "$var becomes 'string_value'":
mode_candidates[var].add(string_value)
For var in mode_candidates:
If mode_candidates[var].size >= 2:
Mark var as mode_variable
2. BUILD MODE STATE MAP
βββββββββββββββββββββ
For each mode_variable M:
For each value V in M.possible_values:
Create state: #{M}_{V}
Example: $angle_mode = "degrees" β #angle_mode_degrees
3. DETECT MODE TRANSITIONS
ββββββββββββββββββββββββ
For each handler H:
For each action A that sets mode_variable M:
pre_mode = current mode or "any"
post_mode = A.new_value
Create transition: #{M}_{pre} β #{M}_{post}
4. ANALYZE MODE IMPACT
ββββββββββββββββββββ
For each handler H:
For each guard G or action A:
If G/A references mode_variable M:
Mark H as mode-dependent
Document behavior difference per mode
RETURN mode_states, mode_transitionsExample: Scientific Calculator Modes β
machine: @calculator
scenario: mode switching
on :set_degrees (api)
$angle_mode becomes "degrees"
on :set_radians (api)
$angle_mode becomes "radians"
on :set_gradians (api)
$angle_mode becomes "gradians"
scenario: trigonometric operations
on :sin (api)
? $angle_mode is "degrees"
$result becomes sin($value * PI / 180)
? $angle_mode is "radians"
$result becomes sin($value)
? $angle_mode is "gradians"
$result becomes sin($value * PI / 200)Context Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MODE VARIABLE DETECTION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Variable: $angle_mode β
β Possible values: "degrees", "radians", "gradians" β
β Set by: :set_degrees, :set_radians, :set_gradians β
β Used by: :sin, :cos, :tan (affects calculation) β
β β
β Classification: MODE VARIABLE β β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DERIVED MODE STATES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β #degrees_mode β
β constraint: $angle_mode is "degrees" β
β entry: :set_degrees β
β behavior: Trig functions convert degrees to radians β
β β
β #radians_mode β
β constraint: $angle_mode is "radians" β
β entry: :set_radians β
β behavior: Trig functions use value directly β
β β
β #gradians_mode β
β constraint: $angle_mode is "gradians" β
β entry: :set_gradians β
β behavior: Trig functions convert gradians to radians β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββDerived State Machine:
CONTEXT-DERIVED MODE STATES
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β ββββββββββββββββββ β
β β #degrees_mode ββββββ :set_degrees β
β βββββββββ¬βββββββββ β
β β β
β β :set_radians β
β βΌ β
β ββββββββββββββββββ β
β β #radians_mode ββββββ :set_radians β
β βββββββββ¬βββββββββ β
β β β
β β :set_gradians β
β βΌ β
β ββββββββββββββββββ β
β β #gradians_mode ββββββ :set_gradians β
β ββββββββββββββββββ β
β β
β Note: All modes can transition to any other β
β mode via the set_* events β
β β
β Trig operations (:sin, :cos, :tan) work in β
β ALL modes but with different behavior β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββCombined Derivation Pipeline β
Overview β
The three algorithms are combined into a unified pipeline that produces a complete state model:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMBINED STATE DERIVATION PIPELINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ β
β β .flow file β β
β ββββββββ¬βββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PHASE 1: Parse & Extract β β
β β βββββββββββββββββββββββββ β β
β β β’ Parse flow file into AST β β
β β β’ Extract handlers, guards, actions, emits β β
β β β’ Build symbol table (events, actors, variables) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PHASE 2: Event-Flow Analysis (HIGH CONFIDENCE) β β
β β ββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β’ Identify emit/response patterns β β
β β β’ Build cross-machine communication graph β β
β β β’ Create wait states for each emit with expected response β β
β β β’ Mark terminal states (no outgoing transitions) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PHASE 3: Guard Analysis (MEDIUM CONFIDENCE) β β
β β βββββββββββββββββββββββββββββββββββββββββββ β β
β β β’ Extract all guard conditions β β
β β β’ Identify state-defining guards (existence, value checks) β β
β β β’ Create implicit states from guard partitions β β
β β β’ Map handlers to their required states β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PHASE 4: Context/Mode Analysis (MEDIUM CONFIDENCE) β β
β β ββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β’ Identify mode variables (enumerated string values) β β
β β β’ Create mode states for each possible value β β
β β β’ Map mode transitions from setter handlers β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PHASE 5: State Merging & Conflict Resolution β β
β β ββββββββββββββββββββββββββββββββββββββββββββ β β
β β β’ Merge states from all three sources β β
β β β’ Resolve naming conflicts β β
β β β’ Handle orthogonal state dimensions (mode Γ workflow state) β β
β β β’ Validate completeness β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PHASE 6: Confidence Scoring & Ambiguity Detection β β
β β βββββββββββββββββββββββββββββββββββββββββββββ β β
β β β’ Score each derived state by confidence β β
β β β’ Identify ambiguous cases β β
β β β’ Flag states that need human confirmation β β
β β β’ Check for machine mode markers β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββ β
β β.states.flow β + confidence report β
β βββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββConfidence Scoring β
Each derived state receives a confidence score:
| Confidence | Score | Source | Requires Confirmation? |
|---|---|---|---|
| High | 90-100 | Event-flow patterns | No |
| Medium | 60-89 | Guard/context patterns | Optional |
| Low | 30-59 | Heuristic inference | Yes |
| Undecidable | 0-29 | No clear pattern | Yes (or mode marker) |
Conflict Resolution β
When multiple derivation sources suggest different states:
CONFLICT RESOLUTION RULES:
1. EXPLICIT > IMPLICIT
If developer wrote "moves to #state", use that state name.
2. EVENT-FLOW > GUARD > CONTEXT
Higher confidence sources take precedence.
3. ORTHOGONAL DIMENSIONS
When states are independent (e.g., mode + workflow), create composite:
#degrees_mode + #awaiting_payment β #degrees_awaiting_payment
Or keep as separate state dimensions.
4. MERGE EQUIVALENT STATES
If two derived states have identical constraints and transitions,
merge them and use the more descriptive name.Comprehensive Machine Configuration Analysis β
This section analyzes ALL possible machine configurations and their state derivation rules.
Configuration 1: Single Machine, Single Actor, No Scenarios (Minimal) β
The simplest possible flow file:
validate order detailsImplicit Structure:
machine: @default // Implicit default machine
scenario: default // Implicit default scenario
on :trigger (api) // Implicit trigger event
validate order detailsState Derivation:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MINIMAL FLOW DERIVATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Input: "validate order details" β
β β
β Implicit elements: β
β β’ Machine: @default β
β β’ Scenario: default β
β β’ Event: :trigger (api) β
β β
β Derivation: β
β 1. Single handler β single entry point β
β 2. No emit β no wait states β
β 3. No guards β no partitions β
β 4. Handler completes β terminal state β
β β
β Result: β
β #idle (initial) β :trigger β #completed (terminal, success) β
β β
β Confidence: 100% (trivial case) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββGenerated States:
#idle
type: initial
waiting_for: :trigger
#completed
type: terminal
outcome: success
after: :triggerConfiguration 2: Single Machine, Single Actor, Single Scenario, Multiple Events β
machine: @order
scenario: checkout
on :add_item (api)
$items adds $item
on :remove_item (api)
$items removes $item
on :checkout (api)
process orderAnalysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SINGLE SCENARIO, MULTIPLE API HANDLERS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Question: Are these sequential or independent? β
β β
β Analysis: β
β β’ :add_item (api) - no guards β
β β’ :remove_item (api) - no guards β
β β’ :checkout (api) - no guards β
β β’ No emit/response patterns β
β β’ All modify $items but no guard checks β
β β
β Decision: NO GUARDS = INDEPENDENT β
β β
β All three handlers can be called at any time, in any order. β
β They share context ($items) but don't REQUIRE prior state. β
β β
β Result: Single #ready state with self-loops β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β βββββββββββ β β
β β :add_item β #ready β :remove_item β β
β βββββββββββββββΊβ ββββββββββββββββββββββββ β
β β β β
β :checkout β β β
β βββββββββββββββΊβ β β
β βββββββββββ β
β β
β Note: :checkout COULD be terminal, but without guards there's no β
β structural indication it's the "final" operation. β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββWhat if :checkout should be final? Add guards:
on :checkout (api)
? $items is not empty // β Guard creates implicit state
process orderNow derivation changes:
:add_itemand:remove_itemcan run anytime:checkoutrequires$items is not emptyβ implies #has_items state
Configuration 3: Single Machine, Single Actor, Multiple Scenarios β
machine: @calculator
scenario: basic operations
on :add (api)
$result increases by $value
on :subtract (api)
$result decreases by $value
scenario: memory operations
on :memory_store (api)
$memory becomes $result
on :memory_recall (api)
$result becomes $memoryAnalysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MULTIPLE SCENARIOS - SAME MACHINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Scenarios are DOCUMENTATION GROUPINGS, not state boundaries. β
β β
β From derivation perspective: β
β β’ All 4 handlers are (api) entry points β
β β’ No guards enforce ordering β
β β’ No emit/response patterns β
β β
β Scenarios help humans understand related functionality: β
β β’ "basic operations" = add, subtract β
β β’ "memory operations" = memory_store, memory_recall β
β β
β But they DON'T create separate state spaces. β
β β
β Result: Single #ready state β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β #ready β β
β β β β
β β Handles: β β
β β scenario: basic operations β β
β β β’ :add, :subtract β β
β β scenario: memory operations β β
β β β’ :memory_store, :memory_recall β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββWhen DO scenarios affect state? When they contain sequential events with guards:
scenario: checkout process
on :start_checkout (api)
$checkout_started becomes true
on :confirm_payment (api)
? $checkout_started // β Guard creates sequence WITHIN scenario
process paymentConfiguration 4: Single Machine, Multiple Actors β
machine: @order
on :place_order from @customer (api)
emit :validate to @validator
on :validated from @validator
emit :charge to @payment
on :payment_success from @payment
emit :confirmation to @customerAnalysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SINGLE MACHINE, MULTIPLE ACTORS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Actors involved: β
β β’ @customer - initiates order (external) β
β β’ @validator - validates order (service) β
β β’ @payment - processes payment (service) β
β β
β Event flow: β
β @customer ββ:place_orderβββΊ @order β
β @order ββββ:validateββββββΊ @validator β
β @validator β:validatedββββΊ @order β
β @order ββββ:chargeββββββββΊ @payment β
β @payment ββ:payment_successββΊ @order β
β @order ββββ:confirmationβββΊ @customer β
β β
β Emit/Response patterns detected: β
β 1. emit :validate to @validator β on :validated from @validator β β
β 2. emit :charge to @payment β on :payment_success from @payment β β
β β
β Each emit creates a WAIT STATE (high confidence): β
β β
β #idle β
β β :place_order β
β #awaiting_validation β emit :validate to @validator β
β β :validated β
β #awaiting_payment β emit :charge to @payment β
β β :payment_success β
β #completed β terminal β
β β
β Confidence: 95% (clear emit/response patterns) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββConfiguration 5: Multiple Machines (System) β
// βββ order.flow βββ
machine: @order
on :checkout from @customer (api)
emit :request_payment to @payment
on :payment_success from @payment
emit :reserve_stock to @warehouse
on :stock_reserved from @warehouse
emit :confirmation to @customer// βββ payment.flow βββ
machine: @payment
on :request_payment from @order
process payment
? payment successful
emit :payment_success to @order
otherwise
emit :payment_failed to @order// βββ warehouse.flow βββ
machine: @warehouse
on :reserve_stock from @order
check inventory
? items available
emit :stock_reserved to @order
otherwise
emit :stock_unavailable to @orderSystem-Level Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MULTI-MACHINE SYSTEM DERIVATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β STEP 1: Build Cross-Machine Communication Graph β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β @customer β
β β β
β β :checkout β
β βΌ β
β @order ββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β :request_payment :payment_success β β
β βΌ :payment_failed β β
β @payment ββββββββββββββββββββββββββββββββββββββββββ€ β
β β β
β @order ββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β β
β β :reserve_stock :stock_reserved β β
β βΌ :stock_unavailable β β
β @warehouse ββββββββββββββββββββββββββββββββββββββββ β
β β
β STEP 2: Derive States Per Machine β
β βββββββββββββββββββββββββββββββββ β
β β
β @order states (orchestrator): β
β #idle β #awaiting_payment β #awaiting_stock β #completed β
β β #payment_failed β #stock_unavailable β
β β
β @payment states (service): β
β #idle β #processing β #completed β
β (simpler - single request/response cycle) β
β β
β @warehouse states (service): β
β #idle β #checking β #completed β
β (simpler - single request/response cycle) β
β β
β STEP 3: Cross-Reference Validation β
β βββββββββββββββββββββββββββββββββββ β
β β
β For each emit in @order: β
β β’ emit :request_payment to @payment β
β β @payment has: on :request_payment from @order β
β β’ emit :reserve_stock to @warehouse β
β β @warehouse has: on :reserve_stock from @order β
β β
β For each response to @order: β
β β’ :payment_success from @payment β
β β @payment emits: emit :payment_success to @order β
β β’ :stock_reserved from @warehouse β
β β @warehouse emits: emit :stock_reserved to @order β
β β
β All cross-machine references validated β β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββConfiguration 6: Single Scenario with Multiple Waiting Events β
machine: @loan
scenario: loan application
on :submit from @customer (api)
emit async :check_credit to @credit_bureau
emit async :verify_identity to @identity_service
emit async :check_fraud to @fraud_service
on :credit_result from @credit_bureau
$credit_score becomes $result.score
check if all complete
on :identity_result from @identity_service
$identity_verified becomes $result.verified
check if all complete
on :fraud_result from @fraud_service
$fraud_clear becomes $result.clear
check if all complete
on :all_checks_complete
? $credit_score > 700 and $identity_verified and $fraud_clear
emit :approved to @customer
otherwise
emit :declined to @customerAnalysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PARALLEL ASYNC - MULTIPLE WAIT EVENTS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Pattern: Fan-out / Fan-in (Scatter-Gather) β
β β
β After :submit, THREE parallel requests are made: β
β β’ emit async :check_credit β
β β’ emit async :verify_identity β
β β’ emit async :check_fraud β
β β
β The machine enters a COLLECTING state where it waits for β
β ALL THREE responses (in any order). β
β β
β State derivation: β
β β
β #idle β
β β :submit β
β #collecting_results β waiting for 3 responses β
β β (when all received) β
β :all_checks_complete β
β β β
β #approved OR #declined β terminal states β
β β
β Key insight: Even though 3 events can arrive, β
β there's ONE waiting state (#collecting_results) β
β with internal tracking of which responses arrived. β
β β
β The "check if all complete" action is the fan-in logic. β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββGenerated States:
#idle
type: initial
waiting_for: :submit from @customer
#collecting_results
type: waiting
after: :submit
waiting_for_all:
- :credit_result from @credit_bureau
- :identity_result from @identity_service
- :fraud_result from @fraud_service
collection_complete_event: :all_checks_complete
#approved
type: terminal
outcome: success
after: :all_checks_complete
when: all checks passed
#declined
type: terminal
outcome: failure
after: :all_checks_complete
when: any check failedWhy Same Scenario?
These 5 event handlers belong in the same scenario because:
- Single Business Flow: All events are part of the "loan application" process
- Causal Dependency:
:credit_result,:identity_result,:fraud_resultcannot occur without:submitfirst - Shared Outcome: All responses contribute to a single decision (approve/decline)
- Temporal Coupling: The process starts together and ends together
If these were independent operations (e.g., standalone credit check API), they would be:
- Separate machines (
@credit_bureau,@identity_service,@fraud_service) - Each with their own scenarios
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SCENARIO GROUPING RULE β
β β
β Events belong in the SAME scenario when: β
β β They form a single business flow (start β ... β end) β
β β Later events DEPEND on earlier events β
β β They share context and contribute to a common outcome β
β β
β Events belong in SEPARATE scenarios when: β
β β They are independent operations (can be called anytime) β
β β No causal dependency between them β
β β Different business capabilities β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββConfiguration 7: Internal Events (Same Machine Communication) β
machine: @order
on :checkout from @customer (api)
validate cart
emit :process_payment // β No "to @actor" = internal
on :process_payment // β Internal handler (no from clause)
charge card
emit :send_confirmation
on :send_confirmation // β Internal handler
send email
order moves to #completedAnalysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INTERNAL EVENTS - SAME MACHINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Internal emit (no "to @actor") triggers handler in SAME machine. β
β β
β Execution model options: β
β A) SYNC: Handler runs immediately inline β
β B) ASYNC: Event queued, handler runs later β
β β
β For state derivation: β
β β’ If SYNC: No wait state (all runs in one transaction) β
β β’ If ASYNC: Wait state between each internal emit β
β β
β Default assumption: SYNC (no wait states for internal events) β
β β
β State derivation (sync model): β
β β
β #idle β
β β :checkout β
β β validate cart β
β β emit :process_payment (sync - continues inline) β
β β charge card β
β β emit :send_confirmation (sync - continues inline) β
β β send email β
β β order moves to #completed β
β β β
β #completed β
β β
β Result: Only 2 states (#idle, #completed) because internal β
β events execute synchronously within the same request. β
β β
β To create wait states, use: β
β emit async :process_payment β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββConfiguration 8: Hybrid - Guards AND Emit/Response β
machine: @order
on :checkout from @customer (api)
? cart is not empty
? $customer.is_premium
apply premium discount
emit :request_payment to @payment
otherwise
emit :empty_cart_error to @customer
on :payment_success from @payment
? $total > 1000
emit :request_approval to @manager
otherwise
emit :confirmation to @customer
on :payment_failed from @payment
emit :checkout_failed to @customer
on :approved from @manager
emit :confirmation to @customer
on :rejected from @manager
emit :request_refund to @payment
emit :order_rejected to @customerAnalysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HYBRID: GUARDS + EMIT/RESPONSE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Multiple derivation sources combine: β
β β
β From GUARDS: β
β β’ ? cart is not empty β #has_cart vs #empty_cart β
β β’ ? $customer.is_premium β affects actions but not state β
β β’ ? $total > 1000 β #needs_approval vs #auto_approved β
β β
β From EMIT/RESPONSE: β
β β’ emit to @payment β #awaiting_payment β
β β’ emit to @manager β #awaiting_approval β
β β
β Combined state machine: β
β β
β :checkout β
β β β
β ββββββββββββββ΄βββββββββββββ β
β βΌ βΌ β
β [cart not empty] [cart empty] β
β β β β
β βΌ βΌ β
β emit :request_payment emit :empty_cart_error β
β β β β
β βΌ βΌ β
β #awaiting_payment #checkout_failed β
β β (terminal) β
β βββββββββββ΄ββββββββββ β
β βΌ βΌ β
β :payment_success :payment_failed β
β β β β
β β βΌ β
β β #payment_failed (terminal) β
β β β
β βββ [$total > 1000] βββββββββββββββββββ β
β β β β
β βΌ βΌ β
β emit :confirmation emit :request_approval β
β β β β
β βΌ βΌ β
β #completed (terminal) #awaiting_approval β
β β β
β ββββββββββββ΄βββββββββββ β
β βΌ βΌ β
β :approved :rejected β
β β β β
β βΌ βΌ β
β #completed (t) #rejected (t) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββWhat is Actually Undecidable? β
After comprehensive analysis, very few cases are truly undecidable:
Truly Undecidable Case 1: Timeout Events Without Clear Ownership β
on :submit (api)
emit :validate to @validator
on :validated from @validator
proceed
on :timeout // β Whose timeout? When does it fire?
cancelWhy undecidable:
:timeouthas nofromclause - could be from system, scheduler, or another service- No clear trigger point - does it start at
:submit? How long? - No explicit connection to the validation flow
Solution: Require explicit source or scheduled event syntax
on :submit (api)
emit :validate to @validator
schedule :validation_timeout after 30 seconds // β Explicit
on :validation_timeout
cancelTruly Undecidable Case 2: Race Conditions in Parallel Responses β
on :start (api)
emit async :task_a to @service_a
emit async :task_b to @service_b
on :task_a_result from @service_a
process a result
on :task_b_result from @service_b
process b result
// What if both arrive at EXACTLY the same time?
// Which handler runs first? Does order matter?Why undecidable:
- The relative order of
:task_a_resultand:task_b_resultis non-deterministic - If actions affect shared state differently based on order, behavior is unpredictable
Solution: This is a runtime concern, not state derivation
- State derivation gives:
#idleβ#collectingβ#completed - Runtime must handle concurrent event processing
NOT Undecidable: Independent Operations β
machine: @calculator
on :add (api)
$result increases by $value
on :subtract (api)
$result decreases by $valueThis IS decidable:
- No guards = no sequence required
- No emit = no waiting
- Result: Single
#readystate, all handlers loop back - Confidence: 100%
Machine Mode Markers (Optional) β
Mode markers are optional documentation hints. The derivation algorithm can determine machine behavior from code structure in most cases. Mode markers are useful for:
- Explicit documentation - Make intent clear to readers
- Derivation optimization - Skip analysis when you know the answer
- Edge cases - Override derivation for special cases
Important: Mode Markers Do NOT Change Behavior β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Mode markers affect STATE DERIVATION only, not runtime behavior. β
β β
β These two are EQUIVALENT at runtime: β
β β
β machine: @calculator machine: @calculator (stateless) β
β β
β on :add (api) on :add (api) β
β $result += $value $result += $value β
β β
β The (stateless) marker just tells the derivation algorithm: β
β "Don't bother analyzing - I know this is a single-state machine." β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββSyntax β
machine: @name (mode)Available Modes β
| Mode | Meaning | When to Use |
|---|---|---|
(stateless) | Explicit single-state machine | Documentation, skip analysis |
(sequential) | Force sequential interpretation | When naming suggests sequence but guards are intentionally omitted |
| (no marker) | Full derivation algorithm | Default - let the algorithm figure it out |
When You DON'T Need Mode Markers β
// This calculator is automatically detected as single-state:
// - No guards
// - No emit/response patterns
// - All (api) handlers
// NO MODE MARKER NEEDED
machine: @calculator
on :add (api)
$result increases by $value
on :subtract (api)
$result decreases by $valueDerivation result (automatic): Single #ready state, confidence 100%
When Mode Markers ARE Useful β
1. Documentation Clarity β
// The (stateless) marker documents intent:
// "This machine intentionally has no state transitions"
machine: @calculator (stateless)
on :add (api)
$result increases by $value2. Sequential Without Guards (Edge Case) β
// You WANT sequential behavior but intentionally skip guards
// (perhaps for a simplified prototype)
machine: @wizard (sequential)
on :step_one (api)
do first thing
on :step_two (api)
do second thing // No guard, but (sequential) forces order
on :step_three (api)
do third thingWarning: This creates a state machine that the CODE doesn't enforce. The derived states exist for documentation/diagrams, but runtime doesn't prevent calling :step_three before :step_one.
Better approach: Add guards if you want enforcement:
machine: @wizard
on :step_one (api)
$step_one_done becomes true
on :step_two (api)
? $step_one_done
do second thing
on :step_three (api)
? $step_two_done
do third thingComprehensive Examples β
Example 1: Basic Calculator (Stateless) β
machine: @calculator (stateless)
on :add (api)
$result increases by $value
on :subtract (api)
$result decreases by $valueDerivation Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DERIVATION ANALYSIS: Basic Calculator β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Mode: STATELESS (explicit marker) β
β β
β Phase 1: Event-Flow Analysis β
β β’ No emit statements found β
β β’ No cross-machine communication β
β β’ Result: No event-flow derived states β
β β
β Phase 2: Guard Analysis β
β β’ No guards found β
β β’ Result: No guard-derived states β
β β
β Phase 3: Context Analysis β
β β’ $result modified but no mode patterns β
β β’ Result: No context-derived states β
β β
β Phase 4: Mode Marker Check β
β β’ Mode: stateless β
β β’ Result: Single state derivation β
β β
β FINAL RESULT: β
β #ready (stateless) - handles all operations β
β Confidence: 100% (explicit mode) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββGenerated States File:
// calculator.states.flow
machine: @calculator
#ready
type: stateless
description: Calculator ready for any arithmetic operation
handles: :add, :subtractExample 2: Calculator with Memory β
machine: @calculator
scenario: basic operations
on :add (api)
$result increases by $value
on :clear (api)
$result becomes 0
$memory becomes 0
scenario: memory operations
on :memory_store (api)
$memory becomes $result
on :memory_recall (api)
? $memory exists
$result becomes $memory
otherwise
emit :error to @user
with "No value in memory"
on :memory_clear (api)
$memory becomes 0Derivation Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DERIVATION ANALYSIS: Calculator with Memory β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Mode: DEFAULT (no marker) β
β β
β Phase 1: Event-Flow Analysis β
β β’ emit :error to @user found in :memory_recall β
β β’ No response handler for :error (fire-and-forget) β
β β’ Result: No wait states from event-flow β
β β
β Phase 2: Guard Analysis β
β β’ Guard found: `? $memory exists` in :memory_recall β
β β’ State-defining: YES (existence check) β
β β’ Derived states: β
β - #has_memory ($memory exists) β
β - #no_memory ($memory not set or 0) β
β β
β Phase 3: Context Analysis β
β β’ $memory set by :memory_store β
β β’ $memory cleared by :memory_clear and :clear β
β β’ Pattern: binary state (has/no memory) β
β β
β Phase 4: Merge Analysis β
β β’ Guard-derived and context-derived agree β
β β’ No conflicts β
β β
β FINAL RESULT: β
β #no_memory (initial) - memory empty β
β #has_memory - memory contains value β
β Confidence: 75% (guard-derived) β
β β
β NOTE: Since no handler REQUIRES a specific state to run β
β (except :memory_recall with guard), these states are β
β INFORMATIONAL rather than ENFORCED. β
β β
β RECOMMENDATION: Mark as (stateless) if states are not important β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββGenerated States File:
// calculator.states.flow
machine: @calculator
#no_memory
type: initial
description: Memory is empty
constraint: $memory is empty or $memory equals 0
transitions_to: #has_memory via :memory_store
#has_memory
type: waiting
description: Memory contains a stored value
constraint: $memory exists and $memory is not 0
transitions_to: #no_memory via :memory_clear, :clearExample 3: Calculator with Pending Operation β
machine: @calculator
on :number (api)
? $pending_operation exists
perform $pending_operation with $operand and $value
$result becomes $calculation_result
$pending_operation becomes empty
$operand becomes $result
otherwise
$operand becomes $value
$result becomes $value
on :operation (api)
? $operand exists
$pending_operation becomes $operation_type
otherwise
emit :error to @user
with "Enter a number first"
on :equals (api)
? $pending_operation exists
? $operand exists
perform $pending_operation with $operand and $value
$result becomes $calculation_result
$pending_operation becomes empty
$operand becomes empty
on :clear (api)
$result becomes 0
$operand becomes empty
$pending_operation becomes empty
on :all_clear (api)
$result becomes 0
$operand becomes empty
$pending_operation becomes empty
$memory becomes 0Derivation Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DERIVATION ANALYSIS: Calculator with Pending Operation β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Phase 2: Guard Analysis β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HANDLER: on :number β β
β β β β
β β Guard Path 1: $pending_operation exists β β
β β β Implies state: #has_pending_op β β
β β β Actions: perform calculation, update $operand β β
β β β Post-state: $pending_operation becomes empty β β
β β $operand becomes $result β β
β β β Returns to: #has_operand β β
β β β β
β β Guard Path 2: otherwise ($pending_operation empty) β β
β β β Implies state: #no_pending_op β β
β β β Actions: $operand becomes $value β β
β β β Post-state: #has_operand β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HANDLER: on :operation β β
β β β β
β β Guard Path 1: $operand exists β β
β β β Pre-state: #has_operand β β
β β β Actions: $pending_operation becomes $operation_type β β
β β β Post-state: #has_pending_op β β
β β β β
β β Guard Path 2: otherwise ($operand empty) β β
β β β Pre-state: #ready β β
β β β Actions: emit error β β
β β β Post-state: #ready (unchanged) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HANDLER: on :equals β β
β β β β
β β Guard: $pending_operation exists AND $operand exists β β
β β β Pre-state: #has_pending_op (with operand) β β
β β β Actions: perform calculation, clear all β β
β β β Post-state: #ready β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HANDLER: on :clear / :all_clear β β
β β β β
β β No guards - runs from any state β β
β β β Pre-state: ANY β β
β β β Actions: reset everything β β
β β β Post-state: #ready β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DERIVED STATE MACHINE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β :clear / :all_clear β
β ββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β β β
β βΌ β β
β ββββββββββββββ β β
β β #ready β $operand = empty β β
β β β $pending_op = empty β β
β βββββββ¬βββββββ β β
β β β β
β β :number (otherwise branch) β β
β β $operand becomes $value β β
β βΌ β β
β ββββββββββββββββββ β β
β β #has_operand β $operand exists β β
β β β $pending_op = empty β β
β βββββββ¬βββββββββββ β β
β β β β
β β :operation β β
β β $pending_op becomes $op_type β β
β βΌ β β
β ββββββββββββββββββ β β
β β#has_pending_op β $pending_op exists β β
β β β β β
β βββββ¬ββββββββ¬βββββ β β
β β β β β
β :number β β :equals β β
β (guard) β β (perform & clear) β β
β β β β β
β βΌ βββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββ β
β β#has_operand β (chain calculation, keep operand) β
β ββββββββββββββββ β
β β
β Confidence: 85% (clear guard patterns) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββGenerated States File:
// calculator.states.flow
machine: @calculator
#ready
type: initial
description: Calculator cleared, ready for first number
constraint: $operand is empty AND $pending_operation is empty
waiting_for: :number, :clear, :all_clear
#has_operand
type: waiting
description: First number entered, waiting for operation
constraint: $operand exists AND $pending_operation is empty
after: :number (when no pending operation)
waiting_for: :operation, :number, :clear, :all_clear
#has_pending_op
type: waiting
description: Operation selected, waiting for second number
constraint: $pending_operation exists
after: :operation
waiting_for: :number, :equals, :clear, :all_clearExample 4: Order Processing (Event-Flow Derived) β
machine: @order
on :checkout from @customer (api)
validate cart
emit :request_payment to @payment
with $order_id, $total
on :payment_success from @payment
emit :reserve_stock to @warehouse
with $order_id, $items
on :payment_failed from @payment
emit :checkout_failed to @customer
on :stock_reserved from @warehouse
emit :confirmation to @customer
emit :schedule_delivery to @shipping
on :stock_unavailable from @warehouse
emit :request_refund to @payment
emit :out_of_stock to @customer
on :refund_complete from @payment
// Order fully cancelled
on :delivery_scheduled from @shipping
// Order completeDerivation Analysis:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DERIVATION ANALYSIS: Order Processing β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Phase 1: Event-Flow Analysis (HIGH CONFIDENCE) β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β EMIT/RESPONSE PATTERN 1: β β
β β β β
β β :checkout β β
β β β β β
β β βββ emit :request_payment to @payment β β
β β β β β
β β βββ on :payment_success from @payment β β β
β β βββ on :payment_failed from @payment β β β
β β β β
β β CREATES: #awaiting_payment β β
β β Confidence: 95% β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β EMIT/RESPONSE PATTERN 2: β β
β β β β
β β :payment_success β β
β β β β β
β β βββ emit :reserve_stock to @warehouse β β
β β β β β
β β βββ on :stock_reserved from @warehouse β β β
β β βββ on :stock_unavailable from @warehouse β β β
β β β β
β β CREATES: #awaiting_stock β β
β β Confidence: 95% β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β EMIT/RESPONSE PATTERN 3: β β
β β β β
β β :stock_unavailable β β
β β β β β
β β βββ emit :request_refund to @payment β β
β β β β β
β β βββ on :refund_complete from @payment β β β
β β β β
β β CREATES: #awaiting_refund β β
β β Confidence: 95% β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β EMIT/RESPONSE PATTERN 4: β β
β β β β
β β :stock_reserved β β
β β β β β
β β βββ emit :schedule_delivery to @shipping β β
β β β β β
β β βββ on :delivery_scheduled from @shipping β β β
β β β β
β β CREATES: #awaiting_delivery β β
β β Confidence: 95% β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β TERMINAL STATES: β
β β’ :payment_failed β #checkout_failed (failure) β
β β’ :refund_complete β #refunded (failure) β
β β’ :delivery_scheduled β #completed (success) β
β β
β Overall Confidence: 95% (all event-flow derived) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββGenerated State Diagram:
ORDER STATE MACHINE
ββββββββββββββ
β #idle β
β (initial) β
βββββββ¬βββββββ
β :checkout
βΌ
ββββββββββββββββββββ
β#awaiting_payment ββββ emit :request_payment to @payment
β (waiting) β
ββββββββββ¬ββββββββββ
β
ββββββββββ΄βββββββββββββββββββββββββ
β β
βΌ βΌ
:payment_success :payment_failed
β β
βΌ βΌ
ββββββββββββββββββββ ββββββββββββββββββ
β #awaiting_stock β β#checkout_failedβ
β (waiting) β β(terminal:fail) β
ββββββββββ¬ββββββββββ ββββββββββββββββββ
β
ββββββββββ΄βββββββββββββββββββββββββ
β β
βΌ βΌ
:stock_reserved :stock_unavailable
β β
βΌ βΌ
ββββββββββββββββββββββ ββββββββββββββββββββ
β#awaiting_delivery β β #awaiting_refund β
β (waiting) β β (waiting) β
ββββββββββ¬ββββββββββββ ββββββββββ¬ββββββββββ
β β
βΌ βΌ
:delivery_scheduled :refund_complete
β β
βΌ βΌ
ββββββββββββββββββ ββββββββββββββββββ
β #completed β β #refunded β
β(terminal:pass) β β(terminal:fail) β
ββββββββββββββββββ ββββββββββββββββββGenerated States File:
// order.states.flow
machine: @order
#idle
type: initial
description: No order started
waiting_for: :checkout from @customer
#awaiting_payment
type: waiting
description: Order submitted, waiting for payment result
after: :checkout
emits: :request_payment to @payment
waiting_for: :payment_success, :payment_failed from @payment
#checkout_failed
type: terminal
outcome: failure
description: Payment failed, order cancelled
after: :payment_failed from @payment
#awaiting_stock
type: waiting
description: Payment received, checking inventory
after: :payment_success from @payment
emits: :reserve_stock to @warehouse
waiting_for: :stock_reserved, :stock_unavailable from @warehouse
#awaiting_refund
type: waiting
description: Stock unavailable, processing refund
after: :stock_unavailable from @warehouse
emits: :request_refund to @payment
waiting_for: :refund_complete from @payment
#refunded
type: terminal
outcome: failure
description: Order cancelled, customer refunded
after: :refund_complete from @payment
#awaiting_delivery
type: waiting
description: Stock reserved, scheduling delivery
after: :stock_reserved from @warehouse
emits: :schedule_delivery to @shipping
waiting_for: :delivery_scheduled from @shipping
#completed
type: terminal
outcome: success
description: Order delivered successfully
after: :delivery_scheduled from @shippingCLI Commands β
derive-states Command β
# Interactive mode (default)
eventflow derive-states order.flow
# Non-interactive mode (CI/CD)
eventflow derive-states order.flow --no-interactive
# Update existing states file
eventflow derive-states order.flow --update
# Show derivation analysis without generating
eventflow derive-states order.flow --analyze
# Set minimum confidence threshold
eventflow derive-states order.flow --min-confidence 70
# Force regeneration, overwrite existing
eventflow derive-states order.flow --forceanalyze-states Command β
# Show detailed derivation analysis
eventflow analyze-states order.flow
# Output format options
eventflow analyze-states order.flow --format json
eventflow analyze-states order.flow --format markdown
# Show only specific derivation type
eventflow analyze-states order.flow --type event-flow
eventflow analyze-states order.flow --type guard-derived
eventflow analyze-states order.flow --type context-derivedExample CLI Output β
$ eventflow derive-states calculator.flow --analyze
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STATE DERIVATION ANALYSIS: calculator.flow β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Machine: @calculator
Mode: DEFAULT (no marker)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PHASE 1: Event-Flow Analysis
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Emit statements found: 1
β’ emit :error to @user (line 15) - fire-and-forget, no wait state
Cross-machine patterns: 0
Wait states derived: 0
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PHASE 2: Guard Analysis
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Guards found: 4
ββ Handler: on :number ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Guard: ? $pending_operation exists β
β Type: EXISTENCE CHECK β
β Creates states: #has_pending_op, #no_pending_op β
β Confidence: 85% β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββ Handler: on :operation βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Guard: ? $operand exists β
β Type: EXISTENCE CHECK β
β Creates states: #has_operand, #no_operand β
β Confidence: 85% β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Guard-derived states: 3
β’ #ready ($operand empty, $pending_op empty)
β’ #has_operand ($operand exists, $pending_op empty)
β’ #has_pending_op ($pending_op exists)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PHASE 3: Context Analysis
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Mode variables: 0
Context-derived states: 0
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SUMMARY
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Total states: 3
Derivation source:
β’ Event-flow: 0
β’ Guard-derived: 3
β’ Context-derived: 0
Overall confidence: 85%
ββ RECOMMENDATION βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β The derived states look complete. Run without --analyze to generate β
β calculator.states.flow β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
? Generate states file? (Y/n):Decision Matrix: Quick Reference β
When Does Each Derivation Source Apply? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STATE DERIVATION DECISION MATRIX β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β CODE PATTERN β DERIVATION β CONFIDENCE β RESULT β
β βββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββΌβββββββββ
β emit :X to @Y + on :Z from @Y β Event-Flow β HIGH (95%) β Wait β
β emit async :X + response handlers β Event-Flow β HIGH (95%) β Wait β
β (queued api) β Event-Flow β HIGH (95%) β Wait β
β βββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββΌβββββββββ
β ? $var exists β Guard-Derived β MED (85%) β Branch β
β ? $var is "value" β Guard-Derived β MED (85%) β Branch β
β ? condition + otherwise β Guard-Derived β MED (85%) β Branch β
β βββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββΌβββββββββ
β $mode becomes "X" (enum values) β Context-Derived β MED (80%) β Mode β
β Multiple mode handlers β Context-Derived β MED (80%) β Mode β
β βββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββΌβββββββββ
β Multiple (api), no guards, no emit β Auto: Single State β HIGH(100%) β Ready β
β Single action only β Auto: InitβTerminal β HIGH(100%) β Simple β
β βββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββΌβββββββββ
β :timeout without from/scheduled β UNDECIDABLE β LOW (20%) β Ask β
β βββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββΌβββββββββ
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββConfiguration Quick Reference β
| Configuration | Example | States | Confidence |
|---|---|---|---|
| Single action | validate order | 2 (idle β done) | 100% |
| Multiple (api), no guards | Calculator | 1 (#ready) | 100% |
| Multiple (api), with guards | Wizard with guards | N (guard-derived) | 85% |
| Single machine, emit to actor | Order β Payment | N (wait states) | 95% |
| Multi-machine system | Order/Payment/Warehouse | N per machine | 95% |
| Parallel async (fan-out) | Loan checks | 1 collecting + terminals | 90% |
| Internal events (sync) | Chained handlers | Collapsed | 90% |
| Mode variables | Scientific calculator | N modes | 80% |
The Golden Rule β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β THE GOLDEN RULE β
β β
β "If you can call handler B without calling handler A first, β
β then A and B are independent and share the same state." β
β β
β The CODE decides this, not the developer's intention. β
β β
β Want sequential? Add guards. β
β Want waiting? Add emit/response. β
β Want modes? Add mode variable checks. β
β β
β No guards + No emit = Independent = Single state. β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββMigration Path β
Phase 1: Introduce v2 Derivation β
- Implement combined derivation pipeline
- Add
--analyzeflag for transparency - Support machine mode markers
- Maintain backward compatibility with
moves to
Phase 2: Encourage Adoption β
- Add warnings for ambiguous cases
- Suggest mode markers where appropriate
- Provide automated refactoring tools
- Update documentation with new examples
Phase 3: Gradual Transition β
- Allow mixed explicit/implicit states
- Validate derived states against explicit ones
- Provide migration reports
Phase 4: Full Integration β
- Make derivation the default
moves tobecomes optional override- Generated
.states.flowbecomes source of truth for diagrams
Scenario Design Guidelines β
Understanding when events belong in the same scenario vs. separate scenarios is crucial for proper machine design.
The Scenario Definition β
A scenario groups events that form a single business flow. It's not just documentation - it represents a cohesive process with:
- A clear starting point (entry event)
- A defined progression (intermediate events)
- Terminal outcomes (success/failure states)
When to Use SAME Scenario β
Events belong in the same scenario when:
| Criterion | Example | Reasoning |
|---|---|---|
| Causal dependency | :checkout β :payment_received | Payment can't happen without checkout |
| Single business flow | Loan: submit β checks β decision | One process from start to finish |
| Shared context | All events use $order_id, $total | Data flows through the process |
| Common outcome | All paths lead to #approved or #declined | Single decision point |
| Temporal coupling | Fan-out/fan-in parallel events | Start and end together |
// CORRECT: Single flow, same scenario
scenario: complete purchase
on :checkout from @customer (api)
emit :request_payment to @payment
on :payment_success from @payment
emit :reserve_stock to @warehouse
on :stock_reserved from @warehouse
emit :confirmation to @customerWhen to Use SEPARATE Scenarios β
Events belong in separate scenarios when:
| Criterion | Example | Reasoning |
|---|---|---|
| Independent operations | Calculator: add, subtract | Can call in any order |
| No causal dependency | CRUD operations | Each stands alone |
| Different capabilities | "cart management" vs "checkout" | Distinct business functions |
| Reusable operations | "add to cart" used by multiple flows | Shared utility |
// CORRECT: Independent operations, separate scenarios
machine: @calculator
scenario: addition
on :add (api)
$result increases by $value
scenario: subtraction
on :subtract (api)
$result decreases by $value
scenario: memory operations
on :memory_store (api)
$memory becomes $result
on :memory_recall (api)
$result becomes $memoryThe Scenario Decision Tree β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SCENARIO DESIGN DECISION TREE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Q1: Can event B happen WITHOUT event A happening first? β
β β β
β βββ YES β Different scenarios (independent operations) β
β β β
β βββ NO β Continue to Q2 β
β β β
β Q2: Do events A and B share context and outcome? β
β β β
β βββ YES β Same scenario (single flow) β
β β β
β βββ NO β Different scenarios (separate concerns) β
β β
β EXAMPLES: β
β β
β :checkout β :payment_received β
β Q1: Can payment happen without checkout? NO β
β Q2: Share context ($order_id, $total) and outcome (#paid)? YES β
β β SAME SCENARIO β β
β β
β :add β :subtract (calculator) β
β Q1: Can subtract happen without add? YES β
β β DIFFERENT SCENARIOS β β
β β
β :add_to_cart β :checkout β
β Q1: Can checkout happen without add_to_cart? NO (cart must have items) β
β Q2: Different capabilities (cart mgmt vs purchase)? YES β
β β DIFFERENT SCENARIOS (but checkout depends on cart state) β β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββScenario vs. Machine Boundary β
Sometimes the question isn't "same or different scenario" but "same or different machine":
| Situation | Solution |
|---|---|
| Independent service with its own lifecycle | Separate machine |
| Reusable capability called by multiple flows | Separate machine |
| Tightly coupled steps in one process | Same machine, same scenario |
| Related but independent operations | Same machine, different scenarios |
// Payment is a SEPARATE MACHINE (independent service)
machine: @payment
scenario: process payment
on :request_payment from @order
process with gateway
? successful
emit :payment_success to @order
otherwise
emit :payment_failed to @orderImpact on State Derivation β
Scenario boundaries do not affect state derivation directly, but they provide important context:
- Same scenario, sequential events β Derived states between events
- Same scenario, parallel events β Single collecting state (fan-out/fan-in)
- Different scenarios, no guards β Single shared #ready state
- Different scenarios, with guards β Guard-derived states
The derivation algorithm looks at code structure (guards, emit/response patterns), not scenario boundaries. Scenarios help humans understand the design intent.
Open Questions for Discussion β
Orthogonal State Dimensions: When mode states and workflow states coexist, should they be:
- Merged into composite states (
#degrees_awaiting_payment) - Kept as separate dimensions (parallel state machines)
- Left to developer choice
- Merged into composite states (
Internal Event Execution Model: Should internal events (no
to @actor) be:- Sync by default (current assumption)
- Async by default (queue-based)
- Configurable via syntax (
emit sync :eventvsemit async :event)
Guard Complexity Limit: How deep should guard-derived state analysis go?
- Limit nesting depth (e.g., max 3 levels)
- Limit total states per handler (e.g., max 8)
- Warn but allow unlimited
Scenario Semantics:
Should scenarios affect state derivation?RESOLVED- Answer: Scenarios do NOT affect state derivation (code structure does)
- Scenarios group events that form a single business flow
- See Scenario Design Guidelines for detailed guidance
Related Documents β
- IMPLICIT_STATES_PROPOSAL.md - Original (rejected) proposal
- LANE_STATE_DIAGRAMS_PROPOSAL.md - Diagram generation
- STATIC_ANALYSIS_PROPOSAL.md - State reachability analysis
Changelog β
| Version | Date | Changes |
|---|---|---|
| v2.2 | 2025-12-18 | Added "Scenario Design Guidelines" section, clarified when events belong in same vs. separate scenarios, resolved Open Question 4, added "Why Same Scenario?" explanation to Configuration 6 |
| v2.1 | 2025-12-16 | Comprehensive machine configuration analysis, corrected "ambiguity" analysis (calculator IS decidable), added decision matrix, updated mode markers as optional |
| v2.0 | 2025-12-16 | Initial v2 proposal with three-algorithm approach |