Multi-agent DAG orchestration, purpose-built for enterprise engineering teams Learn more →

Author a workflow

Last updated 2026-05-21·9 min read

A workflow is the durable plan Cendriix follows to take a unit of work from intake to production. It is a directed graph of agent steps, conditional branches, and human approval gates. This guide shows how to read, author, and publish one.

Overview

Every run executes exactly one workflow at a pinned version. The orchestrator resolves the workflow at trigger time, so editing a workflow never affects runs already in flight. Cendriix ships a small set of built-in workflows, the most common is ship-from-ticket, which reads a ticket, plans a change, opens a pull request, runs CI, deploys a preview, and waits for approval before merging.

You author your own workflows in two ways: by writing a YAML definition and committing it to your workspace, or visually in the Workflow Composer. Both produce the same underlying definition, the composer is a view over the YAML.

Anatomy of a workflow

A workflow definition has four parts:

  • Metadata, a slug, display name, and description. The slug is how you trigger the workflow from the CLI and API.
  • Inputs, typed parameters the run is called with, such as a ticket key or a target environment.
  • Steps, the ordered graph of agent invocations, branches, and gates that make up the lifecycle.
  • Policy bindings, optional workflow-level guardrails layered on top of the workspace policy set.

Writing a workflow in YAML

Workflow files live under .cendriix/workflows/ in any connected repository. Here is a complete definition that builds a change, tests it, and gates a production deploy behind a human approval:

json
# .cendriix/workflows/ship-from-ticket.yaml
slug: ship-from-ticket
name: Ship from ticket
description: Plan, build, test and deploy a single ticket.

inputs:
  ticket:
    type: string
    required: true
  env:
    type: enum
    values: [preview, staging, production]
    default: preview

steps:
  - id: plan
    agent: orchestrator
    input: { ticket: "${inputs.ticket}" }

  - id: build
    agent: dev
    needs: [plan]

  - id: test
    agent: qa
    needs: [build]

  - id: gate
    type: approval
    needs: [test]
    approvers: { role: maintainer }
    when: "${inputs.env == 'production'}"

  - id: deploy
    agent: sre
    needs: [gate]
    input: { env: "${inputs.env}" }
ExpressionsFields wrapped in ${...} are evaluated against run inputs and the outputs of earlier steps. Expressions are sandboxed, they can read run state but cannot make tool calls.

Step types

TypeDescription
agentInvokes a named agent. The most common step type. Accepts an input object and produces a structured output other steps can reference.
approvalPauses the run until a human with the required role approves or rejects. See Approval gates below.
branchEvaluates a condition and routes the run down one of several paths. Useful for environment-specific behaviour.
parallelRuns a set of child steps concurrently and joins on completion. Each child still records its own audit trail.

Every step declares its dependencies with needs. The orchestrator builds the execution graph from these edges; steps with no unmet dependency run as soon as their inputs are ready.

Approval gates

An approval gate is a step of type approval. When the run reaches it, the run pauses, its status flips to awaiting_approval, and the configured approvers are notified, via the dashboard, the CLI, the API, or a connected Slack channel. Gates can be conditional: in the example above, the gate only applies when the target environment is production.

Gates time out after a configurable window, 24 hours by default. A timed-out gate rejects the run rather than proceeding, so an unattended run never ships unreviewed.

Dynamic gatesYou do not have to place every gate in the workflow by hand. A guardrail policy can require approval for a specific action, for example, any call that deletes a production resource, and the orchestrator inserts the gate at runtime. See Policies & guardrails.

The visual composer

The Workflow Composer in the dashboard renders a workflow as a DAG you can edit by dragging steps, drawing dependency edges, and configuring each step in a side panel. Changes made in the composer are written back as YAML, so the two representations never diverge. The composer is the recommended starting point for non-engineers; engineers typically edit the YAML directly and review it in pull requests.

Versioning & publishing

Workflows are versioned. Committing a change to a workflow file, or saving in the composer, creates a new immutable version. Triggering a run resolves the latest published version unless you pin one explicitly. Because the version is recorded on the run, you can always reproduce exactly what a past run did, see Time travel & replay.

bash
# Validate a workflow file before committing
cendriix workflow validate .cendriix/workflows/ship-from-ticket.yaml

# List published versions
cendriix workflow versions ship-from-ticket

# Trigger a run against a pinned version
cendriix run ship-from-ticket --ticket JIRA-3421 --version 7

Next steps