ProcessDefinition
A process definition: a set of `milpa/workflow` {@see StateDefinition}s connected by {@see TransitionDefinition}s, one of them flagged `isInitial`. Implements {@see DefinitionContract} so a {@see Reducer} can fold events against it directly, while also exposing the richer workflow entities (states, raw transitions, gates) a caller composing an actual process — or {@see HumanGate} — needs. `initialState()` is derived from `StateDefinition::isInitial()` (exactly one state must carry it) rather than accepted as a separate constructor argument, so there is exactly one source of truth for "where does this process start". At construction, every transition's `fromState`/`toState` must resolve to a state passed in, and the definition is validated acyclic among UNGATED, NON-SUBPROCESS transitions only: a `GateDefinition` attached to a state's outgoing transitions marks that state a human decision point, and a {@see SubprocessSpec} marks it a "waiting on a child process" point — either kind of checkpoint breaks any loop through it (see {@see self::assertAcyclicAmongUngatedTransitions()} for why a full-graph acyclic check would incorrectly reject a `review_gate --reject--> draft --submit--> review_gate` shape, which is a legitimate revise-and-resubmit loop). A state may ALSO be declared a subprocess state via the `$subprocesses` constructor argument (see {@see self::subprocessFor()}) — mutually exclusive with both being terminal and carrying a gate, enforced at construction.
ProcessDefinition::__construct()
public function __construct(array $states, array $transitions, array $subprocesses = []):Parameters
| Name | Type | Description |
|---|---|---|
| $states | list<StateDefinition> | every state this process can occupy; exactly one must have `isInitial() === true` |
| $transitions | list<TransitionDefinition> | every transition between two of `$states` |
| $subprocesses | array<string, SubprocessSpec> | subprocess declarations keyed by the state code they wait at — see {@see self::subprocessFor()} |
Throws
\RuntimeException on a duplicate state code, a transition referencing an unknown
state, a state count other than exactly-one-initial, a subprocess
spec referencing an unknown or terminal state, a state carrying
both a subprocess spec and a gate, or a cycle among ungated,
non-subprocess transitions
ProcessDefinition::initialState()
public function initialState(): stringThe state a fresh process instance starts in — the one state with `isInitial() === true`.
ProcessDefinition::transitionsFrom()
public function transitionsFrom(string $state): arrayThe narrow `{name, to}` shape {@see Reducer} needs, projected down from the real workflow `TransitionDefinition`s: `name` is the transition's `code` (the literal event `type` that advances it — see {@see Reducer::apply()}), `to` is its destination state's code. Unknown `$state` yields an empty list, matching {@see DefinitionContract}'s "no transitions" case.
Parameters
| Name | Type | Description |
|---|---|---|
| $state | string |
ProcessDefinition::workflowTransitionsFrom()
public function workflowTransitionsFrom(string $state): arrayThe raw `milpa/workflow` transitions leaving `$state`, unprojected — for callers that need more than `{name, to}` (e.g. reading a transition's attached {@see GateDefinition}s directly). Prefer {@see self::transitionsFrom()} or {@see self::gateFor()} where they suffice.
Parameters
| Name | Type | Description |
|---|---|---|
| $state | string |
ProcessDefinition::states()
public function states(): arrayEvery state code in this definition, in the order `$states` was passed to the constructor.
ProcessDefinition::isTerminal()
public function isTerminal(string $state): boolWhether `$state` is terminal (`StateDefinition::isTerminal()`). An unknown `$state` is treated as not terminal.
Parameters
| Name | Type | Description |
|---|---|---|
| $state | string |
ProcessDefinition::gateFor()
public function gateFor(string $state): ?Milpa\Workflow\Entities\GateDefinitionThe gate guarding `$state`'s outgoing transitions: the first {@see GateDefinition} found attached to any of `$state`'s transitions, or `null` when none of them carry one. A gated state's transitions are expected to share the SAME gate instance (one human checkpoint per state, however many outcomes it offers) — this is how {@see HumanGate} finds the single gate to open for a process instance sitting at `$state`.
Parameters
| Name | Type | Description |
|---|---|---|
| $state | string |
ProcessDefinition::subprocessFor()
public function subprocessFor(string $state): ?Milpa\Orchestrator\SubprocessSpecThe {@see SubprocessSpec} declaring `$state` a subprocess state — the process WAITS here for a child process (resolved by name through a {@see ProcessDefinitionRegistry}) to reach its own terminal state, exactly like {@see self::gateFor()}'s state waits for a human decision. `null` when `$state` is not a subprocess state.
Parameters
| Name | Type | Description |
|---|---|---|
| $state | string |
ProcessDefinition::isSubprocess()
public function isSubprocess(string $state): boolWhether `$state` is a subprocess state — shorthand for {@see self::subprocessFor()} `!== null`.
Parameters
| Name | Type | Description |
|---|---|---|
| $state | string |