Reference
Task Launch
How to model process start truthfully, especially for long-running services.
referenceautomation buildersintermediatestable2026-05-30
Recommended next
Purpose
Use this page when you need to decide whether a task should use run, script, command, or launch.
The strong default for long-running service processes is launch.kind: command.
commandowns finite structured argv executionlaunchowns process startruntimeowns what becomes reachable and how readiness is provedrunstays the simple shell shorthand for finite tasksscriptstays the multiline shell escape hatch
Governance rule
Use launch.kind: command for long-running app servers, docs previews, workers, and similar service processes.
Use command for finite argv-owned tasks such as uv run pytest, poetry run pytest, bundle exec rake test, or npm run build when the repo truth is one executable plus stable arguments.
Use run for finite shell tasks, pipelines, or cases where a structured executable shape would be misleading.
- Choose the finite body by owner boundary: use
prepare.kind: sequencewhen one setup task honestly owns more than one typed setup step under shared requirements/effects, including structural prepare steps and deterministic bootstrap steps, useaction.kind: ensure_bundlewhen one setup task honestly owns more than one deterministic setup action under the same owner, and keep steps as separate finite tasks when they need distinct reuse, separate requirements/effects, or independent operator entrypoints. - Use
action.kind: ensure_git_checkoutwhen one setup task honestly owns clone-if-missing materialization of a sibling or vendored repo checkout, including optional declared remote reconciliation, and should not burygit cloneorgit remote add/set-urlbootstrap truth in shell glue. - Use
action.kind: ensure_git_templatewhen one setup task honestly owns deterministic scaffold or factory materialization from a Git-backed template, including inherited.gitremoval and fresh localgit init, and should not burygit clone,rm -rf .git, orgit initbootstrap truth in shell glue. - Use
action.kind: ensure_container_networkwhen one setup task honestly owns shared external Docker network readiness as a standalone lane and should not burydocker network inspect/createlogic in shell glue. - Use
action.kind: build_container_imagewhen one direct task honestly owns Dockerfile-backed local image materialization for a declared container or Compose lane. Declare the repo-relative Dockerfile, build context, and local tag directly, keep Docker/network effects explicit, and do not hidedocker buildin command or shell glue. - Use
action.kind: database_schema_mutationonly for the shipped Unix V12 PostgreSQL adapter:action.effectmust name exactly one same-taskeffects.declaredentry. Ota captures bounded migration bytes, publishes a selected-task-bound non-secret plan in dry-run, and verifies the retained bytes at the selected executor boundary. Mode and OS-variant overlays may refine non-execution inputs, but must not replace the typed action with another executable body. Ota refuses before task conditions, required services, dependencies, or provider contact; non-Unix execution also refuses because race-safe capture is unavailable. Keep it outsideagent.safe_tasksand never describe that refusal as a successful migration, approval, receipt, archive, or assurance claim. - Ota constrains the execution shape, not the binary family
workflows.<name>.adapter_inputs.compose.*is the canonical workflow-owned compose overlay surface for selected compose task paths; use it when one workflow should own the base compose stack, Compose profile set, or project name instead of repeating that truth in task-local adapter inputs.workflows.<name>.adapter_inputs.bake.filesis the workflow-owned Bake overlay surface for selected Bake task paths; ota prepends that base file stack ahead of narrower task-local Bake file additions.
Preferred finite command shape
Finite command taskyaml
tasks: test: command: exe: uv args: [run, pytest]Preferred service shape
Long-running service taskyaml
tasks: dev: launch: kind: command exe: bundle args: [exec, rails, server, -b, 0.0.0.0, -p, "3000"] runtime: kind: service surfaces: - apiWhy this is stronger
- the executable and arguments stay structured instead of disappearing into one shell string
- finite argv tasks stop pretending to be shell just because they are not long-running services
- ota can reason about launch separately from the endpoint contract
- service surfaces, listeners, and readiness stay canonical under
runtime, not duplicated in shell glue - examples and generated docs can teach one stable service-launch shape
Use run when shell is the truth
Finite shell taskyaml
tasks: test: run: pnpm test -- --runInBand