Back to blog
Workflows

Approval gates: letting the agent ship, safely

A workflow that plans, implements, and tests in an isolated worktree — but pauses at a human gate before anything merges. Real control flow, not a one-shot prompt.

SpecShip Desktop — a spec-implement run paused at the human approval gate before merge

There are two ways to let an AI agent ship a change. The first is to let it run end to end and hope. The second is to give it real control flow — plan, build, test, and then stop at a gate where a human says yes — and have every step run somewhere it can't hurt you. SpecShip's workflow engine is built entirely around the second.

A workflow is a DAG, not a prompt

You define a workflow as a small graph of typed steps with dependencies — some run an agent, some run a shell command, some are pure approval gates. It's real control flow: a step can depend on others, branch, or block until a human acts. That's a different object from a single mega-prompt, and it behaves differently: each step's output is captured, and the run can pause and resume across days.

spec-implement.yaml
steps:
  - id: plan        # agent drafts a plan from the spec
  - id: approve     # ← human gate: review the plan
    depends_on: [plan]
  - id: implement   # agent writes code + tests, runs them
    depends_on: [approve]
  - id: verify      # run the suite
  - id: link        # assert spec → code links
  - id: review      # ← human gate: approve the diff

The bundled spec-implement workflow runs exactly this shape: it drafts a plan, pauses, and waits. You read the plan, approve it, and resume — then it implements, runs the tests, asserts the spec links, and pauses again for a final look at the diff before anything lands. Two gates, both human, both load-bearing.

Isolated by construction

Every run executes in its own git worktree — a separate working tree off your repo. A half-finished agent run, a failed test, an experiment you abandon: none of it touches the branch you're sitting on. Your working tree stays exactly as you left it while the agent works in its own copy.

The worktree is the unit of safety. Approve the result and merge it; reject it with a comment and the run parks — worktree and artifacts intact — so resuming revises the work against your feedback and pauses again for review. Deleting a worktree is only ever an explicit workflow purge. Nothing partial ever touches your real tree.

The same workflow, three surfaces

The DAG doesn't care where it runs. The same YAML drives a run from the CLI, from the desktop UI, or from a GitHub Actions runner — same steps, same gates, same behaviour. A gate that's an interactive approval at your desk becomes a required reviewer in CI. You write the workflow once and choose where it lives.

The agent gets to be fast. You keep the one decision that matters: does this merge?

Why gates beat guardrails

You can try to constrain an agent with ever-longer system prompts and hope it never colours outside the lines. Or you can let it work freely inside a sandbox and put a hard checkpoint where the risk actually is — the moment code leaves the sandbox. Gates are cheaper to reason about than guardrails: there's exactly one place a human has to be right, and the worktree guarantees that everything before it was reversible.

This whole blog's backing feature set was built that way — dozens of spec-implement runs, each planned, gated, implemented in a worktree, and reviewed before merge. The agent moved fast. The gates held.

#workflows#worktrees#approval#dag
SE
Selva E.
Building SpecShip · graph, specs & harness
Works on the parsing, storage, and spec layers of SpecShip. Spends an unreasonable amount of time thinking about edges.