Line

Construct

Explain workflows in English

Turn state machines into readable steps and test scenarios.

Construct reads the real XState machines in a feature's workflows/ folder and explains them in plain English, with every start-to-end scenario and any problems it can spot. Nothing is stored, so the explanation cannot go stale.

Use it#

CLI (read-only):

construct research workflow <feature> [<file>] [--format prose|md|json|scenarios] [--dir <path>]

<file> is relative to features/<feature>/workflows/; leave it out to explain every workflow file. --format json prints pure JSON that can be piped.

UI: the Workflows screen shows the same three panels ("In plain English", "Scenarios", "Health") under each diagram. They refresh on load, on "Re-read from source" and right after a confirmed visual edit.

Guardrails: construct validate reports WORKFLOW-002 (unreachable state) and WORKFLOW-003 (non-final state with no way out) as warnings for workflow files it can read. Set them to error or off under rules: in architecture.yml.

WORKFLOW-004 (transition-table completeness) is off by default; opt in with rules: { WORKFLOW-004: warning } in architecture.yml. An event is any name a machine handles in an on: map of at least one state; every atomic, non-final state must decide each of them (events a parent state handles count for its children). Each missing (state, event) pair is one violation at the state's line. To say "this state deliberately does nothing on that event", write the explicit ignore form, a transition with no target, no actions and no guard: on: { CANCEL: {} }. Any real transition, or {}, counts as a decision.

What you get, per machine#

  • In plain English: what each state does, in source order. Identifiers become words (paymentFailed becomes "payment failed", isLowValue becomes "it is low value"), guards become "only if ...", actions become "and then runs notifyCustomer", delays become "After 48 hours", services become "starts issueRefund".
  • Scenarios: every distinct route from the start to an end state as Given/When/Then, happy path first (the shortest route to the first end state). Loops are shown once as "this part can repeat"; output is capped at 25 scenarios. Each scenario also has a machine-readable form (event sequence, steps, end state).
  • Health: unreachable states, dead ends, no end state, conditional transitions with no fallback, two unguarded transitions on one event, and targets that do not exist.

Real output#

construct research workflow shop CheckoutWorkflow.tsx, for a workflow generated by construct create workflow Checkout --feature shop --from fixtures/workflow-graphs/checkout.json:

Checkout
The "checkout" flow has 3 steps. It starts in *idle* and can end in *done*.

Idle
  - This flow starts in *idle*.
  - When "submit" happens, the flow moves to *submitting*.

Submitting
  - When "success" happens, the flow moves to *done*.
  - When "failure" happens, the flow moves to *idle* — only if it has error.

Done
  - *done* is an end state — the flow stops there.

Scenarios
  Happy path
    Route: idle → submitting → done
    Given the flow starts in *idle*
    When "submit" happens
    Then the flow moves to *submitting*
    And when "success" happens
    Then the flow moves to *done*
    And the flow ends — *done* is an end state
    Note: from *submitting* the flow can go back to *idle* when "failure" happens (only if it has error), so this part can repeat.

Health
  - [no-fallback] In *submitting*, "failure" only applies under a condition (it has error) and there is no fallback, so if none holds, nothing happens.

A richer example (guards, a 48 hour timeout, a retry loop, two end states) is fixtures/workflow-graphs/refund-request.ts; its exact expected output is in fixtures/workflow-graphs/golden/.