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.
Recommended onboarding flow
ota doctor- if the repo does not yet have
ota.yaml, preview withota detect --dry-run . - review the first write with
ota init --dry-run . - choose an explicit first write with
ota init .orota detect --write . ota validateota up --dry-runota upota run <task>orota 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 doctorota explainota upota run <task>ota initota envota execution planota execution topologyota proof runtimeota ci projectionota ci github renderota ci github checkota ci github syncota assist declare-readinessota assist declare-serviceota assist bind-taskota assist declare-envota assist add-taskota assist wire-setupota assist normalizeota detectota validateota tasksota workflowsota servicesota diffota checkota annotationsota json validateota agentsota cleanota extensionsota policyota policy initota policy reviewota uninstallota self-update/ota upgradeota skills installota workspace initota workspace detectota workspace validateota workspace tasksota workspace listota workspace execution planota workspace run <task>ota workspace checkota workspace doctorota workspace explainota workspace upota workspace refreshota workspace diffota workspace statusota 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
PATHis treated as the contract boundary - upward discovery from the current directory when no
PATHis 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--debugemits command-phase tracing to stderr
Current progress behavior:
- quiet blocking commands show a delayed spinner in interactive terminals
ota doctorandota checkkeep their own check/progress handlingota runkeeps streaming/progress-focused behavior instead of the shared spinnerota upuses the shared spinner by default;ota up --streamopts into raw live provisioning, service-start, and setup outputota workspace doctoruses the shared spinnerota workspace statususes the shared spinnerota workspace doctor --jsonstill uses the shared spinner on stderr in interactive terminals, while stdout remains valid JSONota workspace doctor --json --progress-jsonswitches that doctor lane from spinner updates to live NDJSON workspace progress on stderr while stdout remains the final JSON reportota workspace status --json --progress-jsonswitches 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 intailota workspace list --jsonalso uses the shared spinner on stderr in interactive terminals, while stdout remains valid JSONota workspace validate,ota workspace tasks,ota workspace list,ota workspace detect, andota workspace inituse 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 --jsonandota doctor --jsonfor repo gating - use
ota workspace validate --json,ota workspace doctor --json, andota workspace explain --jsonfor workspace gating and remediation planning ota validate --jsonkeeps legacywarnings[]strings and also exposes additivewarning_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 --jsonandota workspace list --jsonfor 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 diagnosis1: invalid contract, blocking readiness issue, protected write failure, or general command failure2: CLI usage or argument parsing errorota run: preserves child task exit codes on task failureota 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, andota explain
ota validate
Validate an ota contract.
ota validate [PATH]
ota validate --json [PATH]
ota validate --member api [PATH]
Current behavior:
-
resolves
ota.yamlusing--file,OTA_FILE, or an explicit directory boundary -
when
--memberis set, loads the root contract, merges the declared member override, and validates the merged contract -
when a root contract declares
workspace.type: monorepo,ota validatealso 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_oncrossing 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
0on success and non-zero on failure
Text output:
- success:
VALID <path>followed by next-step guidance intoota doctorandota tasks --use - failure: validation or load error text
JSON output:
- success:
ok,path,summary.error_count,summary.warn_count, andwarnings - failure:
ok,path,summary.error_count,summary.warn_count,warnings, and eithererrorsorerror
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, orfailed; use it withproof_scopeandnot_provedso a narrow green proof is not misread as broader repo completion - validates the contract first
- when a root contract declares
workspace.type: monorepo, plainota taskslists root tasks and grouped summaries for each declared member - when
--memberis set, lists tasks from the merged member contract - repeated
--membervalues list tasks for those members in the provided order - hides
internal: truetasks by default and includes them only when--allis set - prints tasks in deterministic order
- resolves the execution form for the current OS
- renders structured
launchsources additively when a task uses command or packaged-container launch instead of shellrun/script - includes task metadata when present
- includes task
envandinputswhen present - includes declared task
effectswhen present so repo mutation, network dependency, and out-of-repo state changes stay explicit - includes task
descriptionand optionalnoteswhen present, wherenotescarries purpose and extra guidance - includes an
agentsummary when the contract declares one - includes variant summaries when variants are declared
--useexpands 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; plainota tasksremains the full declaration view for dependencies, services, conditions, hooks, notes, and related metadata--safeand--unsafeare mutually exclusive filters over the effective safe set (safe_for_agent: trueplusagent.safe_tasks)--via <native|container>filters to tasks runnable through the selected backend lane--allincludes orchestration tasks markedinternal: true; those entries carryinternal: truein JSON output- task JSON keeps
use.humananduse.agentas selected-mode compatibility projections, whileuse.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
AGENTblock is grouped intoOverview,Execution, andBoundarysections 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
Launchwhen the resolved execution source is structuredlaunch - each task may include
Preparewhen the resolved execution source is a first-classpreparebody, including orderedprepare.kind: sequencesummaries - each task may include
env,inputs,effects,requires_services, andrequires_artifacts; generated artifact producer/consumer truth remains declared under top-levelartifacts - each task may include
DescriptionandNotes, whereNotescan describe purpose and usage - each task includes
Human Run, with every advertised mode in stableContainer,Native, thenRemoteorder; 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
ContainerandNativeremain visible when unavailable, with an explicit unsupported status instead of a fabricated command or silent omission- each task includes
Agent Run; it printsota run <task> --agentonly 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 declaredsafe_for_agenttruth from the effective closure-aware decision - each task includes a short execution preview
Common operator lanes:
ota tasks --safe: show only effective agent-safe tasksota tasks --unsafe: show only non-safe tasksota tasks --safe --use: show safe tasks plus canonical run commands, safety posture, and machine-readable follow-up commandsota tasks --unsafe --use: show non-safe tasks plus canonical run commands, review posture, and machine-readable follow-up commandsota tasks --via native: filter to tasks runnable through native executionota tasks --via container: filter to tasks runnable through container execution
Agent discoverability:
ota tasks --safe --useis the canonical task-discovery surface before agent execution- use it to find the effective safe task surface, the canonical
ota run <task> --agentcommand, 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> --agentwhen the execution itself should enforce the declared agent-safe boundary
JSON output:
- success:
ok,path,tasks agentis included when the contract declares agent guidance- monorepo root summaries include grouped per-member results in
members - repeated
--membervalues return grouped per-member results inmembers - each task includes the resolved execution plus optional
selected_variant_osandvariants - each task may include additive
effectswith declaredwrites,workspace_writes,network, andexternal_state - each task may include additive
prepare; sequence prepares keep a nestedsteps[]tree, while dependency-hydration prepares exposemedium,source_kind,cwd,manager, andmode - each task may include additive
launchwhen the resolved execution source is structured command or packaged-container launch - failure:
ok,path, and eithererrorsorerror
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, plainota workflowslists root workflows and grouped summaries for each declared member - when
--memberis set, lists workflows from the merged member contract - repeated
--membervalues 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, andota execution planwould 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>andProof: ota proof runtime --workflow <name> - workflow
servicessummaries include bothworkflows.<name>.services.requiredand transitive task-levelrequires_servicesfrom 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-entrydefaultflag - when no workflows are declared, the text output says so explicitly and points users back to
ota tasksor contract authoring instead of ending empty
JSON output:
- success:
ok,path,default,workflows - monorepo root summaries include grouped per-member results in
members - repeated
--membervalues return grouped per-member results inmembers - each workflow includes the resolved workflow summary plus a
defaultboolean - failure:
ok,path, and eithererrorsorerror
Agent workflow discoverability:
- use
ota workflows --jsonwhen you need machine-readable workflow posture before selecting one - use
ota tasks --safe --usealongside 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, plainota serviceslists root services and grouped summaries for each declared member - when
--memberis set, lists services from the merged member contract - repeated
--membervalues 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 doctorandota 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 doctoror contract authoring instead of ending empty
JSON output:
- success:
ok,path,services - monorepo root summaries include grouped per-member results in
members - repeated
--membervalues return grouped per-member results inmembers - failure:
ok,path, and eithererrorsorerror
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
--memberis set, inspects the merged member contract - when
--workflowis set, inspects the selected workflow instead of assumingworkflows.default - when the selected workflow declares
env.profile, env resolution prepends that workflow-owned profile truth before reporting values - when
--taskis set, includes the effective execution env for that task alongside the contract env view - when
--taskis set, anytasks.<name>.requirements.enventries are treated as required for that selected task view even when the same top-levelenv.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, andtomlenv 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/Profilelines, a short overview, aDeclared env sourcessection when sources exist, aRendered env artifactssection when the selected workflow profile materializes files, and separateContract env/Execution envsections 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, andNext - each declared source may include
kind,path,label,must_exist,status,detail, andNext - each rendered env artifact may include
path,kind,exists, andincludes - 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, optionalprofile, and optionalrendered_artifacts - success with task scope also includes
task summaryincludes contract, declared-source, task, resolved, missing, and invalid counts- failure:
ok,path,taskandworkflowwhen relevant, anderror
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
--memberis set, inspects the merged member contract - when
--workflowis set, plans the selected workflow instead of assumingworkflows.default - when the selected workflow declares
run.task, planning resolves that canonical runtime path first; if the workflow only declaressetup.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 executiontask - reuses the same backend and lifecycle resolution path as
ota runandota 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
Workflowsection when the repo declares workflows and planning targets one explicitly Resolvedsection with selected backend, lifecycle, image, engine candidates, target, and target strategy- selected
Taskwhen a workflow run task, or setup-only fallback, is the concrete planning source;prepareremains additive workflow context, not runtime identity Contractsection with the same compact contract identity used by receiptsExecutionsection when the contract declares execution intentOverridessection when--mode,--lifecycle, or--ephemeralchanged the resolved result
JSON output:
- success:
ok,path,contract,memberwhen relevant, additiveworkflowandtaskwhen workflow planning selected a canonical path,contract_identity,declared_execution, optionalworkflow_env_artifacts,resolved, andoverrides workflow_env_artifactsreports workflow-owned rendered env files such as rendered dotenv artifacts, including theirpath,kind,profile, currentexistsstate, included env names, and consuming task/service lanes- failure:
ok,path,memberwhen relevant, and eithererrorsorerror
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
--memberis 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> Overviewsection with project and topology countsExecutionsection when the contract declares execution intentShared Backends,Readiness Probes,Surfaces,Services, andTaskssections with runtime/listener/launch/target detail when present
JSON output:
- success:
ok,path,contract,memberwhen relevant,contract_identity,declared_execution,shared_backends,readiness_probes,surfaces,services, andtasks - top-level
readiness_probesentries include literal URL or target source details plus the declared HTTP/TCP request contract fields that belong to that probe - top-level
surfacesentries include declared surface kind, port, optional label, optional purpose, optional visibility, optional path, and optional readiness contract - task entries may include additive
launchwhen the task uses structured command or container launch - task runtime entries may include
backend_binding,readiness,attached_surfaces, additivesurface_attachments, and normalizedlisteners - task target entries may include
activation_mode,override_input,url, and typedservicereferences - failure:
ok,path,memberwhen relevant, and eithererrorsorerror
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
--serviceoutside 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: falseplus the hard-pin and policy evidence - refuses with
replay_input_policy_unavailablebefore 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_terminatedinstead 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_boundariesfor a successful transaction because lifecycle state transitions do not prove broader application output or repo completion; lifecycle proof never emits a barepassed --archivewrites 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 beforeboundary_terminatedis 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 carriesexecution_started: falseplus the hard-pin and policy evidence - refuses with
replay_input_policy_unavailablebefore proof artifacts or child execution when the active policy source cannot be loaded - when
--memberis set, proves the merged member contract from the monorepo root - when
--workflowis 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--memberis set):topology.jsondoctor.jsonup.log
- reuses the existing execution and diagnosis boundaries instead of inventing a parallel proof
implementation: the topology artifact is the same
ota execution topology --jsonsurface, the doctor artifact is the sameota doctor --jsonsurface, and the up log is the repo-level preparation report for the selected path - supports
--archivewith--jsonto 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 explicitreplay_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--archiverecord 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, andretries - 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 as90s,5m, or1h - 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 tofault_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 becomesexerciseddependency evidence - renders
Dependency Evidencebefore 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 doctorandota 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> WorkflowandModelines first- success output includes a compact
Stepssection, finalREADYline, andArtifactspaths - blocked output keeps the shared
NOT READY/BLOCKEDstatus language, shows the failing proof phase, and surfaces one primaryWhy/Nextlane without duplicating the full doctor report - blocked proof output may also include
Likely causewhen ota can derive a higher-confidence runtime-drift hint from captured proof logs - timeout output uses
TIMEOUTwhen runtime-proof wait budget is exhausted and keeps the proof phase atreadiness - interruption output uses
INTERRUPTEDand phaseinterruptedwhen runtime proof is terminated by a signal (for example CI cancellation)
JSON output:
- success or blocked proof output:
ok,path,mode, optionalworkflow,phase, sharedsummary, optionalartifacts, optionalworkflow_env_artifacts, optional advisorylikely_cause, and optional cleanuperror/next workflow_env_artifactsuses the same rendered-artifact summary asota execution plan, so CI and agents can see which workflow-owned env file was materialized and which task/service lanes consume it during proof--archiveaddsarchive.identityandarchive.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 --jsonpublishes a workflowclaim_assurance[]proof_breadthrecord. It issupportedonly when a content-addressed proof archive matches the current semantic contract, clean source identity, resolved execution scope, andreplay_posture: witness_only; a matching failed proof iscontradicted, while missing, stale, source-mismatched, or scope-mismatched evidence remainsunknown - 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:
renderis pure: it prints deterministic reusable-workflow YAML and never writes the optional--outputtarget.ota ci projectionis 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.--modeselects its native, container, or remote plane; provider adapters consume that exact projection rather than reconstructing contract authority.- The projection carries
run_executionasfinite_taskorservice_runtime. Generated adapters prepare every lane withota up; they execute a finite run task throughota run --agentafter preparation, while service and proof lanes retain their one authoritative runtime path. - Omitting
--moderesolves the selected task's effective contract default.--target-osis 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/setupwithsource: contract, provider-adapter setup for contract-required toolchains, selectedota validate,ota doctor --workflow, safe-surface discovery, agent dry-run, and either agent execution plus receipt archival or, for a proof-required lane, one authoritativeota proof runtimeexecution. Each declared, uniqueagent.refusal_canariescontrol renders as its own provider check using Ota's--expect-refusalexit semantics rather than provider-shell assertions. Each check maps one stable canonical V11.5merge_check_idto a scope-qualified provider name such asota.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_identityandota_target_os. It may also chooseota_runner; the renderer's--runnervalue 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 selectedexecution_scopes. The GitHub adapter provisions onlynative-scoped toolchains; container and remote scopes remain owned by their declared execution boundaries. It renders immutableactions/setup-goforsource: go, translating a supported lower-bound range (for example>=1.26or>=1.26,<1.27) into Go's release selector (1.26), and immutableactions/setup-nodefortoolchains.nodefulfilled throughsource: corepack, preserving the declared Node semver selector while disabling setup-node's provider cache until Ota activates the declared package manager, immutableruby/setup-rubyfortoolchains.rubyfulfilled throughsource: ruby, immutableactions/setup-pythonplusastral-sh/setup-uvfortoolchains.pythonfulfilled throughsource: uv, and immutableactions/setup-dotnetfortoolchains.dotnetfulfilled throughsource: 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. checkfails for a missing, stale, manually changed, or unowned generated workflow, or when the caller points at a different semantic projection.syncis 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.--outputand--callermust be normalized repo-relative.ymlor.yamlpaths 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
--writeapplies the proposed readiness mutation and revalidates the updated contract before returning success- supports
--taskfortasks.<name>.runtime.readiness - supports
--serviceforservices.<name>.readiness - supports
--memberthrough the existing merged monorepo contract path while writing only to the selected member overlay file - supports
spring-http,http, andtcpstyles for task readiness - supports
spring-http,http,tcp, andcompose-healthstyles for managed service readiness --style compose-healthis 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
--styleunless 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
--jsonemits 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
--writeapplies the proposed service mutation and revalidates the updated contract before returning success--nameis required and identifies the managed service block underservices--manager compose|hostchooses 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-ownedservices.<name>.producerblock when the service is canonically owned by another repo in the sameota.workspace.yaml--endpoint,--address, and--portcontrol the selected endpoint projection; when safe, ota defaults the endpoint tohostand the address to127.0.0.1--required true|falsesets the service requirement flag explicitly--style spring-http|http|tcpadds or replaces structured readiness anchored to the selected endpoint--style compose-healthis valid for compose-managed services and anchors readiness to compose container health state (readiness.kind: compose_health)--compose-file,--compose-service, and--manager-namerefine 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.nametolocalandmanager.serviceto the declared service name when those values are otherwise absent - supports
--memberthrough 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
--jsonemits 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
--writeapplies the proposedtasks.<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-viewand--activationrefine the shipped target contract directly instead of hiding those fields behind heuristics- preserves an existing
override_inputunless 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
--jsonemits 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
--writeapplies the proposed env mutation and revalidates it before returning success- root env requirements target
env.vars.<NAME>withrequired,secret,default,allowed,prepend, andappend - declared env sources target one curated
env.sources[]entry withkind,path, and optionalmust_exist - task-local env targets only one explicit
tasks.<name>.env.<KEY> = <value>write prependandappendare allowed only forPATHsecret: truemay not be combined with a new default value- supports
--memberthrough the merged monorepo contract path while writing only to the selected member overlay file --jsonemits 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 --writeapplies 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, andsandboxtask kinds - requires
--runor--scriptfor every kind exceptsandbox, which uses the bounded starter bodyecho sandboxwhen no body is supplied --kind setuponly applies to the canonical--name setuptask and defaultsinternal: truewhen you do not override it--kind servicerequires--listener,--protocol, and--port, and currently declares one fixed listener plus a matching fixed host projection without adding readiness- supports
--memberthrough the merged monorepo contract path while writing only to the selected member overlay file - refuses service-only listener inputs on non-service task kinds
--jsonemits 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.setupblock, and the next validation commands --writeapplies 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-towrites a cross-platformaction.kind: copy_if_missingsetup task for file-template setup such as.env.exampleto.env--service <name>setssetup.requires_servicesin the provided order as the pre-setup service phase--clear-servicesremovessetup.requires_services--internal true|falserefinestasks.setup.internaldirectly- supports
--memberthrough the existing merged monorepo contract path while writing only to the selected member overlay file - preserves unrelated existing
tasks.setupfields 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
--jsonemits 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.setupblock, and the next validation commands --writeapplies 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 undertasks.setup - normalizes the moved task to
internal: trueso setup stays anota upsupport task by default - supports
--memberonly 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.setupalready exists, when the selected task does not exist, or when the selected task is alreadysetup --jsonemits 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
0when the comparison succeeds, even if differences exist - surfaces load and parse errors clearly
- public operator guide:
Semantic Snapshots and Correlationfor 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> MATCHorDIFFERENT- explicit
Inputsoverview 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 additivecategoryandrisk- policy-section changes may include
provenance - failure:
ok,base,target, anderror
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:
Plansection with ordered remediation stepsWhyandNextlines for each stepProvenancelines when ota can trace the diagnosis source for that stepOverviewcounts at the end
JSON output:
- success:
ok,path,summary,actions,steps actionsis the ordered grouped remediation plan; each action includesorder,action_key,action_title,severity,count,why, andnextactionsmay also include sharedprovenancewhen the grouped action maps back to one diagnosis sourcestepskeeps the finding-level detail; each step includesorder,code,severity,summary,why, andnextsteps[].codeis 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
provenanceandprovenance_key - failure:
ok,path, anderror
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_blockeris present - does not repeat that same primary blocker as a second finding line
- emits one line per remaining finding
- ignores
finding_groupsand stays one-annotation-per-finding by default - maps
severity: errorto::errororERRORand all other severities to::warningorWARNING --format markdownrenders a compact summary block for step summaries or PR comments with status, counts, the primary blocker when present, and remaining findings--mode receipt-diffexpectsota receipt --json --baseline ...diff output and currently supports--format markdownonly- scopes workspace findings with the repo name and path so annotations stay actionable
- labels additive
Code:,Provenance:, andNext: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: ..., orNOTICE: ...for primary blockers depending on their severityERROR: ...andWARNING: ...for findings- primary-blocker and finding annotations append
Code: <stable-code>when the input JSON includes one - markdown output uses
## <title>,Status,Counts, optionalPrimary blocker, andFindingssections instead of one-line annotations - receipt-diff markdown output uses
Baseline source,Compare,Drift,Counts, optionalGate, optionalPrimary blocker, and compactIntroduced/Resolvedsections
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 to0when omitted - in
--inputmode, uses synthetic exit code0for exit-map assertions - when
--write-payloadis set, writes the captured/read payload before validation and assertions - validates payload shape against schemas under
docs/spec/json-schemas/, including relative$refresolution 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, plainota extensionslists root descriptors and grouped member results for each declared member - when
--memberis set, lists descriptors from the merged member contract - repeated
--membervalues 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 contextota extensions --publish <name>executes one explicitly named, allowlistedexport_providerdescriptor in the current repo or member context- execution currently accepts
kind: check_providerdescriptors withapi_version: 1 - execution currently accepts
kind: export_providerdescriptors withapi_version: 1 - execution also accepts
kind: backend_providerdescriptors for remote execution when named byexecution.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_JSONfor 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, andconfig - 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 doctoror addingextensionsto the contract
JSON output:
- success:
ok,path,extensions - monorepo root summaries include grouped per-member results in
members - repeated
--membervalues return grouped per-member results inmembers --run <name>returns the executed descriptor,exit_code, and capturedstdout/stderr--publish <name>returns the executed descriptor,exit_code, and capturedstdout/stderr- failure:
ok,path, and eithererrorsorerror
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
--memberis set, resolves the merged member contract from the monorepo root - repeated
--membervalues run the task across those members in the provided order --mode,--lifecycle, and--ephemeralcan 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/--persistentrather 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-depsis a local execution override that skipstasks.<name>.depends_onfor the requested task only--skip-depsis rejected when the requested task has no declareddepends_on--agentenforces 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 derivescrossing_requiredfor 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 arenetwork,network:broad,network:dependency_hydration,network:container_image_hydration,network:service_readiness,network:integration_test,network:tool_bootstrap, andexternal_state:<token>- task inputs are declared in
tasks.<name>.inputsand are passed as--kebab-case valueflags - when a task input overlaps an ota command flag name such as
modeorjobs, 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 defaultvalues are applied when the caller omits an inputrequired: truemakes an input mandatory unless a default existsallowedlimits 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-runis 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_identityreturns the typedreplay_input_identity_missingorreplay_input_identity_mismatchpreflight 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'sevaluated_inputs[] - when the active org policy declares
policies.replay_inputs.identity, dry-run and Doctor also publish the canonicalreplay_input_policydecision.ota runandota uprefuse before task startup, dependency hydration, or native prerequisite provisioning ondenyorreview; 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-runprintsRUN PREVIEW, uses the execution-preview vocabularyRUNNABLE/RUNNABLE WITH WARNINGS/BLOCKED, and showsMode: dry-run (no write)plus the selected execution path, requirements, and planned actions- repo-level
--jsonnormally requires--dry-run; the one execution-free exception is a contract-declared refusal canary:ota run --agent --expect-refusal --json <task>emits the runner-authoredrefused_as_expectedorrefusal_not_observedresult without starting the task - preview JSON includes additive
governance, a compact selected-lane summary withsafety_posture,review_required, closure-aware effective safety, effectivedefault_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
--streamforces 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-refusalis the negative-control form of that same boundary. It requires--agentand a matchingagent.refusal_canariestarget; a refusal is exit0, while admission is exit1. It never accepts task inputs or starts the selected lane.- when the requested task is a service runtime with declared readiness,
--streamalso 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/retriesbudget 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 planfirst 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 standaloneruntimesortools - toolchain run-path fulfillment is opt-in:
toolchains.<name>.fulfillment.mode: nonekeepsota runon diagnosis/check-only behavior, whilefulfillment.mode: runlets 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 --jsonexposes additivetoolchains[]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
fulfilledplus additivecommands[] - on failure, text output keeps
WhyandNextfirst, then appends a compactRUN SUMMARYblock withStatusfirst 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
--streamrerun hint before the compactRUN SUMMARY - on
Ctrl-Cduring 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_pidwhen 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.composerecreates 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.jsoninstead 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_pathsentry 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, andservice_taskinstead 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 existingblocked[]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-depsis 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 SUMMARYblock withStatusfirst for quick scanning, followed by the selected mode, container image when relevant, target when one exists, and task --receiptadds the full execution receipt when you need the detailed trail- when replay-input policy refuses admission,
--receiptstill emits the blocked preconditions receipt with policy decision, coverage, rule identity, typed reasons, and observed input posture; genericota receiptdoes not reconstruct that earlier decision - replay admission includes recursive
after_success,after_failure, andafter_alwayshook closures before the parent task starts; an invalid active policy source refuses with typedreplay_input_policy_unavailableevidence 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.checksis declared, ota evaluates those checks before dependency/service startup and skips the task when any condition check fails or times out when.checkssupports declaredprecondition(withrun),file, andchanged_fileschecks; probe-driven checks are not valid execution conditions--skip-depssuppresses 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_successhooks in declared order - if the task body exits with a failure, runs
after_failurehooks in declared order - runs
after_alwayshooks after either outcome when the task body was actually attempted - hook task failures affect the final
ota runexit code for the parent task - resolves the best matching task variant for the current OS when variants are declared
- executes either
runorscript - supports mode-aware task branches under
tasks.<name>.execution.modes - when
tasks.<name>.execution.default_modeis declared and--modeis omitted,ota runuses that mode as the default execution plane - when
--modeis set,ota runuses the matching mode branch for the task - if the selected mode has no declared task branch,
ota runfalls back to the task-level execution body and task-level execution settings - selected mode branches can override task
context,lifecycle,env,run/script, andruntime - resolves task execution backend from:
tasks.<name>.execution.default_modewhen set- selected mode branch context when
tasks.<name>.execution.modes.<mode>.contextis set tasks.<name>.contextwhen setexecution.default_context- legacy
execution.preferred/execution.backends
- for container tasks, runs through the first available configured container engine CLI, falling back to
dockerwhen no engines are listed - for container tasks,
execution.lifecycle: ephemeraluses a fresh container - for container tasks,
execution.lifecycle: persistentreconciles 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
providerandtarget - current shipped remote providers are
daytona,ssh,tsh, andkubectl - remote target guidance:
daytona:sandbox-devssh/tsh:user@hostkubectl:pod/ota-dev- passes
execution.backends.remote.cwdto 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.nativeentries withactivation.kind, ota resolves the declared native activation environment first; Windowsvisual_studio_dev_shelland genericcommandactivations both apply to the real task body instead of assuming the current terminal was already activated correctly - when
tasks.<name>.runtime.listenersdeclare host projection, ota also injects runtime endpoint env values before process start when the projection is known: OTA_PUBLIC_URLOTA_PUBLIC_HOSTOTA_PUBLIC_PORTOTA_PUBLIC_URL_<LISTENER>- when multiple listeners are projected, exactly one projected listener must set
project.host.primary: true; ota uses that listener forOTA_PUBLIC_URLand summary endpoint rendering - for container listeners with
project.host.port.mode: auto,execution.lifecycle: ephemeralpre-reserves a host port before spawn and retries bounded host-port conflicts;execution.lifecycle: persistentresolves 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-portis valid when the selected primary listener usesproject.host.port.mode: fixedand the execution path is either container execution or native structureddocker compose upwith explicitproject.publication.compose.serviceownership--host-portis rejected forproject.host.port.mode: auto, tasks without projected host listeners, ambiguous multi-listener projections without one primary listener, native compose publications withoutproject.publication.compose.service, and native compose engines other thandocker- stream-mode endpoint banners such as
External:andInternal:are printed only after ota itself confirms the projected endpoint; workload logs likereadyor 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)--memoryis valid only when the selected task resolves to container execution- when the selected container context declares
container.resources.memory.minimum, ota rejects--memoryvalues below that minimum before task execution starts - when no
--memoryoverride is passed, ota usescontainer.resources.memory.defaultwhen declared; if onlyminimumis declared ota requests that minimum; otherwise the engine default applies - for container-backed
runtime.kind: servicetasks, 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
--receiptis set - execution receipts include backend, remote
provider/target/ optionalcwdwhen relevant, lifecycle, container image when relevant, resolved container memory when requested, acquired paths, env sources, step summary data, resolved runtime listener endpoints, optionalservice_terminationdetails for post-readiness service stops, and optionalhost_service_cleanupevidence 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 --usefirst to discover the effective safe task surface - then run the selected task with
ota run <task> --agent --agentis 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 underRepo Signalsacross 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 withota detect --dry-run,ota detect --contract, andota init --dry-run -
the human summary now makes the top-level state explicit as
READY,READY WITH WARNINGS, orBLOCKED -
ota --versionnow 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 --jsonexposes the same identity in machine-readable form (semver,source_build,commit,dirty) plusschema_versionand additivecontract_capabilities[]entries (id,introduced_in) for CI and contract-compatibility provenance checks -
when
metadata.ota.minimum_versionrejects 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, plainota doctordiagnoses the root contract and grouped summaries for each declared member -
when
--memberis set, diagnoses the merged member contract -
repeated
--membervalues 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 envfirst so operators can inspect precedence before changing shell values, policy env, or declared sources -
unverifiable required services now route into
ota assist declare-readinesswhen only the probe is missing, orota assist declare-servicewhen the managed service shape itself still lacks a start path -
missing-file precondition failures now point to
ota up/ota run setupwhentasks.setupalready exists, or toota assist wire-setupwhen 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-runbefore any detect write, while still offeringota assist add-taskwhen 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, andreplay_posture: witness_onlyare all required forsupported; a matching failed archive iscontradictedand all other cases areunknown -
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.enventries become missing-env blockers for that diagnosis even when the same top-levelenv.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 ascargoorrustfmtas standalone selected-path tools -
doctor never provisions toolchains:
toolchains.<name>.fulfillment.mode: noneandfulfillment.mode: runboth stay diagnosis-only here; duplicate ownership betweentoolchains,runtimes, andtoolsfails 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.rustcan participate in approved-version and approved-source findings without re-declaringruntimes.rust -
ota doctor --jsonnow exposes additivetoolchains[]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.networkoreffects.external_stateproduce warnings in doctor output instead of leaving those execution risks implicit behind a safe-task label -
when a structured
AGENTS.mdorCLAUDE.mdmanaged block drifts from the declaredagent.safe_tasks,agent.verify_after_changes,agent.writable_paths, oragent.protected_paths, doctor reports agent-boundary drift instead of assuming agent guidance docs are still aligned -
first-class
prepare.kind: dependency_hydrationlanes 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_bootstraplanes 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-managedpnpmis 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
curlornode -
checks required execution backends for the selected
--modeand resolved contexts -
ota doctornow 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 nativediagnoses host/native readiness;--mode containerdiagnoses 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
extendsmerge, while legacy shorthand remains supported for one-context contracts -
warns on suspicious remote target shape:
-
ssh/tshtargets withoutuser@host -
kubectltargets not starting withpod/ -
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-runpreviews deterministic safe fixes without writing files -
--fixapplies only supported deterministic safe fixes for repos with a validota.yaml; current scope is.gitignorehygiene for.ota/state/,.ota/contracts/,.ota/receipts/, and.ota/proof/ -
when no
ota.yamlexists yet,ota doctor --fixdoes not propose repo-hygiene mutations and instead points operators to preview-first onboarding withota detect --dry-runorota 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,
aptfindings 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>.readinessis used, readiness probes run in the declared context and use the matching endpoint projection for reporting -
ota doctor --mode remoteprobes runtime/tool requirements in each executable remote context -
shows any inert top-level
extensionsentries 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>.timeoutwhen a service healthcheck is declared -
warns when
execution.lifecycle: ephemeralis declared and clarifies that current isolation applies toota run <task>and the setup step insideota up; diagnosis, healthchecks, and full repo cleanup are not ephemeral yet, and--ephemeralremains 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.jsonadvertises a Node image that does not satisfy the repo contract's declaredruntimes.noderequirement -
warns when
.devcontainer/devcontainer.jsonbootstraps with a different Node package manager than the repo contract declares -
warns when
agent.writable_pathsincludes sensitive lockfile, env/config, runtime-topology, CI, or repo-contract paths when they exceed the declaredagent.posture; narrow intentional exceptions can still be acknowledged withagent.exceptions.sensitive_writes -
reports an error when no
tasksare declared, because the contract is not operational forota run -
runs configured checks
-
orders findings by severity
-
includes an
agentsummary when the contract declares one -
may include a
provisioningplan 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:
READYorNOT READY Executionincludes aMode:line in text output so the selected diagnosis context is explicit- when agent guidance is present, the shared
AGENTblock is grouped intoOverview,Execution, andBoundarysections 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, whileWhydetail is omitted
JSON output:
okpathagentwhen the contract declares agent guidancefixwhen--fixis requested, including planned/applied action status and any write failuresfindings- findings may also include
provenance/provenance_keywhen 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
--membervalues return grouped per-member results inmembers
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
--bootstrapwrites 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 taskdescriptionfields 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--packslists the built-in starter packs, what they seed, the exactota 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,
--bootstrapcan fall back to the repo directory name forproject.name - supports preview mode with
--dry-run - refuses to run when
ota.yamlalready 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
detectedmode, plainota initwrites the smallest valid starter contract for the repo - in
detectedmode,ota init --bootstrapcan 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_hydrationforsetup, andcommandfor 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.sourcesin the starter contract:.env.local,.env,src/main/resources/application.properties,src/main/resources/application.yml,src/main/resources/application.yaml,appsettings.json, andappsettings.Development.json; explicit--packmode does not infer env sources from repo files - runtime support for declared
env.sourcesalso includes curatedyamlandtoml; 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.nameis 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 initwrites - canonical detected tasks can include short
descriptionfields so the starter contract teaches the task-authoring pattern immediately instead of only relying on notes - confident detected tasks may include a
notesfield that points to the matchingota run <task>command - when the detected tasks are confident enough, the starter contract now keeps a derived
agentblock 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 inagent.writable_pathswithout falling back to., while detector-backed control files such as manifests and lockfiles now surface explicitly inagent.protected_pathsand operational directories such asconfig,database,migrations,manifests,deploy, andinfrastay out of the default starter allowlist - starter contracts now also carry
agent.inferred_boundary.reviewed: falseplus provenance for the inferred writable and protected paths, so the boundary is visible as inferred state rather than silent starter magic; detector-led init usesdetect:...provenance, while explicit pack mode usesinit:...provenance for the starter defaults it owns - those starter
agent.notesnow explicitly tell authors to reviewagent.writable_pathsandagent.protected_paths, then setagent.inferred_boundary.reviewed: truebefore trusting automation - starter contracts protect
ota.yamlby default; if a future starter intentionally grants contract-authoring authority with writableota.yaml, it should also carryagent.exceptions.sensitive_writes: [ota.yaml]instead of leaving that sensitivity implicit - detector-led
ota init --dry-runnow renders the same explicitAgent boundaryoutcome as detect preview, so repos without a safe inferred task see why the starter omitsagentinstead of having to infer that omission from the YAML alone
Choosing an init path:
- use
ota init --dry-runwhen detector-led init should shape the first draft from repo signals - use
ota detect --contractbefore detector-ledota initwhen 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, orappsettings.Development.jsonintoenv.sources - use plain
ota initonly after comparing that detector-led starter againstota detect --contract - use
ota init --packswhen you want to compare the explicit starter catalog first - use
ota init --pack <name> --dry-runwhen you want an explicit conventional starter without detector-led selection - use
ota init --pack node --package-manager <name> --dry-runwhen 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-runwhen the repo is intentionallypytest- orunittest-driven and you want the starter to reflect that test command directly - use
ota init --pack ruby --dry-runwhen the repo is intentionally Ruby/Bundler-based and you want the starter to seed first-class Bundler dependency hydration plus the defaultbundle exec rake testloop, with Bundler version governance undertoolchains.ruby.package_managers.bundler - use
ota init --pack rust --dry-runwhen the repo is intentionally Rust-first and you wanttoolchains.rustownership plus first-class Cargo hydration forsetup, withcargo build/cargo testas the honest first draft - use
ota init --pack dotnet --dry-runwhen the repo is intentionally .NET-first and you wanttoolchains.dotnetownership plus first-classdotnet_restorehydration forsetup, with the standarddotnet build/dotnet testloop as the honest first draft - use
ota init --pack php-composer --dry-runwhen the repo is intentionally Composer-managed PHP andcomposer installplus reuse of an existingscripts.testentry is the honest first draft you want to review - use
ota init --pack java-maven --dry-runwhen the repo is intentionally Maven-based and you wanttoolchains.javaownership plus first-class Maven hydration forsetup, preferringmvnwwhen the wrapper already exists - use
ota init --pack java-gradle --dry-runwhen the repo is intentionally Gradle-based and you wanttoolchains.javaownership plus first-class Gradle hydration forsetup, preferringgradlewwhen the wrapper already exists - explicit packs seed short task
descriptionfields 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 contextdetected: starter contract derived from detected repo signalspack: starter contract derived from an explicit built-in starter packcatalog: starter-pack discovery output fromota init --packs
Text output:
- dry-run header:
INIT <path> - write success:
WROTE <path> - includes
Mode: blankorMode: detected packmode also includesPack: <name>, optionalOptions: ...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, weightedSignals,Selected signals,Strength,Gap, andNextrows when strong repo signals disagree with the selected pack; ota does not auto-switch or merge detector output into the pack --packsrendersINIT PACKS catalog, one entry per pack, the exactota init --pack ...command, any starter-specific option rows, and aNext:line with the matchingota 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 withota up --dry-run blankmode explicitly warns that the starter contract is minimal coverage onlydetectedmode 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:
okpathwrittenmode- optional
packwhen explicit pack mode is used - optional
pack_optionswhen explicit pack mode selected a starter-specific knob such as Node package manager or Python test runner - optional
pack_advisorywhen 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 configinferredpackswhenmodeiscatalogand ota is listing the built-in starter packs instead of previewing one contract; each entry includesname,summary,when, the exactcommand, a safenextpreview command, optional starteroptions, explicitdoes_not_inferboundaries, and the seeded runtimes, tools, checks, and tasks- failure responses can include
nextwhen 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 --reviewinspects the current writable/protected path boundary and provenance,ota agents --confirm --dry-runpreviews the exactreviewed: truemutation, andota agents --confirmwrites that confirmation into the contract before anyAGENTS.mdsync - derives
AGENTS.mdfrom the repo contract’sagentblock when one is present - doctor treats the structured managed block as governed boundary evidence, so stale
safe_tasks,verify_after_changes,writable_paths, andprotected_pathscontent 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, andruncommand 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 reportsAgent contract missing, shows compare-first next steps throughota detect --dry-runandota init --dry-run, and surfaces any trustworthy inferred repo signals plus inferred starter agent boundaries underRepo Signals ota agents --writenow refuses when the repo contract still lacksagent, so Ota does not write generic guidance that looks more authoritative than the authored contract- renders an explicit
Bootstrapsection whenagent.bootstrap.otais present, including the approved shell and PowerShell install commands forota - preserves existing
AGENTS.mdcontent 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 itsota 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.mdby default when--writeis set - accepts
--outputto write elsewhere - keeps output deterministic and reviewable
Text output:
- header:
AGENTS <path> --reviewusesAGENTS REVIEW <path>, reports whether the boundary isREVIEW REQUIREDorREVIEWED, and showsBoundary syncasblocked until review,update needed, orin sync--confirm --dry-runusesAGENTS CONFIRM <path>withPREVIEWand shows the exact reviewed-boundary contract preview before any write--confirmusesAGENTS 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
agentexists, preview mode shows the generated markdown content together with the write and verification next steps - when
agentis missing, preview mode shows a blocked boundary-sync diagnosis withTarget,Primary Blocker,Next, andRepo 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 runota doctor; reviewed boundaries that still need sync keep a two-step next lane forota agents --writeandota doctor
JSON output:
okpathoutputwrittencontent- failure responses can include
nextwhen 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, plainota checkruns root checks and grouped check summaries for each declared member - when
--memberis set, runs checks from the merged member contract only - repeated
--membervalues 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 checkalso 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.probestarget directly inside ota - does not perform runtime, tool, or env diagnosis
- does not execute tasks
Text output:
- header:
CHECK <path> - status line:
READYorNOT READY - when agent guidance is present, the shared
AGENTblock uses groupedOverview,Execution, andBoundarysections with counted wrapped lists and writable-path root/exception collapsing
JSON output:
okpath- additive
agentcan appear when the contract declares agent boundaries findings- additive
toolchains[]evidence can appear for the selected workflow path even thoughota checkitself 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 --archiveleaves a durable readiness artifact behind a CI runota receipt --json --archive --promote-baselinemarks one known-good receipt as the repo's explicit baselineota receipt --json --baseline promotedcompares the current repo state against that reviewed baseline instead of whatever happened to run lastota receipt --workflow frontend --json --archivecaptures one declared workflow's readiness lane as its own archived receipt/baseline stream instead of mixing it with another workflow's historyota receipt --snapshot latestreads 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.yamlusing--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 selectionota receiptnow accepts the same execution-selector family asota doctor:--mode, backend shorthands (--native,--container,--remote),--lifecycle, and lifecycle shorthands (--persistent,--ephemeral)- includes repo contract drift findings from the same
ota detectcomparison path used byota doctor - captures the current repo state as an execution receipt with one
readinessstep - 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 byota run <task> --dry-run --jsonandota up --json - when a recorded execution path actually ran provider fulfillment commands,
receipt.toolchains[]can also include additivefulfilledandcommands[]evidence there - never provisions, runs tasks, starts services, or writes repo state
--jsonreturns a repo receipt artifact withmode: "receipt"- receipt JSON always includes a normalized
receipt.contract_snapshot_hash;--archivealso materializes that normalized snapshot under.ota/contracts latest,promoted, and archived baseline/snapshot selection stay workflow-scoped when--workflowis selected, so one workflow's receipt history cannot silently replace another's- receipt JSON also includes an additive
receipt.assumption_set_hashderived from the canonical extracted assumption map, so automation can fingerprint semantic contract meaning separately from whole-snapshot identity --archivewrites the JSON receipt to.ota/receiptsand keeps the newest 50 archives--archive --promote-baselinealso writes.ota/receipts/repo-baseline.json, pointing at the archived receipt as the repo's explicit promoted baseline--historylists archived repo receipts from.ota/receiptsnewest first without loading or validating the current contract; explicit paths must be a repo directory or anota.yamlfile--baseline promotedcompares the current receipt against the explicit promoted baseline pointer under.ota/receipts/repo-baseline.json--baseline latestcompares 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 latestresolves the newest valid archived repo receipt for the same contract and reads the archived normalized contract snapshot referenced by that receipt--snapshot promotedresolves the explicit promoted repo baseline pointer under.ota/receipts/repo-baseline.jsonand reads the archived normalized contract snapshot--snapshot <receipt.json>accepts an archived repo receipt JSON file and followsreceipt.contract_snapshot_ref--snapshot <snapshot.json>accepts an archived normalized snapshot JSON file directly--snapshotis 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
0when 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 additivecontract_changes[],likely_related_changes[], andsummary.comparison.contract_snapshot_changedin JSON output - compare mode also returns additive
summary.comparison.correlationso automation can distinguishlikely_related,possibly_related, andno_clear_correlationwithout inferring correlation posture from array presence alone;possibly_relatedis reserved for coarse same-family overlap, while unrelated drift staysno_clear_correlation - public operator guide:
Semantic Snapshots and Correlationfor the higher-level snapshot, diff, and correlation workflow --fail-on-new-blockersrequires--baselineand exits1when the diff introduces one or more newseverity: errorfindings 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-baselineaddsBaseline:andPromoted:summary lines so the operator can see which archive became the explicit repo baseline--historyswitches the text header toRECEIPT 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 underSkipped Archives--baselineswitches the text header toRECEIPT 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--baselinealso preserves execution identity on both sides when present, including archived/currentstatus,backend,context,target,provider,lifecycle, andcwd--baselineincludes the advisory correlation posture inside theDrift: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-blockersadds aGate:overview line showing whether the current diff passed or was blocked by newly introduced blockers--snapshotswitches the text header toRECEIPT 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:
okpathmode: "receipt"archive_path(when--archiveis set)promoted_baseline.path,promoted_baseline.archive_path, andpromoted_baseline.promoted_at(when--archive --promote-baselineis set)summarymirroring the receipt summary witherror_count,warn_count,info_count, andstep_countreceipt, including additivereceipt.contract_identitywith declared project, selected metadata, execution intent, compact contract counts, and optionalreceipt.workflow_env_artifactswhen the selected/default workflow owns rendered env artifactsfindings--historyswitchesmodetohistoryand returnssummary.archive_count,summary.invalid_archive_count, anarchivesarray for valid archived receipts, andinvalid_archiveswhen malformed archive files were skipped--snapshotswitchesmodetosnapshotand returns additivesource,selection_kind,selection_path,archive_path,archived_at,promoted_at,snapshot_hash,snapshot_path,contract, and the normalized archivedsnapshot--baselinekeepsmode: "diff"and returns additivesummary.comparison.correlation,baseline.assumption_set_hash, andcurrent.assumption_set_hashalongside the existing archive-backed snapshot-drift evidence- each history archive may preserve
status,backend,context,target,provider,lifecycle, andcwdwhen that execution identity existed in the archived receipt --baselineswitchesmodetodiffand returnsbaseline,current,summary,introduced,resolved, andunchanged, with additive provenance fields onbaseline- diff
summaryalso carries a compactcomparisonblock so wrappers can show baseline/current identity labels plus readiness drift without reconstructing it from the full baseline/current sections --fail-on-new-blockersaddsgate.rule,gate.passed, andgate.new_blocker_countto 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
introducedarray
Current non-goals:
- mutating repo state
- replacing
ota doctoras the full readiness explanation surface - separate receipt storage outside the explicit
.ota/receiptsarchive directory - monorepo multi-member roll-up beyond the selected resolved contract target
- multi-rule diff gating beyond the explicit
--fail-on-new-blockerscompare 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, plainota upprepares the root contract and grouped member summaries for each declared member - when
--memberis set, prepares the merged member contract - repeated
--membervalues prepare those members in the provided order --agentenforces 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-targetota up --jsonmirrors the resulting ota-authored crossing record at bothgovernance.crossingandreceipt.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 upevaluates 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-runnow lists selected toolchains explicitly:fulfillment.mode: nonemeans ota will diagnose/check that toolchain on the selected path without provisioning it, whilefulfillment.mode: runmeans 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 uppreflight, 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-rundescribes 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 sotoolchains.nodereads as Node via Corepack rather than implying that Corepack itself is the runtime; whentoolchains.nodedeclarespackage_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
runtimesortoolsownership ota up --jsonand preview/final execution receipts now expose additivereceipt.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
fulfilledandcommands[]evidence for that toolchain entry - when one selected tool requirement resolves to
tools.<name>.acquisition,ota upcan 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
yarnorpnpmresolves to the contract-owned version rather than an ambient global shim - when the selected workflow setup or run task references
requirements.nativeentries withactivation.kind, ota resolves that native activation environment first; Windowsvisual_studio_dev_shelland genericcommandactivations 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-runnames the provisioning action and explicitly marks the selected acquisition activation as skipped - when a selected workflow declares
prepare.task,ota upruns that native host file-prep action before pre-setup services or setup; this phase is for deterministic file preparation such ascopy_if_missing, not long-lived runtime or service work - when a default workflow declares
setup.task,ota upuses that task as the preparation phase; otherwise it falls back to reposetup - 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 upstays container-authoritative; if no supported container engine is available, ota stops in preconditions instead of falling back to host provisioning - when provisioning fails,
ota upnow 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 upreuses 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 planfirst so the selected backend, lifecycle, image, or target path is visible before you edit execution settings or retryota up --dry-runreuses the same contract path, member targeting, backend selection, lifecycle selection, and provisioning plan resolution asota 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
servicesphase 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 upbehavior uses detached readiness proof semantics: start the run task, wait for readiness, then tear down proof-owned run execution before returningREADY ota up --detachkeeps that proved run task alive intentionally after readiness confirmsota up --attachkeepsota upattached 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 upuses 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 uprefuses before prepare/setup/run execution instead of treating the request as advisory ota up --dry-run --jsoncarriesexecution_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 thisota upinvocation only using the same selectors asota 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 asreplay_verified,replay_failed, orreplay_unavailable- replay execution is intentionally narrower than general receipt comparison today: it is only
supported for mutating single-repo
ota uplanes and is rejected for--dry-run,--stream,--member, and monorepo-root aggregateota 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
READYorNOT READY - reports the phase where execution stopped:
preconditions,provisioning,activation,services,setup,run, orpost-up diagnosis - reports
provisioningwhen early setup ran but the repo is still not ready - includes setup exit code details when the
setuptask 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
--streamopts into raw live child output for provisioning, required servicestartcommands, and thesetuptask--streamis text-only and is only supported for mutatingota up- prints a summary in text output, emits an execution receipt when
--receiptis set, and includessummaryplus areceiptobject in repo-target JSON output; the compactUP SUMMARYnow leads withStatusfor faster scan time, and monorepo aggregate JSON keeps groupedmembersresults instead of inventing a top-level receipt - when the execution receipt carries follow-up guidance, text output appends that shared
Next:block afterUP SUMMARY, and the same receipt-backed lane stays on the repo-target JSONreceipt.nextsurface so repo and workspace preparation flows end the same way --dry-runprintsUP 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-runkeeps the shared readiness verdict in JSONstatus/summary.verdict, but the preview text and additive JSONpreview_statusnow make the execution meaning explicit asRUNNABLE,RUNNABLE WITH WARNINGS, orBLOCKEDota run <task> --dry-runnow diagnoses the selected task dependency path with the same precondition truth surface used byota doctor/ota up, so selected runtime, tool, and toolchain mismatches block preview instead of being silently described as runnable--dry-runnever provisions, starts services, runs setup, or writes repo files--receiptis only for mutatingota up; it conflicts with--dry-run- replay baselines are honest only for what the archived witness actually carried: matching
semantic_contract_snapshotacquits contract drift, while matcheddeclared_replay_inputrecords remain narrowing evidence and keep replay hermeticity atpartly_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 --agentwhen 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 --jsonfirst 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-updateandota upgradeare aliases--versionpins a specific release--channelcurrently acceptsstableandlateststableresolves the latest stable release taglatestresolves the newest release entry, including prereleases if present--versionoverrides 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
otais currently running, the downloaded binary is staged and applied after the current process exits; a successfully scheduled replacement reportspendingand exits successfully instead of surfacing aCopy-Itemstack 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_POLICYas a local file path orhttp(s)://URL override - falls back to the nearest ancestor
.ota/org-policy.yaml - falls back again to the nearest ancestor
ota.workspace.yamlworkspace.policywhen present - remains read-only
Text output:
- header:
POLICY <path> Policy source:shows where ota loaded the policy fromPolicy path:shows the resolved policy file path or URL- effective policy content when one is loaded
- when a policy pack is loaded,
Next:points toota policy reviewfor boundary inspection andota doctorfor 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:
okpathpolicy_sourceandsourcepolicy_pathpolicy- 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.yamlunder the current directory when no path is given - accepts a repo root, a
.ota/directory, or an explicit.ota/org-policy.yamltarget path - supports explicit starter presets:
required-sections,provisioning, andagent - writes the minimal valid starter today:
policies: {} - stays conservative and does not infer org rules or add provisioning approvals automatically
required-sectionsstarts with a small required-section policy (runtimesandtasks)provisioningscaffolds emptyprovisioningandadapter_bootstrapmaps plus inline example guidanceagentstarts with agent-safety andAGENTS.mdexport 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:
okpathwrittenmode(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> Policyis the context block and shows the active source plus resolved policy path or URLOverviewrolls up the policy findings by severity- policy findings use operator-shaped summaries and action-specific
Next:steps instead of pointing back toota policy review - when no policy pack is found, the text output says so explicitly and points users back to
ota policy
JSON output:
okpathpolicy_sourcepolicy_pathsummaryfinding_groupspolicyfindings
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
otaskill from theota-run/skillsdistribution repository - requires network access to fetch the current distribution copy
- requires
--agent codexor--agent claudeso Ota writes to the correct tool-specific skill directory - writes Codex installs to
$CODEX_HOME/skills/otawhenCODEX_HOMEis 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:
okskillagentpath
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 --setupdetects the current shell when possible and installs ota's managed hook into the shell profile or completion file idempotentlyota completion --removedetects the current shell when possible and removes ota's managed hook plus any managed zsh support file idempotentlyota completion <shell> --setupinstalls the managed hook for one explicit shell without relying on auto-detectionota completion <shell> --removeremoves the managed hook for one explicit shell without relying on auto-detectionota completion checkverifies 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 refreshota completion <shell>prints the manual shell setup ota expects for that shell; for zsh it includes both the_otacompletion file and the.zshrcloaderota completion <shell> --scriptprints the exact raw registration script clap generates for that shell so users can inspect the shell-side function directly- zsh setup writes a managed
_otacompletion file under~/.config/ota/zsh/_otaand loads that exact file through the shell completion path instead of relying on late runtimecompdefregistration 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>andota 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>completeslatest,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>, andota 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 --removeonly 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 confirmota completion checkshows bothHook: presentand aCompletion file:line for the managed_otafile;ota completion --removegives you a clean reinstall pathbash: if completions still do not appear after setup, reopen the shell or source the profile again with. ~/.bashrcota completion checkshould reportHook: present; if it reportsmissingorneeds update, rerunota completion --setupota completion <shell> --scriptlets 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.execannot 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>orscheduled 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 doctorand runsota self-update
JSON output:
okpathstatusphasefindingsservicewhen a service-start failure occurstaskwhen a task failure occursexit_codewhen a child command failure occurs- monorepo root and repeated
--membersummaries include grouped per-member results inmembers - contract load/validation failures return the same failure envelope as
ota validate --json(ok,path, and eithererrorsorerror)
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, plainota cleanreports the root cleanup result and grouped member cleanup results - when
--memberis 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(notproject.name) - tracks repo-used container engines in
.ota/state/managed-enginesso 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 --staledoes not requireota.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-runpreviews stale containers without removing themota clean --jsonemits 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 stringota clean --stale --jsonemits the matched engines, containers, and cleanup counts for automationota clean --stalehas its own exit-code contract and is separate from repo-scopedota clean- remote backends do not currently define cleanup semantics; they report
No cleanup needed - reports
No cleanup neededonly when no owned cleanup target is found and no relevant-engine discovery failed - repo-scoped
ota cleannow 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.jsonpnpm-workspace.yamlpnpm-lock.yamlyarn.lockbun.lock/bun.lockbpackage-lock.jsonnpm-shrinkwrap.json.nvmrc.node-version.devcontainer/devcontainer.jsondevbox.jsonAGENTS.mdCLAUDE.md.tool-versionsmise.tomldevenv.nixpyproject.tomlPipfileuv.lockrequirements.txtsetup.cfg.python-version.java-version.sdkmanrcgo.modCargo.tomlrust-toolchain.tomlrust-toolchainsettings.gradle(.kts)build.gradle(.kts)gradle/wrapper/gradle-wrapper.propertiespom.xmlmvnw.mvn/wrapper/maven-wrapper.propertiescomposer.json.ruby-versionGemfileglobal.json*.sln/*.csproj/*.fsprojmix.exsrebar.configbuild.zigdub.json/dub.sdlfpm.tomlshard.ymlelm.jsoncpanfile/Makefile.PL*.hxmldocker-compose.yml/docker-compose.yamlcompose.yml/compose.yaml
For Docker Compose service inference, ota currently derives:
providerat high confidencestart/stopat high confidence- declared
healthcheck.testat high confidence
For external AGENTS.md / CLAUDE.md, ota currently admits only narrow structured shapes:
safe_tasks,verify_after_changes,writable_paths, andprotected_pathslists 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, andcompile; 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 asRun all unit tests,Run type checking, orFormat 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 obviouspath/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:difforpnpm 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 detectis 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.sourcesentries for.env.local,.env,src/main/resources/application.properties,src/main/resources/application.yml,src/main/resources/application.yaml,appsettings.json, andappsettings.Development.json - when
ota.yamlalready 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.yamlalready 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 boundaryoutcome:Inferredwhen safe tasks were inferred,Partially inferredwhen only writable/protected boundary defaults were inferred, andOmittedwhen ota intentionally withholds the starteragentblock because no safe task was inferred - PowerShell-script detection now emits
runtimes.pwshforpwsh-based repos instead of the legacyruntimes.powershellkey, 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 --contractprints the exact starter contract thatota initwould writeota detect --contractomits annotations and comparison outputota detect --contractis text output only
Current write behavior:
ota detect --writewrites 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 rewriteota detect --writeremains conservative even whenota initcan write a valid starter- versioned
pnpm/yarnpackage-manager-backedpackage.json#engines.nodeis high confidence for detect write, merge, rewrite, ownership metadata, and drift comparison, and is written as canonicaltoolchains.nodeCorepack 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, andtoolchains.dotnetwith structured fulfillment instead of legacy split ownership), and detectedtools.bundlerownership folds intotoolchains.ruby.package_managers.bundlerinstead of remaining split - Docker Compose service
start,stop, and declaredhealthcheck.testcommands 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
agentblock 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_admissionwithdirectfor detector-owned fields andpromotedfor 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.yamlexists yet, preview guidance stays compare-first:ota detect --contractfor exact detected text,ota init --dry-runfor the conservative starter path, thenota detect --writefor 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 withota up --dry-run
Current merge-preview behavior:
ota detect --merge --dry-runis 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_agentremovals - 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 driftcommand yet; drift review stays onota detect --merge --dry-run, and operator-facing trust/readiness drift stays onota 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 --mergerequires an existingota.yaml- it applies only
highconfidence missing fields ota detect --merge --apply FIELDapplies only the selected high-confidence detected changes and leaves the rest ofota.yamlunchangedota detect --merge --apply-allapplies all eligible high-confidence detected changes and leaves the rest ofota.yamlunchanged- inferred
env.sourcesadditions 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
comparisonwithout being written - if nothing eligible can be added, it returns success with
written: falseand leavesota.yamlunchanged - after a successful merge write, text output uses explanatory
Next:steps: validate the updated contract first, then review any remaining add-only drift withota detect --merge --dry-run, review rewrite-only drift withota detect --rewrite --dry-runwhen 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 --rewritetargets existing contracts only and is destructiveota detect --rewrite --dry-runpreviews replacement without writingota detect --rewrite --yesreplaces the existingota.yamlwith 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 withota 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 ofproject,runtime,tool,env,service,check,task,agent, orfieldSignal:currently renders one ofconfig,script,lockfile,file,template, orconvention- task-shaped annotations can also include
Agent Safe:(yes,no,unknown) andAgent 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.jsonwins over conflicting Python or Go manifest names - when confidence is equal for package-manager tools,
package.json#packageManagerwins over conflicting.tool-versionsvalues - repo-owned
mise.toml#tools.*runtime and Node package-manager truth wins over conflicting.tool-versionsvalues, but still yields to more specific repo-local markers such as.nvmrc,.python-version, orpackage.json#packageManager .devcontainer/devcontainer.jsoncan 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 arraypostCreateCommand/updateContentCommandentries, but those hints remain lower precedence than repo-local runtime markers,package.json#packageManager, andmise.tomldevbox.jsoncan contribute high-confidencetools.devboxownership andshell.scripts.*task inferencedevenv.nixcurrently contributes only high-confidencetools.devenvpresence; ota does not yet infer broader Nix environment truth from it in this slice- when
package.json#packageManageris present for versionedpnpmoryarn,package.json#engines.nodeis treated as high-confidencetoolchains.noderuntime truth; without a package manager signal, it stays conservative - when
package.json#packageManageris 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 withsafe_for_agent: trueonly when the inferred lane still looks like a bounded verification command; orchestration-flavored lanes such asdocker:teststay 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 Pipfilecan contributepythonruntime inference andpipenvtool inference conservativelyuv.lockcan contributeuvtool inference conservativelyrequirements.txtcan contributepiptool inference conservativelysetup.cfgcan contribute project name andpythonruntime inference conservativelymise.tomlcan contribute high-confidence runtime and Node package-manager inference from repo-owned[tools]entries.devcontainer/devcontainer.jsoncan 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 arraypostCreateCommand/updateContentCommandentriesdevbox.jsoncan contribute high-confidencedevboxtool inference plusdevbox run <script>tasks from declaredshell.scriptsdevenv.nixcan contribute high-confidencedevenvtool presence conservatively- for example,
.nvmrc,.node-version,.python-version,.java-version,.sdkmanrc,go.mod,rust-toolchain.toml, andrust-toolchainwin over conflicting.tool-versionsruntime values
Write behavior:
ota detect --writewrites 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.yamlalready exists, points the user atota detect --merge --dry-runandota detect --rewrite --dry-run - fails if the high-confidence projection is not sufficient
- JSON failure responses can include
nextwhen 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.yamlexists yet, preview-first onboarding is compare-first: reviewota workspace detect --dry-runagainstota workspace init --dry-runbefore any first write ota workspace initwritesota.workspace.yamlby defaultota workspace init --bootstrapcan auto-provision missing repo contracts from detected repo signals before writingota.workspace.yaml--writeremains 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 toota workspace detect --dry-runandota workspace init --dry-runbefore any workspace write - when overwrite is refused, points to
ota workspace validateandota workspace doctor - successful writes now hand directly to
ota workspace validate,ota workspace up --dry-run, andota workspace up - supports JSON for machine-readable write outcomes
Text output:
- preview: compare-first
Next:guidance intoota workspace detect --dry-runor the explicit write path - write:
WORKSPACE INIT WRITE <path>with the same post-write lane intoota workspace validate,ota workspace up --dry-run, andota workspace up
JSON output:
- success:
ok,path,written,mode,config,included,missing_contract - failure:
ok,path,written,mode,error, optionalnext
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.yamlexists yet, preview-first onboarding is compare-first: reviewota workspace detect --dry-runagainstota workspace init --dry-runbeforeota workspace detect --write --writewritesota.workspace.yamlonly for first contract creation--mergerequires an existingota.workspace.yamland adds only missing discovered repo entries underrepos- merge is additive-only and does not overwrite existing repo entries
--rewrite --dry-runpreviews full replacement of an existingota.workspace.yaml--rewrite --yesfully replaces existingota.workspace.yamlwith 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 toota workspace detect --dry-runandota workspace init --dry-runbefore any workspace write - successful writes, merges, and rewrites now hand directly to
ota workspace validate,ota workspace up --dry-run, andota 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.yamlusing--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>.workflowagainst the referenced repo contract when the workspace pins a non-default repo workflow - allows missing repo paths only when
repos.<name>.sourceis declared
Text output:
- header:
WORKSPACE VALIDATE <path> - success:
VALIDplus next steps intoota workspace doctor,ota workspace up, andota workspace tasks - failure: validation or load error text
JSON output:
- success:
ok,path,summary.error_count - failure:
ok,path,summary.error_count, and eithererrorsorerror
ota workspace tasks
List workspace repo tasks in dependency order.
ota workspace tasks [PATH]
ota workspace tasks --json [PATH]
Current behavior:
- resolves
ota.workspace.yamlusing--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, andafter_alwayshook relationships when the repo contract declares them - carries structured task
launchadditively when one repo task uses command or packaged-container launch instead of shellrun/script - reports non-acquired repos with
acquired: falseand 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:
okpathsummarywithrepo_count,acquired_count, andtask_countrepos- each repo includes:
name,path,contract_path,required,acquired,depends_on,tasks - each task includes:
name,kind, optionaldescription, one execution body field (runorscript) or additivelaunch,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.yamlusing--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 (
ACQUIREDvsNOT ACQUIRED) - reports lightweight readiness status per repo (
READYvsNOT READY), and whenrepos.<name>.workflowis 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 compactExecution:block with env provenance when present
JSON output:
okpathsummarymirroring the receipt summary witherror_count,warn_count,info_count, andstep_countrepos- 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.yamlusing--file,OTA_FILE, or upward discovery - validates workspace structure and keeps repo ordering deterministic
- when
repos.<name>.workflowis 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
--repofiltering for focused inspection - supports
--mode,--lifecycle, and--ephemeraloverrides 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:
READYorNOT READY - optional
Overridessection 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 honestWhy/Next - when a repo contract loads, the report also includes the compact
Contractblock and declaredExecutionblock for that repo - a final
Summaryblock reports resolved and unresolved repo counts
JSON output:
okpathmode: "execution-plan"summarywithrepo_count,resolved_count,unresolved_count,required_unresolved_count,not_acquired_count, andmissing_contract_countrepos- each repo includes:
name,path,contract_path,required,acquired,status, additiveworkflowandtaskwhen workflow planning resolves through a canonical path (including repo-default workflow selection), optionalcontract_identity, optionaldeclared_execution, optionalresolved, optionalerror, and optionalnext overridesappears 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.yamlusing--file,OTA_FILE, or upward discovery - validates workspace structure and repo contracts
- acquires missing repos declared with
repos.<name>.sourcebefore execution - executes the task for each repo in workspace dependency order
- can run independent repos concurrently when
--jobsis greater than1 - blocks downstream repos when a dependency repo did not complete successfully
- captures per-repo stdout/stderr in default mode
--streamopts into raw child output (text only, currently requires--jobs 1)--json --progress-jsonemits 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>.inputsand are passed as--kebab-case valueflags - task inputs are exposed to each repo task process as
OTA_INPUT_<NAME>env variables defaultvalues are applied when the caller omits an inputrequired: truemakes an input mandatory unless a default existsallowedlimits 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
--receiptis set, and areceiptobject in JSON output - the workspace receipt includes additive
receipt.contract_identitywith workspace name/type and compact workspace repo/policy counts
Text output:
- header:
WORKSPACE RUN <task> <path> - status line:
READYorNOT READY - per-repo status includes
required/optional, task name, findings, and optional exit details - after
WORKSPACE RUN SUMMARY, ota appends the same receipt-backedNext:lane used by repo-level execution output when a safe follow-up exists
JSON output:
okpathtasksummaryreceiptrepos- each repo includes:
name,path,contract_path,required,ok,status,task,findings, additivenext/next_steps, and optionalexit_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.yamlusing--file,OTA_FILE, or upward discovery - validates workspace structure and referenced repo contracts
- evaluates repo checks in workspace dependency order
- when
repos.<name>.workflowis declared, checks that selected repo workflow instead of assuming the repo default path - can check independent repos concurrently when
--jobsis greater than1 - preserves deterministic repo ordering in text and JSON output even when checks run concurrently
- downgrades findings for optional repos to warnings
--progress-jsonemits 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:
READYorNOT READY - summary roll-up includes repo verdict and agent verdict before the
Overviewcount 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, repoPath/Contractand findingWhydetail are omitted; summary +Nextremain
JSON output:
okpathsummarywithrepo_count,ready_count,not_ready_count,verdict,agent_verdict,error_count,warn_count, andinfo_countsummary.primary_blockermay be present with the workspace headline blocker, including the repo name that currently owns it- each repo may include additive
primary_blockerwith that repo's current highest-priorityseverity,summary,why, andnext summary.primary_blocker.codeandrepos[].primary_blocker.codeare additive and only appear when the blocker comes from a finding with explicit stable identityrepos
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.yamlusing--file,OTA_FILE, or upward discovery - validates workspace structure
- evaluates repos in dependency order
- can diagnose independent repos concurrently when
--jobsis greater than1 - 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>.workflowis 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
--streamis text-only and emits repo completion updates while the final report is being built--progress-jsonemits 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:
READYorNOT 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
Overviewcount 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, repoPath/Contractand findingWhydetail are omitted; summary +Nextremain
JSON output:
okpathsummarymirroring the workspace doctor roll-up withrepo_count,ready_count,not_ready_count,verdict,agent_verdict,error_count,warn_count, andinfo_countsummary.primary_blockermay be present with the workspace headline blocker, including the repo name that currently owns it- each repo may include additive
primary_blockerwith that repo's current highest-priorityseverity,summary,why, andnext summary.primary_blocker.codeandrepos[].primary_blocker.codeare 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
Plansection with explicit repo ownership for each grouped action - one section per workspace repo
- ordered remediation
Plansteps under each repo - an
Overviewcount block at the end
JSON output:
- success:
ok,path,summary, top-levelactions, andrepos - each top-level action identifies the owning
repo,path,contract_path,required, and the grouped action fields - each repo report includes
summary, groupedactions, and detailedsteps - failure:
ok,path, and eithererrorsorerror
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.yamlusing--file,OTA_FILE, or upward discovery - validates workspace structure
- clones missing repos declared with
repos.<name>.sourcebefore repo-level prepare - runs the existing repo-level
upflow for each referenced repo - when
repos.<name>.workflowis declared, prepares that selected repo workflow instead of assuming the repo default workflow - can prepare independent repos concurrently when
--jobsis greater than1 - 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-jsonemits the same live workspace progress as NDJSON on stderr while the final workspace-up JSON report remains on stdout; each event now carries additivephaseandstage_familyso machine consumers can classify acquisition vs prepare/result lanes without inferring from status text alone--quietsuppresses 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
--jobsdefaults to1 --streamopts into raw live child process output instead of buffered per-repo output--streamis 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
--receiptis set, and areceiptobject in JSON output - the workspace receipt includes additive
receipt.contract_identitywith workspace name/type and compact workspace repo/policy counts
Text output:
- header:
WORKSPACE UP <path> - status line:
READYorNOT 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-backedNext:lane used by repo-level execution output when a safe follow-up exists
JSON output:
okpathsummarymirroring the workspace doctor roll-up withrepo_count,ready_count,not_ready_count,error_count,warn_count, andinfo_countreceiptwith additivenext_stepswhen the workspace follow-up lane can be split into ordered machine-readable steps- each repo may include additive
nextandnext_stepsfor 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.yamlusing--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 upremains the bootstrap path - can refresh independent repos concurrently when
--jobsis greater than1 - 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-jsonemits the same live workspace progress as NDJSON on stderr while the final workspace-refresh JSON report remains on stdout; each event now carries additivephaseandstage_familyso machine consumers can distinguish refresh/setup progress from generic status strings--quietsuppresses 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
--jobsdefaults to1 --streamopts into raw live child process output instead of buffered per-repo output--streamis text-only and currently requires--jobs 1--dry-runpreviews the refresh commands without changing repo state--forceforce-fetches and hard-resets refreshed repos to the declared source or--refoverride--pruneprunes stale remote-tracking refs during refresh--ref <branch|tag|sha>overrides the source ref used for refresh- refresh target precedence is: explicit
--ref, then declaredsource.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 pullfailure - 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
--receiptis set, and areceiptobject in JSON output
Text output:
- header:
WORKSPACE REFRESH <path>orWORKSPACE REFRESH PREVIEW <path>for--dry-run - preview mode prints
Mode: dry-run (no write) - status line:
READY,NOT READY, orNOT ACQUIREDfor 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:
okpathmode:refreshfor normal refresh,previewfor--dry-runsummaryfrom the shared workspace execution receipt shape, always includingerror_count,warn_count,info_count, andstep_count, and optionally includingrepo_count,ready_count, andnot_ready_countwhen ota recorded the workspace roll-upreceiptrepos
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.yamlusing--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
MATCHwhen a repo is clean and aligned,DIRTYwhen the worktree has local changes,DIFFERENTwhen commit counts differ,MISSINGwhen the repo is absent, andUNRESOLVEDwhen git state cannot be compared safely - can compare independent repos concurrently when
--jobsis greater than1 - never mutates repo state
--jsonreturns a workspace diff roll-up withmode: "diff"--progress-jsonemits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-diff JSON report on stdout; those events usestatusfor the repo diff verdict such asMATCH,DIRTY, orUNRESOLVED, andtailfor the machinedrift_kind- text and JSON now carry an additive top-level lifecycle
nextlane 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 declaredsource.refversus 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 declaringsource.refwhen the workspace should own the target - per-repo JSON items can also carry additive
nextandnext_stepsso automation can read the repo-owned follow-up lane without reparsing findings - per-repo JSON also carries additive
drift_kindso automation can distinguish local dirtiness, commit divergence, missing repo, missing contract, target ambiguity, and unresolved comparison directly - per-repo JSON also carries additive
target_sourceso automation can tell whether the comparison target came from declaredsource.refor from the repo's upstream branch - text and JSON summaries now also break the collapsed
MissingandUnresolvedbuckets 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.yamlusing--file,OTA_FILE, or upward discovery - validates workspace structure
- reads repo readiness and local git drift for each workspace repo
- when
repos.<name>.workflowis 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
--jobsis greater than1 - never mutates repo state
--jsonreturns a workspace status roll-up withmode: "status"--progress-jsonemits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-status JSON report on stdout; each event usestailfor the repo drift state such asMATCH,DIRTY, orUNRESOLVED, and now also publishes additivephaseandstage_familyso machines can tell this is a status/receipt reporting lane without recovering that intent fromcommandplus status text alone- text and JSON now carry an additive top-level lifecycle
nextlane 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 declaredsource.refversus 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 declaringsource.refwhen the workspace should own the target - per-repo JSON items can also carry additive
nextandnext_stepsso automation can read the repo-owned follow-up lane without reparsing findings - per-repo JSON also carries additive
drift_kindso automation can distinguish local dirtiness, commit divergence, missing repo, missing contract, target ambiguity, and unresolved comparison directly - per-repo JSON also carries additive
target_sourceso automation can tell whether the comparison target came from declaredsource.refor from the repo's upstream branch - text and JSON summaries now also break the collapsed
MissingandUnresolvedbuckets 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:
okpathmode: "status"summarywith readiness counts and drift countsrepos- each repo includes:
name,path,contract_path,required,acquired,ready,readiness_status,drift_status,branch,head,target_ref,ahead,behind,dirty, andfindings
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.yamlusing--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
--jobsis greater than1 - never clones, fetches, resets, or writes repo state
--jsonreturns a workspace receipt roll-up withmode: "receipt"--progress-jsonemits 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 asworkspace status, those events usetailfor the repo drift state such asMATCH,DIRTY, orUNRESOLVED, and now also publish additivephaseandstage_familyso machine consumers can keep receipt/status reporting semantics separate from prepare or task-execution progress- the workspace receipt includes additive
receipt.contract_identitywith workspace name/type and compact workspace repo/policy counts --archivewrites the JSON receipt to.ota/receiptsand 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:
okpathmode: "receipt"archive_path(when--archiveis set)summarymirroring the receipt summary withrepo_count,ready_count,not_ready_count,error_count,warn_count,info_count, andstep_countreceiptrepos
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