> ## Documentation Index
> Fetch the complete documentation index at: https://obversa.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflows

> The shape of the work: steps, reviews and decisions, as a TypeScript file.

Write down once how work moves on your team, and the runtime runs it for
your agents the same way every time: the steps, who does each one, where
the reviews are, and where a person decides. Use it when more than one job
has to happen in order, with a check between. For one job with one prompt,
`agentJob` on its own is enough.

## Motivation

Every team has a way work moves. A ticket gets read, someone builds it,
someone tests it, a few people review it, and a lead says ship. Nobody
draws that on a whiteboard before each ticket. It's just how the team works.
A workflow is that shape written down, in a TypeScript file you version,
review and share like anything else you write, and the runtime runs it the
same way each time.

## Two forms

`workflow()` is the short form: a brief, named roles, and a list of stages,
each an agent, a command, a review panel, a person, or a plain function. The
first run is one:

```ts examples/teams/writer-reviewer-pair.ts (excerpt) theme={null}
  return workflow('writer-reviewer-pair', {
    brief: briefFromFile('briefs/add.md'),
    options: { timeout: '10m' },

    roles: {
      write: engines.claude('claude-sonnet-4-5'),
      review: [engines.codex('gpt-5.6-luna')],
    },

    stages: [
      stage('write', {
        agent: 'write',
        writes: ['src/add.mjs', 'test/add.test.mjs'],
        desc: 'Write the function and its test from the brief.',
        gate: 'The files named in the brief exist in the workspace.',
        refine: 1,
      }),
      stage('test', {
        run: ['node', '--test', 'test/add.test.mjs'],
        desc: 'Run the test command against the written files.',
        gate: 'The test command exits 0.',
        sendsBackTo: 'write',
      }),
      stage('review', {
        panel: 'review',
        agree: 1,
        desc: 'Read the code, the test and its result.',
        gate: 'The change meets the brief.',
        sendsBackTo: 'write',
      }),
    ],
  });
```

`dag()` is the full form, and it is what `workflow()` compiles to. A node is
any job: a seat, a command, a person, a whole `workflow()`, or a plain
function of your own that reads what earlier nodes produced and decides what
runs next. A Shape Up cycle as one run is a dag whose nodes are workflows:

```ts examples/use-cases/product/shape-up-cycle.ts (excerpt) theme={null}
const cycle = dag({
  name: 'shape-up-cycle',
  concurrency: 1,
  nodes: {
    shaping,
    table: { needs: 'shaping', job: table },
    ...builds,
    cooldown: { needs: Object.keys(builds), job: cooldown },
  },
});
```

A plain function is a node with `fnJob`. This one writes a file and reads
the last review's summary from the context:

```ts examples/command-kickback.ts (excerpt) theme={null}
const implement = fnJob('implement', (ctx) => {
  implementRuns += 1;
  writeFileSync(
    source,
    implementRuns === 1
      ? 'export const add = (a, b) => a - b;\n'
      : 'export const add = (a, b) => a + b;\n',
  );
  return ctx.lastReview
    ? `second attempt, after the tests said: ${ctx.lastReview.summary}`
    : 'first attempt';
});
```

Anything you can write as a function that returns an outcome is a node: a
call to your own service, a check against your database, a branch on a
value. Start with `workflow()`; reach for `dag()` when a step needs your own
code, a branch, a tournament, or a team inside a team.

## Parts

* **Steps.** Each step is a job: an agent call, a command, a check. The
  record carries whose work each step was.
* **Sequence and dependencies.** A pipeline runs steps in order. A graph
  runs independent steps together and waits where one depends on another.
  Both are one call.
* **Reviews.** A review judges another step's result. A panel passes on the
  count you set. When a review fails a step, the findings go to the stage
  that owns the fix, which runs again with them.
  [Feedback loops](/docs/concepts/feedback-loops) covers the shapes.
* **Decisions for a person.** A callback gate stops the run, puts one
  question in front of someone, and carries on from that step with the
  answer. No model interprets it.
* **Limits.** An attempt can carry a time budget, and a step runs again
  only as many times as you allow. No step can hang the run or burn the
  budget, and the record says why it stopped.

## Descriptions and gates

A step carries two sentences beside its job: `desc`, what it does, and
`gate`, what done means. Both go into the plan and the record. An agent
step built with `graphContext: true` also gets `desc` in its prompt. In a
`workflow()`, the writing seat gets both. So does each agent reviewer,
from the stage that declares it. A criterion written beside the code is
hard to write vaguely:

```ts examples/described-team.ts (excerpt) {4-5,9-11} theme={null}
const describedTeam = dag({
  name: 'described-team',
  nodes: {
    brief: {
      desc: 'Turn the request into a short delivery brief.',
      gate: 'The brief names the user, outcome, and constraints.',
      job: stage('brief', 'brief ready'),
    },
    build: {
      needs: 'brief',
      desc: 'Build the smallest useful change from the brief.',
      gate: 'The change meets the brief and its checks pass.',
      job: stage('build', 'change built'),
    },
    review: {
      needs: 'build',
      desc: 'Check the change before it reaches the user.',
      gate: 'The change is safe to release and easy to explain.',
      job: stage('review', 'review complete'),
    },
  },
});
```

The plan the runtime renders shows both sentences under each step:

```text Example record theme={null}
{
  "plan": "dag \"described-team\" (3 nodes)\n  - brief\n      desc: Turn the request into a short delivery brief.\n      gate: The brief names the user, outcome, and constraints.\n      fn \"brief\"\n  - build (needs brief)\n      desc: Build the smallest useful change from the brief.\n      gate: The change meets the brief and its checks pass.\n      fn \"build\"\n  - review (needs build)\n      desc: Check the change before it reaches the user.\n      gate: The change is safe to release and easy to explain.\n      fn \"review\"",
  "status": "pass"
}
```

## Orders

Someone asks for a change: a passkey login, a report export with a header
row. On a team that's a ticket: what's wanted, why, the constraints, and
what done looks like. An order is that ticket, and a workflow is what the
team does with it. In `examples/feature-delivery.ts` the order is one line:
build a retry helper that retries failed requests, caps attempts, and stops
when the caller aborts.

The runtime has no order type, no ticket store and no opinion about where
your tickets live. Your application decides what an order holds, where it's
kept and which workflow runs on it, so it can come from Linear, GitHub or a
text file.

### Lifecycle

An order moves through its workflow without waiting for others, gets its
reviews and a person's decisions on the way, and closes when its evidence
says the requirements are met. Some orders need more than one workflow. A
payment feature is three in a row: implement it, review it for security,
release it. Your application starts each when the one before finishes, and
runs independent ones together.

#### Properties

Four things travel with an order, linked in the record, so a result reads
without the agent's conversation:

* **Context.** The requirements, designs and constraints the work needs.
* **The workflow.** The steps that carry it out, as code.
* **The output.** The files and artifacts the work made or changed.
* **The evidence.** The checks, reviews and decisions that accepted the
  output. [Proof acceptance](/docs/reviewing/proof-acceptance) binds a review or
  an approval to the exact bytes it judged.

### Best practice

Keep an order small enough to follow from request to result. A report
export is three orders: the endpoint, the CSV writer, the download button.
Each is reviewed and approved on its own, so a finding lands on a few
files.

### Working directory

An order's workflow can run in its own worktree, so two writers never
collide. The [workspace contract](/docs/workspace/contract) captures, verifies,
leases and forks one Git repository for that.

## Next steps

| Goal | Page |
| - | - |
| Copy a complete software delivery team | [Feature delivery](/docs/workflows/feature-team), [Shipping through GitHub](/docs/workflows/forge-helper), [Backlog grooming](/docs/workflows/backlog-groom-then-rank) |
| See a team outside software | [Contract review](/docs/workflows/contract-playbook), [Translate and reflect](/docs/workflows/translate-reflect) |
| Read the contract a shape is built on | [Outside graph types](/docs/graphs/contract) |
| Use the shape that ships | [Built-in pipeline](/docs/graphs/pipeline) |
| Check a plan before it runs | [Plan admission](/docs/graphs/plan-admission) |
| Run a compiled graph | [Graph executor](/docs/graphs/executor) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.