| name | plan-state-machines |
| description | State enums, transition validators per WP |
| argument-hint | Invoked by Planner Coordinator - do not call directly |
plan-state-machines
Phase: 2 (Contract Generation)
Common contract: .github/skills/PLAN-SKILL-CONTRACT.md
Spec refs: FR-046, FR-047, FR-039
This skill is dispatched by the Planner Coordinator during Phase 2. It reads the plan accumulator and generates language-specific state machine contract files for WPs with stateful entities.
Input Contract (FR-023)
| # | Input | Description |
|---|
| 1 | skill_path | Path to this SKILL.md file |
| 2 | plan_dir | Path to .sdd/plans/ directory |
| 3 | contracts_dir | Path to .sdd/plans/contracts/ directory |
| 4 | spec_path | Path to the source spec file |
| 5 | spec_artifacts_dir | Path to spec companion artifacts |
| 6 | research_summary | Key findings from the research phase |
| 7 | target_language | Programming language for contract generation |
| 8 | patterns | Active plan-domain patterns to avoid |
| 9 | phase | Phase indicator (always 2 for this skill) |
Execution Sequence (FR-024)
- Read SKILL.md - Load this file for state machine generation instructions
- Read plan state - Read README and all WP files to identify WPs with stateful entities
- Read spec + artifacts - Read the spec and the companion artifact
state-machines.<ext> from spec_artifacts_dir. Read spec and artifact files in parallel.
- Write contract files - Write
state-machines.<ext> to contracts_dir/<WP-slug>/ per applicable WP
Step 1 - Identify Stateful Entities
Read the plan accumulator and spec data model. Identify entities with:
- A status or state field (e.g.,
status: 'draft' | 'pending' | 'approved' | 'rejected')
- Lifecycle stages (e.g., order workflow, review pipeline)
- Defined state transitions in the spec
Skip WPs that:
- Do not create or modify entities with state fields
- Only read stateful entities without changing their state
Build the entity-state mapping:
Order -> [draft, pending, approved, shipped, delivered, cancelled]
Review -> [open, in_progress, changes_requested, approved, rejected]
Step 2 - Read Spec Companion Artifact (FR-047)
Check if spec_artifacts_dir contains a state-machines.<ext> file. If it exists:
- Parse the spec-level state definitions, transitions, and guards
- These are canonical -- WP-level state machines MUST match them
- Scope each WP's state machine to only the entities that WP creates or modifies
If the spec artifact does NOT exist:
- Derive state machines from the spec's data model section and FR descriptions
- Look for status/state fields, lifecycle descriptions, and workflow diagrams
- Note in a comment that these were derived from spec prose
Step 3 - Generate State Machine Contract Files (FR-046)
For each applicable WP, generate <contracts_dir>/<WP-slug>/state-machines.<ext>:
3a. Manifest Header (FR-039)
// Generated by: plan-state-machines skill
// Source spec: <spec_path>
// Work package: WP<NN>-<slug>
// Target language: <target_language>
// DO NOT EDIT MANUALLY -- regenerated on plan revision
3b. State Enum Definitions (FR-046.1)
Define all valid states for each stateful entity:
export enum OrderStatus {
DRAFT = 'draft',
PENDING = 'pending',
APPROVED = 'approved',
SHIPPED = 'shipped',
DELIVERED = 'delivered',
CANCELLED = 'cancelled',
}
from enum import Enum
class OrderStatus(str, Enum):
DRAFT = 'draft'
PENDING = 'pending'
APPROVED = 'approved'
SHIPPED = 'shipped'
DELIVERED = 'delivered'
CANCELLED = 'cancelled'
3c. Transition Validation Functions (FR-046.2)
Define which transitions are valid with guard conditions:
export interface StateTransition<S> {
from: S;
to: S;
guard?: () => boolean;
description: string;
}
export const ORDER_TRANSITIONS: StateTransition<OrderStatus>[] = [
{ from: OrderStatus.DRAFT, to: OrderStatus.PENDING, description: 'Submit order for review' },
{ from: OrderStatus.PENDING, to: OrderStatus.APPROVED, description: 'Approve order' },
{ from: OrderStatus.PENDING, to: OrderStatus.CANCELLED, description: 'Cancel pending order' },
{ from: OrderStatus.APPROVED, to: OrderStatus.SHIPPED, description: 'Mark as shipped' },
{ from: OrderStatus.SHIPPED, to: OrderStatus.DELIVERED, description: 'Mark as delivered' },
];
export function isValidTransition(current: OrderStatus, next: OrderStatus): boolean {
return ORDER_TRANSITIONS.some(t => t.from === current && t.to === next);
}
ORDER_TRANSITIONS: list[tuple[OrderStatus, OrderStatus, str]] = [
(OrderStatus.DRAFT, OrderStatus.PENDING, 'Submit order for review'),
(OrderStatus.PENDING, OrderStatus.APPROVED, 'Approve order'),
(OrderStatus.PENDING, OrderStatus.CANCELLED, 'Cancel pending order'),
(OrderStatus.APPROVED, OrderStatus.SHIPPED, 'Mark as shipped'),
(OrderStatus.SHIPPED, OrderStatus.DELIVERED, 'Mark as delivered'),
]
def is_valid_transition(current: OrderStatus, next_state: OrderStatus) -> bool:
return any(f == current and t == next_state for f, t, _ in ORDER_TRANSITIONS)
Guard conditions:
- Express as typed functions that return boolean
- Document what each guard checks (e.g., "user must have admin role")
- Reference the spec's preconditions for each transition
3d. Side Effect Declarations (FR-046.3)
Document side effects triggered by each transition:
export interface TransitionSideEffect<S> {
transition: { from: S; to: S };
effects: string[];
}
export const ORDER_SIDE_EFFECTS: TransitionSideEffect<OrderStatus>[] = [
{
transition: { from: OrderStatus.PENDING, to: OrderStatus.APPROVED },
effects: ['Send approval notification email', 'Create shipment record'],
},
{
transition: { from: OrderStatus.APPROVED, to: OrderStatus.SHIPPED },
effects: ['Send shipping notification email', 'Update inventory'],
},
];
Side effects are declarative descriptions, not implementations:
- What happens (send email, update record, trigger webhook)
- Who is notified
- What secondary records are created/modified
Step 4 - 800-Line Block Compliance (FR-013)
If a WP's state machine file exceeds 800 lines, split generation across multiple writes.
Constraints
- Output MUST be valid syntax in the target language
- Do NOT include state machine runtime logic (e.g., event loops, handlers) -- only definitions and validators
- Do NOT modify WP markdown files or spec artifacts -- read only
- Only write to
<contracts_dir>/<WP-slug>/state-machines.<ext>
- Do NOT generate files for WPs without stateful entities
- Transition validation MUST be exhaustive -- every valid transition from the spec must be represented
- Use plain ASCII hyphens and straight quotes only -- no em dashes, smart quotes, or curly apostrophes