# Procedures

> Reuse · https://spoletta.com/docs/procedures

A procedure is a flow other flows run as one of their steps. Draw a piece of work once, such as “tell the team what changed” or “triage a ticket”, and every flow that needs it runs the same procedure. Change the procedure and they all change with it.

*Diagram: Left, a flow runs the procedure through a procedure process (dashed double border). Right, the procedure itself: what it leaves is linked to End.*

## Why procedures

Several flows often end, or begin, the same way: post the change to Slack, open a pull request, classify a ticket. Copying those steps into each flow means fixing them in each flow too. A procedure keeps them in one place:

-   **One definition.** Its prompts, models and steps live in the procedure; every caller runs the current version.
-   **A clear contract.** What it takes and what it leaves are named objects, so callers know exactly what to hand in and what comes back.
-   **A small caller.** The caller's board shows one step, *Tell the team*, rather than its insides.

## Making a procedure

1.  **Create it**
    
    On the flow list, type a name under **Procedures** and press **New procedure**. Or turn a flow you already have into one: in its **Settings**, tick **Procedure: a group of processes reused inside other flows**. That box can't be unticked while other flows use it.
    
2.  **Draw its board**
    
    A procedure is an ordinary flow: a Start, processes and objects. The objects on its top board that it should be given are its **inputs**: callers hand values to them by name.
    
3.  **Mark its outcomes**
    
    Link each object it leaves to the **End** node. Those *end objects* are its outcomes: what the procedure's run left of them goes back to the caller.
    

## Using a procedure

1.  **Drag it in**
    
    Procedures are listed under **Procedures** in the board's sidebar. Drag one onto the board, and you get a procedure process already pointed at it, together with **copies** of the procedure's outcomes. You can also add any process and set **Type** to **Procedure**, then pick **Procedure it runs**.
    
2.  **Link what it takes**
    
    Link the caller's objects to the procedure process as you would to any step. Each is handed to the object of the **same name** on the procedure's top board, in the state of the same name.
    
3.  **Check the names**
    
    The process's settings list what it **Takes** and **Generates**. A name with no match is flagged: *no object of that name on its top board*, or *its namesake there holds different fields*. **Open the flow** jumps to the procedure.
    

## How values cross

The caller and the procedure share no objects. Values cross between them by name, in both directions:

*Diagram: Top, the caller's board; bottom, the procedure's run. What the caller takes in goes to the namesake inside; the end objects come back out.*

-   An object the procedure process **takes** is given to the procedure's top-board object of the same name, in the state of the same name.
-   An object it **yields** is what the procedure's run left of the end object of the same name.

## Copies follow their source

On the caller's board the outcomes are **copies** of the procedure's objects. A copy follows its source: its name, quantity, states, attributes and parts change only in the procedure. In the copy, **Rename** and **Add state** are disabled and its settings are locked, with a note linking to the source. Only where it sits on the board and whether it is stored are its own.

Copies are also how a caller produces exactly what a procedure takes. In the example below, Bump yields a copy of the procedure's *Change*, so its output has exactly the fields the procedure expects.

> **Note:** If a procedure is deleted, its copies become objects of their own, and flows using it fail to run it.

## A run of its own

When a run reaches a procedure process, the procedure runs as a **run of its own**, linked to the caller's run:

-   The step in the caller's run shows *Ran its board as run #N*. Click it, or zoom into the procedure process while the run is shown, and the procedure's page opens on that run.
-   That run's panel says *The run of Tell the team in run #P*, with a link back. The browser's back button returns too.
-   On the flow list, a procedure's card says how many flows use it, and may read *Never run on its own*.

> **Its own workspace:** A procedure has **its own** repositories, in its own workspace on a branch of its own. It can't carry on the caller's checkout. Keep code work that must land on the caller's branch in the caller, and put in the procedure only what needs no checkout, or have the procedure list the repositories itself. See [Workspaces and git](https://spoletta.com/docs/workspaces.md).

A flow can't run itself, however many procedures apart: a procedure that, through others, runs its own flow is refused.

## Example: announce a change

Several flows end with “tell the team what changed”. The procedure *Announce change* takes a *Change* and leaves an *Announcement*; *Bump dependencies* runs it after opening its pull request.

As a setup file:

```yaml
procedures:
  - name: Announce change
    start:
      next: Announce
    objects:
      - name: Change
        attributes:
          - name: title
          - name: description
          - name: link
            definition: Where to see the change, such as its pull request.
      - name: Announcement
        end: true
        attributes:
          - name: url
            definition: The link to the message posted.
    processes:
      - name: Announce
        prompt: |
          Post the Change to #changes on Slack: its title, two sentences
          of what it means for the team, and the link.
        mcpServers: [Slack]
        takes: [Change]
        yields: [Announcement]
flows:
  - name: Bump dependencies
    start:
      next: Bump
    objects:
      - copyOf: {flow: Announce change, object: Change}
      - copyOf: {flow: Announce change, object: Announcement}
    processes:
      - name: Bump
        prompt: |
          Bump every dependency with a patch release available, run the
          tests, push the branch and open a pull request. Describe it as
          the Change.
        yields: [Change]
      - name: Tell the team
        kind: subflow
        runs: Announce change
        takes: [Change]
        yields: [Announcement]
```

OPL:

```text
Tell the team is a procedure, which runs the flow Announce change.
Tell the team takes Change.
Tell the team yields Announcement.
```

Bump pushes and opens the pull request itself, because the procedure works in a workspace of its own. To share only the shape of an object, without any steps, use a [struct](https://spoletta.com/docs/structs.md).
