# Workflows

A workflow defines the steps a task follows and what must happen before it moves on.
Agents do the work; Enso runs any required checks and records whether it accepted the
handoff. You can use one step or a development process with planning, implementation,
review, and integration.

**Checks are optional to configure and enforced once configured.** A single
`work → done` flow with no tests, Git repository, or review is fully supported.
The development preset is a starting point you choose, not a requirement for every task.

## Availability

This guide describes the transactional workflow implementation in Enso's beta source.
It is not included in the published **v0.1.2** release. Check `enso workflow --help`
on your installation before using these commands; an older installation does not gain
them just by changing its configuration. See [releases](https://github.com/geekforbrains/enso/releases)
for published availability and [installation](/docs/installation/) for release updates.

## Ask Enso to build a workflow

The bundled `enso-workflow` skill helps Enso inspect a project, choose its real validation
commands, configure stages and scripts, and test the result. For example:

> Set up a development workflow for my app. Plan first, implement in a sibling worktree,
> require the project's lint and unit tests, review the change, then integrate locally
> into main. Keep the jobs disabled until we have tried a small task.

Or start smaller:

> Give this project one work step with no checks. I'll add review later if I need it.

You can also edit the project configuration and job files directly. A **project** owns
its ordered stages, checks, repository, and worktree settings. A **stage job** owns the
agent instructions and, for agent work, the provider, model, and effort. Human stages
have no job; command and integration stages use jobs without invoking a model.

## How a handoff is accepted

Consider an implementation stage that requires lint and unit tests:

1. Enso claims the task and reserves its worktree, if needed.
2. The agent implements the change, commits it in the worktree, and submits a handoff.
   The task stays in implementation.
3. After the agent and job postrun work stop, Enso runs lint and tests itself against the
   submitted **candidate** — the commits on the task's branch. An uncommitted or untracked
   file in the worktree fails the candidate instead of being checked.
4. If a check fails, Enso returns the actual output to the agent for repair within the
   configured limits. The repaired candidate must be checked again.
5. When the required checks pass, Enso records acceptance, advances the task, and queues
   any configured lifecycle scripts. The next writer waits for ownership to be released.

That accepted handoff is the **transaction**: recorded progress backed by evidence.
A failed attempt keeps its files for repair; it does not roll back shell commands or
external effects. A provider's successful exit or “tests passed” message cannot satisfy
an Enso check. A stage with no checks simply requires a successful submitted handoff
after execution finishes.

Missing results, timeouts, interrupted commands, and nonzero exits cannot pass a required
check. Repair and review-return budgets persist across runs and restarts. Exhaustion or
an unrecoverable failure leaves the task blocked with its work and diagnostics retained.
Returning a broken candidate for changes, or blocking a task, does not require its forward
checks to pass.

## Start with one step

For an existing `research` workspace, create a project and its unchecked job:

```bash
enso project add RS --name Research --workspace research --flow basic
enso workflow init RS --preset basic
enso job show rs-work
```

This creates `work → done` and a disabled `rs-work` job. Edit that job's instructions to
say what a useful result looks like, such as a short research note with source links.
There is no repository or mandatory evaluation. See [stage jobs](/docs/jobs/#stage-jobs)
for testing and enabling it.

## Default development workflow

The development preset supplies:

**Plan → Implement → Review → Integrate → Done**

| Stage | What happens |
| --- | --- |
| Plan | An agent records scope, acceptance criteria, and a validation approach. It uses the workspace without allocating a worktree. |
| Implement | An agent changes code, useful tests, and related docs in the task worktree, then commits and submits. Enso runs the required lint and test commands, with up to two repairs. |
| Review | A separate stage run reviews the candidate against the task. It can return work to implementation up to twice. Review judgment is recorded separately from executable checks. |
| Integrate | Enso serializes local landing, rebases against the recorded target, reruns the required checks, and lands the accepted candidate. No model runs in this stage. |
| Done | Enso records the outcome, delivers configured completion scripts, and removes the worktree when cleanup is eligible. |

For an existing `dev` workspace and a repository whose real commands are `npm ci`,
`npm run lint`, and `npm test`:

```bash
enso project add APP --name "My app" --workspace dev \
  --repo ~/Projects/my-app --flow basic --setup 'npm ci'
enso workflow init APP --preset dev --base main \
  --worktree-root '../.worktrees/my-app' \
  --lint 'npm run lint' --test 'npm test'
enso config check
enso job show app-implement
```

Use your repository's actual commands and target branch. The preset requires both lint
and test commands; an empty or missing command is refused. Add type checking, builds, or
browser tests when they help establish the task's acceptance criteria.

The preset creates four **disabled** jobs: `app-plan`, `app-implement`, `app-review`, and
`app-integrate`. Agent jobs snapshot the workspace's agent choice, or the installation
default, when generated; you can select a different reviewer. Inspect every job, its
permissions, setup, and checks before enabling it. After a small manual trial, set
`enabled: true` in the jobs' `JOB.md` files to allow ready work to run automatically.

For a manual trial, add a small task and run each stage only after the previous handoff
has been accepted:

```bash
enso task add "Fix the empty-state label and add a regression test" --project APP
enso job run app-plan
enso workflow show APP-001
```

Use the reference returned by `task add`; it is not always `APP-001`. Continue with
`app-implement`, `app-review`, and `app-integrate`, inspecting evidence between runs.
A manual `job run` runs even when disabled and selects the next ready task in that stage;
use a scratch project or an otherwise empty queue for a controlled first trial.

Here, `done` means **checked and integrated into the local target branch**. The preset
does not push, open a pull request, or deploy. A workflow that ends at a human checkpoint
or a script that opens a pull request can define a different finish line.

## Worktree location and concurrency

The product default is `<repo>/.worktrees/<TASK-REF>`, outside the Enso home. Set
`worktree_root` to a repository-relative path, an absolute path, or a path beginning with
`~`. The example above chooses a sibling root. This is useful when development servers
watch nested directories: Git ignoring a worktree does not necessarily stop a watcher
from rebuilding the main app.

Enso creates a task's worktree only when a stage needs one, then reuses it across
implementation, review, repair, and human checkpoints. It records the path, branch,
starting revision, and target. Configuration changes affect new worktrees; existing
ones keep their recorded identity. Setup installs dependencies in each worktree.
[Selected ignored-file copying](/docs/tasks/#preparation) is available without automatically
sharing mutable dependency directories.

The dev preset sets `max_concurrency` to three. Different tasks can be planning,
implementing, and reviewing at once; each task worktree has one execution owner, and
integration is serialized per repository. A stage job itself has one active run.
Use a shared job `concurrency_group` for fixed ports, test databases, or other shared
resources. Worktrees do not isolate those resources. Checks that start servers must
clean them up, including on timeout and cancellation.

Blocked work and human-review worktrees are retained. Cleanup waits for active execution
and worktree-using hooks; dirty or unmerged work is preserved. **Ignored files can be
removed with an otherwise clean worktree**: copy durable artifacts elsewhere or archive
them during teardown. See [integration and cleanup](/docs/tasks/#integration-and-cleanup).

## Other examples

These are project configuration snippets to merge into `config.json`, not complete home
configurations. Create the named workspaces first. Agent, command, and integration stages
each need a matching [stage job](/docs/jobs/#stage-jobs); changing the stage list alone does
not create jobs. Each example script is project-authored: provide it and test its exit behavior
before making it a required check.

### A writing flow with human approval

`research → draft → approve → done` keeps the final decision with a person, without
requiring Git or executable checks:

```json
{
  "projects": {
    "WR": {
      "name": "Writing",
      "workspace": "writing",
      "stages": ["research", "draft", "approve:human"]
    }
  }
}
```

Create jobs for research and draft. The approval stage waits for an operator; advancing
it means the draft is approved, not published. You can add an executable citation or
formatting check to the draft stage later without changing how the agent writes.

### A checked implementation followed by a person

For small changes, skip planning and automated integration. Run tests after implementation
and stop at `approve` for a person to decide how to land the change:

```json
{
  "projects": {
    "FIX": {
      "name": "Small fixes",
      "workspace": "dev",
      "repo": "~/Projects/my-app",
      "base": "main",
      "worktree_root": "../.worktrees/my-app-fixes",
      "setup": "npm ci",
      "stages": [
        {
          "name": "implement",
          "checks": [
            {"name": "lint", "command": "npm run lint", "timeout": 600},
            {"name": "unit", "command": "npm test", "timeout": 600}
          ],
          "max_repairs": 1
        },
        {"name": "approve", "human": true}
      ]
    }
  }
}
```

The agent gets one repair opportunity. At `approve` a person decides: advance the task, or
send it back with `enso task return` for another pass through implementation. `max_repairs`
and `max_returns` bound the loops an agent run can take, so a human stage does not need
them. This workflow contains no integration stage: approval alone does not merge anything,
and unmerged work remains available after completion.

### An automated validation step without a model

`prepare → validate → approve → done` can assemble a report with an agent, then validate
its artifact with an ordinary program before a person reviews it:

```json
{
  "projects": {
    "RP": {
      "name": "Reports",
      "workspace": "reports",
      "stages": [
        "prepare",
        {"name": "validate", "command": "python3 scripts/validate_report.py"},
        "approve:human"
      ]
    }
  }
}
```

The validation command runs in the reports workspace. Have it use `ENSO_TASK` to find the
right task's artifact, print useful diagnostics, and exit nonzero when validation fails.
It can check required files, schema, references, or a numerical threshold. No model is
invoked for this stage. An external CI check can use the same pattern, provided its script
waits for the relevant candidate's completed result and fails on missing or skipped checks.

## Checks and lifecycle scripts

Use **checks** when failure must prevent progress, and **lifecycle hooks** for reactions to
an already-recorded transition:

| Need | Put it in |
| --- | --- |
| Install per-worktree dependencies | Project `setup` |
| Require lint, tests, a build, or a benchmark threshold | The stage's `checks` array |
| Run a whole step without a model | The stage's `command` |
| Require a person's decision | A human stage |
| Update a local index when a task finishes | Project `hooks["after:done"]` |
| Archive artifacts before removing a worktree | Project `hooks.teardown` |

For example, add these fields to a project whose scripts already exist:

```json
{
  "hooks": {
    "after:done": "bash scripts/record-completion.sh",
    "after:blocked": "bash scripts/record-blocker.sh",
    "teardown": "bash scripts/archive-task-artifacts.sh"
  },
  "script_timeout": 120
}
```

Hooks receive task, project, stage, repository/worktree, and attempt context, including a
stable `ENSO_EVENT_ID`. They run in the recorded worktree when available, otherwise the
workspace. Delivery can repeat, so scripts must deduplicate that event ID or be safe to
repeat. A failed after-transition hook stays visible and is retried; it does not undo the
accepted stage. Teardown failure retains the worktree. Never move the task recursively
from a lifecycle hook. [Task lifecycle details](/docs/tasks/#lifecycle-scripts) and the
[script environment](/docs/cli/#environment-for-agents) define the full contract.

Ordinary job prerun/postrun hooks remain useful, but putting a test in postrun is not the
same as configuring a required task check. Put acceptance requirements in the stage so
Enso can enforce them through all supported handoff paths.

## Inspect and recover

Open a task in the [web viewer](/docs/web/#tasks) to see submissions, checks, repair
attempts, review handoffs, integration, and cleanup. The workflow history distinguishes
a provider's outcome from stage acceptance. Command output and candidate/spec/workflow
versions remain available for inspection; old failed attempts are not rewritten as passes.

```bash
enso task show APP-001
enso workflow show APP-001 --json
```

Fix the cause before retrying. An operator can use `workflow retry` with a reason to reset
an exhausted budget, then `task resume` for blocked work; retry does not resume the task
or manufacture passing checks. `workflow verify` submits an operator handoff, runs the
configured checks without a provider, and **advances the task when accepted**. On an
integration stage it can also land the candidate locally. The viewer labels it
**Operator verification**. Use the stage job for a command stage: manual verification
does not execute that stage's command. See [CLI recovery commands](/docs/cli/#workflow-commands)
for the full contract.

Checks are tied to the candidate and chosen specification/workflow. A changed candidate,
rebase, or changed acceptance inputs requires fresh evidence. Existing tests and common
validation manifests are protected; intentional changes require explicit operator review
with `workflow approve-rules`, followed by real verification. Agents cannot use these
operator commands through the supported in-run CLI, and `--force` does not bypass checks.

These controls enforce Enso's supported workflow paths. They are not an OS sandbox against
an unrestricted agent running as the same account. Provider permissions and separately
protected execution are needed for that stronger boundary.

## Migrate an existing flow

Inspect the existing tasks, jobs, and scripts; disable the project's stage jobs and let
active work finish, because migration refuses to start while a project job is running, a
task is claimed, or a lifecycle script is still pending.
Then use the development initialization command with `--migrate` rather than overwriting
an existing pipeline. Migration maps `triage` to `plan` and `todo` to `implement`, preserves
task history and recorded worktrees, and retains old job definitions and scripts.
Review custom stage mappings and carry useful instructions into the new jobs.

Move acceptance tests into stage checks and completion reactions into lifecycle hooks.
The replacement jobs start disabled. Validate them, exercise a failed check and repair,
and inspect the task history before enabling queues. See the
[migration procedure](/docs/tasks/#migrating-an-existing-pipeline) for backups and interrupted
migration recovery.
