Browse docsWorkflows

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 for published availability and 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:

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 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:

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:

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 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.

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; 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:

{
  "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:

{
  "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:

{
  "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:

{
  "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 and the script environment 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 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.

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 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 for backups and interrupted migration recovery.