Spec-driven development with approval loops
Comparison
The engine exposes two layers for running spec-driven workflows: a
high-level interactive layer (spec.runner.execute_with_approval) and
a low-level pipeline layer (PipelineOrchestrator). Both execute the
same tasks with the same quality gates, but differ in who controls the
approval loop and how much state they manage for you.
| Capability | spec layer (execute_with_approval) |
pipeline layer (PipelineOrchestrator) |
|---|---|---|
| Import path | from attune.spec.runner import execute_with_approval |
from attune.pipeline import PipelineOrchestrator |
| Approval loop | Per-task, interactive — pauses after each task | Batch — runs all tasks unless you pass skip_task_ids |
| Resume support | Yes — load_state / save_state / find_resumable_plans persist SpecState |
No built-in persistence; caller owns resumability |
| Progress feedback | Presenter functions render live output | Callback only — wire on_task_complete yourself |
| Skip flags | skip_gates, skip_tests, skip_simplify |
Same flags on __init__ |
| Task filtering | get_pending_tasks against persisted state |
Pass skip_task_ids: set[str] to run_all |
| Result model | PipelineResult (shared) |
PipelineResult (shared) |
| Concurrency | Async coroutine — await it |
Async coroutine — await run_all (or asyncio.run) |
| Typical caller | Conversational / interactive session | Automated scripts, CI pipelines |
Use the spec layer when a human approves each task, you want
automatic resume support, and you want formatted output without
writing presenter logic. Use the pipeline layer when running in
CI with no interactive step, when you need to skip tasks by ID at call
time, or when you want to inspect gate results programmatically with
no display logic in the way.
When in doubt, start with the spec layer. The pipeline layer is
the better fit only when you are certain you do not need state
persistence or interactive approval — and are prepared to manage both
yourself.