Plugin lifecycle state machine
Plugin lifecycle state machine
stateDiagram-v2
[*] --> Discovered
Discovered --> Registered : manifest parsed, validated
Registered --> Launching : first request / ping
Launching --> Ready : ping response received
Launching --> Crashed : ping timeout
Ready --> Busy : request dispatched
Ready --> Terminated : idle timeout
Ready --> Crashed : process exit
Busy --> Ready : response received
Busy --> Crashed : process exit
Busy --> Crashed : request timeout
Crashed --> Launching : below crash threshold (auto-restart)
Crashed --> Disabled : at crash threshold
Terminated --> Launching : next request
Disabled --> [*] : (app restart required)
note right of Crashed
Crash threshold: 5 crashes in 5 minutes
(sliding window, in-memory).
Counts reset on app restart.
end note
Transitions
| From | To | Trigger | Notes |
|---|---|---|---|
| Discovered | Registered | Manifest parsed, validated | Plugin.toml valid, capabilities known |
| Registered | Launching | First request or ping | Process spawned, stdin/stdout connected |
| Launching | Ready | Ping response received | Capabilities re-confirmed |
| Launching | Crashed | Ping timeout (2s) | Process may be hung or incompatible |
| Ready | Busy | Request dispatched | JSON-RPC sent to stdin |
| Ready | Terminated | Idle timeout (5min) | Clean shutdown, process killed gracefully |
| Ready | Crashed | Process exit | Unexpected exit during idle |
| Busy | Ready | Response received | JSON-RPC response parsed |
| Busy | Crashed | Process exit | Crash during request handling |
| Busy | Crashed | Request timeout | Per-method timeout exceeded, process killed |
| Crashed | Launching | Below threshold | Auto-restart on next request |
| Crashed | Disabled | At threshold | 5 crashes in 5 minutes |
| Terminated | Launching | Next request | Lazy restart on next request |
Events emitted
plugin_status_changed event emitted on every transition out of Ready, Crashed, and Disabled.
Crash threshold algorithm
Per-plugin VecDeque<Instant>:
- On each crash, push
Instant::now() - Drain entries older than 5 minutes from the front
- If
len() >= 5, set status toDisabled - No persistence — counts reset on app restart
References
- ADR-001 (plugin IPC)
- FR-PLUGIN-3 (plugin lifecycle)