# Graph — every state and every transition of a workflow

> The whole shape of a workflow — every state (with its label, and whether it starts or ends a run) and every transition the declaration makes possible — as an ordinary value. Built by the compiler from the declaration, so it costs nothing to read anywhere; it is what you draw a picture of a process from.

<!-- id: workflow-graph · area: workflow · stability: preview · html: https://osysharp.com/reference/workflow/graph/ -->

## Summary        {#summary}
**`<Wf>.Graph`** is the answer to *"what does this process look like?"* — the question
[Transitions — where this item may go next](https://osysharp.com/reference/workflow/transitions/) does not answer, because it is asked of ONE run and says only where that run may go next.

```osy syntax
var g = OrderFlow.Graph;
g.States        // every state, in declaration order
g.Transitions   // every declared transition, each joining two real states
```

It is **not a server read**. The graph is the declaration, the same for every run, so the compiler builds it — from
the very graph `osy model`, the workflow diagram and the workflow lints use — and a page reads it like a constant.

Where a run **is** is its own tracked field (`order.Status`, see [Tracks and Initial (the field a workflow drives)](https://osysharp.com/reference/workflow/tracks/)), live like any field; where it
**has been** is `<Wf>.For(item).TimeInStates` ([Flow metrics — how long an item took, and how much was waiting](https://osysharp.com/reference/workflow/flow-metrics/)). Those three are everything a live picture
of a run needs.

## Signature      {#signature}
```osy syntax
<Wf>.Graph                       // WorkflowGraph
  .States                        // WorkflowStateInfo[]  — Id, Name, Label, IsInitial, IsTerminal, Outcome
  .Transitions                   // WorkflowTransitionInfo[] — Id, From, To, Trigger, Kind, Guard
```

| `WorkflowStateInfo` | |
|---|---|
| `Id` | a key — the state's name, unique in its workflow |
| `Name` | as declared (`Placed`) — what `goto` and the tracked enum spell |
| `Label` | for a person: the tracked enum member's `[Label]`, or its name |
| `IsInitial` / `IsTerminal` | where a run starts; where it ends |
| `Outcome` | `Success`, `Cancel` or `Error` for a terminal; empty otherwise |

| `WorkflowTransitionInfo` | |
|---|---|
| `Id` | a key unique in the graph |
| `From` / `To` | the two states' `Id`s |
| `Trigger` | what fires it: an event (`Ship`), `complete when`, `enter`, a clock (`Expire`), an SLA breach |
| `Kind` | `event`, `completion`, `timeout`, `cancel` or `sla` |
| `Guard` | a `when (…)` arm's condition as written; null when unconditional |

## Description    {#description}

### What counts as a transition   {#edges}

Every way the declaration can move a run: each state's `on X { goto Y; }` (every arm of it), `complete when (…) goto Y`,
a `goto` in a state's `enter` hook, and an SLA breach body. A route declared on the **workflow** (`on Cancel { goto
Cancelled; }`) applies in every state that is not terminal, so it appears once **per state it leaves** — each edge
joins two real states. Its `Kind` (`cancel`) is how a picture leaves that fan out when it wants to:

```osy syntax
var moves = OrderFlow.Graph.Transitions.Where(t => t.Kind != "cancel");
```

### Reading it   {#reading}

```osy title="a list of a workflow's states, and how many ways lead into each" test app=workflow-graph
using Osysharp.Workflow;

enum ParcelStatus { Placed, [Label("Out for delivery")] Shipped, Delivered, Cancelled }

entity Parcel {
  [Required, MaxLength(40)] string Ref;
  ParcelStatus Status;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow Shipping {
  Tracks    = Parcel.Status;
  Autostart = true;
  Initial   = Placed;

  event Ship();
  event Deliver();
  event Cancel();

  state Placed {
    subscribe Ship();
    on Ship { goto Shipped; }
  }

  state Shipped {
    subscribe Deliver();
    on Deliver { goto Delivered; }
  }

  on Cancel { goto Cancelled; }

  terminal success Delivered { }
  terminal cancel Cancelled { }
}

[Route("/process")] [AllowAnonymous]
component Process() {
  render {
    Stack(gap: 2) {
      foreach (var s in Shipping.Graph.States) {
        Text(s.Label + " — " + Shipping.Graph.Transitions.Count(t => t.To == s.Id) + " way(s) in");
      }
    }
  }
}
```

### Drawing it   {#drawing}

The [The flow kit — a node-and-edge canvas whose nodes are your own Osy#](https://osysharp.com/reference/ui/flow-kit/) canvas draws a workflow from this directly — `nodes: OrderFlow.Graph.States`,
`edges: OrderFlow.Graph.Transitions`, `sourceField: e => e.From`, `targetField: e => e.To` — lays it out itself, and marks a
run's current state and path with `highlighted` and `visited`. See *A live picture of a workflow run* there.

## See also       {#see-also}
- [Transitions — where this item may go next](https://osysharp.com/reference/workflow/transitions/) — where ONE run may go next, with its guards evaluated
- [Tracks and Initial (the field a workflow drives)](https://osysharp.com/reference/workflow/tracks/) — the field that says where a run is
- [Flow metrics — how long an item took, and how much was waiting](https://osysharp.com/reference/workflow/flow-metrics/) — `TimeInStates`: where a run has been
- [The flow kit — a node-and-edge canvas whose nodes are your own Osy#](https://osysharp.com/reference/ui/flow-kit/) — the canvas that draws it
