Getting started

First ota.yaml

A minimal contract that still feels operational.

learnmaintainersbasicstable2026-05-30

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: command for long-running service processes
  • use runtime.kind: service with surfaces or listeners for the endpoint and readiness contract
  • use aggregate.tasks for named verification entrypoints that only run other tasks
  • keep run for 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