Author a workflow
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:
# .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}" }${...} 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
| Type | Description | |
|---|---|---|
agent | Invokes a named agent. The most common step type. Accepts an input object and produces a structured output other steps can reference. | |
approval | Pauses the run until a human with the required role approves or rejects. See Approval gates below. | |
branch | Evaluates a condition and routes the run down one of several paths. Useful for environment-specific behaviour. | |
parallel | Runs 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.
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.
# 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