ota Command Reference

July 29, 2026 · View on GitHub

This document describes the current shipped CLI surface.

ota's canonical repo contract is ota.yaml. This reference covers the current repo-level CLI surface only.

For machine-readable command contracts, see json-output-reference.md. For the shipped assist workflow and refusal rules, see assist-workflow.md. For canonical exit-code behavior, see exit-codes.md. For service behavior across commands, see service-behavior.md. For platform shell behavior, see shell-semantics.md. For text rendering and plain-mode behavior, see output-style.md. For visual identity tokens and CLI/docs branding, see brand-style.md. For docs clarity rules and command-UX wording standards, see docs-clarity-spec.md. For compatibility boundaries in the active version, see compatibility-surface.md. For extension execution staging, see extension-execution-boundary.md. For provider-neutral CI shells and receipt/annotation wiring, see ci-pipeline-workflow.md. For hosted validation and PR-gating guidance, see hosted-validation-workflow.md. For the official GitHub Actions wrapper, see github-action-workflow.md.

Doctor first, contract second.

  1. ota doctor
  2. if the repo does not yet have ota.yaml, preview with ota detect --dry-run .
  3. review the first write with ota init --dry-run .
  4. choose an explicit first write with ota init . or ota detect --write .
  5. ota validate
  6. ota up --dry-run
  7. ota up
  8. ota run <task> or ota proof runtime --workflow <name>

Global

ota --help
ota --version
ota --version --json
ota --debug <command>
ota --plain <command>
ota --concise <command>
ota --verbose <command>
ota --file /path/to/ota.yaml <command>

Repo commands that read an existing ota.yaml can also target a monorepo member with:

ota <command> --member <name> [PATH]

ota currently ships these commands:

  • ota doctor
  • ota explain
  • ota up
  • ota run <task>
  • ota init
  • ota env
  • ota execution plan
  • ota execution topology
  • ota proof runtime
  • ota ci projection
  • ota ci github render
  • ota ci github check
  • ota ci github sync
  • ota assist declare-readiness
  • ota assist declare-service
  • ota assist bind-task
  • ota assist declare-env
  • ota assist add-task
  • ota assist wire-setup
  • ota assist normalize
  • ota detect
  • ota validate
  • ota tasks
  • ota workflows
  • ota services
  • ota diff
  • ota check
  • ota annotations
  • ota json validate
  • ota agents
  • ota clean
  • ota extensions
  • ota policy
  • ota policy init
  • ota policy review
  • ota uninstall
  • ota self-update / ota upgrade
  • ota skills install
  • ota workspace init
  • ota workspace detect
  • ota workspace validate
  • ota workspace tasks
  • ota workspace list
  • ota workspace execution plan
  • ota workspace run <task>
  • ota workspace check
  • ota workspace doctor
  • ota workspace explain
  • ota workspace up
  • ota workspace refresh
  • ota workspace diff
  • ota workspace status
  • ota workspace receipt

Start here:

ota doctor
ota detect --dry-run .
ota init --dry-run
ota up
ota run ci

Workspace:

ota workspace doctor .
ota workspace up

The command set is intentionally small. V1 is about making the core readiness path trustworthy, inspectable, and stable on real repositories.

When a command accepts a PATH, it may be either:

  • a direct path to ota.yaml
  • a directory containing ota.yaml

For commands that read an existing contract, ota now resolves in this order:

  • --file <path>
  • OTA_FILE
  • explicit file PATH
  • an explicitly supplied directory PATH is treated as the contract boundary
  • upward discovery from the current directory when no PATH is supplied

When the discovered ota.yaml is a declared monorepo member contract, ota now loads the merged member contract automatically from that member path.

ota detect is different. Its PATH is a repo root to inspect.

Global output modifiers:

  • --concise: reduce high-noise text output while preserving decisions and actions
  • --verbose: preserve full explanatory text output
  • --json: unaffected by --concise/--verbose
  • --debug emits command-phase tracing to stderr

Current progress behavior:

  • quiet blocking commands show a delayed spinner in interactive terminals
  • ota doctor and ota check keep their own check/progress handling
  • ota run keeps streaming/progress-focused behavior instead of the shared spinner
  • ota up uses the shared spinner by default; ota up --stream opts into raw live provisioning, service-start, and setup output
  • ota workspace doctor uses the shared spinner
  • ota workspace status uses the shared spinner
  • ota workspace doctor --json still uses the shared spinner on stderr in interactive terminals, while stdout remains valid JSON
  • ota workspace doctor --json --progress-json switches that doctor lane from spinner updates to live NDJSON workspace progress on stderr while stdout remains the final JSON report
  • ota workspace status --json --progress-json switches that status lane from spinner updates to live NDJSON workspace progress on stderr while stdout remains the final JSON report, and each event carries the repo drift state in tail
  • ota workspace list --json also uses the shared spinner on stderr in interactive terminals, while stdout remains valid JSON
  • ota workspace validate, ota workspace tasks, ota workspace list, ota workspace detect, and ota workspace init use the shared spinner when they are waiting on work
  • successful interactive commands may print a best-effort update notice when a newer release exists, and the notice says A newer \ota` release is available: vX.Y.Zand points toota self-updateorota upgrade`

Hosted validation guidance:

  • use ota validate --json and ota doctor --json for repo gating
  • use ota workspace validate --json, ota workspace doctor --json, and ota workspace explain --json for workspace gating and remediation planning
  • ota validate --json keeps legacy warnings[] strings and also exposes additive warning_details[] entries with stable advisory codes for governance-aware tooling; selected dependency-plane boundary advisories also carry additive provenance describing which backend selection lane won on the parent and dependency task
  • use ota workspace tasks --json and ota workspace list --json for workspace inventory, task availability, and preflight readiness summaries
  • do not mutate contracts during hosted validation

Current exit semantics

  • 0: success, ready state, or warning-only diagnosis
  • 1: invalid contract, blocking readiness issue, protected write failure, or general command failure
  • 2: CLI usage or argument parsing error
  • ota run: preserves child task exit codes on task failure
  • ota up: preserves provisioning, service-start, and setup child exit codes when those commands fail

The canonical registry is in exit-codes.md.

--debug

--debug emits command-phase tracing to stderr.

Current intent:

  • help humans and agents understand which path or mode a command resolved
  • keep normal stdout stable
  • avoid persistent trace output or verbose default output
  • use the trace channel for multi-step commands like ota up, ota run, ota workspace up, ota workspace refresh, ota workspace diff, ota workspace status, ota workspace run, ota doctor, ota detect, ota diff, and ota explain

ota validate

Validate an ota contract.

ota validate [PATH]
ota validate --json [PATH]
ota validate --member api [PATH]

Current behavior:

  • resolves ota.yaml using --file, OTA_FILE, or an explicit directory boundary

  • when --member is set, loads the root contract, merges the declared member override, and validates the merged contract

  • when a root contract declares workspace.type: monorepo, ota validate also validates each declared merged member contract

  • parses the contract

  • applies semantic validation

  • emits advisory warnings when authoring choices are valid but likely misleading, such as depends_on crossing execution boundaries or isolated cache paths that are not wired to the tool's effective /workspace/... path

  • includes provider-specific target examples for remote target validation errors:

  • daytona: sandbox-dev

  • ssh / tsh: user@host

  • kubectl: pod/ota-dev

  • exits 0 on success and non-zero on failure

Text output:

  • success: VALID <path> followed by next-step guidance into ota doctor and ota tasks --use
  • failure: validation or load error text

JSON output:

  • success: ok, path, summary.error_count, summary.warn_count, and warnings
  • failure: ok, path, summary.error_count, summary.warn_count, warnings, and either errors or error

ota tasks

List tasks from a validated contract.

ota tasks [PATH]
ota tasks --json [PATH]
ota tasks --all [PATH]
ota tasks --safe [PATH]
ota tasks --unsafe [PATH]
ota tasks --via native [PATH]
ota tasks --via container [PATH]
ota tasks --member api [PATH]
ota tasks --member api --member web --json [PATH]
ota tasks --safe --use [PATH]
ota tasks --unsafe --use [PATH]

Current behavior:

  • runtime-proof output publishes a terminal proof_verdict: passed, passed_with_unproven_boundaries, or failed; use it with proof_scope and not_proved so a narrow green proof is not misread as broader repo completion
  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota tasks lists root tasks and grouped summaries for each declared member
  • when --member is set, lists tasks from the merged member contract
  • repeated --member values list tasks for those members in the provided order
  • hides internal: true tasks by default and includes them only when --all is set
  • prints tasks in deterministic order
  • resolves the execution form for the current OS
  • renders structured launch sources additively when a task uses command or packaged-container launch instead of shell run / script
  • includes task metadata when present
  • includes task env and inputs when present
  • includes declared task effects when present so repo mutation, network dependency, and out-of-repo state changes stay explicit
  • includes task description and optional notes when present, where notes carries purpose and extra guidance
  • includes an agent summary when the contract declares one
  • includes variant summaries when variants are declared
  • --use expands each task with human and agent run commands, closure-aware agent policy, command preview, safety posture, declared effects, dry-run JSON, receipt follow-up guidance, plus task description and required inputs when present; plain ota tasks remains the full declaration view for dependencies, services, conditions, hooks, notes, and related metadata
  • --safe and --unsafe are mutually exclusive filters over the effective safe set (safe_for_agent: true plus agent.safe_tasks)
  • --via <native|container> filters to tasks runnable through the selected backend lane
  • --all includes orchestration tasks marked internal: true; those entries carry internal: true in JSON output
  • task JSON keeps use.human and use.agent as selected-mode compatibility projections, while use.modes[] is the canonical human/agent mode matrix; it names supported and unavailable Container/Native planes explicitly and includes Remote only when the task advertises it

Text output:

  • header: TASKS <path>
  • when agent guidance is present, the shared AGENT block is grouped into Overview, Execution, and Boundary sections with counted wrapped lists and writable-path root/exception collapsing
  • each task may include kind, os, category, depends_on, safe_for_agent, and variant count
  • each task may include Launch when the resolved execution source is structured launch
  • each task may include Prepare when the resolved execution source is a first-class prepare body, including ordered prepare.kind: sequence summaries
  • each task may include env, inputs, effects, requires_services, and requires_artifacts; generated artifact producer/consumer truth remains declared under top-level artifacts
  • each task may include Description and Notes, where Notes can describe purpose and usage
  • each task includes Human Run, with every advertised mode in stable Container, Native, then Remote order; the selected lane is marked (Default)
  • aggregate tasks inherit a mode only when every concrete task in their dependency closure supports that backend on the current platform; the aggregate itself never needs a duplicate command body merely to make its runnable modes discoverable
  • Container and Native remain visible when unavailable, with an explicit unsupported status instead of a fabricated command or silent omission
  • each task includes Agent Run; it prints ota run <task> --agent only when the full dependency closure is effectively agent-callable, otherwise it names the review-required task or closure
  • each task includes Agent Policy, which distinguishes declared safe_for_agent truth from the effective closure-aware decision
  • each task includes a short execution preview

Common operator lanes:

  • ota tasks --safe: show only effective agent-safe tasks
  • ota tasks --unsafe: show only non-safe tasks
  • ota tasks --safe --use: show safe tasks plus canonical run commands, safety posture, and machine-readable follow-up commands
  • ota tasks --unsafe --use: show non-safe tasks plus canonical run commands, review posture, and machine-readable follow-up commands
  • ota tasks --via native: filter to tasks runnable through native execution
  • ota tasks --via container: filter to tasks runnable through container execution

Agent discoverability:

  • ota tasks --safe --use is the canonical task-discovery surface before agent execution
  • use it to find the effective safe task surface, the canonical ota run <task> --agent command, selected mode hints, safety posture, and matching dry-run / receipt follow-up commands
  • once a task is selected from that surface, use ota run <task> --agent when the execution itself should enforce the declared agent-safe boundary

JSON output:

  • success: ok, path, tasks
  • agent is included when the contract declares agent guidance
  • monorepo root summaries include grouped per-member results in members
  • repeated --member values return grouped per-member results in members
  • each task includes the resolved execution plus optional selected_variant_os and variants
  • each task may include additive effects with declared writes, workspace_writes, network, and external_state
  • each task may include additive prepare; sequence prepares keep a nested steps[] tree, while dependency-hydration prepares expose medium, source_kind, cwd, manager, and mode
  • each task may include additive launch when the resolved execution source is structured command or packaged-container launch
  • failure: ok, path, and either errors or error

ota workflows

List declared workflows from a validated contract.

ota workflows [PATH]
ota workflows --json [PATH]
ota workflows --member api [PATH]
ota workflows --member api --member web --json [PATH]

Current behavior:

  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota workflows lists root workflows and grouped summaries for each declared member
  • when --member is set, lists workflows from the merged member contract
  • repeated --member values list workflows for those members in the provided order
  • prints declared workflows in deterministic order
  • resolves workflow surface exposes through the selected run task so the output shows the same URL surfaces ota doctor, ota up, and ota execution plan would target
  • keeps workflow discovery read-only; it does not prepare or run the workflow

Text output:

  • header: WORKFLOWS <path>
  • overview includes workflow count and the selected default workflow when declared
  • each workflow is rendered in the same flat scan-friendly layout as ota tasks
  • each workflow includes workflow-native command hints such as Use: ota up --workflow <name> and Proof: ota proof runtime --workflow <name>
  • workflow services summaries include both workflows.<name>.services.required and transitive task-level requires_services from the selected prepare/setup/run dependency closure
  • each workflow may include intent, description, notes, prepare, setup, run, services, run_launch, readiness_checks, readiness_probes, readiness_surfaces, signal_readiness_checks, signal_readiness_probes, signal_readiness_surfaces, exposes, and the per-entry default flag
  • when no workflows are declared, the text output says so explicitly and points users back to ota tasks or contract authoring instead of ending empty

JSON output:

  • success: ok, path, default, workflows
  • monorepo root summaries include grouped per-member results in members
  • repeated --member values return grouped per-member results in members
  • each workflow includes the resolved workflow summary plus a default boolean
  • failure: ok, path, and either errors or error

Agent workflow discoverability:

  • use ota workflows --json when you need machine-readable workflow posture before selecting one
  • use ota tasks --safe --use alongside it when the workflow decision depends on which safe task lanes are actually callable underneath that workflow

ota services

List declared services from a validated contract.

ota services [PATH]
ota services --json [PATH]
ota services --member api [PATH]
ota services --member api --member web --json [PATH]

Current behavior:

  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota services lists root services and grouped summaries for each declared member
  • when --member is set, lists services from the merged member contract
  • repeated --member values list services for those members in the provided order
  • prints declared service fields in deterministic order
  • services are not direct task entrypoints; they are managed by ota doctor and ota up

Text output:

  • header: SERVICES <path>
  • each service may include required, provider, depends_on, start, stop, healthcheck, timeout, and a management note
  • when no services are declared, the text output says so explicitly and points users back to ota doctor or contract authoring instead of ending empty

JSON output:

  • success: ok, path, services
  • monorepo root summaries include grouped per-member results in members
  • repeated --member values return grouped per-member results in members
  • failure: ok, path, and either errors or error

ota env

Inspect resolved environment requirements from a validated contract.

ota env [PATH]
ota env --json [PATH]
ota env --workflow docker-build [PATH]
ota env --task test [PATH]
ota env --member api --workflow docker-build --task test [PATH]

Current behavior:

  • validates the contract first
  • when --member is set, inspects the merged member contract
  • when --workflow is set, inspects the selected workflow instead of assuming workflows.default
  • when the selected workflow declares env.profile, env resolution prepends that workflow-owned profile truth before reporting values
  • when --task is set, includes the effective execution env for that task alongside the contract env view
  • when --task is set, any tasks.<name>.requirements.env entries are treated as required for that selected task view even when the same top-level env.vars.<name> entry is optional in the repo-wide contract
  • resolves values in the same precedence order as task execution
  • reports declared env source status alongside the env-variable view
  • shows the winning source for each contract env entry
  • declared source provenance applies uniformly across curated dotenv, properties, json, yaml, and toml env sources
  • reports missing required env and invalid allowed values
  • stays read-only
  • uses the shared declared-source loader, so parse failures, structure failures, and normalized-key collisions are reported with the same source-scoped truth as execution and doctor

Text output:

  • header: ENV <path>
  • includes a readiness status line, optional Workflow / Profile lines, a short overview, a Declared env sources section when sources exist, a Rendered env artifacts section when the selected workflow profile materializes files, and separate Contract env / Execution env sections when task-specific execution env is present
  • each env entry may include kind, required, value, source, source kind, source path, source status, status, allowed, default, and Next
  • each declared source may include kind, path, label, must_exist, status, detail, and Next
  • each rendered env artifact may include path, kind, exists, and includes
  • missing or invalid contract env entries point to a specific fix rather than guessing

Example:

ENV ./ota.yaml

Ready: yes

Declared env sources
- properties app.properties label=properties:app.properties status=loaded
- json env/runtime.json label=json:env/runtime.json must_exist=true status=loaded

Contract env
- DISCORD_TOKEN required=true value=*** source=properties:app.properties source_kind=properties source_path=app.properties source_status=loaded status=resolved
- DOCS_SITE_BASE_URL required=true value=https://docs.internal.example source=org policy status=resolved
- RELEASE_CHANNEL required=false value=stable source=default status=resolved allowed=[stable, canary]

Execution env
- OTA_WORKSPACE value=/workspace source=execution status=task
- CI value=true source=task status=task

JSON output:

  • success: ok, path, summary, sources, env
  • success with workflow scope also includes workflow, optional profile, and optional rendered_artifacts
  • success with task scope also includes task
  • summary includes contract, declared-source, task, resolved, missing, and invalid counts
  • failure: ok, path, task and workflow when relevant, and error

ota execution plan

Inspect the resolved execution context without provisioning, starting services, or running tasks.

ota execution plan [PATH]
ota execution plan --json [PATH]
ota execution plan --mode container --ephemeral [PATH]
ota execution plan --member api [PATH]
ota execution plan --workflow backend [PATH]

Current behavior:

  • validates the contract first
  • when --member is set, inspects the merged member contract
  • when --workflow is set, plans the selected workflow instead of assuming workflows.default
  • when the selected workflow declares run.task, planning resolves that canonical runtime path first; if the workflow only declares setup.task, planning uses that setup path as the fallback
  • when the selected workflow also declares prepare.task, planning reports that host file-prep phase inside the additive workflow summary but does not use it as the concrete execution task
  • reuses the same backend and lifecycle resolution path as ota run and ota up
  • when named contexts use execution.contexts.<name>.extends, planning resolves the merged context first and reports that concrete backend/lifecycle/image shape
  • reports the resolved backend, lifecycle, image, container-engine selection, and target strategy
  • fails with the same backend-configuration errors as runtime execution when the selected native/container/remote path is not actually runnable from the current contract
  • shows the deterministic per-run target name for ephemeral containers, but does not create that target
  • stays read-only

Text output:

  • header: EXECUTION PLAN <path>
  • status line: RESOLVED
  • optional Workflow section when the repo declares workflows and planning targets one explicitly
  • Resolved section with selected backend, lifecycle, image, engine candidates, target, and target strategy
  • selected Task when a workflow run task, or setup-only fallback, is the concrete planning source; prepare remains additive workflow context, not runtime identity
  • Contract section with the same compact contract identity used by receipts
  • Execution section when the contract declares execution intent
  • Overrides section when --mode, --lifecycle, or --ephemeral changed the resolved result

JSON output:

  • success: ok, path, contract, member when relevant, additive workflow and task when workflow planning selected a canonical path, contract_identity, declared_execution, optional workflow_env_artifacts, resolved, and overrides
  • workflow_env_artifacts reports workflow-owned rendered env files such as rendered dotenv artifacts, including their path, kind, profile, current exists state, included env names, and consuming task/service lanes
  • failure: ok, path, member when relevant, and either errors or error

ota execution topology

Inspect the declared execution topology without provisioning, starting services, or running tasks.

ota execution topology [PATH]
ota execution topology --json [PATH]
ota execution topology --member api [PATH]

Current behavior:

  • validates the contract first
  • when --member is set, inspects the merged member contract
  • stays read-only
  • reports the contract identity, declared execution surface, shared backends, reusable readiness probes, reusable runtime surfaces, services, normalized runtime listeners, and task target bindings exactly as the repo declares them
  • when tasks use structured launch, surfaces render that launch source additively instead of flattening it back into shell-only text
  • when runtimes attach reusable surfaces, shows both the declared top-level surfaces and the normalized listener truth that those attachments produced
  • task-target readiness probes surface their current reachability plane explicitly; top-level probes report whether they resolve from the invoking command host or from one named observer task plane
  • does not resolve effective readiness state or start anything; this is topology inspection, not execution planning

Text output:

  • header: EXECUTION TOPOLOGY <path>
  • Overview section with project and topology counts
  • Execution section when the contract declares execution intent
  • Shared Backends, Readiness Probes, Surfaces, Services, and Tasks sections with runtime/listener/launch/target detail when present

JSON output:

  • success: ok, path, contract, member when relevant, contract_identity, declared_execution, shared_backends, readiness_probes, surfaces, services, and tasks
  • top-level readiness_probes entries include literal URL or target source details plus the declared HTTP/TCP request contract fields that belong to that probe
  • top-level surfaces entries include declared surface kind, port, optional label, optional purpose, optional visibility, optional path, and optional readiness contract
  • task entries may include additive launch when the task uses structured command or container launch
  • task runtime entries may include backend_binding, readiness, attached_surfaces, additive surface_attachments, and normalized listeners
  • task target entries may include activation_mode, override_input, url, and typed service references
  • failure: ok, path, member when relevant, and either errors or error

ota proof lifecycle

Prove one bounded, manager-owned service lifecycle transaction without copying start, readiness, or teardown shell commands into the workflow.

ota proof lifecycle --workflow smoke [PATH]
ota proof lifecycle --workflow smoke --service database [PATH]
ota proof lifecycle --workflow smoke --mode container [PATH]
ota proof lifecycle --json --workflow smoke [PATH]
ota proof lifecycle --json --archive --workflow smoke [PATH]

Current behavior:

  • requires the selected workflow to declare proof.lifecycle.services[]
  • refuses an explicit --service outside that declared lifecycle scope
  • evaluates declared replay-input pins and active replay-input policy over the exact workflow prerequisite-plus-assertion closure before creating a lifecycle transaction, running a task, observing or starting a service, or executing the assertion; JSON refusal carries execution_started: false plus the hard-pin and policy evidence
  • refuses with replay_input_policy_unavailable before lifecycle work when the active policy source cannot be loaded
  • executes the selected workflow's prerequisite closure before acquiring lifecycle ownership
  • refuses a concurrent lifecycle transaction for the same repository before it can observe manager state or acquire a cleanup lease
  • observes manager-owned initial state, refuses a pre-existing active service, and acquires a cleanup lease only after a current inactive observation. A service declaring boundary_terminated instead runs inside a fresh runner-owned ephemeral container session; Ota attests that boundary absent before start and does not claim a host-wide manager state
  • starts dependency services in declared order, checks declared readiness, and runs the optional finite assertion after readiness; assertion service requirements reuse the transaction-owned services rather than issuing another start command
  • JSON emits a typed assertion result with its terminal state and exit code when available; failed or interrupted assertions include runner-captured stdout/stderr tails bounded to 8 KiB per stream after declared secret values are redacted. Diagnostic output does not widen lifecycle proof into application-output proof
  • attempts teardown in reverse order and requires a positive manager inactive observation before releasing each lease. For an isolated boundary, it removes the exact session in the finalizer and re-observes its absence before emitting boundary_terminated; a successful remove command alone is insufficient. Failed isolated cleanup is runner-attested incomplete finalization, not manager-derived inactivity. Exact session removal proves the runner-owned session cannot retain a process, not that a host manager or broader application output is stopped
  • emits passed_with_unproven_boundaries for a successful transaction because lifecycle state transitions do not prove broader application output or repo completion; lifecycle proof never emits a bare passed
  • --archive writes local content-addressed transaction evidence. Its filename, semantic snapshot reference, contract identity, complete resolved service closure, declared teardown authority, terminal finalization, and effective execution scope are verified before Ota reports the archive. An isolated-boundary record and archive scope also carry Ota's exact engine/session identity, which must match before boundary_terminated is accepted. It remains outside claim assurance, replay, and CI projection

Use it for a contract-owned contributor smoke that must prove Ota starts and cleans up only the service state it owns. It is not a replacement for an application-output or deployment proof.

ota proof runtime

Prove that one selected runtime path can become ready, capture the canonical execution artifacts, and tear the runtime back down.

ota proof runtime [PATH]
ota proof runtime --workflow app [PATH]
ota proof runtime --ready-timeout 10m --workflow app [PATH]
ota proof runtime --mode container --persistent [PATH]
ota proof runtime --member api --workflow backend [PATH]
ota proof runtime --json --workflow app [PATH]
ota proof runtime --json --archive --workflow app [PATH]
ota proof runtime --workflow app --negative-control postgres-unavailable [PATH]

Current behavior:

  • validates the contract first
  • evaluates declared replay-input pins and active replay-input policy before creating .ota/proof, writing topology or Doctor artifacts, or spawning the child runtime. The evaluated closure includes post-readiness seam observers and the selected negative-control task; JSON refusal carries execution_started: false plus the hard-pin and policy evidence
  • refuses with replay_input_policy_unavailable before proof artifacts or child execution when the active policy source cannot be loaded
  • when --member is set, proves the merged member contract from the monorepo root
  • when --workflow is set, proves that selected workflow path; otherwise it uses the effective default workflow or the default task path when the repo has no workflows
  • captures the canonical runtime-proof artifacts under .ota/proof/<workflow>/ (and under the selected member prefix when --member is set):
    • topology.json
    • doctor.json
    • up.log
  • reuses the existing execution and diagnosis boundaries instead of inventing a parallel proof implementation: the topology artifact is the same ota execution topology --json surface, the doctor artifact is the same ota doctor --json surface, and the up log is the repo-level preparation report for the selected path
  • supports --archive with --json to write an immutable, content-addressed terminal proof record under .ota/proof/archives/; it binds the terminal proof JSON to an archived semantic contract snapshot, clean Git source identity when available, the resolved execution scope (workflow, primary task, backend, provider, lifecycle, and target), and explicit replay_posture: witness_only; later assurance verifies the referenced snapshot's content identity before admitting the proof archive
  • treats .ota/proof/<workflow>/ as a mutable working bundle only. Its topology, doctor, and log paths may support diagnosis, but only the --archive record is a replay-grade proof witness
  • honors the selected path's declared readiness timing policy when a workflow/task surface defines startup timing such as start_period, interval, timeout, and retries
  • when the selected run task is a service launcher that exits successfully before the runtime is actually ready, such as docker compose up -d, proof keeps probing until the declared readiness budget expires instead of treating the launcher exit itself as a readiness failure
  • supports --ready-timeout <DURATION> to cap the runtime-proof readiness wait budget with explicit values such as 90s, 5m, or 1h
  • supports --negative-control <id> for a workflow-declared finite control task; Ota runs the selected task only after the ordinary proof path and its declared green seam obligation have passed. The task must exit non-zero and write a matching transaction-bound failure attestation for the contract-declared expected failure; only then does Ota validate the control and promote that exact dependency evidence to fault_tested
  • runs declared workflows.<name>.proof.seam_observations[] after readiness and before teardown; Ota injects one opaque marker into the declared producer task, never the finite observer. The observer must recover it through the declared dependency and write a runner-owned transaction attestation; only a verified attestation becomes exercised dependency evidence
  • renders Dependency Evidence before proof boundaries when Ota has runner-derived service reachability or caller-side attempt evidence; the render keeps its level, origin, and authority class visible instead of collapsing it into the proof verdict
  • uses the same backend/lifecycle override rules as ota doctor and ota up
  • attempts repo-scoped runtime cleanup after capturing artifacts so proof does not leave persistent Ota-managed runtime state behind
  • surfaces cleanup failures explicitly as a proof failure instead of silently dropping them

Use it when you need clean-machine proof that one declared front door actually becomes operational, or when you want one archiveable readiness lane for CI and case-study adoption work.

Text output:

  • header: PROOF <path>
  • Workflow and Mode lines first
  • success output includes a compact Steps section, final READY line, and Artifacts paths
  • blocked output keeps the shared NOT READY / BLOCKED status language, shows the failing proof phase, and surfaces one primary Why / Next lane without duplicating the full doctor report
  • blocked proof output may also include Likely cause when ota can derive a higher-confidence runtime-drift hint from captured proof logs
  • timeout output uses TIMEOUT when runtime-proof wait budget is exhausted and keeps the proof phase at readiness
  • interruption output uses INTERRUPTED and phase interrupted when runtime proof is terminated by a signal (for example CI cancellation)

JSON output:

  • success or blocked proof output: ok, path, mode, optional workflow, phase, shared summary, optional artifacts, optional workflow_env_artifacts, optional advisory likely_cause, and optional cleanup error / next
  • workflow_env_artifacts uses the same rendered-artifact summary as ota execution plan, so CI and agents can see which workflow-owned env file was materialized and which task/service lanes consume it during proof
  • --archive adds archive.identity and archive.path; the archive identity is the SHA-256 of the stored record, so consumers can verify the exact terminal proof they select
  • after an archived runtime proof, ota doctor --json publishes a workflow claim_assurance[] proof_breadth record. It is supported only when a content-addressed proof archive matches the current semantic contract, clean source identity, resolved execution scope, and replay_posture: witness_only; a matching failed proof is contradicted, while missing, stale, source-mismatched, or scope-mismatched evidence remains unknown
  • contract load or validation failures still use the standard validation failure surface instead of inventing a second invalid-contract payload

ota ci projection and ota ci github

Render a dedicated, Ota-owned reusable GitHub Actions workflow after ota.yaml has become the reviewed execution authority. Use it to remove duplicated bootstrap and verification shell from CI without letting Ota take over triggers, permissions, secrets, runners, environments, or deployment jobs.

ota ci github render --workflow verify --target-os linux
ota ci github render --workflow verify --target-os linux --output .github/workflows/ota-governance.yml --json
ota ci github check --workflow verify --target-os linux \
  --output .github/workflows/ota-governance.yml \
  --caller .github/workflows/ci.yml
ota ci github sync --workflow verify --target-os linux \
  --output .github/workflows/ota-governance.yml \
  --caller .github/workflows/ci.yml
ota ci projection --workflow verify --mode container --target-os linux --json
ota ci github render --workflow verify --mode container --target-os linux

Current behavior:

  • render is pure: it prints deterministic reusable-workflow YAML and never writes the optional --output target.
  • ota ci projection is the provider-neutral source of truth. It evaluates the full selected workflow agent closure, including declared prepare, setup, run, and attach task roots, and any declared proof assurance before rendering; policy-denied or review-required lanes fail projection with the same canonical reason the runner would enforce. --mode selects its native, container, or remote plane; provider adapters consume that exact projection rather than reconstructing contract authority.
  • The projection carries run_execution as finite_task or service_runtime. Generated adapters prepare every lane with ota up; they execute a finite run task through ota run --agent after preparation, while service and proof lanes retain their one authoritative runtime path.
  • Omitting --mode resolves the selected task's effective contract default. --target-os is explicit projection truth and must match the intended CI operating system. Supplying an unavailable mode for that target is refused before rendering. A denied projection remains inspectable in JSON with its evaluated identity, governance decision, and refusal basis.
  • The generated workflow owns checkout, immutable adapter revisions, ota-run/setup with source: contract, provider-adapter setup for contract-required toolchains, selected ota validate, ota doctor --workflow, safe-surface discovery, agent dry-run, and either agent execution plus receipt archival or, for a proof-required lane, one authoritative ota proof runtime execution. Each declared, unique agent.refusal_canaries control renders as its own provider check using Ota's --expect-refusal exit semantics rather than provider-shell assertions. Each check maps one stable canonical V11.5 merge_check_id to a scope-qualified provider name such as ota.refusal-canary.task.publish (linux/container), so native and container or OS lanes do not collapse into one GitHub check. A proof claim never bypasses agent admission.
  • The human-owned caller must invoke the generated path and pass its exact ota_projection_identity and ota_target_os. It may also choose ota_runner; the renderer's --runner value is only the reusable workflow's default. Ota checks the projection identity rather than reconstructing execution commands from caller YAML.
  • The generated workflow verifies identity through ota ci projection --expect-identity, not a provider-shell assertion. Runner selection remains provider-owned; target OS is bound explicitly. Ota refuses the projection before rendering when any executable member of the selected workflow closure or its resolved execution context does not support that target OS and mode. projection.toolchains[] is provider-neutral contract truth and each entry carries its selected execution_scopes. The GitHub adapter provisions only native-scoped toolchains; container and remote scopes remain owned by their declared execution boundaries. It renders immutable actions/setup-go for source: go, translating a supported lower-bound range (for example >=1.26 or >=1.26,<1.27) into Go's release selector (1.26), and immutable actions/setup-node for toolchains.node fulfilled through source: corepack, preserving the declared Node semver selector while disabling setup-node's provider cache until Ota activates the declared package manager, immutable ruby/setup-ruby for toolchains.ruby fulfilled through source: ruby, immutable actions/setup-python plus astral-sh/setup-uv for toolchains.python fulfilled through source: uv, and immutable actions/setup-dotnet for toolchains.dotnet fulfilled through source: dotnet, translating supported lower-bound Python and .NET ranges to concrete provider releases. It refuses a required toolchain source or range it cannot provision rather than silently assuming the selected runner image provides it.
  • check fails for a missing, stale, manually changed, or unowned generated workflow, or when the caller points at a different semantic projection.
  • sync is explicit and atomic. It creates a missing output or replaces only a file carrying the Ota ownership marker. It never writes the caller, so update that provider-owned file first when a contract change produces a new projection identity.
  • --output and --caller must be normalized repo-relative .yml or .yaml paths under .github/workflows. Ota rejects absolute or traversal paths and refuses to use the caller as its managed output.

The first cut projects one declared workflow with run.task. It intentionally does not generate a full CI workflow or infer provider policy. Existing workflow YAML remains useful detection evidence, but it cannot silently rewrite ota.yaml.

JSON output carries ok, operation, projection.identity, selected workflow/task, runner, merge_check_ids, required toolchains, adapter provider_checks, proof_required, render_identity, binding_identity, managed output path, caller path, and whether sync mutated its owned file. render_identity is adapter-scoped; binding_identity also binds the parsed human-owned caller reference. Failures carry a stable code, including managed_output_unowned, managed_output_stale, and caller_projection_reference_mismatch.

ota assist declare-readiness

Declare or refine structured readiness for one existing task runtime service or one existing managed service.

ota assist declare-readiness --task <name> [--style spring-http|http|tcp] [PATH]
ota assist declare-readiness --service <name> [--style spring-http|http|tcp|compose-health] [PATH]
ota assist declare-readiness --member api --task <name> [PATH]
ota assist declare-readiness --json --task <name> [PATH]
ota assist declare-readiness --write --task <name> [PATH]

Use it when the runtime surface already exists and you want Ota to propose the readiness contract instead of hand-authoring it.

Current behavior:

  • defaults to preview mode and shows assumptions, the exact readiness block, and the next validation commands
  • --write applies the proposed readiness mutation and revalidates the updated contract before returning success
  • supports --task for tasks.<name>.runtime.readiness
  • supports --service for services.<name>.readiness
  • supports --member through the existing merged monorepo contract path while writing only to the selected member overlay file
  • supports spring-http, http, and tcp styles for task readiness
  • supports spring-http, http, tcp, and compose-health styles for managed service readiness
  • --style compose-health is service-only and requires a compose-managed service (services.<name>.manager.kind: compose)
  • task targeting can infer from existing runtime and listener truth when that choice is unique
  • managed service targeting requires explicit --style unless the service already has a structured readiness kind that assist is refining
  • refuses when the target is ambiguous, unknown, missing the runtime/service surface needed for a truthful readiness declaration, or when the requested style conflicts with the selected listener protocol
  • text preview shows both current and proposed readiness when an existing readiness block would be replaced
  • --json emits the stable assist proposal/apply result shape described in assist-operations.md

Examples:

ota assist declare-readiness --task dev
ota assist declare-readiness --task dev --style spring-http --write
ota assist declare-readiness --service api --style http
ota assist declare-readiness --service postgres --style tcp
ota assist declare-readiness --service worker --style compose-health
ota assist declare-readiness --member api --task dev --json

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota assist declare-service

Declare or refine one top-level managed service.

ota assist declare-service --name <service> --manager compose|host --port <port> [PATH]
ota assist declare-service --name <service> --manager compose --compose-file docker-compose.yml --style tcp [PATH]
ota assist declare-service --name <service> --producer-repo <repo> --producer <task>[:listener] [PATH]
ota assist declare-service --member api --name <service> --manager compose --port <port> [PATH]
ota assist declare-service --json --name <service> --manager host --port <port> [PATH]
ota assist declare-service --write --name <service> --manager compose --port <port> [PATH]

Use it when the right next step is to create or refine a managed services.<name> block instead of hand-authoring manager, endpoint, and readiness YAML.

Current behavior:

  • defaults to preview mode and shows assumptions, the exact service block, and the next validation commands
  • --write applies the proposed service mutation and revalidates the updated contract before returning success
  • --name is required and identifies the managed service block under services
  • --manager compose|host chooses the manager kind for a new service and can refine an existing manager
  • --producer-repo <repo> plus --producer <task>[:listener] creates or refines a producer-owned services.<name>.producer block when the service is canonically owned by another repo in the same ota.workspace.yaml
  • --endpoint, --address, and --port control the selected endpoint projection; when safe, ota defaults the endpoint to host and the address to 127.0.0.1
  • --required true|false sets the service requirement flag explicitly
  • --style spring-http|http|tcp adds or replaces structured readiness anchored to the selected endpoint
  • --style compose-health is valid for compose-managed services and anchors readiness to compose container health state (readiness.kind: compose_health)
  • --compose-file, --compose-service, and --manager-name refine compose-managed service metadata
  • producer-owned service previews remove conflicting local manager, endpoint, and readiness truth in favor of the canonical workspace producer binding
  • compose-managed previews default manager.name to local and manager.service to the declared service name when those values are otherwise absent
  • supports --member through the existing merged monorepo contract path while writing only to the selected member overlay file
  • refuses when the requested service shape is ambiguous or under-specified, such as a new service without an explicit manager kind
  • --json emits the stable assist proposal/apply result for this service declaration

Examples:

ota assist declare-service --name postgres --manager compose --compose-file docker-compose.yml --port 5432 --style tcp
ota assist declare-service --name api --manager compose --compose-file docker-compose.yml --port 3000 --style http --write
ota assist declare-service --name worker --manager compose --compose-file docker-compose.yml --style compose-health
ota assist declare-service --name cache --manager host --port 6379 --json
ota assist declare-service --name user-api --producer-repo api --producer dev:http --write
ota assist declare-service --member api --name api --manager compose --port 3000 --write

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota assist bind-task

Create or refine one tasks.<consumer>.targets.<name> binding to a producer task runtime.

ota assist bind-task --task <consumer> --target <name> --to <producer>[:listener] [PATH]
ota assist bind-task --task <consumer> --target <name> --to <producer>:<listener> --address-view topology|host|internal [PATH]
ota assist bind-task --task <consumer> --target <name> --to <producer>:<listener> --activation ensure_ready [PATH]
ota assist bind-task --member api --task <consumer> --target <name> --to <producer>:<listener> --write [PATH]
ota assist bind-task --json --task <consumer> --target <name> --to <producer> [PATH]

Use it when the producer task runtime already exists and the correct next move is to wire one consumer target edge truthfully instead of hand-authoring targets.

Current behavior:

  • defaults to preview mode and shows assumptions, the exact target block, and the next validation commands
  • --write applies the proposed tasks.<consumer>.targets.<name> mutation and revalidates it before returning success
  • --to <producer> works only when the producer exposes exactly one declared service listener or the existing target already pins one safe listener
  • --to <producer>:<listener> is the explicit selector when the producer exposes multiple listeners
  • currently binds only to producer task runtimes, not directly to top-level managed service endpoints
  • --producer-member <name> selects a producer task from another declared monorepo member
  • --address-view and --activation refine the shipped target contract directly instead of hiding those fields behind heuristics
  • preserves an existing override_input unless a new one is supplied
  • refuses when the consumer task, producer task, or selected listener does not exist, or when assist cannot pick one listener safely
  • --json emits the stable assist proposal/apply result for this target-binding change

Examples:

ota assist bind-task --task smoke --target api --to dev:http
ota assist bind-task --task smoke --target api --to dev --json
ota assist bind-task --task smoke --target api --to dev:http --activation ensure_ready
ota assist bind-task --member api --task smoke --target api --to dev:http --write

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota assist declare-env

Create or refine one root env requirement, one declared env source, or one explicit task-local env override.

ota assist declare-env --name <ENV> [--required true|false] [--secret true|false] [--default <value>] [PATH]
ota assist declare-env --name PATH [--prepend <path> ...] [--append <path> ...] [PATH]
ota assist declare-env --source-kind dotenv|properties|json|yaml|toml --source-path <path> [--must-exist true|false] [PATH]
ota assist declare-env --task <name> --name <ENV> --value <value> [PATH]
ota assist declare-env --member api --task <name> --name <ENV> --value <value> --write [PATH]
ota assist declare-env --json --source-kind dotenv --source-path .env.local [PATH]

Use it when the contract already knows which env surface should exist and the next safe move is one reviewed env mutation instead of broad contract inference.

Current behavior:

  • defaults to preview mode and shows assumptions, the exact env block or task-local value, and the next validation commands
  • --write applies the proposed env mutation and revalidates it before returning success
  • root env requirements target env.vars.<NAME> with required, secret, default, allowed, prepend, and append
  • declared env sources target one curated env.sources[] entry with kind, path, and optional must_exist
  • task-local env targets only one explicit tasks.<name>.env.<KEY> = <value> write
  • prepend and append are allowed only for PATH
  • secret: true may not be combined with a new default value
  • supports --member through the merged monorepo contract path while writing only to the selected member overlay file
  • --json emits the stable assist proposal/apply result for this env mutation

Examples:

ota assist declare-env --name APP_PORT --required true --default 8080
ota assist declare-env --name PATH --prepend ./node_modules/.bin --append /opt/ota/bin
ota assist declare-env --source-kind dotenv --source-path .env.local --must-exist true --json
ota assist declare-env --task smoke --name API_BASE --value http://127.0.0.1:3000
ota assist declare-env --member api --task smoke --name API_BASE --value http://127.0.0.1:3000 --write

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota assist add-task

Create one new declared task with an explicit execution body.

ota assist add-task --name <task> --run "<command>" [PATH]
ota assist add-task --name <task> --script "<body>" [PATH]
ota assist add-task --name <task> --kind sandbox [PATH]
ota assist add-task --name <task> --kind service --run "<command>" --listener <name> --protocol http|tcp --port <port> [PATH]
ota assist add-task --member api --name <task> --run "<command>" --write [PATH]
ota assist add-task --json --name <task> --run "<command>" [PATH]

Use it when the contract needs one new task entry and the right next step is a reviewed starter task instead of hand-authoring tasks.<name>.

Current behavior:

  • defaults to preview mode and shows assumptions, the exact new tasks.<name> block, and the next validation commands
  • --write applies the proposed task creation and revalidates the updated contract before returning success
  • creates only new tasks in this slice; it refuses when the selected task name already exists in the effective contract
  • supports command, service, setup, check, and sandbox task kinds
  • requires --run or --script for every kind except sandbox, which uses the bounded starter body echo sandbox when no body is supplied
  • --kind setup only applies to the canonical --name setup task and defaults internal: true when you do not override it
  • --kind service requires --listener, --protocol, and --port, and currently declares one fixed listener plus a matching fixed host projection without adding readiness
  • supports --member through the merged monorepo contract path while writing only to the selected member overlay file
  • refuses service-only listener inputs on non-service task kinds
  • --json emits the stable assist proposal/apply result for this task creation change

Examples:

ota assist add-task --name smoke --run "cargo test"
ota assist add-task --name setup --kind setup --run "npm install"
ota assist add-task --name sandbox --kind sandbox
ota assist add-task --name dev --kind service --run "npm run dev" --listener http --protocol http --port 3000 --json
ota assist add-task --member api --name smoke --run "npm test" --write

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota assist wire-setup

Create or refine the setup task and its pre-setup service phase for ota up.

ota assist wire-setup --run "<command>" [PATH]
ota assist wire-setup --script "<body>" [PATH]
ota assist wire-setup --copy-from <source> --copy-to <target> [PATH]
ota assist wire-setup --run "<command>" --service <name> [--service <name> ...] [PATH]
ota assist wire-setup --member api --run "<command>" --write [PATH]
ota assist wire-setup --json --script "<body>" [PATH]

Use it when the contract needs one truthful tasks.setup declaration or when setup.requires_services should define which managed services must start before setup runs.

Current behavior:

  • defaults to preview mode and shows assumptions, the exact tasks.setup block, and the next validation commands
  • --write applies the proposed setup mutation and revalidates the updated contract before returning success
  • --run, --script, and --copy-from <source> --copy-to <target> set the setup body explicitly; a new setup task requires one of them
  • --copy-from / --copy-to writes a cross-platform action.kind: copy_if_missing setup task for file-template setup such as .env.example to .env
  • --service <name> sets setup.requires_services in the provided order as the pre-setup service phase
  • --clear-services removes setup.requires_services
  • --internal true|false refines tasks.setup.internal directly
  • supports --member through the existing merged monorepo contract path while writing only to the selected member overlay file
  • preserves unrelated existing tasks.setup fields instead of rewriting the whole task
  • refuses when a new setup task has no explicit body, when no actual setup change was requested, or when a named managed service does not exist
  • --json emits the stable assist proposal/apply result for this setup wiring change

Examples:

ota assist wire-setup --run "npm install" --service postgres
ota assist wire-setup --copy-from .env.example --copy-to .env --write
ota assist wire-setup --script "cargo fetch\ncargo build" --json
ota assist wire-setup --member api --run "npm install" --service postgres --write

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota assist normalize

Normalize one existing task into the canonical tasks.setup slot.

ota assist normalize --task <name> --into setup [PATH]
ota assist normalize --member api --task <name> --into setup --write [PATH]
ota assist normalize --json --task <name> --into setup [PATH]

Use it when the contract already has one setup-like task under the wrong task name and the right next step is to move that existing declaration into tasks.setup instead of hand-editing both the old and new task entries.

Current behavior:

  • defaults to preview mode and shows assumptions, the current task block, the proposed canonical tasks.setup block, and the next validation commands
  • --write applies the normalization and revalidates the updated contract before returning success
  • the current shipped scope is one intent only: --into setup
  • removes the original tasks.<name> entry and writes the moved task under tasks.setup
  • normalizes the moved task to internal: true so setup stays an ota up support task by default
  • supports --member only when the selected task is declared in that member overlay file; it refuses inherited root tasks because member overlays cannot delete those safely in this shipped slice
  • refuses when tasks.setup already exists, when the selected task does not exist, or when the selected task is already setup
  • --json emits the stable assist proposal/apply result for this normalization change

Examples:

ota assist normalize --task bootstrap --into setup
ota assist normalize --member api --task bootstrap --into setup --write
ota assist normalize --json --task bootstrap --into setup

Use assist-workflow.md when you need the fuller operator guide, refusal cases, or monorepo/member behavior.

ota diff

Compare two ota contracts semantically.

ota diff ./before/ota.yaml ./after/ota.yaml
ota diff ./repo-a ./repo-b
ota diff --json ./before/ota.yaml ./after/ota.yaml

Current behavior:

  • compares two repo or workspace contracts as normalized semantic contract truth instead of raw YAML structure
  • also accepts archived receipt JSON or archived .ota/contracts/... snapshot JSON as either side of the comparison when you want to compare current truth against archived run truth
  • reports added, missing-in-target, and changed assumption keys in deterministic order
  • text output now includes explicit input identity for each side, so archived receipt snapshots are visible as first-class diff inputs instead of only being implied by the path
  • remains read-only
  • exits 0 when the comparison succeeds, even if differences exist
  • surfaces load and parse errors clearly
  • public operator guide: Semantic Snapshots and Correlation for archived semantic truth, snapshot inspection, and receipt-to-receipt drift correlation
  • local core spec: semantic-snapshots-and-correlation.md

Text output:

  • header: DIFF <base> -> <target>
  • MATCH or DIFFERENT
  • explicit Inputs overview with per-side path, semantic input kind, and resolved snapshot path when a receipt-side diff resolves through .ota/contracts/...
  • readiness impact summary
  • grouped added, missing-in-target, and changed paths
  • additive category and risk labels per changed assumption
  • policy-section changes may include provenance labels
  • summary counts at the end

JSON output:

  • success: ok, base, target, summary, changes
  • changes[] now comes from normalized semantic assumptions and can also include additive category and risk
  • policy-section changes may include provenance
  • failure: ok, base, target, and error

Use this when you want to compare contract states before writing changes or to review the impact of a proposed edit in CI.

ota explain

Explain readiness findings as an ordered remediation plan.

ota explain ./repo
ota explain --json ./repo
ota explain --member api ./repo

Current behavior:

  • requires an existing ota.yaml
  • diagnoses the contract first
  • turns grouped findings into an ordered remediation plan
  • prioritizes preview-first and contract-authoring actions ahead of later runtime follow-ups when several fixes are available
  • stays read-only and deterministic
  • prints a compact overview with step counts at the end

If the repo does not yet have ota.yaml, start with ota doctor, then use ota detect --dry-run ., ota detect --contract ., and ota init --dry-run . before coming back to ota explain.

Text output:

  • Plan section with ordered remediation steps
  • Why and Next lines for each step
  • Provenance lines when ota can trace the diagnosis source for that step
  • Overview counts at the end

JSON output:

  • success: ok, path, summary, actions, steps
  • actions is the ordered grouped remediation plan; each action includes order, action_key, action_title, severity, count, why, and next
  • actions may also include shared provenance when the grouped action maps back to one diagnosis source
  • steps keeps the finding-level detail; each step includes order, code, severity, summary, why, and next
  • steps[].code is the stable finding/advisory identity surface for machine consumers; advisory-backed steps carry the contract advisory code directly instead of re-deriving it from rendered summary text
  • steps may also include provenance and provenance_key
  • failure: ok, path, and error

ota annotations

Render ota doctor findings as CI annotations or provider-neutral log lines.

ota annotations --mode doctor --format github --input ./doctor.json
ota annotations --mode workspace-doctor --format plain --input ./workspace-doctor.json
ota annotations --mode doctor --format markdown --input ./doctor.json
ota annotations --mode receipt-diff --format markdown --input ./receipt-diff.json
ota doctor --json | ota annotations --mode doctor --format github --input -

Current behavior:

  • reads ota JSON from a file or from stdin when --input - is used
  • emits one primary blocker line when summary.primary_blocker is present
  • does not repeat that same primary blocker as a second finding line
  • emits one line per remaining finding
  • ignores finding_groups and stays one-annotation-per-finding by default
  • maps severity: error to ::error or ERROR and all other severities to ::warning or WARNING
  • --format markdown renders a compact summary block for step summaries or PR comments with status, counts, the primary blocker when present, and remaining findings
  • --mode receipt-diff expects ota receipt --json --baseline ... diff output and currently supports --format markdown only
  • scopes workspace findings with the repo name and path so annotations stay actionable
  • labels additive Code:, Provenance:, and Next: segments when those fields are present in the input JSON
  • serves as the canonical binary entrypoint for repo-local and CI annotation adapters

Text output:

  • ERROR: ..., WARNING: ..., or NOTICE: ... for primary blockers depending on their severity
  • ERROR: ... and WARNING: ... for findings
  • primary-blocker and finding annotations append Code: <stable-code> when the input JSON includes one
  • markdown output uses ## <title>, Status, Counts, optional Primary blocker, and Findings sections instead of one-line annotations
  • receipt-diff markdown output uses Baseline source, Compare, Drift, Counts, optional Gate, optional Primary blocker, and compact Introduced / Resolved sections

JSON output:

  • none; this is a rendering command, not a contract reader

ota json validate

Validate a JSON payload against a published Ota schema with optional assertion gates. Payload can come from a command (-- <command>) or an existing artifact (--input <file|->).

ota json validate \
  --schema run-preview.json \
  --allow-exit 0 \
  --allow-exit 1 \
  --assert-eq dry_run=true \
  --assert-in preview_status='["RUNNABLE","RUNNABLE WITH WARNINGS","BLOCKED"]' \
  -- ota run ci --dry-run --json .

ota json validate \
  --schema run-preview.json \
  --input ./repo-run-preview.json \
  --assert-type plan.actions:array

Current behavior:

  • with --input <path>, reads JSON from the file; with --input -, reads JSON from stdin
  • without --input, executes the provided command after -- and captures JSON from stdout (falling back to stderr when stdout is empty)
  • accepts one or more allowed exit codes (--allow-exit); defaults to 0 when omitted
  • in --input mode, uses synthetic exit code 0 for exit-map assertions
  • when --write-payload is set, writes the captured/read payload before validation and assertions
  • validates payload shape against schemas under docs/spec/json-schemas/, including relative $ref resolution used by published Ota JSON schemas
  • supports optional assertion gates for CI:
    • --assert-eq path=value
    • --assert-in path=[...]
    • --assert-type path:type (string|array|object|number|boolean)
    • --assert-non-empty-string path
    • --assert-exit-map path=0:[...];1:[...]
  • is the canonical first-party gate for preview JSON contract checks in smoke CI

Text output:

  • success: validated <payload-source> against <schema-name>
  • failure: command execution, payload parse, schema mismatch, or assertion mismatch details

JSON output:

  • none; this is a validation/guard command

ota extensions

List staged extension descriptors declared in ota.yaml.

ota extensions [PATH]
ota extensions --json [PATH]
ota extensions --member api [PATH]
ota extensions --run demo-check [PATH]
ota extensions --publish release-upload [PATH]

Current behavior:

  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota extensions lists root descriptors and grouped member results for each declared member
  • when --member is set, lists descriptors from the merged member contract
  • repeated --member values list descriptors for those members in the provided order
  • ota extensions --run <name> executes one explicitly named, allowlisted descriptor in the current repo or member context
  • ota extensions --publish <name> executes one explicitly named, allowlisted export_provider descriptor in the current repo or member context
  • execution currently accepts kind: check_provider descriptors with api_version: 1
  • execution currently accepts kind: export_provider descriptors with api_version: 1
  • execution also accepts kind: backend_provider descriptors for remote execution when named by execution.backends.remote.provider
  • backend providers receive a structured JSON request and must return a structured JSON response; the request is delivered on stdin and mirrored in OTA_BACKEND_PROVIDER_REQUEST_JSON for shell adapters
  • the seam is useful for external adapter contracts such as check providers, export targets, and execution backends that should be discoverable without being hidden in shell scripts

Text output:

  • header: EXTENSIONS <path>
  • each descriptor may include kind, command, api_version, description, and config
  • the report is read-only unless --run <name> is set
  • when no descriptors are staged, the text output says so explicitly and points users back to ota doctor or adding extensions to the contract

JSON output:

  • success: ok, path, extensions
  • monorepo root summaries include grouped per-member results in members
  • repeated --member values return grouped per-member results in members
  • --run <name> returns the executed descriptor, exit_code, and captured stdout/stderr
  • --publish <name> returns the executed descriptor, exit_code, and captured stdout/stderr
  • failure: ok, path, and either errors or error

ota run

Run a validated task.

ota run <task> [PATH]
ota run <task> --dry-run [PATH]
ota run <task> --dry-run --json [PATH]
ota run <task> --stream [PATH]
ota run <task> --member api [PATH]
ota run <task> --member api --member web [PATH]
ota run <task> --mode native [PATH]
ota run <task> --mode container --ephemeral [PATH]
ota run <task> --mode remote [PATH]
ota run <task> --skip-deps [PATH]
ota run <task> --effect-override network:broad=allow [PATH]
ota run <task> --memory 4GiB [PATH]
ota run <task> --agent [PATH]
ota run <task> --reason "release requested" [PATH]
ota run <task> [PATH] --base-url http://localhost:8080

Current behavior:

  • validates the contract first
  • when --member is set, resolves the merged member contract from the monorepo root
  • repeated --member values run the task across those members in the provided order
  • --mode, --lifecycle, and --ephemeral can select a contract-advertised execution mode for one invocation; ota refuses an unadvertised backend instead of forcing a host-owned task into a container or remote runtime
  • explicit execution options are capability requests, not advisory hints: ota refuses unsupported mode, lifecycle, host-port, memory, or dependency overrides before task startup; native and remote tasks without a managed shared backend reject --ephemeral / --persistent rather than pretending those lifecycles changed host or provider execution
  • dry-run and real execution use the same option-admission decision; refused preview JSON carries execution_started: false, the requested override, and a typed primary blocker
  • --skip-deps is a local execution override that skips tasks.<name>.depends_on for the requested task only
  • --skip-deps is rejected when the requested task has no declared depends_on
  • --agent enforces the declared agent-safe boundary before execution starts: ota refuses the run when the requested task is outside the safe set or when a declared-safe task still reaches an unsafe dependency / aggregate / hook closure
  • --reason <text> attaches operator intent when the selected task path crosses a heavier audited execution boundary; the current shipped slice records that reason only when ota derives crossing_required for the selected lane instead of attaching free-form narration to every run
  • --effect-override <effect>=<allow|warn|deny> temporarily overrides one effect-governance decision for this invocation only; supported selectors are network, network:broad, network:dependency_hydration, network:container_image_hydration, network:service_readiness, network:integration_test, network:tool_bootstrap, and external_state:<token>
  • task inputs are declared in tasks.<name>.inputs and are passed as --kebab-case value flags
  • when a task input overlaps an ota command flag name such as mode or jobs, put the ota command flag before the task and the task input after the task
  • task inputs are exposed to the task process as OTA_INPUT_<NAME> env variables
  • default values are applied when the caller omits an input
  • required: true makes an input mandatory unless a default exists
  • allowed limits the accepted values for that input
  • task inputs only apply to the task you invoked, not its dependencies
  • if every declared input has a default, you can omit all input flags
  • --dry-run is the read-only repo run preview surface: it resolves the selected task path, env, toolchains, native prerequisites, dependencies, and execution plan without running setup, dependencies, task processes, or containers
  • dry-run also evaluates declared replay-input identity pins. A missing or mismatched expected_identity returns the typed replay_input_identity_missing or replay_input_identity_mismatch preflight result before Ota presents the lane as runnable; real execution blocks on the same condition before task startup and retains the expected identity plus the observed identity when the input was readable in the receipt's evaluated_inputs[]
  • when the active org policy declares policies.replay_inputs.identity, dry-run and Doctor also publish the canonical replay_input_policy decision. ota run and ota up refuse before task startup, dependency hydration, or native prerequisite provisioning on deny or review; receipts retain the same selected subject, applicable rule identities, task-qualified input records, coverage, and reasons. An unavailable observation fails closed. Hard-pin failures remain unconditional and retain the active policy record. Unselected lanes preserve optional-pin behavior.
  • --dry-run prints RUN PREVIEW, uses the execution-preview vocabulary RUNNABLE / RUNNABLE WITH WARNINGS / BLOCKED, and shows Mode: dry-run (no write) plus the selected execution path, requirements, and planned actions
  • repo-level --json normally requires --dry-run; the one execution-free exception is a contract-declared refusal canary: ota run --agent --expect-refusal --json <task> emits the runner-authored refused_as_expected or refusal_not_observed result without starting the task
  • preview JSON includes additive governance, a compact selected-lane summary with safety_posture, review_required, closure-aware effective safety, effective default_mode, runnable mode commands, selected effect surface, and the next durable receipt command
  • preview JSON includes additive plan.dependency_steps[], so automation can inspect each planned task step's selected backend, context, parent task, and backend-selection source instead of inferring dependency-plane inheritance from the task names alone
  • by default, interactive terminals stream raw child output live, while non-interactive text runs buffer output into the final report for a cleaner failure/success surface
  • --stream forces raw live child output in text mode when you want the old firehose behavior explicitly
  • agent refusals are ota-authored execution outcomes, not generic task failures: text output renders AGENT EXECUTION REFUSED, receipts use blocked status, and no task process or dependency path starts before the refusal returns
  • --expect-refusal is the negative-control form of that same boundary. It requires --agent and a matching agent.refusal_canaries target; a refusal is exit 0, while admission is exit 1. It never accepts task inputs or starts the selected lane.
  • when the requested task is a service runtime with declared readiness, --stream also shows live readiness probe progress while ota is still trying to prove startup
  • for service runtimes with declared readiness, ota now treats the declared startup readiness budget as authoritative: if the configured start_period / interval / timeout / retries budget is exhausted before the runtime endpoint becomes reachable, startup fails instead of waiting indefinitely for the workload to exit or for the user to interrupt it
  • startup failure output now includes the final readiness-budget summary, including attempts used, timeout per attempt, interval, start period, and the last probe failure
  • backend-configuration failures now point through ota execution plan first so the selected execution path can be inspected before you change contract execution settings or retry the task
  • declared env-source failures now point through ota env --task <name> first so source status and precedence stay visible before you repair files or rerun the task
  • when the selected task path uses requirements.toolchains, ota treats that toolchain as the owner for the selected path instead of describing its owned capabilities as standalone runtimes or tools
  • toolchain run-path fulfillment is opt-in: toolchains.<name>.fulfillment.mode: none keeps ota run on diagnosis/check-only behavior, while fulfillment.mode: run lets ota attempt run-path provisioning for the selected toolchain only
  • when toolchain fulfillment fails, run output names the selected toolchain, the chosen fulfillment source, the declared requirement slice ota checked, and the rerun lane instead of reducing the failure to a generic tool install error
  • ota run <task> --dry-run --json exposes additive toolchains[] evidence for the selected preview path, including provider, backend, target OS, version, fulfillment mode, owned runtime, and any owned tools/components/targets ota selected on that path
  • when ota actually runs provider fulfillment commands on an executed path, the corresponding receipt-backed toolchain evidence records fulfilled plus additive commands[]
  • on failure, text output keeps Why and Next first, then appends a compact RUN SUMMARY block with Status first for quick scanning, followed by the selected mode, container image when relevant, target when one exists, and task
  • on non-interactive text success, large task output is shown as a bounded excerpt before the compact RUN SUMMARY
  • on non-interactive text failure, task output is shown as a bounded excerpt with a --stream rerun hint before the compact RUN SUMMARY
  • on Ctrl-C during ephemeral container runs, ota still attempts to remove the repo-owned container created for that invocation and surfaces cleanup failures in the final summary
  • before starting a new ephemeral container run, ota reclaims repo-owned orphaned ephemeral containers for the same repo, uses bounded retries when reclaim resolves stale host-publication holders, and can reclaim legacy running ephemerals without dev.ota.owner_pid when they are the conflicting holder
  • persistent container runs reconcile shape before reuse and recreate when projection/publication drift would make runtime endpoint metadata stale
  • Compose attachment namespace drift also counts as persistent execution-shape drift, so changing attachments.compose recreates the persistent backend instead of reusing a container bound to the old Compose network family
  • service tasks with projected listeners classify post-readiness exits as service-stop failures (including interrupted) so summaries and receipts stay truthful across both ephemeral and persistent lifecycle modes
  • active repo execution ownership is now tracked in .ota/state/active-executions.json instead of a single whole-run lock, so compatible runs can coexist when their execution ownership does not conflict
  • the current shipped conflict rule is ownership-shaped: duplicate long-running service-task ownership still blocks, and so do shared host-managed service ownership, shared Compose project ownership, shared persistent backend-family ownership, and shared deterministic env-file materialization ownership; finite task paths can still run alongside an active service owner when they do not claim the same owned resources
  • shared logical write paths are now compared on effective ownership namespace, not path text alone: when two container contexts declare the same repo-relative attachments.isolated_paths entry but resolve to different Ota-managed dependency-isolation volume families, ota allows them to coexist; raw repo-worktree writes or unresolved shared namespaces still conflict
  • execution-conflict reporting now carries typed reason identities such as active_execution_present, host_service, compose_project, persistent_backend_family, env_materialization_path, and service_task instead of reducing the failure to owner detail text alone
  • failure receipts now also publish an execution_conflict.reasons[] object derived from that same ownership truth while keeping the existing blocked[] compatibility lane
  • stale active-execution records are pruned by owner PID before conflict checks, so interrupted or crashed ota processes do not leave a permanent fake-active barrier behind
  • when --skip-deps is used, receipts and run summaries mark the override explicitly and point back to rerunning without it when you need to validate the full declared task flow
  • on success, text output includes the compact RUN SUMMARY block with Status first for quick scanning, followed by the selected mode, container image when relevant, target when one exists, and task
  • --receipt adds the full execution receipt when you need the detailed trail
  • when replay-input policy refuses admission, --receipt still emits the blocked preconditions receipt with policy decision, coverage, rule identity, typed reasons, and observed input posture; generic ota receipt does not reconstruct that earlier decision
  • replay admission includes recursive after_success, after_failure, and after_always hook closures before the parent task starts; an invalid active policy source refuses with typed replay_input_policy_unavailable evidence rather than behaving like no policy

Example:

tasks:
  api-automation-tests:
    inputs:
      base_url:
        default: http://localhost:8080
      suite_mode:
        default: standard
        allowed:
          - standard
          - contract-drift
  version:bump:
    inputs:
      version:
        required: true
ota run api-automation-tests
ota run api-automation-tests --base-url http://localhost:8080 --suite-mode contract-drift
ota run version:bump --version minor
ota run version:bump --version 0.2.0
ota run version:bump --version major
ota run dev --host-port 4000
ota run dev --memory 4GiB
ota run build --skip-deps
  • resolves task dependencies before execution
  • when tasks.<name>.when.checks is declared, ota evaluates those checks before dependency/service startup and skips the task when any condition check fails or times out
  • when.checks supports declared precondition (with run), file, and changed_files checks; probe-driven checks are not valid execution conditions
  • --skip-deps suppresses that dependency execution for the requested task only; required service acquisition, hooks, and the selected task body still run
  • if the task body exits successfully, runs after_success hooks in declared order
  • if the task body exits with a failure, runs after_failure hooks in declared order
  • runs after_always hooks after either outcome when the task body was actually attempted
  • hook task failures affect the final ota run exit code for the parent task
  • resolves the best matching task variant for the current OS when variants are declared
  • executes either run or script
  • supports mode-aware task branches under tasks.<name>.execution.modes
  • when tasks.<name>.execution.default_mode is declared and --mode is omitted, ota run uses that mode as the default execution plane
  • when --mode is set, ota run uses the matching mode branch for the task
  • if the selected mode has no declared task branch, ota run falls back to the task-level execution body and task-level execution settings
  • selected mode branches can override task context, lifecycle, env, run/script, and runtime
  • resolves task execution backend from:
    • tasks.<name>.execution.default_mode when set
    • selected mode branch context when tasks.<name>.execution.modes.<mode>.context is set
    • tasks.<name>.context when set
    • execution.default_context
    • legacy execution.preferred / execution.backends
  • for container tasks, runs through the first available configured container engine CLI, falling back to docker when no engines are listed
  • for container tasks, execution.lifecycle: ephemeral uses a fresh container
  • for container tasks, execution.lifecycle: persistent reconciles a named container: ota reuses it when the resolved execution shape is equivalent and recreates it when image/publication/isolation shape drifts
  • supports remote execution when the resolved task/context backend declares provider and target
  • current shipped remote providers are daytona, ssh, tsh, and kubectl
  • remote target guidance:
  • daytona: sandbox-dev
  • ssh / tsh: user@host
  • kubectl: pod/ota-dev
  • passes execution.backends.remote.cwd to the provider CLI when set
  • runs in the effective target contract directory
  • applies configured environment values, approved policy env values, and task input env variables
  • when the selected native task path references requirements.native entries with activation.kind, ota resolves the declared native activation environment first; Windows visual_studio_dev_shell and generic command activations both apply to the real task body instead of assuming the current terminal was already activated correctly
  • when tasks.<name>.runtime.listeners declare host projection, ota also injects runtime endpoint env values before process start when the projection is known:
  • OTA_PUBLIC_URL
  • OTA_PUBLIC_HOST
  • OTA_PUBLIC_PORT
  • OTA_PUBLIC_URL_<LISTENER>
  • when multiple listeners are projected, exactly one projected listener must set project.host.primary: true; ota uses that listener for OTA_PUBLIC_URL and summary endpoint rendering
  • for container listeners with project.host.port.mode: auto, execution.lifecycle: ephemeral pre-reserves a host port before spawn and retries bounded host-port conflicts; execution.lifecycle: persistent resolves the reconciled container's published host mapping before exec
  • --host-port <port> overrides one run's projected host/public port on the selected primary projected listener without changing the internal bind port; ota updates runtime env, summary output, and receipts to the overridden public URL
  • --host-port is valid when the selected primary listener uses project.host.port.mode: fixed and the execution path is either container execution or native structured docker compose up with explicit project.publication.compose.service ownership
  • --host-port is rejected for project.host.port.mode: auto, tasks without projected host listeners, ambiguous multi-listener projections without one primary listener, native compose publications without project.publication.compose.service, and native compose engines other than docker
  • stream-mode endpoint banners such as External: and Internal: are printed only after ota itself confirms the projected endpoint; workload logs like ready or framework-local URLs are not treated as authoritative host-reachability proof
  • if Docker is running through Colima, published ports may be reachable inside the Colima VM but not on macOS localhost; when this happens, ota keeps the endpoint banner withheld and the interrupted pre-confirmation path calls out the Colima boundary explicitly
  • --memory <size> overrides one run's requested container memory (examples: 512MiB, 2GiB, 4TiB)
  • --memory is valid only when the selected task resolves to container execution
  • when the selected container context declares container.resources.memory.minimum, ota rejects --memory values below that minimum before task execution starts
  • when no --memory override is passed, ota uses container.resources.memory.default when declared; if only minimum is declared ota requests that minimum; otherwise the engine default applies
  • for container-backed runtime.kind: service tasks, ota now captures container termination state before ephemeral teardown and reports post-readiness service stops as first-class failures (including explicit OOM classification when the engine reports it)
  • prints task progress and advisory notes on stderr when output is streaming
  • prints a summary in text output, and emits an execution receipt on stderr after task output when --receipt is set
  • execution receipts include backend, remote provider / target / optional cwd when relevant, lifecycle, container image when relevant, resolved container memory when requested, acquired paths, env sources, step summary data, resolved runtime listener endpoints, optional service_termination details for post-readiness service stops, and optional host_service_cleanup evidence when ota attempted host-managed service shutdown during cleanup; text receipts also print the winning env source for each resolved value
  • task-backed execution receipts also carry additive dependency_steps[], so the archived run path preserves each executed dependency step's selected backend, optional context, parent task, and backend-selection source instead of only the flattened dependency order
  • returns the child process exit code

Use this when the contract is already the source of truth and you want deterministic task execution.

Agent execution:

  • use ota tasks --safe --use first to discover the effective safe task surface
  • then run the selected task with ota run <task> --agent
  • --agent is the enforcement lane: ota refuses unsafe requested tasks and unsafe reachable task closures instead of treating safety as review-only guidance
  • without --agent, declared tasks still run as the normal human/operator execution lane

ota doctor

Diagnose repo readiness from a validated contract.

ota doctor [PATH]
ota doctor --mode native [PATH]
ota doctor --mode container [PATH]
ota doctor --container --persistent [PATH]
ota doctor --remote --ephemeral [PATH]
ota doctor --json [PATH]
ota doctor --fix --dry-run [PATH]
ota doctor --fix [PATH]
ota doctor --member api [PATH]
ota doctor --member api --member web --json [PATH]
  • Current behavior:

  • when no contract exists, reports Contract missing, shows any trustworthy repo and host signals under Repo Signals across mainstream and long-tail detector-supported stacks, including repo type, dependency/build tools, likely runnable tasks, services, and host tool availability, and keeps the next step compare-first with ota detect --dry-run, ota detect --contract, and ota init --dry-run

  • the human summary now makes the top-level state explicit as READY, READY WITH WARNINGS, or BLOCKED

  • ota --version now exposes build identity when available, including the git commit and dirty marker for source builds, so released and unreleased binaries do not masquerade as the same version string

  • ota --version --json exposes the same identity in machine-readable form (semver, source_build, commit, dirty) plus schema_version and additive contract_capabilities[] entries (id, introduced_in) for CI and contract-compatibility provenance checks

  • when metadata.ota.minimum_version rejects a contract, ota now reports the contract minimum, current binary identity, detected unsupported contract feature when one is known, and the next install/rebuild step instead of only saying the binary is too old

  • validates the contract first when one is present

  • when a root contract declares workspace.type: monorepo, plain ota doctor diagnoses the root contract and grouped summaries for each declared member

  • when --member is set, diagnoses the merged member contract

  • repeated --member values diagnose those members in the provided order

  • prints the highest-priority blocker first in the human-readable output so the fastest next action is visible immediately

  • when findings are warning-only, still surfaces one highest-priority primary finding before grouped detail so the next safe action is visible without scanning the whole report

  • doctor now surfaces selected task-path side-effect signals: network-dependent paths stay explicit, and selected tasks that mutate external systems surface warnings before execution

  • environment blockers now point through ota env first so operators can inspect precedence before changing shell values, policy env, or declared sources

  • unverifiable required services now route into ota assist declare-readiness when only the probe is missing, or ota assist declare-service when the managed service shape itself still lacks a start path

  • missing-file precondition failures now point to ota up / ota run setup when tasks.setup already exists, or to ota assist wire-setup when the repo still needs a contract-first setup path

  • when a contract has no tasks, doctor now keeps that path preview-first too: it suggests ota detect --dry-run before any detect write, while still offering ota assist add-task when the right fix is clearly one explicit task

  • checks configured env requirements, declared checks, and service healthchecks in native mode

  • reports warning findings for missing or mismatched pinned tasks.<name>.replay_inputs[] so operators can repair deterministic replay inputs before invoking an execution lane; execution remains the hard preflight boundary

  • publishes additive workflow claim_assurance[] proof-breadth records when a workflow declares runtime proof. The same immutable archive matching rule applies: current semantic contract, clean source identity, resolved execution scope, and replay_posture: witness_only are all required for supported; a matching failed archive is contradicted and all other cases are unknown

  • when the selected or default workflow task closure declares tasks.<name>.requirements, doctor scopes runtime, tool, env, and precondition-check diagnosis to that setup/run dependency path instead of treating unrelated task-specific prerequisites as repo-global truth

  • selected-path requirements.env entries become missing-env blockers for that diagnosis even when the same top-level env.vars.<name> entry is optional outside the selected task/workflow path

  • when the selected path resolves through requirements.toolchains, doctor diagnoses the selected toolchain/provider/components/targets as one owned surface and does not restate owned capabilities such as cargo or rustfmt as standalone selected-path tools

  • doctor never provisions toolchains: toolchains.<name>.fulfillment.mode: none and fulfillment.mode: run both stay diagnosis-only here; duplicate ownership between toolchains, runtimes, and tools fails early as an invalid contract instead of degrading into an advisory finding

  • org-policy version/provisioning reasoning now sees the selected toolchain-owned runtime lane too, so toolchains.rust can participate in approved-version and approved-source findings without re-declaring runtimes.rust

  • ota doctor --json now exposes additive toolchains[] entries for the selected workflow/task path, including the provider, effective backend, target OS, version, fulfillment mode, and the owned runtime/tool capabilities ota checked on that path

  • agent-safe tasks that declare broad effects.network or effects.external_state produce warnings in doctor output instead of leaving those execution risks implicit behind a safe-task label

  • when a structured AGENTS.md or CLAUDE.md managed block drifts from the declared agent.safe_tasks, agent.verify_after_changes, agent.writable_paths, or agent.protected_paths, doctor reports agent-boundary drift instead of assuming agent guidance docs are still aligned

  • first-class prepare.kind: dependency_hydration lanes keep their network truth visible, but ota does not emit the generic agent-safe dependency-hydration warning when that bounded lane is already modeled on the typed hydration surface

  • first-class prepare.kind: tool_bootstrap lanes keep contract-owned tool installation visible too; ota can now distinguish typed tool bootstrap from both repo dependency hydration and broad network execution

  • when one selected tool requirement resolves to tools.<name>.acquisition, doctor names that activation lane directly instead of reducing the fix to a generic install hint; Corepack-managed pnpm is the first shipped provider path

  • when a selected task declares requirements.native, doctor runs the selected platform precondition check and reports an OS-specific native prerequisite finding instead of a vague shell-check failure

  • probe-backed checks and workflow readiness probes execute directly inside ota; they do not depend on repo-local helper commands such as curl or node

  • checks required execution backends for the selected --mode and resolved contexts

  • ota doctor now accepts the same execution-selector family shape as the other mode-bearing repo commands: --mode, backend shorthands (--native, --container, --remote), --lifecycle, and lifecycle shorthands (--persistent, --ephemeral)

  • --mode native diagnoses host/native readiness; --mode container diagnoses selected container context requirements

  • when a lifecycle override is selected, doctor keeps that lifecycle on its reported execution identity and rerun guidance instead of silently collapsing container diagnosis back to ephemeral

  • context diagnostics use the resolved named-context shape after extends merge, while legacy shorthand remains supported for one-context contracts

  • warns on suspicious remote target shape:

  • ssh / tsh targets without user@host

  • kubectl targets not starting with pod/

  • checks runtime and tool presence on PATH

  • for contract-backed repos, when Ota-owned local artifacts are git-backed but .ota/state/, .ota/contracts/, .ota/receipts/, or .ota/proof/ is not ignored, reports a fixable repo-hygiene finding

  • --fix --dry-run previews deterministic safe fixes without writing files

  • --fix applies only supported deterministic safe fixes for repos with a valid ota.yaml; current scope is .gitignore hygiene for .ota/state/, .ota/contracts/, .ota/receipts/, and .ota/proof/

  • when no ota.yaml exists yet, ota doctor --fix does not propose repo-hygiene mutations and instead points operators to preview-first onboarding with ota detect --dry-run or ota init --dry-run

  • in container mode, runtime and tool findings are evaluated against the selected container image instead of the host PATH

  • in container mode, ota also uses safe non-mutating installability probes for the shipped mutating provisioning adapters when policy-backed provisioning is declared

  • in container mode, apt findings distinguish pinned-version unavailable, package unavailable, and apt-index/source failures when the backend evidence supports that classification

  • in container mode, host-bound env, check, and service healthchecks are omitted so container diagnosis does not mix execution contexts

  • when services.<name>.readiness is used, readiness probes run in the declared context and use the matching endpoint projection for reporting

  • ota doctor --mode remote probes runtime/tool requirements in each executable remote context

  • shows any inert top-level extensions entries in the human-readable report so adapter metadata is visible without execution

  • warns when a required service has no healthcheck, because readiness cannot be verified

  • honors services.<name>.timeout when a service healthcheck is declared

  • warns when execution.lifecycle: ephemeral is declared and clarifies that current isolation applies to ota run <task> and the setup step inside ota up; diagnosis, healthchecks, and full repo cleanup are not ephemeral yet, and --ephemeral remains the shorthand for a fresh task-execution path when supported

  • reports contract drift as warning findings when repo signals no longer match the declared contract, and still preserves the most important blocker first

  • tags contract-drift findings with repo-contract ownership and provenance so consumers can distinguish stale contract truth from host or service failures

  • warns when .devcontainer/devcontainer.json advertises a Node image that does not satisfy the repo contract's declared runtimes.node requirement

  • warns when .devcontainer/devcontainer.json bootstraps with a different Node package manager than the repo contract declares

  • warns when agent.writable_paths includes sensitive lockfile, env/config, runtime-topology, CI, or repo-contract paths when they exceed the declared agent.posture; narrow intentional exceptions can still be acknowledged with agent.exceptions.sensitive_writes

  • reports an error when no tasks are declared, because the contract is not operational for ota run

  • runs configured checks

  • orders findings by severity

  • includes an agent summary when the contract declares one

  • may include a provisioning plan when the contract declares runtimes or tools and policy provides approved provisioning sources

  • prints the reason and next action for each finding

Text output:

  • header: DOCTOR <path>
  • status line: READY or NOT READY
  • Execution includes a Mode: line in text output so the selected diagnosis context is explicit
  • when agent guidance is present, the shared AGENT block is grouped into Overview, Execution, and Boundary sections with counted wrapped lists and writable-path root/exception collapsing
  • summary includes repo verdict and agent verdict before per-finding details
  • grouped finding sections include Provenance: when the grouped findings share one diagnosis source
  • with --concise, findings keep severity + summary + Next, while Why detail is omitted

JSON output:

  • ok
  • path
  • agent when the contract declares agent guidance
  • fix when --fix is requested, including planned/applied action status and any write failures
  • findings
  • findings may also include provenance / provenance_key when ota can trace the diagnosis back to the repo contract, org policy, or repo signals
  • monorepo root summaries include grouped per-member results in members
  • repeated --member values return grouped per-member results in members

Warnings can still produce READY. Errors produce NOT READY.

ota init

Create a starter ota contract for a repo that does not yet have one.

ota init [PATH]
ota init --bootstrap [PATH]
ota init --pack <node|python|ruby|go|rust|dotnet|php-composer|java-maven|java-gradle> [PATH]
ota init --pack node --package-manager <npm|pnpm|yarn|bun> [PATH]
ota init --pack python --test-runner <pytest|unittest> [PATH]
ota init --packs
ota init --dry-run [PATH]
ota init --json [PATH]

Current behavior:

  • inspects the repo using the detection engine
  • writes by default
  • --bootstrap writes the fuller detected starter contract when it is safe to do so
  • --pack <node|python|ruby|go|rust|dotnet|php-composer|java-maven|java-gradle> skips detector-led starter selection and seeds an explicit conventional starter contract pack, including short task description fields on the seeded starter tasks
  • --pack node --package-manager <npm|pnpm|yarn|bun> keeps pack mode explicit while swapping the conventional Node starter setup hydration and script command bodies to the selected package manager
  • --pack python --test-runner <pytest|unittest> keeps pack mode explicit while swapping the conventional Python test entrypoint to the selected runner
  • --packs lists the built-in starter packs, what they seed, the exact ota init --pack ... selection command, the safe dry-run preview command to use next, and any explicit starter knobs exposed by that pack
  • when no stronger project identity is inferred, --bootstrap can fall back to the repo directory name for project.name
  • supports preview mode with --dry-run
  • refuses to run when ota.yaml already exists
  • can initialize both detected repos and blank repos
  • keeps JSON output stable while using text output to guide review, write, and first validation steps
  • in detected mode, plain ota init writes the smallest valid starter contract for the repo
  • in detected mode, ota init --bootstrap can include lower-confidence fields when they are needed to capture the fuller starter contract
  • when detector-led init has strong ecosystem signals, it normalizes shipped starter truth onto current first-class surfaces instead of leaving obvious setup and task bodies in raw shell: supported Node, Ruby, Java, and .NET starters now prefer toolchains.*, prepare.kind: dependency_hydration for setup, and command for simple finite task execution where ota can model the body directly
  • when standard env source files already exist, detector-led init can declare them as explicit env.sources in the starter contract: .env.local, .env, src/main/resources/application.properties, src/main/resources/application.yml, src/main/resources/application.yaml, appsettings.json, and appsettings.Development.json; explicit --pack mode does not infer env sources from repo files
  • runtime support for declared env.sources also includes curated yaml and toml; detector-led init auto-infers the explicit standard dotenv, Spring properties/yaml, and .NET JSON files listed above, but does not yet auto-suggest standard TOML paths
  • when project.name is still missing in bootstrap mode, ota falls back to the repo directory name rather than leaving the contract invalid
  • low-confidence fields remain excluded from plain ota init writes
  • canonical detected tasks can include short description fields so the starter contract teaches the task-authoring pattern immediately instead of only relying on notes
  • confident detected tasks may include a notes field that points to the matching ota run <task> command
  • when the detected tasks are confident enough, the starter contract now keeps a derived agent block and review notes even when writable-path inference is still partial; ota now combines broader common app/source directories with detector-backed nested project roots and a bounded stack-aware source-root scan so custom code roots can surface in agent.writable_paths without falling back to ., while detector-backed control files such as manifests and lockfiles now surface explicitly in agent.protected_paths and operational directories such as config, database, migrations, manifests, deploy, and infra stay out of the default starter allowlist
  • starter contracts now also carry agent.inferred_boundary.reviewed: false plus provenance for the inferred writable and protected paths, so the boundary is visible as inferred state rather than silent starter magic; detector-led init uses detect:... provenance, while explicit pack mode uses init:... provenance for the starter defaults it owns
  • those starter agent.notes now explicitly tell authors to review agent.writable_paths and agent.protected_paths, then set agent.inferred_boundary.reviewed: true before trusting automation
  • starter contracts protect ota.yaml by default; if a future starter intentionally grants contract-authoring authority with writable ota.yaml, it should also carry agent.exceptions.sensitive_writes: [ota.yaml] instead of leaving that sensitivity implicit
  • detector-led ota init --dry-run now renders the same explicit Agent boundary outcome as detect preview, so repos without a safe inferred task see why the starter omits agent instead of having to infer that omission from the YAML alone

Choosing an init path:

  • use ota init --dry-run when detector-led init should shape the first draft from repo signals
  • use ota detect --contract before detector-led ota init when you want the exact starter text without annotations or pack commentary
  • use detector-led init when you want ota to carry existing declared-source candidates such as .env.local, .env, src/main/resources/application.properties, src/main/resources/application.yml, src/main/resources/application.yaml, appsettings.json, or appsettings.Development.json into env.sources
  • use plain ota init only after comparing that detector-led starter against ota detect --contract
  • use ota init --packs when you want to compare the explicit starter catalog first
  • use ota init --pack <name> --dry-run when you want an explicit conventional starter without detector-led selection
  • use ota init --pack node --package-manager <name> --dry-run when the repo is intentionally npm-, pnpm-, yarn-, or bun-based and you want the starter to match that package-manager boundary from the first write, including first-class setup hydration for every shipped Node manager lane
  • use ota init --pack python --test-runner <name> --dry-run when the repo is intentionally pytest- or unittest-driven and you want the starter to reflect that test command directly
  • use ota init --pack ruby --dry-run when the repo is intentionally Ruby/Bundler-based and you want the starter to seed first-class Bundler dependency hydration plus the default bundle exec rake test loop, with Bundler version governance under toolchains.ruby.package_managers.bundler
  • use ota init --pack rust --dry-run when the repo is intentionally Rust-first and you want toolchains.rust ownership plus first-class Cargo hydration for setup, with cargo build / cargo test as the honest first draft
  • use ota init --pack dotnet --dry-run when the repo is intentionally .NET-first and you want toolchains.dotnet ownership plus first-class dotnet_restore hydration for setup, with the standard dotnet build / dotnet test loop as the honest first draft
  • use ota init --pack php-composer --dry-run when the repo is intentionally Composer-managed PHP and composer install plus reuse of an existing scripts.test entry is the honest first draft you want to review
  • use ota init --pack java-maven --dry-run when the repo is intentionally Maven-based and you want toolchains.java ownership plus first-class Maven hydration for setup, preferring mvnw when the wrapper already exists
  • use ota init --pack java-gradle --dry-run when the repo is intentionally Gradle-based and you want toolchains.java ownership plus first-class Gradle hydration for setup, preferring gradlew when the wrapper already exists
  • explicit packs seed short task description fields so the authoring pattern is visible immediately

Examples:

# detector-led path
ota detect --contract
ota init --dry-run
ota init
ota up --dry-run

# pack-led path
ota init --packs
ota init --pack node --dry-run
ota init --pack node --package-manager yarn --dry-run
ota init --pack python --dry-run
ota init --pack python --test-runner unittest --dry-run
ota init --pack ruby --dry-run
ota init --pack go --dry-run
ota init --pack rust --dry-run
ota init --pack dotnet --dry-run
ota init --pack php-composer --dry-run
ota init --pack java-maven --dry-run
ota init --pack java-gradle --dry-run

Modes:

  • blank: starter contract derived from minimal repo context
  • detected: starter contract derived from detected repo signals
  • pack: starter contract derived from an explicit built-in starter pack
  • catalog: starter-pack discovery output from ota init --packs

Text output:

  • dry-run header: INIT <path>
  • write success: WROTE <path>
  • includes Mode: blank or Mode: detected
  • pack mode also includes Pack: <name>, optional Options: ... when the selected starter pack supports explicit knobs, plus an explicit pack-policy note
  • explicit pack mode can also include an advisory note with Why, weighted Signals, Selected signals, Strength, Gap, and Next rows when strong repo signals disagree with the selected pack; ota does not auto-switch or merge detector output into the pack
  • --packs renders INIT PACKS catalog, one entry per pack, the exact ota init --pack ... command, any starter-specific option rows, and a Next: line with the matching ota init --pack ... --dry-run . preview command plus why that preview is the right next move
  • successful init writes now use explanatory Next: steps instead of bare commands: validate the written contract, inspect the runnable task surface, review readiness with doctor, then preview preparation with ota up --dry-run
  • blank mode explicitly warns that the starter contract is minimal coverage only
  • detected mode write output explicitly calls out the write policy and any excluded low-confidence fields
  • includes inferred-field annotations with field type, source, signal, confidence, and task-scoped agent metadata when applicable

JSON output:

  • ok
  • path
  • written
  • mode
  • optional pack when explicit pack mode is used
  • optional pack_options when explicit pack mode selected a starter-specific knob such as Node package manager or Python test runner
  • optional pack_advisory when explicit pack mode disagrees with strong detected repo signals; it includes the selected pack, suggested pack, distinct-signal scores, score gap, normalized signal markers, weighted signal details for both the suggested and selected pack, and a safe dry-run follow-up command
  • config
  • inferred
  • packs when mode is catalog and ota is listing the built-in starter packs instead of previewing one contract; each entry includes name, summary, when, the exact command, a safe next preview command, optional starter options, explicit does_not_infer boundaries, and the seeded runtimes, tools, checks, and tasks
  • failure responses can include next when ota can point to one safe follow-up command

ota agents

Generate or sync a repo-local AGENTS.md from the current contract.

Use this after ota doctor, ota explain, or ota up when you want the same repo contract to produce reviewable agent guidance for humans and coding agents.

ota agents [PATH]
ota agents --review [PATH]
ota agents --confirm --dry-run [PATH]
ota agents --confirm [PATH]
ota agents --write [PATH]
ota agents --json [PATH]
ota agents --write --output AGENTS.md [PATH]

Current behavior:

  • keeps the contract-first boundary workflow inside ota.yaml: ota agents --review inspects the current writable/protected path boundary and provenance, ota agents --confirm --dry-run previews the exact reviewed: true mutation, and ota agents --confirm writes that confirmation into the contract before any AGENTS.md sync
  • derives AGENTS.md from the repo contract’s agent block when one is present
  • doctor treats the structured managed block as governed boundary evidence, so stale safe_tasks, verify_after_changes, writable_paths, and protected_paths content surfaces as agent-boundary drift instead of silently staying outdated
  • when the default workflow is declared, the generated default-workflow summary now carries the explicit prepare, setup, and run command forms instead of collapsing host file prep into setup implicitly
  • when the repo contract does not declare agent, preview mode now behaves like a blocked agent-boundary sync surface instead of a generic scaffold preview: it reports Agent contract missing, shows compare-first next steps through ota detect --dry-run and ota init --dry-run, and surfaces any trustworthy inferred repo signals plus inferred starter agent boundaries under Repo Signals
  • ota agents --write now refuses when the repo contract still lacks agent, so Ota does not write generic guidance that looks more authoritative than the authored contract
  • renders an explicit Bootstrap section when agent.bootstrap.ota is present, including the approved shell and PowerShell install commands for ota
  • preserves existing AGENTS.md content and appends or refreshes an ota-managed block instead of overwriting user-authored guidance
  • skips the write if the existing file already contains the generated AGENTS content
  • keeps the generated file lightweight by using short provenance (Generated from ... by \ota agents`.`) instead of an Ota copyright or license banner
  • renders a Managed block: label in text output so the ota-owned section is explicit and shows each task list item together with its ota run ... command form
  • text preview points directly at the missing boundary and the next safe authoring lane instead of only previewing generated markdown when the contract still lacks agent
  • writes to AGENTS.md by default when --write is set
  • accepts --output to write elsewhere
  • keeps output deterministic and reviewable

Text output:

  • header: AGENTS <path>
  • --review uses AGENTS REVIEW <path>, reports whether the boundary is REVIEW REQUIRED or REVIEWED, and shows Boundary sync as blocked until review, update needed, or in sync
  • --confirm --dry-run uses AGENTS CONFIRM <path> with PREVIEW and shows the exact reviewed-boundary contract preview before any write
  • --confirm uses AGENTS CONFIRM <path> and reports whether the boundary was just confirmed or whether no confirmation write was needed because the boundary was already reviewed or already declared as confirmed
  • when agent exists, preview mode shows the generated markdown content together with the write and verification next steps
  • when agent is missing, preview mode shows a blocked boundary-sync diagnosis with Target, Primary Blocker, Next, and Repo Signals
  • write mode reports whether the target was written or already in sync and points back to ota doctor
  • reviewed boundaries that are already synced end with Boundary is already synced. plus a plain Next hint to run ota doctor; reviewed boundaries that still need sync keep a two-step next lane for ota agents --write and ota doctor

JSON output:

  • ok
  • path
  • output
  • written
  • content
  • failure responses can include next when ota can point to one safe follow-up command

ota check

Run configured checks from a validated contract.

ota check [PATH]
ota check --json [PATH]
ota check --member api [PATH]
ota check --member api --member web --json [PATH]

Current behavior:

  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota check runs root checks and grouped check summaries for each declared member
  • when --member is set, runs checks from the merged member contract only
  • repeated --member values run checks for those members in the provided order
  • when the selected workflow declares explicit readiness checks or probes, runs that workflow readiness surface
  • when the selected or default workflow task closure declares explicit tasks.<name>.requirements.checks, ota check also runs those prerequisite checks without broadening into runtime, tool, or env diagnosis
  • otherwise runs the repo-wide configured checks surface
  • command-backed checks keep their existing shell execution model
  • probe-backed checks and workflow readiness probes run the named readiness.probes target directly inside ota
  • does not perform runtime, tool, or env diagnosis
  • does not execute tasks

Text output:

  • header: CHECK <path>
  • status line: READY or NOT READY
  • when agent guidance is present, the shared AGENT block uses grouped Overview, Execution, and Boundary sections with counted wrapped lists and writable-path root/exception collapsing

JSON output:

  • ok
  • path
  • additive agent can appear when the contract declares agent boundaries
  • findings
  • additive toolchains[] evidence can appear for the selected workflow path even though ota check itself still stays on the configured readiness/check surface instead of broadening into toolchain diagnosis or fulfillment
  • monorepo root summaries include grouped per-member results in members

ota baseline record and ota baseline promote

Use these commands for any declared artifacts.<name>.replay authority lane. The artifact keeps its kind, so an existing generated_source can gain replay authority without duplicate output ownership.

ota baseline record --artifact recorded-baseline --json
ota baseline promote --artifact recorded-baseline \
  --attestation .ota/replay-baselines/recorded-baseline/attestation-<sha>.json --json

record executes the declared producer, archives its execution receipt, and writes a content-addressed attestation of the complete generated output set, exact task scope, resolved backend, lifecycle when one applies, and the clean Git source identity verified before and after the producer runs. Declared baseline outputs and Ota-owned .ota runtime state are excluded from that check; every other tracked or untracked source mutation refuses recording. record does not update a baseline or select the newest recording. Every attestation also carries the producer's V11.16 execution-boundary graph and its asserted target and derivation-input closure identities. When no material prerequisite adapter observed a graph, Ota records the canonical empty graph with unknown posture rather than omitting that provenance. promote verifies the selected attestation against its successful archived receipt and current contract identity, then atomically writes only the contract-declared portable authority manifest. That manifest embeds the selected attestation, retaining reviewable source, scope, receipt, and boundary provenance without requiring local archive retention.

ota doctor evaluates the selected replay-consumer closure against that portable authority before a replay task starts. For a generated_source artifact, a consumer that explicitly depends on the producer is ordinary generated lineage execution, not replay; dedicated replay_baseline artifacts always require authority. A missing, revoked, malformed, or identity-mismatched authority emits OTA_REPLAY_BASELINE_UNAVAILABLE; record and explicitly promote a reviewed replacement instead of editing a digest or letting replay regenerate the baseline.

consumption: read_only requires an ephemeral runner-owned container boundary across the selected closure. Ota mounts a run-scoped snapshot stored outside the writable workspace; it does not treat verify_unchanged as read-only enforcement. Use consumption: verify_unchanged when native execution must remain available: Ota verifies the authority before the task and re-checks it afterward. A changed output fails with replay_artifact_mutation_detected; this detects a write after it occurred and does not claim it was prevented. Command-capable typed preparation remains declarative in ota.yaml; Ota projects it through the same strict container boundary instead of requiring a shell replacement.

Use this when a fixture, generated store, or model baseline must change through an intentional recording lane. Review the producer output and selected attestation; never hand-edit a digest to make replay pass.

ota receipt

Capture the current repo readiness scan as a read-only receipt artifact for CI, archival use, or baseline comparison.

Use this when you want a stable handoff between local readiness and CI history:

  • ota receipt --json --archive leaves a durable readiness artifact behind a CI run
  • ota receipt --json --archive --promote-baseline marks one known-good receipt as the repo's explicit baseline
  • ota receipt --json --baseline promoted compares the current repo state against that reviewed baseline instead of whatever happened to run last
  • ota receipt --workflow frontend --json --archive captures one declared workflow's readiness lane as its own archived receipt/baseline stream instead of mixing it with another workflow's history
  • ota receipt --snapshot latest reads the archived normalized contract truth directly from the latest matching archived receipt instead of routing that inspection through compare mode
ota receipt [PATH]
ota receipt --json [PATH]
ota receipt --mode container [PATH]
ota receipt --container --persistent [PATH]
ota receipt --remote --ephemeral [PATH]
ota receipt --workflow frontend [PATH]
ota receipt --archive [PATH]
ota receipt --archive --promote-baseline [PATH]
ota receipt --baseline promoted [PATH]
ota receipt --baseline latest [PATH]
ota receipt --baseline ./baseline-receipt.json [PATH]
ota receipt --baseline latest --fail-on-new-blockers [PATH]
ota receipt --snapshot latest [PATH]
ota receipt --snapshot promoted [PATH]
ota receipt --snapshot ./.ota/contracts/sha256-....json [PATH]
ota receipt --snapshot ./.ota/receipts/repo-receipt-....json [PATH]
ota receipt --history [PATH]
ota receipt --member api [PATH]

Current behavior:

  • resolves ota.yaml using --file, OTA_FILE, or upward discovery
  • --member <name> captures the merged monorepo member contract instead of the root contract
  • validates the contract first
  • runs repo readiness diagnosis in the selected execution context
  • --workflow <name> captures one declared workflow's readiness selection instead of the repo's default readiness path, including workflow-owned env-profile rendering and readiness selection
  • ota receipt now accepts the same execution-selector family as ota doctor: --mode, backend shorthands (--native, --container, --remote), --lifecycle, and lifecycle shorthands (--persistent, --ephemeral)
  • includes repo contract drift findings from the same ota detect comparison path used by ota doctor
  • captures the current repo state as an execution receipt with one readiness step
  • when a lifecycle override is selected, the receipt preserves that selected lifecycle, image, target, and rerun path instead of falling back to the default doctor container lifecycle
  • receipt JSON now includes additive receipt.toolchains[] evidence for the selected readiness path, mirroring the same provider/backend/target OS/fulfillment shape used by ota run <task> --dry-run --json and ota up --json
  • when a recorded execution path actually ran provider fulfillment commands, receipt.toolchains[] can also include additive fulfilled and commands[] evidence there
  • never provisions, runs tasks, starts services, or writes repo state
  • --json returns a repo receipt artifact with mode: "receipt"
  • receipt JSON always includes a normalized receipt.contract_snapshot_hash; --archive also materializes that normalized snapshot under .ota/contracts
  • latest, promoted, and archived baseline/snapshot selection stay workflow-scoped when --workflow is selected, so one workflow's receipt history cannot silently replace another's
  • receipt JSON also includes an additive receipt.assumption_set_hash derived from the canonical extracted assumption map, so automation can fingerprint semantic contract meaning separately from whole-snapshot identity
  • --archive writes the JSON receipt to .ota/receipts and keeps the newest 50 archives
  • --archive --promote-baseline also writes .ota/receipts/repo-baseline.json, pointing at the archived receipt as the repo's explicit promoted baseline
  • --history lists archived repo receipts from .ota/receipts newest first without loading or validating the current contract; explicit paths must be a repo directory or an ota.yaml file
  • --baseline promoted compares the current receipt against the explicit promoted baseline pointer under .ota/receipts/repo-baseline.json
  • --baseline latest compares the current receipt against the newest valid archived repo receipt for the same contract under .ota/receipts
  • --baseline <file> compares the current receipt against an explicit repo receipt JSON file
  • --snapshot latest resolves the newest valid archived repo receipt for the same contract and reads the archived normalized contract snapshot referenced by that receipt
  • --snapshot promoted resolves the explicit promoted repo baseline pointer under .ota/receipts/repo-baseline.json and reads the archived normalized contract snapshot
  • --snapshot <receipt.json> accepts an archived repo receipt JSON file and follows receipt.contract_snapshot_ref
  • --snapshot <snapshot.json> accepts an archived normalized snapshot JSON file directly
  • --snapshot is read-only and does not rerun doctor, archive a new receipt, or mutate repo state
  • compare mode is read-only and does not archive or mutate repo state; it exits 0 when the comparison itself succeeds, even if the current or baseline receipt is not ready
  • when the selected baseline receipt carries receipt.contract_snapshot_ref, compare mode also diffs the archived normalized contract snapshot against the current normalized contract truth and returns additive contract_changes[], likely_related_changes[], and summary.comparison.contract_snapshot_changed in JSON output
  • compare mode also returns additive summary.comparison.correlation so automation can distinguish likely_related, possibly_related, and no_clear_correlation without inferring correlation posture from array presence alone; possibly_related is reserved for coarse same-family overlap, while unrelated drift stays no_clear_correlation
  • public operator guide: Semantic Snapshots and Correlation for the higher-level snapshot, diff, and correlation workflow
  • --fail-on-new-blockers requires --baseline and exits 1 when the diff introduces one or more new severity: error findings relative to the baseline

Text output:

  • header: RECEIPT <path>
  • prints the receipt steps, compact contract identity, summary, env sources, policy lines, and blocked items when present
  • --archive --promote-baseline adds Baseline: and Promoted: summary lines so the operator can see which archive became the explicit repo baseline
  • --history switches the text header to RECEIPT HISTORY <path> and lists archived receipt files with their archived time, archived status, contract path, and any preserved execution identity fields such as context, backend, target, provider, lifecycle, and cwd; malformed archived files are skipped and surfaced under Skipped Archives
  • --baseline switches the text header to RECEIPT DIFF <path> and reports the baseline source plus provenance such as the selection path, promoted time, contract identity, introduced findings, resolved findings, and unchanged findings when there are no newly introduced or resolved changes
  • --baseline also preserves execution identity on both sides when present, including archived/current status, backend, context, target, provider, lifecycle, and cwd
  • --baseline includes the advisory correlation posture inside the Drift: overview line so operators can see whether new blocker findings look likely related, possibly related, or have no clear contract-change correlation
  • --fail-on-new-blockers adds a Gate: overview line showing whether the current diff passed or was blocked by newly introduced blockers
  • --snapshot switches the text header to RECEIPT SNAPSHOT <path> and prints the archived snapshot source, selection kind, archive/snapshot paths, whole-snapshot hash, assumption-set hash, assumption count, any preserved contract identity, and the pretty-printed normalized semantic contract JSON itself

JSON output:

  • ok
  • path
  • mode: "receipt"
  • archive_path (when --archive is set)
  • promoted_baseline.path, promoted_baseline.archive_path, and promoted_baseline.promoted_at (when --archive --promote-baseline is set)
  • summary mirroring the receipt summary with error_count, warn_count, info_count, and step_count
  • receipt, including additive receipt.contract_identity with declared project, selected metadata, execution intent, compact contract counts, and optional receipt.workflow_env_artifacts when the selected/default workflow owns rendered env artifacts
  • findings
  • --history switches mode to history and returns summary.archive_count, summary.invalid_archive_count, an archives array for valid archived receipts, and invalid_archives when malformed archive files were skipped
  • --snapshot switches mode to snapshot and returns additive source, selection_kind, selection_path, archive_path, archived_at, promoted_at, snapshot_hash, snapshot_path, contract, and the normalized archived snapshot
  • --baseline keeps mode: "diff" and returns additive summary.comparison.correlation, baseline.assumption_set_hash, and current.assumption_set_hash alongside the existing archive-backed snapshot-drift evidence
  • each history archive may preserve status, backend, context, target, provider, lifecycle, and cwd when that execution identity existed in the archived receipt
  • --baseline switches mode to diff and returns baseline, current, summary, introduced, resolved, and unchanged, with additive provenance fields on baseline
  • diff summary also carries a compact comparison block so wrappers can show baseline/current identity labels plus readiness drift without reconstructing it from the full baseline/current sections
  • --fail-on-new-blockers adds gate.rule, gate.passed, and gate.new_blocker_count to diff JSON when the compare gate is active
  • when that compare gate blocks, diff JSON also carries the first blocking summary, next step, and provenance so CI summaries and PR comments do not need to scrape the full introduced array

Current non-goals:

  • mutating repo state
  • replacing ota doctor as the full readiness explanation surface
  • separate receipt storage outside the explicit .ota/receipts archive directory
  • monorepo multi-member roll-up beyond the selected resolved contract target
  • multi-rule diff gating beyond the explicit --fail-on-new-blockers compare gate

ota up

Prepare a repo for use with minimal prior knowledge.

ota up [PATH]
ota up --json [PATH]
ota up --stream [PATH]
ota up --dry-run [PATH]
ota up --dry-run --json [PATH]
ota up --mode container --ephemeral [PATH]
ota up --effect-override network:broad=allow [PATH]
ota up --workflow verify --replay-baseline promoted [PATH]
ota up --member api [PATH]
ota up --member api --member web [PATH]
ota up --agent [PATH]
ota up --reason "release approved" [PATH]

Current behavior:

  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota up prepares the root contract and grouped member summaries for each declared member
  • when --member is set, prepares the merged member contract
  • repeated --member values prepare those members in the provided order
  • --agent enforces the declared agent-safe task boundary before setup or workflow execution starts; ota refuses the selected workflow path when any selected prepare/setup/run/attach task sits outside the safe set or reaches an unsafe task closure
  • --reason <text> attaches operator intent when the selected workflow crosses a heavier audited execution boundary; repo-target ota up --json mirrors the resulting ota-authored crossing record at both governance.crossing and receipt.crossing
  • runs inherited or overridden setup in the effective member directory
  • runs blocking precondition checks
  • when the selected or default workflow task closure declares tasks.<name>.requirements, ota up evaluates and provisions that merged prerequisite surface before setup instead of unrelated task-local quickstart or packaged-runtime requirements elsewhere in the repo
  • ota up --dry-run now lists selected toolchains explicitly: fulfillment.mode: none means ota will diagnose/check that toolchain on the selected path without provisioning it, while fulfillment.mode: run means ota may provision that selected toolchain on the selected run path if the chosen fulfillment source and policy allow it
  • mixed-backend workflows now keep selected prerequisites on their own execution boundary during ota up preflight, so a native run task is diagnosed on the host while a container setup task is diagnosed in the selected container lane instead of flattening both into one doctor mode
  • selected toolchain preview lines stay toolchain-owned: when a declared toolchain owns the selected ecosystem path, ota up --dry-run describes that provider-owned toolchain requirement instead of pretending owned capabilities are standalone setup tools, and the preview now names the owned runtime capability alongside the provider so toolchains.node reads as Node via Corepack rather than implying that Corepack itself is the runtime; when toolchains.node declares package_managers, those activation lanes stay attached to the same selected toolchain
  • policy-backed version/provisioning previews now include selected toolchain-owned runtime lanes too, and selected toolchain-owned tool lanes when the provider projects them there, so a repo can show governed install paths without reintroducing duplicate runtimes or tools ownership
  • ota up --json and preview/final execution receipts now expose additive receipt.toolchains[] evidence so the selected workflow path's toolchain/provider/backend decisions stay machine-readable alongside readiness findings
  • when ota actually runs provider fulfillment commands on the selected run path, the final receipt can also include additive fulfilled and commands[] evidence for that toolchain entry
  • when one selected tool requirement resolves to tools.<name>.acquisition, ota up can run that activation lane before setup when it is safe and selected; shipped paths now cover both Corepack-managed tools and explicit shell-command acquisition
  • on a selected native Corepack path, ota activates every declared package manager before direct task commands run, so yarn or pnpm resolves to the contract-owned version rather than an ambient global shim
  • when the selected workflow setup or run task references requirements.native entries with activation.kind, ota resolves that native activation environment first; Windows visual_studio_dev_shell and generic command activations both apply to the affected native task bodies instead of assuming the current terminal was pre-activated
  • when policy-backed provisioning is selected for a tool that also has an acquisition path, ota up --dry-run names the provisioning action and explicitly marks the selected acquisition activation as skipped
  • when a selected workflow declares prepare.task, ota up runs that native host file-prep action before pre-setup services or setup; this phase is for deterministic file preparation such as copy_if_missing, not long-lived runtime or service work
  • when a default workflow declares setup.task, ota up uses that task as the preparation phase; otherwise it falls back to repo setup
  • when blocking preconditions fail and the selected workflow declares a setup task, ota runs that setup phase early and then re-checks readiness
  • when the effective execution mode is container, policy-backed provisioning adapters run inside that container instead of on the host
  • explicit or effective container-backed ota up stays container-authoritative; if no supported container engine is available, ota stops in preconditions instead of falling back to host provisioning
  • when provisioning fails, ota up now surfaces a higher-level backend diagnosis for every shipped adapter while still preserving the raw backend stdout/stderr in the failure output
  • when the initial provisioning stderr is too generic to classify safely, ota up reuses the read-only installability probe for that adapter to refine the diagnosis without hiding the original backend output
  • when container/Linux provisioning uses apt, ota also classifies supported provisioning failures as pinned-version unavailable, package unavailable, or apt-index/source failures
  • execution-plane precondition failures, backend startup failures, and provisioning failures now point through ota execution plan first so the selected backend, lifecycle, image, or target path is visible before you edit execution settings or retry ota up
  • --dry-run reuses the same contract path, member targeting, backend selection, lifecycle selection, and provisioning plan resolution as ota up, but does not mutate repo or execution state
  • runs selected workflow service startup through the declared service owner before and after setup as declared
  • starts required services, and required-service dependencies, in declared dependency order
  • verifies required service healthchecks before setup and treats them as readiness gates
  • stops in the services phase when required-service readiness still fails
  • runs the selected workflow setup task if one exists, using the configured execution backend when present
  • activates the selected workflow run task when it has a declared service runtime
  • for service-runtime workflow run tasks, default ota up behavior uses detached readiness proof semantics: start the run task, wait for readiness, then tear down proof-owned run execution before returning READY
  • ota up --detach keeps that proved run task alive intentionally after readiness confirms
  • ota up --attach keeps ota up attached to the workflow run task process instead of detached proof behavior
  • --ready-timeout <duration> overrides readiness wait budget for detached service-runtime proof behavior
  • when setup binds to a named context that uses extends, ota up uses the merged context backend/lifecycle/image shape
  • can override execution mode and lifecycle for the selected workflow setup/run phase with --mode, --lifecycle, or the shorthand --ephemeral
  • explicit workflow execution overrides use the same fail-closed admission as ota run; if the selected task path cannot enforce a requested mode or lifecycle, ota up refuses before prepare/setup/run execution instead of treating the request as advisory
  • ota up --dry-run --json carries execution_started: false; option-admission blockers identify the requested flag and selected task before any workflow phase starts
  • --effect-override <effect>=<allow|warn|deny> temporarily overrides one effect-governance decision for this ota up invocation only using the same selectors as ota run
  • --replay-baseline <latest|promoted|archive-path> resolves an archived repo receipt baseline before execution, runs the selected workflow lane, and publishes replay posture from the execution-authored receipt as replay_verified, replay_failed, or replay_unavailable
  • replay execution is intentionally narrower than general receipt comparison today: it is only supported for mutating single-repo ota up lanes and is rejected for --dry-run, --stream, --member, and monorepo-root aggregate ota up
  • the current workflow-task backend path supports native, container, and the shipped remote providers
  • prints a lifecycle note on stderr when the selected workflow task uses backend-backed execution
  • reruns readiness diagnosis
  • still runs service start commands, service healthchecks, and diagnosis on the host today
  • returns READY or NOT READY
  • reports the phase where execution stopped: preconditions, provisioning, activation, services, setup, run, or post-up diagnosis
  • reports provisioning when early setup ran but the repo is still not ready
  • includes setup exit code details when the setup task fails
  • includes service start exit code details when a required service start command fails
  • keeps child output compact by default and surfaces failed service/setup output inside the final report
  • --stream opts into raw live child output for provisioning, required service start commands, and the setup task
  • --stream is text-only and is only supported for mutating ota up
  • prints a summary in text output, emits an execution receipt when --receipt is set, and includes summary plus a receipt object in repo-target JSON output; the compact UP SUMMARY now leads with Status for faster scan time, and monorepo aggregate JSON keeps grouped members results instead of inventing a top-level receipt
  • when the execution receipt carries follow-up guidance, text output appends that shared Next: block after UP SUMMARY, and the same receipt-backed lane stays on the repo-target JSON receipt.next surface so repo and workspace preparation flows end the same way
  • --dry-run prints UP PREVIEW, shows the selected execution backend, lifecycle, container image when relevant, a real named target when one would exist, the setup task, the actions ota would attempt, the actions ota would skip because current state already satisfies them, the compact contract identity, and the first actionable readiness finding when one exists
  • --dry-run keeps the shared readiness verdict in JSON status / summary.verdict, but the preview text and additive JSON preview_status now make the execution meaning explicit as RUNNABLE, RUNNABLE WITH WARNINGS, or BLOCKED
  • ota run <task> --dry-run now diagnoses the selected task dependency path with the same precondition truth surface used by ota doctor / ota up, so selected runtime, tool, and toolchain mismatches block preview instead of being silently described as runnable
  • --dry-run never provisions, starts services, runs setup, or writes repo files
  • --receipt is only for mutating ota up; it conflicts with --dry-run
  • replay baselines are honest only for what the archived witness actually carried: matching semantic_contract_snapshot acquits contract drift, while matched declared_replay_input records remain narrowing evidence and keep replay hermeticity at partly_ambient
  • the detailed preview contract lives in up-preview.md

This is the onboarding command. It is intentionally narrower than a general-purpose environment orchestrator.

Agent execution:

  • use ota up --agent when the selected workflow path should be enforced against the declared agent-safe boundary before setup or runtime work starts
  • combine it with --workflow <name> when one specific workflow is intended: ota up --workflow <name> --agent
  • use ota workflows --json first when you need machine-readable workflow posture before selecting that lane

ota self-update

Update the installed ota binary.

ota self-update
ota self-update --version v0.1.3
ota self-update --channel stable
ota upgrade
ota upgrade --version v0.1.3
ota upgrade --channel stable

Current behavior:

  • ota self-update and ota upgrade are aliases
  • --version pins a specific release
  • --channel currently accepts stable and latest
  • stable resolves the latest stable release tag
  • latest resolves the newest release entry, including prereleases if present
  • --version overrides the channel when both are set
  • when the chosen target matches the installed binary, the command exits successfully and prints the up-to-date banner instead of reinstalling
  • on Windows, when ota is currently running, the downloaded binary is staged and applied after the current process exits; a successfully scheduled replacement reports pending and exits successfully instead of surfacing a Copy-Item stack trace

ota policy

Show the active policy pack, its source, and the resolved path.

ota policy [PATH]
ota policy --json [PATH]
ota policy --file /path/to/ota.yaml
ota policy --file /path/to/ota.yaml --json

Current behavior:

  • resolves the policy pack using the same precedence ota uses for repo commands
  • shows the effective policy content and where it came from
  • accepts OTA_POLICY as a local file path or http(s):// URL override
  • falls back to the nearest ancestor .ota/org-policy.yaml
  • falls back again to the nearest ancestor ota.workspace.yaml workspace.policy when present
  • remains read-only

Text output:

  • header: POLICY <path>
  • Policy source: shows where ota loaded the policy from
  • Policy path: shows the resolved policy file path or URL
  • effective policy content when one is loaded
  • when a policy pack is loaded, Next: points to ota policy review for boundary inspection and ota doctor for readiness with that active policy applied
  • when no policy pack is found, the text output says so explicitly and points users back to repo readiness or .ota/org-policy.yaml

JSON output:

  • ok
  • path
  • policy_source and source
  • policy_path
  • policy
  • failure responses include error

Use this when you need to confirm which org policy ota actually applied before a run or diagnosis.

ota policy init

Create a conservative starter org policy pack.

ota policy init [PATH]
ota policy init --preset required-sections [PATH]
ota policy init --preset provisioning [PATH]
ota policy init --preset agent [PATH]
ota policy init --dry-run [PATH]
ota policy init --json [PATH]
ota policy init --dry-run --json [PATH]

Current behavior:

  • writes by default
  • refuses to overwrite an existing policy pack
  • defaults to .ota/org-policy.yaml under the current directory when no path is given
  • accepts a repo root, a .ota/ directory, or an explicit .ota/org-policy.yaml target path
  • supports explicit starter presets: required-sections, provisioning, and agent
  • writes the minimal valid starter today: policies: {}
  • stays conservative and does not infer org rules or add provisioning approvals automatically
  • required-sections starts with a small required-section policy (runtimes and tasks)
  • provisioning scaffolds empty provisioning and adapter_bootstrap maps plus inline example guidance
  • agent starts with agent-safety and AGENTS.md export requirements enabled

Text output:

  • write header: POLICY INIT <path>
  • preview header: POLICY INIT PREVIEW <path>
  • Preset: is shown when a preset is selected
  • preview shows the starter policy pack YAML without writing it
  • write output confirms the written path and points back to ota policy
  • overwrite refusal stays explicit and non-mutating

JSON output:

  • ok
  • path
  • written
  • mode (policy)
  • optional preset
  • config
  • failure responses include error
  • overwrite refusals may include next

Use this when a team needs a valid .ota/org-policy.yaml scaffold without guessing policy intent or hand-authoring the starter shape.

ota policy review

Review the policy-vs-contract boundary and approved policy sources.

ota policy review [PATH]
ota policy review --json [PATH]
ota policy review --file /path/to/ota.yaml
ota policy review --file /path/to/ota.yaml --json

Example:

ota policy review

Current behavior:

  • resolves the active policy pack using the same precedence as ota policy
  • focuses only on policy-authority findings, approved provisioning sources, and adapter bootstrap sources
  • stays read-only
  • points repo-owned conflicts back to ota.yaml
  • points governance-owned conflicts back to .ota/org-policy.yaml

Text output:

  • header: POLICY REVIEW <path>
  • Policy is the context block and shows the active source plus resolved policy path or URL
  • Overview rolls up the policy findings by severity
  • policy findings use operator-shaped summaries and action-specific Next: steps instead of pointing back to ota policy review
  • when no policy pack is found, the text output says so explicitly and points users back to ota policy

JSON output:

  • ok
  • path
  • policy_source
  • policy_path
  • summary
  • finding_groups
  • policy
  • findings

Use this when you need to understand what policy ota enforced, why a repo-contract request is outside the approved policy boundary, or whether the org policy pack itself needs to change.

ota skills install

Install the canonical Ota skill for an agent tool after the ota binary is available.

ota skills install --agent codex
ota skills install --agent claude
ota skills install --agent codex --json

Current behavior:

  • installs the first-party ota skill from the ota-run/skills distribution repository
  • requires network access to fetch the current distribution copy
  • requires --agent codex or --agent claude so Ota writes to the correct tool-specific skill directory
  • writes Codex installs to $CODEX_HOME/skills/ota when CODEX_HOME is set, otherwise ~/.codex/skills/ota
  • writes Claude Code installs to ~/.claude/skills/ota
  • stages and validates the complete skill tree before replacing an existing install
  • keeps skill lifecycle ownership in the CLI instead of installer scripts

JSON output:

  • ok
  • skill
  • agent
  • path

Use this after installing or updating Ota when you want Codex or Claude Code to use Ota-specific contract authoring and repo-readiness guidance.

ota completion

Show how to enable shell completion for ota.

ota completion --setup
ota completion --remove
ota completion check
ota completion bash
ota completion bash --script
ota completion zsh
ota completion fish
ota completion powershell
ota completion elvish

Current behavior:

  • ota completion --setup detects the current shell when possible and installs ota's managed hook into the shell profile or completion file idempotently
  • ota completion --remove detects the current shell when possible and removes ota's managed hook plus any managed zsh support file idempotently
  • ota completion <shell> --setup installs the managed hook for one explicit shell without relying on auto-detection
  • ota completion <shell> --remove removes the managed hook for one explicit shell without relying on auto-detection
  • ota completion check verifies the detected shell, the current ota binary path, the target profile or completion file, any managed zsh completion file, and whether the managed hook is present or needs refresh
  • ota completion <shell> prints the manual shell setup ota expects for that shell; for zsh it includes both the _ota completion file and the .zshrc loader
  • ota completion <shell> --script prints the exact raw registration script clap generates for that shell so users can inspect the shell-side function directly
  • zsh setup writes a managed _ota completion file under ~/.config/ota/zsh/_ota and loads that exact file through the shell completion path instead of relying on late runtime compdef registration alone
  • once the shell has sourced that setup, ota <TAB> completes commands first and keeps global flags after them in zsh
  • once the shell has sourced that setup, ota run <TAB> completes task names only when one shared invocation can satisfy the selected repo/member target set, and shells that support candidate help can also show each task description when the contract declares one
  • once the shell has sourced that setup, ota run <task> <TAB> completes shared task input flags and any constrained values that remain valid across the selected repo/member target set
  • once the shell has sourced that setup, ota env --task <TAB> completes task names from the active repo or selected monorepo member, using the same task-description metadata when available
  • once the shell has sourced that setup, ota extensions --run <TAB> and ota extensions --publish <TAB> complete declared extension names for the active repo or selected member target
  • once the shell has sourced that setup, ota receipt --baseline <TAB> completes latest, promoted, and archived receipt JSON files from the active repo's .ota/receipts
  • once the shell has sourced that setup, --member <TAB> completes monorepo member names from the active repo contract
  • once the shell has sourced that setup, ota workspace run <TAB> completes task names only when one shared invocation can satisfy the currently available workspace repos, with shared task descriptions when the participating repos agree on that description
  • once the shell has sourced that setup, ota workspace run <task> <TAB> completes shared task input flags and any constrained values that remain valid across the currently available workspace repos
  • once the shell has sourced that setup, ota workspace doctor --repo <TAB>, ota workspace explain --repo <TAB>, and ota workspace list --repo <TAB> complete declared workspace repo names
  • when no repo contract is available, shell completion falls back to static command and flag suggestions
  • the auto-installed hook is managed between # >>> ota completion >>> and # <<< ota completion <<< markers so rerunning setup updates or reuses the same block instead of appending duplicates
  • ota completion --remove only strips ota's managed block and managed zsh support file; it does not try to edit unrelated shell completion setup
  • users should reload or re-source their shell after upgrading ota so the shell-side glue and the installed binary stay in sync

Use this when you want contract-aware shell suggestions instead of memorizing task names and task input flags.

Automatic setup:

ota completion --setup
ota completion --remove
ota completion zsh --setup
ota completion zsh --remove

Verification and inspection:

ota completion check
ota completion bash --script

Manual setup examples:

bash

ota completion bash
# >>> ota completion >>>
if command -v ota >/dev/null 2>&1; then
  source <(COMPLETE=bash ota)
fi
# <<< ota completion <<<

zsh

ota completion zsh
Manual completion file (~/.config/ota/zsh/_ota):
#compdef ota
_ota() {
    local _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
    local _CLAP_IFS=$'\n'

    local completions=("${(@f)$( \
        _CLAP_IFS="$_CLAP_IFS" \
        _CLAP_COMPLETE_INDEX="$_CLAP_COMPLETE_INDEX" \
        COMPLETE="zsh" \
        ota -- "${words[@]}" 2>/dev/null \
    )}")

    if [[ -n $completions ]]; then
        local -a primary_values=()
        local -a primary_display=()
        local -a option_values=()
        local -a option_display=()
        local completion
        for completion in $completions; do
            local value="${completion%%:*}"
            if [[ "$value" == -* ]]; then
                option_values+=("$value")
                if [[ "$completion" == *:* ]]; then
                    option_display+=("$value -- ${completion#*:}")
                else
                    option_display+=("$value")
                fi
            else
                primary_values+=("$value")
                if [[ "$completion" == *:* ]]; then
                    primary_display+=("$value -- ${completion#*:}")
                else
                    primary_display+=("$value")
                fi
            fi
        done
        [[ -n $primary_values ]] && compadd -Q -V ota_primary -d primary_display -o nosort -- "${primary_values[@]}"
        [[ -n $option_values ]] && compadd -Q -X 'Options' -V ota_options -d option_display -o nosort -- "${option_values[@]}"
    fi
}

Manual setup (~/.zshrc):
# >>> ota completion >>>
if command -v ota >/dev/null 2>&1; then
  _ota_completion_file="$HOME/.config/ota/zsh/_ota"
  if [[ -f "$_ota_completion_file" ]]; then
    _ota_completion_dir="${_ota_completion_file:h}"
    if (( ${fpath[(Ie)$_ota_completion_dir]} == 0 )); then
      fpath=("$_ota_completion_dir" $fpath)
    fi
    autoload -Uz _ota 2>/dev/null
    if typeset -p _comps >/dev/null 2>&1; then
      _comps[ota]=_ota
    elif whence compdef >/dev/null 2>&1; then
      compdef _ota ota
    else
      autoload -Uz compinit
      compinit
      _comps[ota]=_ota
    fi
  fi
  unset _ota_completion_file _ota_completion_dir
fi
# <<< ota completion <<<

fish

ota completion fish
# >>> ota completion >>>
if type -q ota
    COMPLETE=fish ota | source
end
# <<< ota completion <<<

PowerShell

ota completion powershell
# >>> ota completion >>>
if (Get-Command ota -ErrorAction SilentlyContinue) {
  $env:COMPLETE = "powershell"
  ota | Out-String | Invoke-Expression
  Remove-Item Env:\COMPLETE -ErrorAction SilentlyContinue
}
# <<< ota completion <<<

elvish

ota completion elvish
# >>> ota completion >>>
if (has-external-command ota) {
  eval (E:COMPLETE=elvish ota | slurp)
}
# <<< ota completion <<<

Troubleshooting:

  • zsh: if completions still do not appear after setup, reopen the shell or confirm ota completion check shows both Hook: present and a Completion file: line for the managed _ota file; ota completion --remove gives you a clean reinstall path
  • bash: if completions still do not appear after setup, reopen the shell or source the profile again with . ~/.bashrc
  • ota completion check should report Hook: present; if it reports missing or needs update, rerun ota completion --setup
  • ota completion <shell> --script lets you inspect the exact raw registration script when the shell-side behavior itself looks wrong

ota uninstall

Remove ota from this laptop.

ota uninstall

Current behavior:

  • removes the installed ota binary from the current machine
  • on Windows, schedules removal of the running executable through a detached helper because the live ota.exe cannot delete itself while it is executing
  • on Unix-like systems, removes the current executable directly when possible
  • does not touch repo state, contracts, or workspace state

Text output:

  • success: removed ota from <path> or scheduled ota removal from <path>; Windows will delete the running executable after this command exits. Open a new terminal to verify \ota` is gone.`
  • already removed: ota was already removed from <path>

Use this when you want to remove ota from the machine itself, not when you want to clean a repo.

  • on success, the command runs the installer for the chosen release target

Use this when:

  • you already have ota installed and want to update it in place

Use-case:

  • a developer sees the update notice after ota doctor and runs ota self-update

JSON output:

  • ok
  • path
  • status
  • phase
  • findings
  • service when a service-start failure occurs
  • task when a task failure occurs
  • exit_code when a child command failure occurs
  • monorepo root and repeated --member summaries include grouped per-member results in members
  • contract load/validation failures return the same failure envelope as ota validate --json (ok, path, and either errors or error)

ota clean

Clean persistent execution state for a repo.

ota clean [PATH]
ota clean --member api [PATH]
ota clean --member api --member web [PATH]
ota clean --stale
ota clean --stale --dry-run
ota clean --stale --json

Current behavior:

  • validates the contract first
  • when a root contract declares workspace.type: monorepo, plain ota clean reports the root cleanup result and grouped member cleanup results
  • when --member is set, targets those merged member contracts in the provided order
  • removes current contract-derived Ota-managed persistent containers and dependency-isolation volumes
  • rediscovers and removes drifted Ota-managed persistent containers and dependency-isolation volumes for the same repo via ownership labels (dev.ota.managed, cleanup kind/lifecycle labels, and repo ownership token)
  • repo identity for cleanup is anchored by .ota/state/ownership-id (not project.name)
  • tracks repo-used container engines in .ota/state/managed-engines so drift cleanup can still query a previously used engine after contract engine changes
  • scopes discovery to relevant engines (current contract targets plus recorded repo-used engines) and does not fail because an unrelated installed engine is unavailable
  • when no relevant engine evidence exists for the repo, falls back to best-effort discovery across locally available container engines and only fails if none of those discovery probes succeed
  • fails explicitly when discovery for a relevant engine fails; it does not downgrade to No cleanup needed
  • reports ownership-ambiguous Ota-managed state as skipped (not removed) when repo ownership cannot be proven
  • ota clean --stale does not require ota.yaml; it scans available local container engines for exited ota-managed containers from any repo
  • stale cleanup uses ota ownership labels first and falls back to legacy ota-* container names for older persistent backends
  • if a local container engine cannot answer ps, stale cleanup continues with other available engines and only fails when none of them can be queried
  • ota clean --stale --dry-run previews stale containers without removing them
  • ota clean --json emits structured repo/member/workspace cleanup counters on success and structured cleanup failure details on failure; active execution cleanup barriers now classify separately from engine/resource failures instead of falling back to a generic error string
  • ota clean --stale --json emits the matched engines, containers, and cleanup counts for automation
  • ota clean --stale has its own exit-code contract and is separate from repo-scoped ota clean
  • remote backends do not currently define cleanup semantics; they report No cleanup needed
  • reports No cleanup needed only when no owned cleanup target is found and no relevant-engine discovery failed
  • repo-scoped ota clean now treats any live active-execution registry entry as a cleanup barrier; it fails with an execution-conflict report instead of stopping host services or tearing down persistent state underneath an active run
  • does not stop services or perform workspace-wide cleanup

ota detect

Infer a starting contract from repo state.

ota detect --dry-run [PATH]
ota detect --json --dry-run [PATH]
ota detect --contract [PATH]
ota detect --write [PATH]
ota detect --json --write [PATH]
ota detect --merge --dry-run [PATH]
ota detect --merge --apply FIELD [PATH]
ota detect --merge --apply-all [PATH]
ota detect --merge [PATH]
ota detect --rewrite --dry-run [PATH]
ota detect --rewrite --yes [PATH]
ota detect [PATH]

Current detect sources:

  • package.json
  • pnpm-workspace.yaml
  • pnpm-lock.yaml
  • yarn.lock
  • bun.lock / bun.lockb
  • package-lock.json
  • npm-shrinkwrap.json
  • .nvmrc
  • .node-version
  • .devcontainer/devcontainer.json
  • devbox.json
  • AGENTS.md
  • CLAUDE.md
  • .tool-versions
  • mise.toml
  • devenv.nix
  • pyproject.toml
  • Pipfile
  • uv.lock
  • requirements.txt
  • setup.cfg
  • .python-version
  • .java-version
  • .sdkmanrc
  • go.mod
  • Cargo.toml
  • rust-toolchain.toml
  • rust-toolchain
  • settings.gradle(.kts)
  • build.gradle(.kts)
  • gradle/wrapper/gradle-wrapper.properties
  • pom.xml
  • mvnw
  • .mvn/wrapper/maven-wrapper.properties
  • composer.json
  • .ruby-version
  • Gemfile
  • global.json
  • *.sln / *.csproj / *.fsproj
  • mix.exs
  • rebar.config
  • build.zig
  • dub.json / dub.sdl
  • fpm.toml
  • shard.yml
  • elm.json
  • cpanfile / Makefile.PL
  • *.hxml
  • docker-compose.yml / docker-compose.yaml
  • compose.yml / compose.yaml

For Docker Compose service inference, ota currently derives:

  • provider at high confidence
  • start / stop at high confidence
  • declared healthcheck.test at high confidence

For external AGENTS.md / CLAUDE.md, ota currently admits only narrow structured shapes:

  • safe_tasks, verify_after_changes, writable_paths, and protected_paths lists as medium-confidence agent-boundary evidence
  • labeled command bullets under explicit command sections such as ## Commands, ## Individual Commands, ## Common Commands, ## Build/Test Commands, or ## Build & Development Commands, including nearby bounded variants such as ## Quick Reference Commands
  • bounded structured command tables inside those explicit command sections as low-confidence task guidance for narrow canonical lanes such as build, check, lint, test, docs, and compile; ota currently admits exact | Task | Command | tables and adjacent | Command | description | forms when it can still recover a stable canonical task name from the command itself or from a bounded task label such as Run all unit tests, Run type checking, or Format code
  • placeholder-heavy command templates stay out of detect evidence even inside those structured sections; commands that still carry metavars such as <PROJECT>, <tests>, or obvious path/to/... scaffolding are guidance prose, not runnable task truth
  • when a structured boundary-doc command already exposes a stable canonical task identity such as pnpm run format:diff or pnpm run test, ota prefers that command-owned task identity over a prose alias from the table label

Ota-generated/self-origin agent docs stay excluded from detect evidence, and broader prose is not promoted into executable contract truth.

Dry-run behavior:

  • ota detect is read-only by default
  • prints a candidate ota.yaml
  • prints per-field provenance
  • prints per-field confidence
  • when curated standard env source files exist, includes inferred env.sources entries for .env.local, .env, src/main/resources/application.properties, src/main/resources/application.yml, src/main/resources/application.yaml, appsettings.json, and appsettings.Development.json
  • when ota.yaml already exists, text output leads with the existing-contract comparison and drift review before the inferred contract details
  • existing-contract add/update lines include the detector source and confidence for the proposed value
  • when ota.yaml already exists and only drift is present, text output says there are no additive detected changes and points users at merge vs rewrite review
  • text preview now renders an explicit Agent boundary outcome: Inferred when safe tasks were inferred, Partially inferred when only writable/protected boundary defaults were inferred, and Omitted when ota intentionally withholds the starter agent block because no safe task was inferred
  • PowerShell-script detection now emits runtimes.pwsh for pwsh-based repos instead of the legacy runtimes.powershell key, so the inferred contract and later runtime probes stay aligned with the detected task/tool surface
  • does not write anything

Contract preview behavior:

  • ota detect --contract prints the exact starter contract that ota init would write
  • ota detect --contract omits annotations and comparison output
  • ota detect --contract is text output only

Current write behavior:

  • ota detect --write writes a conservative detect-write candidate: high-confidence detected fields plus a narrow starter-owned subset ota can model structurally without broadening into a full starter rewrite
  • ota detect --write remains conservative even when ota init can write a valid starter
  • versioned pnpm/yarn package-manager-backed package.json#engines.node is high confidence for detect write, merge, rewrite, ownership metadata, and drift comparison, and is written as canonical toolchains.node Corepack ownership so a detected Node contract does not silently omit the runtime or generate legacy split ownership
  • detected Go, Ruby, and .NET runtime/tool lanes now converge to canonical toolchain ownership on write/merge (toolchains.go, toolchains.ruby, and toolchains.dotnet with structured fulfillment instead of legacy split ownership), and detected tools.bundler ownership folds into toolchains.ruby.package_managers.bundler instead of remaining split
  • Docker Compose service start, stop, and declared healthcheck.test commands are high confidence and can be written with the inferred service block
  • detect preview, exact starter preview, and detect write now keep the same derived starter agent block that init uses, while detect-owned field metadata remains scoped to actually inferred fields and writable-path inference can include broader common directories plus bounded custom source roots
  • detect write metadata now distinguishes direct high-confidence writes from conservative promotion: written contracts record metadata.ota.detect.field_admission with direct for detector-owned fields and promoted for the narrow starter-owned fields admitted by the conservative detect-write lane
  • validates the generated contract before writing
  • refuses to overwrite an existing ota.yaml
  • when no ota.yaml exists yet, preview guidance stays compare-first: ota detect --contract for exact detected text, ota init --dry-run for the conservative starter path, then ota detect --write for the first detected write
  • after a successful first detect write, text output uses explanatory Next: steps: validate the written contract, inspect the runnable task surface, review readiness with doctor, then preview preparation with ota up --dry-run

Current merge-preview behavior:

  • ota detect --merge --dry-run is a review-only mode
  • it requires an existing ota.yaml
  • it does not write
  • it reuses the comparison preview instead of applying changes, including stale contract fields that no longer match repo reality
  • JSON comparison entries carry stable ownership/provenance labels; add/update entries also carry direct detector source and confidence
  • task drift in text output is grouped by task name instead of raw dotted paths
  • when both kinds are present, task drift splits command removals from safe_for_agent removals
  • task drift text starts with a compact summary showing affected task count and removal counts by kind
  • with --concise, task drift collapses to one line per affected task with removal counts instead of listing every command
  • there is no standalone ota drift command yet; drift review stays on ota detect --merge --dry-run, and operator-facing trust/readiness drift stays on ota doctor
  • CI verification drift stays on verification-oriented workflow evidence only; deploy, release, publish, sync, and similar workflows no longer become canonical verification truth just because they contain one verifier step
  • declared verifier aggregates can now be satisfied by exact coverage split across more than one verification-oriented workflow file instead of forcing one workflow file to restate the whole aggregate

Current merge-write behavior:

  • ota detect --merge requires an existing ota.yaml
  • it applies only high confidence missing fields
  • ota detect --merge --apply FIELD applies only the selected high-confidence detected changes and leaves the rest of ota.yaml unchanged
  • ota detect --merge --apply-all applies all eligible high-confidence detected changes and leaves the rest of ota.yaml unchanged
  • inferred env.sources additions participate in the same high-confidence merge/apply path and are never auto-loaded at runtime unless they are declared in the contract
  • it does not overwrite conflicting existing values
  • it validates the merged contract before writing
  • it is additive only in the current implementation
  • on mixed repos, lower-confidence fields can still appear in comparison without being written
  • if nothing eligible can be added, it returns success with written: false and leaves ota.yaml unchanged
  • after a successful merge write, text output uses explanatory Next: steps: validate the updated contract first, then review any remaining add-only drift with ota detect --merge --dry-run, review rewrite-only drift with ota detect --rewrite --dry-run when the current contract is stale, and only drift-free merges hand into the same task/doctor/preparation lane used by first writes

Current rewrite behavior:

  • ota detect --rewrite targets existing contracts only and is destructive
  • ota detect --rewrite --dry-run previews replacement without writing
  • ota detect --rewrite --yes replaces the existing ota.yaml with the regenerated detected contract
  • rewrite creates a timestamped backup file (ota.yaml.bak-<timestamp>) before writing
  • rewrite validates the regenerated contract before replacing the existing file
  • after a successful rewrite, text output uses explanatory Next: steps: validate the rewritten contract, inspect the runnable task surface, review readiness with doctor, then preview preparation with ota up --dry-run

Example dry-run annotations for detected Compose services:

---
Annotations:
✦ Field: services.db.provider
  Type: service
  Value: docker-compose
  Source: docker-compose.yml#services.db
  Signal: config
  Confidence: high

Annotation metadata stays additive but stable:

  • Type: currently renders one of project, runtime, tool, env, service, check, task, agent, or field
  • Signal: currently renders one of config, script, lockfile, file, template, or convention
  • task-shaped annotations can also include Agent Safe: (yes, no, unknown) and Agent Signal: (verification_candidate, bootstrap_candidate)

Current precedence is conservative:

  • higher confidence beats lower confidence
  • when confidence is equal, more repo-specific runtime sources win before generic version-manager aggregation
  • when confidence is equal for project names, package.json wins over conflicting Python or Go manifest names
  • when confidence is equal for package-manager tools, package.json#packageManager wins over conflicting .tool-versions values
  • repo-owned mise.toml#tools.* runtime and Node package-manager truth wins over conflicting .tool-versions values, but still yields to more specific repo-local markers such as .nvmrc, .python-version, or package.json#packageManager
  • .devcontainer/devcontainer.json can contribute high-confidence runtime/toolchain truth from known devcontainer features (node, python, go, github-cli, kubectl-helm-minikube) plus Node package-manager truth from string, object, or array postCreateCommand / updateContentCommand entries, but those hints remain lower precedence than repo-local runtime markers, package.json#packageManager, and mise.toml
  • devbox.json can contribute high-confidence tools.devbox ownership and shell.scripts.* task inference
  • devenv.nix currently contributes only high-confidence tools.devenv presence; ota does not yet infer broader Nix environment truth from it in this slice
  • when package.json#packageManager is present for versioned pnpm or yarn, package.json#engines.node is treated as high-confidence toolchains.node runtime truth; without a package manager signal, it stays conservative
  • when package.json#packageManager is absent, known repo-local Node package-manager markers such as workspace files and lockfiles can determine the tool and task command prefix conservatively
  • verifier-style inferred tasks (for example test, lint, typecheck, check, verify, fmt) are marked with safe_for_agent: true only when the inferred lane still looks like a bounded verification command; orchestration-flavored lanes such as docker:test stay unsafe-by-default, watch/dev/serve variants stay unsafe-by-default because they are usually long-running, and other inferred tasks stay unsafe-by-default
  • GitHub Actions run: lines that still contain ${{ ... }} interpolation are excluded from command-truth inference instead of being promoted into repo task bodies
  • Pipfile can contribute python runtime inference and pipenv tool inference conservatively
  • uv.lock can contribute uv tool inference conservatively
  • requirements.txt can contribute pip tool inference conservatively
  • setup.cfg can contribute project name and python runtime inference conservatively
  • mise.toml can contribute high-confidence runtime and Node package-manager inference from repo-owned [tools] entries
  • .devcontainer/devcontainer.json can contribute high-confidence Node runtime inference from the declared image, conservative runtime/tool presence from known devcontainer features, and Node package-manager inference from string, object, or array postCreateCommand / updateContentCommand entries
  • devbox.json can contribute high-confidence devbox tool inference plus devbox run <script> tasks from declared shell.scripts
  • devenv.nix can contribute high-confidence devenv tool presence conservatively
  • for example, .nvmrc, .node-version, .python-version, .java-version, .sdkmanrc, go.mod, rust-toolchain.toml, and rust-toolchain win over conflicting .tool-versions runtime values

Write behavior:

  • ota detect --write writes only the conservative detect-write candidate, not the full detected starter contract
  • validates the projected contract before writing
  • refuses to overwrite an existing ota.yaml
  • when ota.yaml already exists, points the user at ota detect --merge --dry-run and ota detect --rewrite --dry-run
  • fails if the high-confidence projection is not sufficient
  • JSON failure responses can include next when ota can point to one safe follow-up command

This is intentionally conservative. Review mode comes first, write mode second.

ota workspace init

Create a starter workspace contract from existing repo contracts.

ota workspace init [PATH]
ota workspace init --json [PATH]

Current behavior:

  • infers workspace repos by scanning common local repo roots (top-level plus containers like apps/, services/, repos/, packages/)
  • includes only repos that already have ota.yaml
  • skips candidate repos that do not yet have ota.yaml
  • when no ota.workspace.yaml exists yet, preview-first onboarding is compare-first: review ota workspace detect --dry-run against ota workspace init --dry-run before any first write
  • ota workspace init writes ota.workspace.yaml by default
  • ota workspace init --bootstrap can auto-provision missing repo contracts from detected repo signals before writing ota.workspace.yaml
  • --write remains a compatibility alias for the write path
  • writes ota.workspace.yaml
  • refuses to overwrite an existing ota.workspace.yaml
  • when no repos are available to bootstrap, points to ota init <repo-path>, ota detect --dry-run <repo-path>, then back to ota workspace detect --dry-run and ota workspace init --dry-run before any workspace write
  • when overwrite is refused, points to ota workspace validate and ota workspace doctor
  • successful writes now hand directly to ota workspace validate, ota workspace up --dry-run, and ota workspace up
  • supports JSON for machine-readable write outcomes

Text output:

  • preview: compare-first Next: guidance into ota workspace detect --dry-run or the explicit write path
  • write: WORKSPACE INIT WRITE <path> with the same post-write lane into ota workspace validate, ota workspace up --dry-run, and ota workspace up

JSON output:

  • success: ok, path, written, mode, config, included, missing_contract
  • failure: ok, path, written, mode, error, optional next

ota workspace detect

Infer workspace contract shape and additive merge candidates.

ota workspace detect [PATH]
ota workspace detect --write [PATH]
ota workspace detect --dry-run [PATH]
ota workspace detect --merge [PATH]
ota workspace detect --merge --dry-run [PATH]
ota workspace detect --rewrite --dry-run [PATH]
ota workspace detect --rewrite --yes [PATH]
ota workspace detect --json [PATH]

Current behavior:

  • infers workspace repos by scanning common local repo roots (top-level plus containers like apps/, services/, repos/, packages/)
  • includes only repos that already have ota.yaml
  • skips candidate repos that do not yet have ota.yaml
  • default mode is preview
  • when no ota.workspace.yaml exists yet, preview-first onboarding is compare-first: review ota workspace detect --dry-run against ota workspace init --dry-run before ota workspace detect --write
  • --write writes ota.workspace.yaml only for first contract creation
  • --merge requires an existing ota.workspace.yaml and adds only missing discovered repo entries under repos
  • merge is additive-only and does not overwrite existing repo entries
  • --rewrite --dry-run previews full replacement of an existing ota.workspace.yaml
  • --rewrite --yes fully replaces existing ota.workspace.yaml with regenerated detected workspace contract
  • rewrite creates a timestamped backup file (ota.workspace.yaml.bak-<timestamp>) before writing
  • when no repo contracts are found, points to ota init <repo-path>, ota detect --dry-run <repo-path>, then back to ota workspace detect --dry-run and ota workspace init --dry-run before any workspace write
  • successful writes, merges, and rewrites now hand directly to ota workspace validate, ota workspace up --dry-run, and ota workspace up
  • supports JSON for machine-readable preview/write outcomes

ota workspace validate

Validate an ota workspace contract.

ota workspace validate [PATH]
ota workspace validate --json [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or an explicit directory boundary
  • parses the workspace contract
  • validates the workspace shape
  • validates each present referenced repo contract through the workspace contract
  • validates repos.<name>.workflow against the referenced repo contract when the workspace pins a non-default repo workflow
  • allows missing repo paths only when repos.<name>.source is declared

Text output:

  • header: WORKSPACE VALIDATE <path>
  • success: VALID plus next steps into ota workspace doctor, ota workspace up, and ota workspace tasks
  • failure: validation or load error text

JSON output:

  • success: ok, path, summary.error_count
  • failure: ok, path, summary.error_count, and either errors or error

ota workspace tasks

List workspace repo tasks in dependency order.

ota workspace tasks [PATH]
ota workspace tasks --json [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or an explicit directory boundary
  • validates workspace shape and present repo contracts
  • preserves workspace dependency order in output
  • lists non-internal task declarations for each acquired repo contract, including task descriptions and declared after_success, after_failure, and after_always hook relationships when the repo contract declares them
  • carries structured task launch additively when one repo task uses command or packaged-container launch instead of shell run / script
  • reports non-acquired repos with acquired: false and empty task lists
  • does not execute tasks

Text output:

  • header: WORKSPACE TASKS <path>
  • each repo includes required/optional status, acquisition status, dependency list, and task summaries

JSON output:

  • ok
  • path
  • summary with repo_count, acquired_count, and task_count
  • repos
  • each repo includes: name, path, contract_path, required, acquired, depends_on, tasks
  • each task includes: name, kind, optional description, one execution body field (run or script) or additive launch, depends_on, requires_services, requires_artifacts, after_success, after_failure, after_always

ota workspace list

List workspace repos, contract presence, and lightweight readiness status without running workspace doctor.

ota workspace list [PATH]
ota workspace list --status ready [PATH]
ota workspace list --status not-ready [PATH]
ota workspace list --repo <name> [PATH]
ota workspace list --json [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace shape for deterministic repo ordering
  • lists all declared repos (or filters by --repo / --status)
  • reports acquisition state per repo (ACQUIRED vs NOT ACQUIRED)
  • reports lightweight readiness status per repo (READY vs NOT READY), and when repos.<name>.workflow is declared the readiness lane follows that selected repo workflow instead of the repo default
  • shows execution metadata and env provenance when the repo contract declares it
  • reports contract presence per repo (contract_present)
  • for missing contracts in text output, embeds a repo-specific setup hint using ota init <repo-path>

Text output:

  • header: WORKSPACE LIST <path>
  • each repo includes required/optional status, acquisition status, readiness status, path, contract path state, dependencies, and execution metadata with env provenance when present
  • each repo shows acquisition on the summary line, readiness on a dedicated Status: line, and execution metadata in a compact Execution: block with env provenance when present

JSON output:

  • ok
  • path
  • summary mirroring the receipt summary with error_count, warn_count, info_count, and step_count
  • repos
  • each repo includes: name, path, contract_path, contract_present, required, acquired, status, depends_on

ota workspace execution plan

Inspect the resolved execution context for each workspace repo without running anything.

ota workspace execution plan [PATH]
ota workspace execution plan --json [PATH]
ota workspace execution plan --repo api [PATH]
ota workspace execution plan --mode container --ephemeral [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure and keeps repo ordering deterministic
  • when repos.<name>.workflow is declared, plans that selected repo workflow instead of silently assuming the repo default path
  • reuses the same per-repo backend validation boundary as ota execution plan
  • reports one execution plan per selected workspace repo
  • supports --repo filtering for focused inspection
  • supports --mode, --lifecycle, and --ephemeral overrides across the selected repos
  • fails the command when any selected repo cannot produce a runnable execution plan
  • still preserves each repo’s required/optional declaration in the report instead of flattening workspace metadata
  • never mutates repo state

Text output:

  • header: WORKSPACE EXECUTION PLAN <path>
  • status line: READY or NOT READY
  • optional Overrides section when backend or lifecycle is forced
  • each repo includes required/optional status, path, contract path, acquired state, optional selected Workflow / Task, and either resolved execution details or an honest Why / Next
  • when a repo contract loads, the report also includes the compact Contract block and declared Execution block for that repo
  • a final Summary block reports resolved and unresolved repo counts

JSON output:

  • ok
  • path
  • mode: "execution-plan"
  • summary with repo_count, resolved_count, unresolved_count, required_unresolved_count, not_acquired_count, and missing_contract_count
  • repos
  • each repo includes: name, path, contract_path, required, acquired, status, additive workflow and task when workflow planning resolves through a canonical path (including repo-default workflow selection), optional contract_identity, optional declared_execution, optional resolved, optional error, and optional next
  • overrides appears only when execution overrides are supplied

Current non-goals:

  • running setup or task commands
  • provisioning missing repos automatically
  • hiding unrunnable execution choices behind a synthetic success state
  • inventing one workspace-wide execution backend when repo contracts disagree

ota workspace run

Run one task across workspace repos in dependency order.

ota workspace run <task> [PATH]
ota workspace run <task> --json [PATH]
ota workspace run <task> --json --progress-json [PATH]
ota workspace run <task> --jobs 4 [PATH]
ota workspace run <task> --stream [PATH]
ota workspace run <task> [PATH] --base-url http://localhost:8080

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure and repo contracts
  • acquires missing repos declared with repos.<name>.source before execution
  • executes the task for each repo in workspace dependency order
  • can run independent repos concurrently when --jobs is greater than 1
  • blocks downstream repos when a dependency repo did not complete successfully
  • captures per-repo stdout/stderr in default mode
  • --stream opts into raw child output (text only, currently requires --jobs 1)
  • --json --progress-json emits live NDJSON workspace progress events on stderr while keeping the final workspace run JSON report on stdout
  • optional repo task failures do not fail the overall workspace status
  • task inputs are declared in tasks.<name>.inputs and are passed as --kebab-case value flags
  • task inputs are exposed to each repo task process as OTA_INPUT_<NAME> env variables
  • default values are applied when the caller omits an input
  • required: true makes an input mandatory unless a default exists
  • allowed limits the accepted values for that input
  • task inputs only apply to the targeted repo task, not its dependencies
  • if every declared input has a default, you can omit all input flags

Example:

tasks:
  api-automation-tests:
    inputs:
      base_url:
        default: http://localhost:8080
      suite_mode:
        default: standard
        allowed:
          - standard
          - contract-drift
  version:bump:
    inputs:
      version:
        required: true
ota workspace run api-automation-tests
ota workspace run api-automation-tests --base-url http://localhost:8080 --suite-mode contract-drift
ota workspace run version:bump --version 0.2.0
  • prints a summary in text output, emits an execution receipt when --receipt is set, and a receipt object in JSON output
  • the workspace receipt includes additive receipt.contract_identity with workspace name/type and compact workspace repo/policy counts

Text output:

  • header: WORKSPACE RUN <task> <path>
  • status line: READY or NOT READY
  • per-repo status includes required/optional, task name, findings, and optional exit details
  • after WORKSPACE RUN SUMMARY, ota appends the same receipt-backed Next: lane used by repo-level execution output when a safe follow-up exists

JSON output:

  • ok
  • path
  • task
  • summary
  • receipt
  • repos
  • each repo includes: name, path, contract_path, required, ok, status, task, findings, additive next / next_steps, and optional exit_code/stdout/stderr

ota workspace check

Run configured checks across workspace repos in dependency order.

ota workspace check [PATH]
ota workspace check --json [PATH]
ota workspace check --json --progress-json [PATH]
ota workspace check --jobs 4 [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure and referenced repo contracts
  • evaluates repo checks in workspace dependency order
  • when repos.<name>.workflow is declared, checks that selected repo workflow instead of assuming the repo default path
  • can check independent repos concurrently when --jobs is greater than 1
  • preserves deterministic repo ordering in text and JSON output even when checks run concurrently
  • downgrades findings for optional repos to warnings
  • --progress-json emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-check JSON report on stdout

Text output:

  • header: WORKSPACE CHECK <path>
  • status line: READY or NOT READY
  • summary roll-up includes repo verdict and agent verdict before the Overview count block at the bottom of the report
  • each repo includes required/optional status, contract path, and findings rendered through the shared grouped finding UX
  • when one repo has several findings, ota also surfaces that repo's primary next action before the grouped finding list so the operator does not have to choose the first move by hand
  • with --concise, repo Path/Contract and finding Why detail are omitted; summary + Next remain

JSON output:

  • ok
  • path
  • summary with repo_count, ready_count, not_ready_count, verdict, agent_verdict, error_count, warn_count, and info_count
  • summary.primary_blocker may be present with the workspace headline blocker, including the repo name that currently owns it
  • each repo may include additive primary_blocker with that repo's current highest-priority severity, summary, why, and next
  • summary.primary_blocker.code and repos[].primary_blocker.code are additive and only appear when the blocker comes from a finding with explicit stable identity
  • repos

ota workspace doctor

Diagnose workspace repo readiness from an ota workspace contract.

ota workspace doctor [PATH]
ota workspace doctor --json [PATH]
ota workspace doctor --jobs 4 [PATH]
ota workspace doctor --repo <name> [PATH]
ota workspace doctor --status ready|not-ready [PATH]
ota workspace doctor --severity error|warn|info [PATH]
ota workspace doctor --stream [PATH]
ota workspace doctor --json --progress-json [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure
  • evaluates repos in dependency order
  • can diagnose independent repos concurrently when --jobs is greater than 1
  • preserves deterministic repo ordering in text and JSON output even when diagnosis runs concurrently
  • evaluates each referenced repo through its own ota.yaml
  • when repos.<name>.workflow is declared, diagnoses that selected repo workflow instead of assuming the repo default path
  • reports missing-but-acquirable repos as not yet acquired
  • keeps workspace logic above repo diagnosis instead of duplicating it
  • downgrades findings for optional repos to warnings
  • rejects required repos that depend on optional repos
  • supports repo/status/severity filtering for focused diagnosis views
  • --stream is text-only and emits repo completion updates while the final report is being built
  • --progress-json emits the same live workspace progress events as compact NDJSON on stderr while preserving the final doctor JSON report on stdout

Text output:

  • header: WORKSPACE DOCTOR <path>
  • status line: READY or NOT READY
  • when the workspace is blocked, a primary blocker appears immediately under the readiness status
  • summary roll-up includes repo verdict and agent verdict before the Overview count block at the bottom of the report
  • each repo includes required/optional status, contract path, and findings rendered through the shared grouped finding UX
  • with --concise, repo Path/Contract and finding Why detail are omitted; summary + Next remain

JSON output:

  • ok
  • path
  • summary mirroring the workspace doctor roll-up with repo_count, ready_count, not_ready_count, verdict, agent_verdict, error_count, warn_count, and info_count
  • summary.primary_blocker may be present with the workspace headline blocker, including the repo name that currently owns it
  • each repo may include additive primary_blocker with that repo's current highest-priority severity, summary, why, and next
  • summary.primary_blocker.code and repos[].primary_blocker.code are additive and only appear when the blocker comes from a finding with explicit stable identity
  • repo execution metadata may include env provenance for inherited workspace policy values
  • repos

Current non-goals:

  • passing a repo URL directly on the CLI without a workspace contract

ota workspace explain

Explain workspace readiness findings as an ordered remediation plan.

ota workspace explain [PATH]
ota workspace explain --json [PATH]
ota workspace explain --repo api [PATH]

Current behavior:

  • diagnoses the workspace first
  • exposes one top-level ordered workspace plan before the per-repo drill-in
  • keeps the same grouped remediation actions and detailed steps under each repo
  • stays read-only and deterministic
  • prints a summary with repo and step counts at the end

Text output:

  • one top-level Plan section with explicit repo ownership for each grouped action
  • one section per workspace repo
  • ordered remediation Plan steps under each repo
  • an Overview count block at the end

JSON output:

  • success: ok, path, summary, top-level actions, and repos
  • each top-level action identifies the owning repo, path, contract_path, required, and the grouped action fields
  • each repo report includes summary, grouped actions, and detailed steps
  • failure: ok, path, and either errors or error

The summary object on success mirrors the top-level receipt summary and includes error_count, warn_count, info_count, and step_count.

ota workspace up

Prepare every repo in an ota workspace contract.

ota workspace up [PATH]
ota workspace up --json [PATH]
ota workspace up --json --progress-json [PATH]
ota workspace up --jobs 4 [PATH]
ota workspace up --quiet [PATH]
ota workspace up --stream [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure
  • clones missing repos declared with repos.<name>.source before repo-level prepare
  • runs the existing repo-level up flow for each referenced repo
  • when repos.<name>.workflow is declared, prepares that selected repo workflow instead of assuming the repo default workflow
  • can prepare independent repos concurrently when --jobs is greater than 1
  • respects declared workspace repo dependency order
  • blocks downstream repos when a dependency does not become ready
  • aggregates per-repo status, phase, findings, and exit details
  • captures repo child stdout and stderr per repo so text and JSON output remain deterministic
  • emits live repo progress on stderr in text mode so users can see queued/running/completed state while buffered output is still being collected
  • --json --progress-json emits the same live workspace progress as NDJSON on stderr while the final workspace-up JSON report remains on stdout; each event now carries additive phase and stage_family so machine consumers can classify acquisition vs prepare/result lanes without inferring from status text alone
  • --quiet suppresses live progress output and prints only the final workspace report
  • optional repo failures do not fail the overall workspace result
  • defaults to sequential execution because --jobs defaults to 1
  • --stream opts into raw live child process output instead of buffered per-repo output
  • --stream is text-only and currently requires --jobs 1
  • does not pull or update repos that already exist locally
  • prints a summary in text output, emits an execution receipt when --receipt is set, and a receipt object in JSON output
  • the workspace receipt includes additive receipt.contract_identity with workspace name/type and compact workspace repo/policy counts

Text output:

  • header: WORKSPACE UP <path>
  • status line: READY or NOT READY
  • each repo includes required/optional status, phase, findings, exit details, and captured stdout/stderr when present
  • after WORKSPACE UP SUMMARY, ota appends the same receipt-backed Next: lane used by repo-level execution output when a safe follow-up exists

JSON output:

  • ok
  • path
  • summary mirroring the workspace doctor roll-up with repo_count, ready_count, not_ready_count, error_count, warn_count, and info_count
  • receipt with additive next_steps when the workspace follow-up lane can be split into ordered machine-readable steps
  • each repo may include additive next and next_steps for that repo's current follow-up lane
  • repos

Current non-goals:

  • passing a repo URL directly on the CLI without a workspace contract
  • host or workstation provisioning beyond workspace bootstrap plus repo readiness
  • GitHub API integration or non-git acquisition modes

ota workspace refresh

Refresh existing repos in an ota workspace contract without cloning missing ones.

ota workspace refresh [PATH]
ota workspace refresh --json [PATH]
ota workspace refresh --json --progress-json [PATH]
ota workspace refresh --jobs 4 [PATH]
ota workspace refresh --dry-run [PATH]
ota workspace refresh --quiet [PATH]
ota workspace refresh --stream [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure
  • refreshes repos that already exist locally and have a declared source
  • leaves missing repos alone so ota workspace up remains the bootstrap path
  • can refresh independent repos concurrently when --jobs is greater than 1
  • respects declared workspace repo dependency order
  • blocks downstream repos when a dependency does not become ready
  • aggregates per-repo status, phase, findings, and exit details
  • captures repo child stdout and stderr per repo so text and JSON output remain deterministic
  • emits live repo progress on stderr in text mode so users can see queued/running/completed state while buffered output is still being collected
  • --json --progress-json emits the same live workspace progress as NDJSON on stderr while the final workspace-refresh JSON report remains on stdout; each event now carries additive phase and stage_family so machine consumers can distinguish refresh/setup progress from generic status strings
  • --quiet suppresses live progress output and prints only the final workspace report
  • optional repo failures do not fail the overall workspace result
  • defaults to sequential execution because --jobs defaults to 1
  • --stream opts into raw live child process output instead of buffered per-repo output
  • --stream is text-only and currently requires --jobs 1
  • --dry-run previews the refresh commands without changing repo state
  • --force force-fetches and hard-resets refreshed repos to the declared source or --ref override
  • --prune prunes stale remote-tracking refs during refresh
  • --ref <branch|tag|sha> overrides the source ref used for refresh
  • refresh target precedence is: explicit --ref, then declared source.ref, then the repo's current upstream branch
  • when none of those targets exist, ota refuses before preview or apply instead of falling through to a vague git pull failure
  • refresh failures now distinguish wrong remote target (source.ref / --ref), remote access/auth problems, and generic local git-state failures so the follow-up lane stays specific
  • prints a summary in text output, emits an execution receipt when --receipt is set, and a receipt object in JSON output

Text output:

  • header: WORKSPACE REFRESH <path> or WORKSPACE REFRESH PREVIEW <path> for --dry-run
  • preview mode prints Mode: dry-run (no write)
  • status line: READY, NOT READY, or NOT ACQUIRED for normal refresh; preview mode does not claim readiness
  • each repo includes required/optional status, phase, findings, exit details, and captured stdout/stderr when present

JSON output:

  • ok
  • path
  • mode: refresh for normal refresh, preview for --dry-run
  • summary from the shared workspace execution receipt shape, always including error_count, warn_count, info_count, and step_count, and optionally including repo_count, ready_count, and not_ready_count when ota recorded the workspace roll-up
  • receipt
  • repos

Current non-goals:

  • cloning missing repos
  • passing a repo URL directly on the CLI without a workspace contract
  • host or workstation provisioning beyond workspace bootstrap plus repo readiness
  • GitHub API integration or non-git acquisition modes

ota workspace diff

Compare local workspace repos against their declared source state without mutating anything.

ota workspace diff [PATH]
ota workspace diff --json [PATH]
ota workspace diff --json --progress-json [PATH]
ota workspace diff --jobs 4 [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure
  • compares each acquired repo’s local git state against the declared source ref or upstream branch
  • reports MATCH when a repo is clean and aligned, DIRTY when the worktree has local changes, DIFFERENT when commit counts differ, MISSING when the repo is absent, and UNRESOLVED when git state cannot be compared safely
  • can compare independent repos concurrently when --jobs is greater than 1
  • never mutates repo state
  • --json returns a workspace diff roll-up with mode: "diff"
  • --progress-json emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-diff JSON report on stdout; those events use status for the repo diff verdict such as MATCH, DIRTY, or UNRESOLVED, and tail for the machine drift_kind
  • text and JSON now carry an additive top-level lifecycle next lane when ota can name the safest refresh or acquisition follow-up directly
  • text output now makes the comparison provenance explicit on each Target: line when ota is using declared source.ref versus upstream-branch fallback
  • when drift is being compared against upstream-branch fallback instead of declared source.ref, the repo-level follow-up lane now says that explicitly and suggests declaring source.ref when the workspace should own the target
  • per-repo JSON items can also carry additive next and next_steps so automation can read the repo-owned follow-up lane without reparsing findings
  • per-repo JSON also carries additive drift_kind so automation can distinguish local dirtiness, commit divergence, missing repo, missing contract, target ambiguity, and unresolved comparison directly
  • per-repo JSON also carries additive target_source so automation can tell whether the comparison target came from declared source.ref or from the repo's upstream branch
  • text and JSON summaries now also break the collapsed Missing and Unresolved buckets into explicit missing-contract and target-unavailable subcounts when present
  • differences do not fail the command; the command succeeds and surfaces drift in the report

Current non-goals:

  • refreshing or mutating repo state
  • cloning missing repos automatically

ota workspace status

Compact workspace status combines readiness and drift without mutating repo state.

ota workspace status [PATH]
ota workspace status --json [PATH]
ota workspace status --json --progress-json [PATH]
ota workspace status --jobs 4 [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure
  • reads repo readiness and local git drift for each workspace repo
  • when repos.<name>.workflow is declared, readiness is evaluated through that selected repo workflow
  • reports readiness and drift together so you can scan one operational summary
  • can compare independent repos concurrently when --jobs is greater than 1
  • never mutates repo state
  • --json returns a workspace status roll-up with mode: "status"
  • --progress-json emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-status JSON report on stdout; each event uses tail for the repo drift state such as MATCH, DIRTY, or UNRESOLVED, and now also publishes additive phase and stage_family so machines can tell this is a status/receipt reporting lane without recovering that intent from command plus status text alone
  • text and JSON now carry an additive top-level lifecycle next lane when ota can name the safest doctor, refresh, or acquisition follow-up directly
  • text output now makes the comparison provenance explicit on each Target: line when ota is using declared source.ref versus upstream-branch fallback
  • when drift is being compared against upstream-branch fallback instead of declared source.ref, the repo-level follow-up lane now says that explicitly and suggests declaring source.ref when the workspace should own the target
  • per-repo JSON items can also carry additive next and next_steps so automation can read the repo-owned follow-up lane without reparsing findings
  • per-repo JSON also carries additive drift_kind so automation can distinguish local dirtiness, commit divergence, missing repo, missing contract, target ambiguity, and unresolved comparison directly
  • per-repo JSON also carries additive target_source so automation can tell whether the comparison target came from declared source.ref or from the repo's upstream branch
  • text and JSON summaries now also break the collapsed Missing and Unresolved buckets into explicit missing-contract and target-unavailable subcounts when present
  • readiness findings and drift findings are surfaced in the same report

Text output:

  • header: WORKSPACE STATUS <path>
  • each repo includes required/optional status, combined readiness and drift status, path, contract path, source metadata, and local git comparison details when present
  • a summary block reports readiness and drift roll-ups in one place

JSON output:

  • ok
  • path
  • mode: "status"
  • summary with readiness counts and drift counts
  • repos
  • each repo includes: name, path, contract_path, required, acquired, ready, readiness_status, drift_status, branch, head, target_ref, ahead, behind, dirty, and findings

Current non-goals:

  • mutating repo state
  • cloning missing repos automatically
  • cross-repo dependency scheduling
  • passing a repo URL directly on the CLI without a workspace contract
  • host or workstation provisioning
  • a workspace-only bootstrap engine that bypasses repo contracts
  • GitHub API integration or non-git acquisition modes

ota workspace receipt

Capture the current workspace scan as a read-only receipt artifact for CI or archival use.

ota workspace receipt [PATH]
ota workspace receipt --json [PATH]
ota workspace receipt --json --progress-json [PATH]
ota workspace receipt --jobs 4 [PATH]
ota workspace receipt --archive [PATH]

Current behavior:

  • resolves ota.workspace.yaml using --file, OTA_FILE, or upward discovery
  • validates workspace structure
  • reads repo readiness and local git drift for each workspace repo without mutating anything
  • captures the combined workspace state as an execution receipt with one step per repo
  • can inspect independent repos concurrently when --jobs is greater than 1
  • never clones, fetches, resets, or writes repo state
  • --json returns a workspace receipt roll-up with mode: "receipt"
  • --progress-json emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-receipt JSON report on stdout; because receipt reuses the same scan as workspace status, those events use tail for the repo drift state such as MATCH, DIRTY, or UNRESOLVED, and now also publish additive phase and stage_family so machine consumers can keep receipt/status reporting semantics separate from prepare or task-execution progress
  • the workspace receipt includes additive receipt.contract_identity with workspace name/type and compact workspace repo/policy counts
  • --archive writes the JSON receipt to .ota/receipts and keeps the newest 50 archives
  • the receipt records the same readiness, drift, and findings scan so CI or agents can archive it deterministically

Text output:

  • header: WORKSPACE RECEIPT <path>
  • each receipt step shows the repo name, readiness status, and drift status
  • the summary block mirrors the execution receipt counts

JSON output:

  • ok
  • path
  • mode: "receipt"
  • archive_path (when --archive is set)
  • summary mirroring the receipt summary with repo_count, ready_count, not_ready_count, error_count, warn_count, info_count, and step_count
  • receipt
  • repos

Current non-goals:

  • mutating repo state
  • cloning missing repos automatically
  • cross-repo dependency scheduling
  • passing a repo URL directly on the CLI without a workspace contract
  • host or workstation provisioning