SpolettaDocs

Reuse

Structs

A struct is a single object defined once, for many flows to use: its states, attributes and parts. When several flows exchange the same thing, such as a Ticket with a key, a summary and a priority, a struct keeps that shape in one place.

Struct · TicketTriageEscalatecopycopyTicketkey · summary · prioritybugfeatureTicketFrom a structbugfeatureClassifyAgentTicketFrom a structbugfeatureEscalateHuman
One Ticket struct, used by two flows through copies. Change the struct and both copies change.

Why structs

An object's shape is a contract: an agent fills in exactly its attributes, a script prints exactly those keys, a person's form asks for exactly those fields. When two flows hand the same thing to each other, or both work on the same kind of record, the contract should be the same in both. Drawing it twice lets the two drift apart; a struct can't.

Making a struct

  1. Create it

    On the flow list, type a name under Structs and press New struct.

  2. Shape the object

    A struct's board holds only its one object, unfolded: there is no Start, no End, no processes and no runs. Add attributes, with their definitions, in its settings; add parts on its board as for any unfolded object; add states with right-click, Add state, and their rules under State rules.

  3. Name it

    The struct's name is its object's name. Renaming the struct renames the object.

Using a struct

Structs are listed under Structs in the board's sidebar. Drag one onto a board and you get a copy of its object, which you link like any other object. Its subtitle says where it comes from, and its settings link to the struct.

A copy follows its source:

Set in the structThe copy's own
Name, quantity, states, state rules, attributes, parts, what it specializesWhere it sits on the board, its width, and whether it is stored (Store what runs generate), unless the struct shares its records

In the copy, Rename and Add state are disabled and the settings fields are locked. Links name its states as the struct names them. On the flow list, a struct's card says how many flows use it, or Not used yet.

Shared records

Normally each flow keeps its own copy's values to itself. Tick Share its records with every flow using it on a struct's object and every copy of it, in whatever flow, reads and writes one store of records, like a table the flows have in common. Use it when one flow adds things and another works on them, such as one fetching Sentry issues and another fixing them. A shared object, and every copy of it, wears a round database badge on its top right corner, as a stored object does; hovering over it says Shared.

  • The object needs a Unique attribute first: it is what tells records apart. A record written again with the same value is the same record.
  • A process that only generates the object, such as a Sentry step or a fetching script, adds the records that are new, in the state its link names, and refreshes the fields of those it knows, keeping their state. Fetching an issue again doesn't undo how far it got. Name the state new records start in on the link, rather than by a rule that holds of every record.
  • A process that also takes it, by any input link or a query, moves the records it generates into the state they come out in: the one named on the link, one a rule gives, or one an agent decides. Fields it leaves out stay as they were.
  • A list writes a record per item, all in the state the list comes out in.
  • Which records get worked on, or worked on again, is up to queries. A query reads the records in a state, whichever flow wrote them, and a rule on their fields narrows them, such as attempts < 3.

An example: fetch in one flow, fix in another

Fetch exceptionsFix exceptionsrecordsFetchSentry · shopExceptionSharednewExceptionSharednewcompletedfailedFixAgentReportAgent
Two flows, one store of Exception records: Fetch adds them as new, Fix takes the oldest new one and leaves it completed, and Report leaves one Fix couldn't fix as failed.

A struct Exception is a shared list with a unique id and the states new, completed and failed.

  • Fetch exceptions, scheduled every hour, has a Sentry step yielding Exception in new. Issues it has seen before keep whatever state they reached.
  • Fix exceptions, scheduled every ten minutes, has an agent Fix that queries the oldest record in new and yields Exception in completed.
  • Fix's failure handler, Report, takes Exception and yields it in failed. To try failed ones again, query failed too, narrowed by a rule such as attempts < 3.
spoletta-setup.yamlyaml
structs:
  - name: Exception
    shared: true
    many: true
    states: [new, completed, failed]
    attributes:
      - name: id
        unique: true
      - name: title
      - name: permalink
flows:
  - name: Fetch exceptions
    start: {next: Fetch}
    objects:
      - copyOf: {flow: Exception}
    processes:
      - name: Fetch
        kind: sentry
        sentry: {organization: acme, project: shop}
        yields:
          - object: Exception
            state: new
  - name: Fix exceptions
    start: {next: Fix}
    objects:
      - copyOf: {flow: Exception}
    processes:
      - name: Fix
        prompt: Find the cause of the exception in Exception and fix it.
        takes:
          - object: Exception
            query: {state: new, limit: 1, oldest: true}
        yields:
          - object: Exception
            state: completed
        onFailure: [Report]
      - name: Report
        prompt: Say on the Sentry issue why Exception couldn't be fixed.
        takes: [Exception]
        yields:
          - object: Exception
            state: failed

A shared object's copy can't also be stored in its own flow's runs (Store what runs generate), and in a setup file shared: true goes on the struct, never together with persistent.

OPL

Exception is shared, so every flow using it reads and writes the same records.

Fix looks up the earliest 1 stored records of Exception in new.

Fix yields Exception as completed.

The records table

The struct's board lists its records in the sidebar under Records, and the Records button on its toolbar opens them as a table, at #/flows/<struct>/records.

  • One row per record, newest first: its number, its state, a column for each field in the order the board has them, when it was last updated, and a link to the run that wrote it, on that run's own flow.
  • The query bar searches every stored record, up to 20 000, such as state:new title:*timeout*. Facets on the left count each column's values, the State facet included, with no value for records without a state. Click a column's name to sort by it. It all works as on a flow's Data page: see Searching and sorting the data.
  • Click a cell to see all of its value.
  • The table shows the latest 1000 matching records and refreshes every ten seconds.

Struct or procedure?

StructProcedure
SharesThe shape of one objectA piece of flow: steps and the objects between them
RunsNeverAs a run of its own, whenever a caller reaches it
Used throughA copy of its objectA procedure process, plus copies of its outcomes
Good forRecords several flows exchange: a Ticket, a Change, a ReleaseWork several flows repeat: announce, triage, open a PR

In a setup file

yaml
structs:
  - name: Ticket
    attributes:
      - name: key
        definition: The Jira key, such as SHOP-123.
      - name: summary
      - name: priority
    states: [bug, feature]
flows:
  - name: Triage ticket
    objects:
      - copyOf: {flow: Ticket}

A copy lists none of its source's fields, and its name may be left out. See Setup files.

Spoletta · spoletta.comBusiness processes, drawn and run.