Operate

Defining Readiness

How env vars, services, verify, checks, and proof fit together so readiness stays explicit instead of collapsing into one vague setup story.

learnmaintainersintermediatestable2026-06-27

Why this page exists

Teams often say a repo is ready when they really mean several different things at once.

One person means the right env vars exist. Another means Postgres is reachable. Another means the app boots. Another means the verification task passed. Another means there is evidence the whole path worked on a clean boundary.

Ota keeps those truths separate on purpose so readiness stays explainable.

  • env vars are config truth
  • services are dependency truth
  • checks are diagnostic truth
  • verify is completion truth
  • proof is evidence truth

The wrong model

The weak model is to collapse everything into one vague setup command and one vague success claim.

That is how repos end up with drift between README steps, shell scripts, CI, and agent behavior.

Problem

Config gets hidden in local state

The repo depends on env values, but nobody can tell whether they are required, optional, secret, defaulted, or sourced from a declared file.

What breaks

The first failure looks random because the contract never made config truth explicit.

Problem

Dependencies get smuggled into shell

A setup script implicitly assumes a database, queue, or sibling service is already running.

What breaks

The operational dependency exists, but the contract does not name it cleanly.

Problem

Success becomes guesswork

A command exits zero, but nobody can tell whether the repo is diagnosable, runnable, verified, or actually proven ready on a clean path.

What breaks

Humans, CI, and agents all walk away with different interpretations of what passed.

Env vars define config truth

Readiness starts with configuration truth. If the repo needs a value to operate, that fact should have a home.

In ota, env.vars defines the requirement layer and env.sources defines which files ota is allowed to read. That keeps config reviewable instead of hiding it in shell folklore.

  • use env.vars for required, secret, default, and allowed-value truth
  • use env.sources only when file-backed loading is intentional repo truth
  • use task-scoped env requirements when a value belongs to one path, not the whole repo
  • use ota env and ota doctor when you need to explain who won and what is missing
Config truth belongs in the contractyaml
env:  vars:    DATABASE_URL:      required: true      secret: true    WEB_ORIGIN:      default: "http://127.0.0.1:3000"  sources:    - kind: dotenv      path: .env.local

Services define dependency truth

A repo can be configured correctly and still not be ready because a dependency is missing or unreachable.

That truth belongs under services when the repo depends on infrastructure such as Postgres, Redis, Compose services, or workspace-produced endpoints.

  • declare services when the dependency should be visible to doctor, up, workflows, and agents
  • attach readiness where the dependency is owned instead of hiding it in setup shell
  • use service endpoints and structured readiness so hostnames, ports, and protocol truth do not drift
  • scope required services per workflow when one repo has more than one runtime path
Dependency truth belongs under servicesyaml
services:  postgres:    required: true    manager:      kind: compose      name: local      file: compose.yaml      service: postgres    endpoints:      app:        address: postgres        port: 5432    readiness:      from: app      kind: tcp

Checks define diagnostic truth

Checks answer: what should ota be able to test directly without turning that logic into a business task or a human-only note.

They are the right surface for named readiness gates used by ota check, ota doctor, workflow readiness, and CI.

  • use checks for repeatable diagnostic gates
  • prefer structured kinds like precondition, file, and env when ota already owns the assertion surface
  • keep shell run checks for logic that still does not fit the structured lanes
  • use severity to make the difference between blocking and advisory truth explicit
Checks should be named and machine-repeatableyaml
checks:  - name: backend-config    kind: env    severity: error    file: .env.local    key: DATABASE_URL    state: present  - name: app-ready    kind: health    severity: error    probe: backend-ready

Verify defines completion truth

Readiness is not only about whether the app can start. It is also about whether the repo's declared work is complete.

That is where verification tasks and aggregate verification flows matter. They prove the repo's own quality bar, not just bootability.

  • use declared tasks for lint, test, build, and other completion gates
  • use aggregate verification when the repo wants one canonical verify surface composed from smaller tasks
  • keep verify truth explicit instead of assuming command success means the change is acceptable
  • teach agents and CI the same verification path the maintainers trust
Verification should be explicityaml
tasks:  lint:    command:      exe: npm      args: [run, lint]  test:    command:      exe: npm      args: [run, test]  verify:    aggregate:      tasks:        - lint        - test

Proof defines evidence truth

Proof is the surface for when someone needs more than a claim that a task ran.

Use proof when you need artifact-backed evidence that a selected workflow really became ready on a fresh boundary, or when a failure should preserve machine-readable evidence instead of dying as terminal scrollback.

  • use ota proof runtime for clean-machine workflow proof
  • use receipts when task execution needs durable machine-readable evidence
  • keep proof separate from diagnosis and verification so operators can tell what was actually demonstrated
  • treat proof as evidence truth, not as a replacement for the contract itself
Proof is the evidence lanebash
ota doctorota checkota run verify --receiptota proof runtime --workflow app

The mental model

These surfaces work best when each one owns the narrowest truthful question.

  • env vars answer what configuration must exist
  • services answer which dependencies must be reachable
  • checks answer which diagnostic gates should run directly
  • verify answers which declared work proves completion
  • proof answers what evidence shows the path actually worked

A practical authoring rule

  • if the repo needs a value, start with env.vars
  • if the repo needs infrastructure, declare a service
  • if the repo needs a named readiness gate, add a check
  • if the repo needs a canonical quality bar, expose a verify task or aggregate
  • if the repo needs auditable runtime evidence, use proof
  • do not collapse these into one shell command unless ota truly does not yet own the surface