# Procedural links

> The OPM language · https://spoletta.com/docs/procedural-links

Procedural links join a process and an object on the same board, and say what the process does to the object. They are what a run follows. Each is drawn below as the board draws it, with the sentence Spoletta writes for it.

## Drawing a link

Inputs enter a card at its top, outputs leave from its bottom, and each state of an object has handles of its own. Drag from one handle to another:

-   **Object → process** makes an input link: the process takes the object.
-   **Process → object** makes an output link: the process yields the object. A process links to a given object only once.
-   Dropping on a state's row links that state; dropping on the card's body still connects.

Select an input link to choose how the process takes the object: **takes**, **instrument** or **consumes**. To delete a link, select it and press the delete key.

## Transforming links

What the process does to the object itself: makes it, uses it up, or changes it.

### Result: the process creates the object

OPL: **Draft** yields **Notes**.

The everyday output link. Once Draft succeeds, Notes exists, and processes waiting for it can run. Drawn to a state, it creates the object in that state: *“Draft” yields “Notes” as “draft”.*

### Consumption: the process uses the object up

OPL: **Ship** consumes **Token**.

Once taken, the object is gone: no later process gets it until something generates it again. Only one process may consume a given object.

### Effect: the process changes the object

OPL: **Edit** affects **Notes**.

The process both takes and yields the same object. An agent sees it marked as `affected`, its to change, and what it hands in is the new version.

## Enabling links

What the process needs but does not change.

### Instrument: needed, not changed

OPL: **Fix** requires **Style guide**, which it doesn't change.

A reference the process reads and must leave alone. Its link ends in a hollow circle. An agent is told it is an instrument.

### Agent: a person carries the process out

OPL: **Editor** handles **Review**.

A person object taken by a human process. Its link ends in a filled circle. Only people given the *Editor* role (or admins) can decide that review; see [People in the loop](https://spoletta.com/docs/people.md).

## Event and condition

A link out of one of an object's **states** makes the process care about the state. It comes in two flavours, chosen on the selected link: **when it enters** or **only if in it**.

### Event: entering the state triggers the process

OPL: **Post** entering **weak** triggers **Write**.

Write runs **whenever** Post enters `weak`, and only then. Several event links on one process are alternatives: any one triggers it. This is how review loops send work back.

### Condition: run only if the object is in the state

OPL: **Attempt fix** occurs if **Triage** is **bug**, and is skipped otherwise.

The link counts as an ordinary input for readiness, and is marked **c**. Once the process is ready the state is checked: if the object isn't in it, the process is **skipped** and what it would have generated is settled as absent. Use conditions for if/else branches that rejoin.

> **Event or condition?:** Use an event when an object can enter a state many times, as in a review loop. Use a condition when a branch is decided once. [What happens in a run](https://spoletta.com/docs/engine.md) explains both in detail.

## Looking up stored records

### Query: take what earlier runs stored

OPL: **Summarize** looks up the latest 5 stored records of **Review** in **approved** where score >= 4.

An agent's or script's input link to a [stored](https://spoletta.com/docs/objects.md#stored-objects) object (or a [shared](https://spoletta.com/docs/structs.md#shared-records) one) can carry a **query**. The process then gets the records that runs ending done stored of the object, rather than the object in this run, as a JSON array in the object's shape. The array may be empty. The link is labelled **records**.

Set it in the process's panel, under **Stored records**: tick **Look up stored Review**, then narrow it down.

| Setting | Picks |
| --- | --- |
| Where | Records whose fields hold a [rule](https://spoletta.com/docs/states.md#rules), written as a state's, such as `score >= 4 and kind == "bug"`. Left empty, all of them. |
| State | Records in one state, or **In any state**. |
| Order | **Newest first** or **Oldest first**. |
| At most | How many records, 20 by default and 100 at most. With 1 it is the latest record, or the first. |

A query doesn't make the process wait, nor does it make it run: what else it takes, the start or a next does that. What this run stored isn't among the records. A query must be the process's only link taking that object, and it names no state and consumes nothing.

## Deciding a state

### Decision: the process picks the state

OPL: **Classify** yields **Ticket** as **bug** or **feature**, deciding which.

An agent's or a person's link to the object itself (not one of its states) can list states to choose between. Select the link and press the pill on it (**Decide a state** for an agent, **Choose states** for a person) to pick them.

If the process also takes the object, it is a **review**: the object keeps its content and only gains a state. *“Critic” decides whether “Post” is “good” or “weak”.* A human process's link with no states picked reads **Filled in**: the person fills the object in instead.

## Connections between processes

OPM links processes only through objects. Spoletta adds three connections a running system needs, drawn between processes.

### Start: where a run begins

OPL: The flow starts with **Pick release**.

Every board has one Start node. Drag from it to the process a run begins with; a new link replaces the old. Without one, nothing runs, and the Run button stays disabled.

### Failure: a process handles another's failure

OPL: **Report** handles a failure of **Fix**.

Drag from one process onto another. When the source fails (after its retries), the handler runs instead of the run failing. Only agent, script and Sentry processes can have one. See [Handling failure](https://spoletta.com/docs/failures.md).

### Next: run right after, whatever objects say

OPL: **Open PR** follows **Push branch**.

Ordering without data, written in setup files as `next`. Prefer data links: if the second process needs what the first made, link it to that object instead. A next out of a human process is not followed.

## All links at a glance

| Link | Drawn as | Sentence |
| --- | --- | --- |
| Result | process → object, arrow | “P” yields “O”. |
| Takes | object → process, arrow | “P” takes “O”. |
| Consumption | arrow labelled consumes | “P” consumes “O”. |
| Effect | arrows both ways | “P” affects “O”. |
| Instrument | hollow circle at the process | “P” requires “O”, which it doesn't change. |
| Agent | filled circle, from a person | “Person” handles “P”. |
| Event | from a state's pill | “O” entering “s” triggers “P”. |
| Condition | from a state's pill, marked c | “P” occurs if “O” is “s”, and is skipped otherwise. |
| Query | object → process, labelled records | “P” looks up the latest 20 stored records of “O”. |
| Failure | red dashed, process → process | “H” handles a failure of “P”. |
