Declarative State Machines¶
rp1 supports declarative workflow state management via embedded Mermaid state diagrams. Skills and agents that include a ## STATE-MACHINE section with a stateDiagram-v2 block get validated state transitions, dashboard visibility with step timelines, and run isolation -- without any changes to CLI, API, or UI code.
How It Works¶
A skill or agent opts in to state management by embedding a stateDiagram-v2 mermaid block inside a ## STATE-MACHINE section in its markdown file:
## STATE-MACHINE
```mermaid
stateDiagram-v2
[*] --> plan
plan --> build : plan_ready
build --> review : build_complete
review --> [*] : done
On each phase transition, report via: ...
The system extracts and parses the state diagram into a typed model and uses it for:
- **Transition validation**: The CLI rejects invalid state transitions
- **Dashboard step timelines**: Steps are derived dynamically from the state machine
- **Run isolation**: Each workflow invocation is tracked independently via run IDs
- **WebSocket events**: Real-time progress updates pushed to the dashboard
- **Agent sub-state tracking**: Agents report state validated against their own state machine, nested within the parent workflow
Skills and agents without a `## STATE-MACHINE` section are completely unaffected -- no tracking, no validation, no dashboard presence.
---
## Defining a State Machine
Embed a standard [Mermaid stateDiagram-v2](https://mermaid.js.org/syntax/stateDiagram.html) block inside a `## STATE-MACHINE` section:
```mermaid
stateDiagram-v2
[*] --> plan
plan --> build : plan_ready
build --> review : build_complete
review --> [*] : done
Supported Syntax¶
| Syntax | Example | Purpose |
|---|---|---|
| Initial transition | [*] --> state_id |
Marks the starting state(s) |
| Terminal transition | state_id --> [*] |
Marks ending state(s) |
| Simple transition | source --> target |
State-to-state edge |
| Labeled transition | source --> target : label |
Edge with description (informational) |
| State declaration | state state_id : Description |
State with display label |
| Comment | %% comment text |
Ignored by parser |
Not Supported¶
These features are intentionally out of scope:
- Nested/composite states (state Parent { ... })
- Fork/join (<<fork>>, <<join>>)
- Concurrent regions
- Notes, direction directives
Rules¶
- State IDs must match step field values used in
rp1 agent-tools emit --stepcommands - At least one initial state is required (
[*] --> state_id) - Terminal states are optional but recommended (
state_id --> [*]) - Transition labels are informational -- validation operates on state-to-state edges, not labels
- One state machine per file -- the extractor uses the first
stateDiagram-v2block in the## STATE-MACHINEsection
Skill State Machines¶
Skills define their state machine directly in SKILL.md:
## STATE-MACHINE
```mermaid
stateDiagram-v2
[*] --> plan
plan --> build : plan_ready
build --> review : build_complete
review --> [*] : done
On each phase transition, report via: rp1 agent-tools emit \ --workflow {WORKFLOW_NAME} \ --type status_change \ --run-id {RUN_ID} \ --step {CURRENT_STATE} \ --data '{"status": "running"}'
- Generate
RUN_IDas a UUID at workflow start - Report each step with
--data '{"status": "running"}'when entering it - For non-terminal states: moving to the next state implies the previous completed
- For terminal states (those with
→ [*]transitions): report with--data '{"status": "completed"}'when the step's work finishes - Follow transition edges in the graph; do not skip states
--- ## Agent State Machines Agents can also define state machines, enabling validated state tracking at the agent level. Agent state machines are embedded in the agent `.md` file: ```markdown ## STATE-MACHINE ```mermaid stateDiagram-v2 [*] --> building building --> completed : build_success building --> failed : build_error completed --> [*] failed --> [*]
On each transition, report via: rp1 agent-tools emit \ --workflow {WORKFLOW} \ --type status_change \ --run-id {RUN_ID} \ --step {CURRENT_STATE} \ --unit {TASK_ID} \ --data '{"status": "running"}'
### Agent Status Reporting
Agents report status using the same `emit` command. The `--unit` flag can be used for per-task tracking:
```bash
rp1 agent-tools emit \
--workflow build \
--type status_change \
--run-id "550e8400-e29b-41d4-a716-446655440000" \
--step building \
--unit T1 \
--data '{"status": "running"}'
--workflowidentifies which workflow this run belongs to--run-idassociates the event with the parent workflow run--unitenables per-task tracking within the agent
Sub-Agent Namespaced Steps¶
Sub-agents that emit steps into a parent run must prefix their step names with the agent identifier and a colon separator. This prevents sub-agent state IDs from colliding with parent workflow states.
Format: {agent-name}:{step-name}
| Agent | Namespaced Steps |
|---|---|
| task-builder | task-builder:building, task-builder:completed, task-builder:failed |
| feature-verifier | feature-verifier:verifying, feature-verifier:completed, feature-verifier:failed |
| task-reviewer | task-reviewer:reviewing, task-reviewer:completed, task-reviewer:failed |
| hypothesis-tester | hypothesis-tester:testing, hypothesis-tester:completed, hypothesis-tester:failed |
Namespaced steps are persisted with their full prefixed name and are not validated against the parent workflow's state machine.
Per-Task Tracking with --unit¶
When an agent processes multiple tasks (e.g., task-builder implementing T1, T2, T3), each task's state is tracked independently using the --unit flag:
# T1 starts building
rp1 agent-tools emit --workflow build --type status_change --run-id run-1 \
--step building --unit T1 --data '{"status": "running"}'
# T1 completes
rp1 agent-tools emit --workflow build --type status_change --run-id run-1 \
--step completed --unit T1 --data '{"status": "completed"}'
# T2 starts building (independent of T1)
rp1 agent-tools emit --workflow build --type status_change --run-id run-1 \
--step building --unit T2 --data '{"status": "running"}'
- Each task progresses through the workflow independently
- The dashboard shows per-task state within the agent's nested view
Parent Skill Context¶
Parent skills that spawn agents with state machines must pass workflow context so agent updates are attributed to the correct run:
| Parameter | Purpose |
|---|---|
| WORKFLOW | Parent skill name (e.g., "build") |
| RUN_ID | Parent workflow's run UUID |
| FEATURE_ID | Feature identifier |
Two-Layer State Model¶
The system maintains two orthogonal state dimensions for state-machine-enabled workflows:
| Dimension | What it represents | Values | Storage |
|---|---|---|---|
| StatusValue | Activity category (WHAT is happening) | not_started, running, waiting, completed, failed, skipped | status column |
| WorkflowState | Workflow phase (WHERE in the workflow) | Defined by state diagram (e.g., requirements, design, build) | step column |
These are independent: a workflow can be "in_progress" at the "design" phase, or "waiting-input" at the "requirements" phase.
CLI Usage¶
Reporting State Transitions (Skills)¶
rp1 agent-tools emit \
--workflow build \
--type status_change \
--run-id "550e8400-e29b-41d4-a716-446655440000" \
--step design \
--data '{"status": "running", "feature": "my-feature"}'
Reporting State Transitions (Agents)¶
rp1 agent-tools emit \
--workflow build \
--type status_change \
--run-id "550e8400-e29b-41d4-a716-446655440000" \
--step building \
--unit T1 \
--data '{"status": "running", "feature": "my-feature"}'
CLI Flags¶
| Flag | Required | Description |
|---|---|---|
--type |
Yes | Event type (e.g., status_change, artifact_registered) |
--workflow |
Yes | Workflow name (e.g., build, pr-review) |
--run-id |
Yes | UUID grouping events into a discrete workflow run |
--step |
Yes (for status_change) | The workflow/agent state (must be a valid state ID) |
--unit |
No | Task/unit identifier for per-task tracking |
--data |
Yes (for status_change) | JSON payload with status (e.g., '{"status": "running"}') |
--project |
No | Project path (defaults to cwd) |
Step Validation¶
Every --step value is validated against the workflow's state machine before the event is persisted. Validation is strict -- there is no lenient mode, no opt-out flag, and no warn-and-persist fallback.
Rules:
- If the step name is not a valid state ID in the workflow's state machine, the emit is rejected with a non-zero exit code and the event is not persisted.
- Workflows without a
## STATE-MACHINEsection skip validation entirely (no error). - Namespaced steps (containing a colon, e.g.,
task-builder:building) bypass parent state machine validation. See Sub-Agent Namespaced Steps.
Error message format (with known current state):
Error: step "biulding" is not a valid state in the "build" state machine. Valid states: [requirements, design, tasks, build, verify, archive]. Current state: "tasks". Valid transitions from "tasks": [build].
Error message format (unknown current state, e.g., first emit):
Error: step "biulding" is not a valid state in the "build" state machine. Valid states: [requirements, design, tasks, build, verify, archive].
Error messages include valid transitions from the current state so that calling agents can self-correct on retry without consulting external documentation.
Predecessor Auto-Completion¶
When a step-level status_change event with status "running" is emitted (and no --unit is set), the system automatically completes direct predecessor steps that are still in "running" or "waiting" status.
How it works:
- The system uses the state machine's transition graph to identify direct predecessors of the current step. A direct predecessor is any state that has a transition edge leading to the current step.
- For each direct predecessor whose latest status is
"running"or"waiting", astatus_changeevent with{ "status": "completed" }is automatically inserted, timestamped just before the current event. - Predecessors with other statuses (
"completed","failed","skipped") are not modified.
Why graph-based predecessors only:
Only direct predecessors in the state machine graph are auto-completed -- not all steps currently in "running" status. This preserves correctness for parallel workflow branches where multiple steps may legitimately be running concurrently. Sibling branches are never touched.
Exclusions:
| Condition | Behavior |
|---|---|
--unit is set |
No predecessor auto-completion (unit-level events manage their own lifecycle) |
| Step is namespaced (contains colon) | No predecessor auto-completion |
Status is not "running" |
No predecessor auto-completion |
Example:
Given a workflow with transitions tasks --> build --> verify, and tasks has latest status "running":
rp1 agent-tools emit --workflow build --type status_change --run-id run-1 \
--step build --data '{"status": "running"}'
The system auto-inserts a "completed" event for tasks (timestamped just before the build event), then persists the build running event. The run timeline shows tasks as completed and build as running.
Transition Validation¶
Invalid transitions are rejected:
The first update for a run must target an initial state (one reached via [*] --> in the diagram).
Run Isolation¶
Each --run-id creates an independent workflow invocation. Multiple concurrent runs of the same workflow on the same feature are tracked separately:
# Run A at "verify" phase
rp1 agent-tools emit --workflow build --type status_change --run-id run-A --step verify --data '{"status": "running"}'
# Run B at "design" phase (independent)
rp1 agent-tools emit --workflow build --type status_change --run-id run-B --step design --data '{"status": "running"}'
Examples¶
Existing State Machines¶
Skills:
| Skill | States | Shape |
|---|---|---|
| build | requirements, design, tasks, build, verify, archive | Linear with verify->build retry loop |
| build-fast | plan, build, review | Linear (3 steps) |
| pr-review | split, review, synthesize, post | Linear (4 steps) |
| deep-research | clarify, plan, explore, synthesize, report | Linear (5 steps) |
| blueprint | detect, charter, prd | Branching (detect -> charter or prd) |
Agents:
| Agent | States | Shape |
|---|---|---|
| task-builder | building, completed, failed | Linear with error branch |
| task-reviewer | reviewing, completed, failed | Linear with error branch |
| feature-verifier | verifying, completed, failed | Linear with error branch |
| hypothesis-tester | testing, completed, failed | Linear with error branch |
Adding State Tracking to a New Skill¶
- Add a
## STATE-MACHINEsection to yourSKILL.md:
## STATE-MACHINE
```mermaid
stateDiagram-v2
[*] --> scan
scan --> analyze : scan_complete
analyze --> report : analysis_complete
report --> [*] : done
2. Include the CLI command template for reporting transitions (see template above).
3. The skill now appears in the dashboard with a 3-step timeline -- no API or UI code changes needed.
### Adding State Tracking to an Agent
1. Add a `## STATE-MACHINE` section to the agent `.md` file with the state diagram and CLI template.
2. Ensure the parent skill passes `WORKFLOW`, `RUN_ID`, and `FEATURE_ID` to the agent.
3. Agent state transitions appear nested within the parent workflow's phase on the dashboard.
### Registering Artifacts
Skills that produce output files (reports, design docs, task files) should register them explicitly so the dashboard can display them:
```bash
rp1 agent-tools emit \
--workflow {WORKFLOW} \
--type artifact_registered \
--run-id {RUN_ID} \
--step {step} \
--data '{"path": "{relative_path_to_artifact}", "feature": "{FEATURE_ID}"}'
pathin the data payload is relative to the project root (e.g.,.rp1/work/features/my-feature/tasks.md)- For subflow diagrams, add
"subflow": trueto the data payload - Artifacts are stored in the
artifactstable in~/.rp1/rp1.db - The dashboard queries this table instead of scanning the filesystem
Related Concepts¶
- SKILL.md Format -- How skills are structured
- Skill-Agent Pattern -- How skills delegate to agents
- Web UI Dashboard -- Where workflow progress is displayed