Download all docs

Composition

Elements rarely stand alone — you compose them: an app references the elements it deploys, an automation routes data between steps, a wire connects one element’s output port to another’s input, a lab connects a brain/ears/mouth.

Composition is about how elements relate — the static structure that links one element to another. It is distinct from a run, which is what happens when you actually invoke an operation. You compose first; the composed elements run later.

Hex canvas with the Data category hover card open in a logged-in Triform workspace.
Hover cards explain the element or category under the pointer without leaving the canvas.Live triform.dev - 1920x1080 - 433914e4d138Open full size

How elements relate

Every circle’s elements live as a flat list in the circle’s private schema — there is no owning tree of folders. Relationships between those flat elements are expressed in a few distinct ways, and it matters which one you reach for.

Containment (parent_id). One element can own another. This is the owner link a board has over its cards, or a lab over its brains. After the 2026-05-04 flat-element migration most elements sit top-level with no parent; containment survives only for the cases below.

References (element_references). An explicit “uses” link with an alias and a version_pin, stored in a per-circle table. Reach for this when element A must point at a specific element B and you want that choice pinned.

Dispatch-by-lookup. The idiomatic “element A uses element B” at runtime: A looks up B by element type (e.g. a sales-board finding the circle’s io/linkedin) and calls an operation on it. No foreign key, no structural link — the connection is resolved when it’s needed and re-resolved each time. If you find yourself asking “what’s the FK from this element to that one?”, the answer is usually this: there isn’t one.

Compound elements nest their children

Two element families are genuinely compositional on disk — they parent their children, and those children only make sense inside the parent:

  • automation parents condition, loop, and wait — the control-flow steps that make up an automation.
  • lab parents brain, ears, and mouth — the faculties of an intelligence lab.

These six are the only remaining parent_id consumers after the flat-element migration. A nested child is addressed under its parent’s slug:

/api/{circle}/{parent_slug}/{child_slug}

Everything else is a flat element at /api/{circle}/{slug}, with category and element-type as metadata for dispatch and filtering — never path segments.

Bonds (relational metadata)

Some relationships are neither containment nor a pinned reference but facts about an element — “this pursuit card is about lead X, organization Y”. Those are bonds, stored today as JSON in the element’s meta.bonds and resolved on read. Bonds are live for sales-board pursuit state (a card’s lead_id / organization_id / contact_ids); for everything else they remain intent rather than a queryable structure. A dedicated bonds table is staged but not yet shipped, so do not assume bonds can be queried across elements in SQL — parent_id is still authoritative for non-sales paths.

Authoring: on the canvas or in YAML

You compose elements two ways, both landing in the same place. On the canvas you create elements and draw the connections between them — for example a wire joining one element’s output port to another’s input — and the platform records each element and its relationships as rows in the circle’s schema. The same structure can be declared as element YAML and generated, which is how the built-in element types and their compositional rules (what a compound element may contain) are defined. Canvas edits and YAML are two front-ends onto one model: flat elements plus the relationship records that link them.

Related

  • Concept: elements
  • Concept: runs — composed elements executing together
  • Element: wire, automation, app, lab