Getting started
First ota.yaml
A minimal contract that still feels operational.
learnmaintainersbasicstable2026-05-30
Recommended next
Minimal useful starter
A starter contract should be obvious, stable, and runnable.
Starter contractyaml
version: 1metadata: ota: minimum_version: "1.6.26"project: name: ota-web type: applicationtoolchains: node: version: ">=22" package_managers: pnpm: "10" fulfillment: source: corepack mode: runtasks: setup: prepare: kind: dependency_hydration medium: package_dependencies source: kind: node_package_manager cwd: . manager: pnpm mode: install requirements: toolchains: [ node ] effects: writes: [ node_modules ] network: true network_kind: dependency_hydration build: depends_on: [ setup ] command: exe: pnpm args: [ build ] requirements: toolchains: [ node ] effects: writes: [ dist ] test: depends_on: [ build ] command: exe: pnpm args: [ test ] requirements: toolchains: [ node ] deploy: depends_on: [ test ] command: exe: pnpm args: [ deploy ] effects: writes: [ release ] safe_for_agent: falseagent: entrypoint: setup default_task: test safe_tasks: [ setup, build, test ] refusal_canaries: - task: deploy protected_paths: [ ota.yaml, .env, .env.local ] verify_after_changes: [ build, test ] What makes it useful
- it names the repo
- it declares the core runtime and tools
- it defines safe tasks and verification tasks
- it gives agents a deterministic entrypoint
When the first task is a service
If the starter contract includes a long-running app server, docs preview, or worker process, prefer launch.kind: command instead of hiding that start path inside raw run shell text.
launch owns process start. runtime plus surfaces owns the reachable URL, listener, and readiness truth.
- use
launch.kind: commandfor long-running service processes - use
runtime.kind: servicewithsurfacesorlistenersfor the endpoint and readiness contract - use
aggregate.tasksfor named verification entrypoints that only run other tasks - keep
runfor finite shell tasks, pipelines, or real escape-hatch cases
Structured service taskyaml
tasks: dev: launch: kind: command exe: bundle args: [exec, rails, server, -b, 0.0.0.0, -p, "3000"] runtime: kind: service surfaces: - api