# Objects

> The OPM language · https://spoletta.com/docs/objects

An object is a named thing that processes generate and take. In a run, an object holds a value, always JSON, and that value is what flows from one step to the next.

*Diagram: A plain object, one with states, a list (drawn stacked) and a person.*

## What an object holds

### A single value

An object with no attributes holds one value under `data`. Most text artefacts are like this: a draft, a summary, a description of a patch. An agent hands in `{"data": "…"}`, and the value can be any JSON, text included.

### Attributes

Give an object **attributes** and it holds a JSON object with exactly those keys, in order. A *Ticket* with `key`, `summary` and `priority` holds `{"key": "SHOP-12", "summary": "…", "priority": 2}`. Give each attribute a **definition**: it is the contract the agent filling it in sees, so say what the field holds and in what form.

> **Tip:** Attributes are added in the object's settings under **Attributes** (“New attribute, e.g. ticket\_id”). The panel's **Processes hand in** shows an example of the JSON the object expects.

### Lists

Set **How many** to more than one and the object is a **list**: a JSON array of items, each shaped by the attributes. You write the quantity as OPM does:

| How many | Means |
| --- | --- |
| `1` | One object (the default) |
| `4` | Exactly four items |
| `2..5` | Between two and five items |
| `1..*` | At least one item |
| `*` | Any number of items |

A generated or entered list must have that many items. A flow process that takes a list runs once per item; see [Lists and items](https://spoletta.com/docs/lists.md).

## Kinds of object

### Persons

Tick **A person** and the object stands for someone who carries out human processes, such as *Editor* or *Finance*. A person is only ever taken by a human process (OPM's agent link, “Editor handles Review”). It is never an instrument, never consumed, and never part of another object. Its name doubles as a role: people given that role decide its reviews. See [People in the loop](https://spoletta.com/docs/people.md).

### Stored objects

**Store what runs generate** keeps what each run generated of the object, one instance per run. Mark an attribute **Unique** and no two stored instances may share its value, which is how a flow tells it already handled something, such as a Sentry issue id:

On the board, a stored object wears a small round **database badge** on its top right corner, and so does a struct's object that [shares its records](https://spoletta.com/docs/structs.md#shared-records). Hover over it to see which of the two it is.

-   A step that generates a value another run already stored is a **duplicate**. Its run ends *terminated*, not failed, and nothing further starts.
-   Only runs that end done keep their values, so work whose run failed is picked up again next time.
-   For a stored *list*, each item is claimed by its own run. An item another run already holds is skipped, so fetching the same issues every morning processes only the new ones.
-   A stored object (not a list) on the top board that only the processes taking it yield, an [effect](https://spoletta.com/docs/procedural-links.md#effect-the-process-changes-the-object), is **carried over**: each run of the flow starts with what the last run to end done stored of it. That is how a flow keeps a **cursor**, such as when it last fetched or the offset it got to. On the first run there is nothing yet, and a failed run doesn't move it on. Leave its attributes not unique, or a run that leaves it as it was is a duplicate. Only a whole run of the flow carries it: not a flow process running the flow, nor the items of a list.
-   An agent or script can also **look up** what earlier runs stored, through a [query link](https://spoletta.com/docs/procedural-links.md#looking-up-stored-records).

OPL:

```text
Fetch changes affects Cursor.
Cursor is stored, so each run keeps what it generated of it.
```

A struct's object can go further and **share its records** with every flow using it, so one flow adds them and another works on them. See [Shared records](https://spoletta.com/docs/structs.md#shared-records).

### Files

Mark an attribute **File** and it holds the path of a file the step wrote: a script writes it in `$OUTPUTS`, an agent in its step's files directory. The run keeps the file for download. A person can't enter a file by hand.

### End objects

Link an object on the top board to the **End** node and it is one of what the flow leaves. For a procedure, those are its outcomes, handed back to the flow that ran it.

## When an object exists

> **A run starts with nothing:** An object that no process generates exists from the start of a run, but **empty**. Whatever a flow works on must come from inside it: a person filling it in, a script or Sentry step fetching it, or an agent finding it. A first agent that “takes the request” when nothing produces the request gets nothing.

OPL:

```text
Release manager is a person.
Exceptions is a list of items, at least 1.
Exceptions is stored, so each run keeps what it generated of it.
Exception is shared, so every flow using it reads and writes the same records.
Ticket can be bug or feature.
Ticket exhibits key and summary.
```
