Agent skill
state-machine-transition
Add new states or transitions to Bob The Skull's state machine. Use when adding new states, defining transitions, implementing state behavior, or modifying state machine logic.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/state-machine-transition-want2bet-bobtheskull5
SKILL.md
State Machine Transitions
Adds new states or transitions to Bob The Skull's finite state machine that coordinates system behavior.
When to Use
- Adding new states to the state machine
- Defining valid state transitions
- Implementing state entry/exit actions
- Adding timeout handling for states
- Documenting state behavior
State Machine Overview
Bob uses a finite state machine (FSM) to coordinate all system behavior:
State Machine receives events → Decides transitions → Coordinates responses
Current States:
IDLE- Waiting for wake wordWAKE_LISTENING- Brief pause after wake wordGREETING- Playing greeting audioLISTENING- Recording user speechPROCESSING- LLM thinkingSPEAKING- Bob responding (TTS)OBSERVING- Vision-only modeERROR- Error state with recovery
State Machine Files
Primary file: state_machine/state_machine.py
Key components:
Stateenum - Define all statesBobStateMachineclass - Core logic_setup_event_handlers()- Event subscriptions_transition_to()- State transition logicon_<event>()methods - Event handlers_enter_<state>()methods - State entry actions_exit_<state>()methods - State exit actions
Adding a New State
1. Define State in Enum
python
class State(Enum):
"""Bob's finite state machine states"""
IDLE = "idle"
WAKE_LISTENING = "wake_listening"
GREETING = "greeting"
LISTENING = "listening"
PROCESSING = "processing"
SPEAKING = "speaking"
OBSERVING = "observing"
YOUR_NEW_STATE = "your_new_state" # Add here
ERROR = "error"
2. Define Valid Transitions
python
# Valid state transitions
VALID_TRANSITIONS = {
State.IDLE: [State.WAKE_LISTENING, State.OBSERVING, State.ERROR],
State.WAKE_LISTENING: [State.GREETING, State.LISTENING, State.IDLE, State.ERROR],
State.GREETING: [State.LISTENING, State.IDLE, State.ERROR],
State.LISTENING: [State.PROCESSING, State.IDLE, State.ERROR],
State.PROCESSING: [State.SPEAKING, State.IDLE, State.ERROR],
State.SPEAKING: [State.IDLE, State.OBSERVING, State.ERROR],
State.OBSERVING: [State.IDLE, State.WAKE_LISTENING, State.ERROR],
State.YOUR_NEW_STATE: [State.IDLE, State.ERROR], # Add here
State.ERROR: [State.IDLE]
}
3. Define State Timeouts
python
# State timeout configuration (seconds)
STATE_TIMEOUTS = {
State.WAKE_LISTENING: 1.0, # Brief pause
State.GREETING: 10.0, # Max greeting time
State.LISTENING: 30.0, # Max listening time
State.PROCESSING: 60.0, # Max LLM time
State.SPEAKING: 120.0, # Max TTS time
State.OBSERVING: None, # No timeout (continuous)
State.YOUR_NEW_STATE: 15.0, # Add appropriate timeout
State.IDLE: None, # No timeout
State.ERROR: 5.0 # Error recovery time
}
4. Implement Entry Action
python
def _enter_your_new_state(self):
"""Entry action for YOUR_NEW_STATE"""
logger.info("Entering YOUR_NEW_STATE")
# Start operations for this state
# Example: Start a component, publish event, etc.
# Publish state change event
self.event_bus.publish(StateChangedEvent(
old_state=self.previous_state.value if self.previous_state else None,
new_state=State.YOUR_NEW_STATE.value,
timestamp=datetime.now()
))
5. Implement Exit Action (Optional)
python
def _exit_your_new_state(self):
"""Exit action for YOUR_NEW_STATE"""
logger.info("Exiting YOUR_NEW_STATE")
# Cleanup operations
# Example: Stop component, clear state, etc.
6. Add Event Handlers
python
def _setup_event_handlers(self):
"""Subscribe to events"""
# ... existing subscriptions ...
self.event_bus.subscribe(YourTriggerEvent, self.on_your_trigger_event)
def on_your_trigger_event(self, event: YourTriggerEvent):
"""Handle event that triggers transition to YOUR_NEW_STATE"""
logger.info(f"Received YourTriggerEvent: {event}")
if self.current_state == State.IDLE:
# Transition to new state
self._transition_to(State.YOUR_NEW_STATE)
7. Add Timeout Handler (If Needed)
python
def _on_timeout(self, state: State):
"""Handle state timeout"""
logger.warning(f"Timeout in state: {state.value}")
if state == State.YOUR_NEW_STATE:
# Handle timeout for your state
logger.error("YOUR_NEW_STATE timed out")
self._transition_to(State.IDLE) # Or appropriate fallback
# ... handle other states ...
State Transition Pattern
python
def _transition_to(self, new_state: State):
"""
Transition to a new state
Args:
new_state: Target state
"""
# Validate transition
if new_state not in VALID_TRANSITIONS.get(self.current_state, []):
logger.error(f"Invalid transition: {self.current_state.value} -> {new_state.value}")
return False
# Exit current state
exit_method = getattr(self, f'_exit_{self.current_state.value}', None)
if exit_method:
try:
exit_method()
except Exception as e:
logger.error(f"Error in exit action: {e}", exc_info=True)
# Update state
self.previous_state = self.current_state
self.current_state = new_state
self.state_start_time = time.time()
# Enter new state
entry_method = getattr(self, f'_enter_{new_state.value}', None)
if entry_method:
try:
entry_method()
except Exception as e:
logger.error(f"Error in entry action: {e}", exc_info=True)
self._transition_to(State.ERROR)
return False
# Start timeout timer if configured
timeout = STATE_TIMEOUTS.get(new_state)
if timeout:
self._start_timeout_timer(timeout)
logger.info(f"State transition: {self.previous_state.value} -> {new_state.value}")
return True
Common State Patterns
Transient States (Brief, automatic exit)
python
State.WAKE_LISTENING # 1 second pause, then auto-transition
State.GREETING # Play audio, then transition
Characteristics:
- Short timeout
- Entry action starts operation
- Auto-transition when operation completes
Active States (Wait for events)
python
State.LISTENING # Wait for speech or silence
State.PROCESSING # Wait for LLM response
Characteristics:
- Moderate timeout
- Entry action starts component
- Exit on component completion event
Continuous States (No timeout)
python
State.IDLE # Wait indefinitely for wake word
State.OBSERVING # Vision-only mode, no timeout
Characteristics:
- No timeout (None)
- Only exit on specific events
- May have periodic actions
Error State (Recovery)
python
State.ERROR # Brief error state, then return to IDLE
Characteristics:
- Short timeout (5s)
- Entry logs error details
- Auto-transition to IDLE on timeout
Event-Driven Transitions
Most transitions are triggered by events:
python
# Wake word detected → WAKE_LISTENING
def on_wake_word_detected(self, event: WakeWordDetectedEvent):
if self.current_state == State.IDLE:
self._transition_to(State.WAKE_LISTENING)
# Speech recognized → PROCESSING
def on_speech_recognized(self, event: SpeechRecognizedEvent):
if self.current_state == State.LISTENING:
self.user_input = event.transcript
self._transition_to(State.PROCESSING)
# LLM response → SPEAKING
def on_llm_response_ready(self, event: LLMResponseReadyEvent):
if self.current_state == State.PROCESSING:
self._transition_to(State.SPEAKING)
Configuration Integration
States can be configured via BobConfig.py:
python
# State machine configuration
STATE_MACHINE_WAKE_LISTENING_DURATION: float = 1.0
STATE_MACHINE_LISTENING_TIMEOUT: int = 30
STATE_MACHINE_PROCESSING_TIMEOUT: int = 60
STATE_MACHINE_SPEAKING_TIMEOUT: int = 120
STATE_MACHINE_ERROR_RECOVERY_TIME: int = 5
STATE_MACHINE_YOUR_NEW_STATE_TIMEOUT: int = 15 # Add config
Reference in state machine:
python
STATE_TIMEOUTS = {
State.YOUR_NEW_STATE: getattr(config, 'STATE_MACHINE_YOUR_NEW_STATE_TIMEOUT', 15.0)
}
State Diagram Documentation
Update state diagram in documentation:
[IDLE] --wake word--> [WAKE_LISTENING] --pause complete--> [GREETING]
[GREETING] --greeting done--> [LISTENING]
[LISTENING] --speech recognized--> [PROCESSING]
[PROCESSING] --LLM response--> [SPEAKING]
[SPEAKING] --speech done--> [IDLE]
[YOUR_NEW_STATE] --trigger--> [IDLE] # Document new transitions
Testing State Transitions
python
# test_state_machine.py
def test_your_new_state_transition():
"""Test transition to YOUR_NEW_STATE"""
state_machine = BobStateMachine(event_bus, config)
# Start in IDLE
assert state_machine.current_state == State.IDLE
# Trigger transition
event_bus.publish(YourTriggerEvent(...))
# Verify transition
assert state_machine.current_state == State.YOUR_NEW_STATE
# Verify timeout
time.sleep(16) # Wait for timeout
assert state_machine.current_state == State.IDLE
Checklist
- Add state to State enum
- Add valid transitions to VALID_TRANSITIONS
- Add timeout to STATE_TIMEOUTS
- Implement
_enter_<state>()method - Implement
_exit_<state>()method (if needed) - Add event handlers for transitions
- Add timeout handler in
_on_timeout() - Add configuration parameters (if needed)
- Update state diagram documentation
- Write unit tests for state
- Test transitions from all valid states
- Test timeout behavior
- Test error recovery
Pro Tips
- Keep states focused - Each state should have one clear purpose
- Define all transitions - Explicitly list valid from→to pairs
- Add timeouts - Every active state should have a timeout for recovery
- Log everything - State transitions are critical for debugging
- Test edge cases - What if event arrives in wrong state?
- Document intent - Explain why state exists in docstring
- Handle errors - All entry/exit actions should handle exceptions
Didn't find tool you were looking for?