Skip to content

Workflows

A workflow is a directed graph of steps. Triggers fire on task events. Steps run agents, wait for humans, set statuses, or branch on conditions. This is how “task created” becomes “PR merged” without you clicking buttons in between.

Workflow editorWorkflow editor

id: simple-task
name: Simple task
description: Triage → Plan → Implement → Review → Merge
trigger:
on: task.created
steps:
- id: triage
type: run_agent
config:
role: triage
mode: headless
model: sonnet
prompt: "Classify this task and set tags."
allowed_tools: []
- id: plan
type: run_agent
config:
role: plan
mode: headless
needs_worktree: true
prompt: "Write an implementation plan."
- id: plan-review
type: wait_human
config:
status: plan-review
human_actions: [approve, reject]
- id: implement
type: run_agent
config:
role: "" # empty = implementation
mode: headless
needs_worktree: true
max_retries: 2
reuse_agent: false
- id: open-pr
type: link_pr_and_review
- id: merge-check
type: ensure_pr_closes_issue
builtin: true

Three event sources, one is always required:

on:Fires when
task.createdA new task appears (any source)
task.status_changedA task’s status field changes
pr.eventA PR linked to a task gets a GitHub event

Triggers can be gated by conditions — a list of {field, operator, value} tuples combined with AND. Fields reference the task (task.id, task.title, task.status, task.tags, task.agent_mode, task.project_id, task.branch, task.pr_number, task.reviewed) or the linked PR (pr.issue_kind).

Trigger panelTrigger panel

Operators: equals, not_equals, contains, not_contains, exists.

TypePurpose
run_agentDispatches a Claude Code agent with prompt + mode + tools
wait_humanPauses workflow, sets the task to a status you can act on
set_statusUpdates the task status unconditionally
conditionBranches the DAG based on task/PR state
shellRuns a shell command (tests, linters, build checks)
ensure_pr_closes_issueAsserts PR body has Closes #N if task has issue:
verify_commitsAsserts at least one commit on the branch
link_pr_and_reviewOpens PR from branch, attaches review workflow
evaluateRuns an LLM judge step (for grading agent output)
FieldDefaultNotes
role""triage / plan / eval / pr-fix / "" (implementation)
modeheadlessor interactive
modelOverrides the default model for this step
providerclaude or codex
promptTemplate; {{ task.body }} interpolates
allowed_tools[]Empty = all with skip-permissions
needs_worktreefalseIf true, creates a worktree before the run
max_retries0Retries on failure
reuse_agentfalseIf true, resumes the prior agent session ID
type: wait_human
config:
status: plan-review
human_actions: [approve, reject]
wait_for_status: todo # optional — auto-advance if someone sets this

Sybra surfaces human_actions as buttons on the task detail page. Approving completes the step; rejecting fails it.

wait_human steps render action buttons inline on task detail. The Plan Review flow uses this:

Plan reviewPlan review

Shortcuts: A approve, R reject, C focus the feedback box. Messages typed into the feedback box go back to the plan agent via session resume.

Sybra ships a default workflow set:

  • simple-task — default workflow for untagged tasks
  • testing-task — manual testing loop
  • pr-review — review-tagged tasks
  • pr-fix — CI red or merge conflicts on linked PRs

Workflows listWorkflows list

Built-in workflows are marked builtin: true. You can fork them into your own (which clears the flag) or reset them to ship defaults from the Workflows page.

When a workflow runs, Sybra stamps the task frontmatter with:

workflow:
execution_id: exec-abc123
status: running # running | completed | failed | cancelled
current_step_id: implement

This is how restart recovery works: on startup, Sybra resumes any workflow that was running by replaying from current_step_id.

Early versions used a state machine (status field drives everything). It fell over as soon as you needed:

  • multiple agents per task (plan → implement → fix-review)
  • parallel fan-out (eval agent + test agent)
  • conditional branches (“if PR has merge conflict, run conflict-resolver”)

Workflows let you encode those topologies once and reuse them. The status field is now a side effect of workflow execution, not the source of truth.

  • You’re exploring a new problem interactively. Skip workflows; use chat mode.
  • You want per-task idiosyncratic behavior. Workflows are for patterns, not one-offs.
  • You’re debugging Sybra itself. Turn all workflows off, drive the board by hand.