Reference
Ownership Layers
The canonical ownership model for ecosystem capability truth, repo-level orchestration, simple runtimes, standalone commands, and native prerequisites.
Recommended next
Purpose
Use this page to decide where prerequisite truth belongs.
The canonical split is capability-first for toolchains and mediation-first for orchestrators.
The goal is one owner per capability and one explicit repo execution story.
toolchainsown required ecosystem capability truth.orchestratorsown repo-level trust, prepare, and mediated task execution.runtimesown simple unmanaged runtime checks.toolsown standalone commands on PATH.native_prerequisitesown host-native build bundles, shell activation, and selected host package preparation truth.
Ownership rule
Pick the highest useful owner and do not repeat the same capability below it.
toolchainsare capability-first:versionsays what the repo needs, whilefulfillmentsays how ota may fulfill it on the selected path.orchestratorsdo not replacetoolchains; they model repo-level trust, install, and selected task execution.- Current shipped toolchain names are
toolchains.rust,toolchains.node,toolchains.java,toolchains.python,toolchains.go,toolchains.ruby, andtoolchains.dotnet. - Current shipped canonical fulfillment sources are
rustup,corepack,sdkman,uv,go,ruby, anddotnet. - Legacy toolchain
provideris still accepted for compatibility, but it is not the canonical public model. - If a declared toolchain owns the capability, do not also declare it under
runtimesortools.
Command behavior
This is what the ownership model changes in day-to-day use.
Read these behaviors as operational consequences of the contract: what ota can inspect, what it may fulfill, and where repo execution truth should live.
ota doctordiagnoses selected toolchains, orchestrators, and native prerequisites without mutation.ota doctor --jsonandota check --jsonexpose additive selected-pathtoolchains[]evidence so automation can see the chosen fulfillment source, backend, target OS, fulfillment mode, and owned capabilities.ota up --dry-runshows selected toolchains before execution.fulfillment.mode: nonemeans diagnose only;fulfillment.mode: runmeans ota may provision the selected toolchain on the selected run path.ota upandota runmay fulfill selected native prerequisite package-manager guidance (apt,brew,winget,choco,scoop) before rerunning preconditions.- keep package-manager lanes aligned to the platform entry they live under: prefer
aptunderlinux,brewundermacos, andwinget/choco/scoopunderwindowsinstead of mixing likely wrong-OS package-manager lanes into the same platform entry - avoid mixing opaque
installshell glue with manager-owned package lanes on the same platform entry when the shell command is only there to install host packages; keep package-manager truth underapt/brew/winget/choco/scoop, and reserveinstallfor the remaining manual step only when no first-class lane owns it yet - when an org policy pack is active, those native package installs must be approved under
policies.native_packages.<manager>.approved; ota does not bypass that policy gate tasks.<name>.execution.orchestratormakes mediated execution first-class instead of burying repo truth inrun: mise ...shell strings.
Use this lookup when you already know the toolchain owner and need the canonical fulfillment source name Ota expects in the contract.
toolchains: rust: fulfillment: source: rustup node: fulfillment: source: corepack java: fulfillment: source: sdkman python: fulfillment: source: uv go: fulfillment: source: go ruby: fulfillment: source: ruby dotnet: fulfillment: source: dotnetWhen to use each layer
Toolchain
A managed ecosystem capability such as Rust, Node, Java, Python, Go, Ruby, or .NET.
Example
Orchestrator
A repo-level manager such as mise that mediates trust, install, and selected task execution.
Example
Runtime
A simple language/runtime version requirement with no managed ecosystem ownership.
Example
Tool
A standalone command on PATH.
Example
Before and after
Without the canonical model, repo truth often disappears into setup shell strings.
The first snippet shows the common anti-pattern: the repo works, but all the important ownership is hidden in commands.
The second snippet shows the same intent modeled structurally so ota can validate, diagnose, and mediate it.
This works mechanically, but the repo-level trust, install, and execution story is trapped inside shell commands that Ota cannot govern precisely.
tools: mise: "*" tasks: setup: run: mise trust && mise install server:verify: run: mise run //server:ci-unitThis is the governed version of the same flow: Node capability lives under the toolchain, while mise owns trust, install, and mediated task execution explicitly.
toolchains: node: version: "24.15.0" package_managers: pnpm: "10.33.4" fulfillment: source: mise mode: run orchestrators: mise: kind: mise required: true config_files: - mise.toml activation: trust: true prepare: install: true tasks: server:verify: run: //server:ci-unit execution: orchestrator: ref: mise mode: taskTask requirement examples
Task requirements should select the owner that actually applies to the selected path.
These examples answer a simple question: what kind of thing does this task need before it can run?
Pick the narrowest truthful owner. If the task needs a governed ecosystem, require a toolchain. If it only needs a version on PATH, require a runtime. If it needs a standalone binary, require a tool. If it needs a host bundle, require native prerequisites.
Choose a toolchain requirement when the task depends on an ecosystem Ota understands structurally, such as Node with its owned package-manager lane.
tasks: setup: requirements: toolchains: - nodeChoose a runtime requirement when the task only needs an interpreter or runtime version on PATH and no deeper ecosystem ownership is necessary.
tasks: start: requirements: runtimes: node: ">=24"Choose a tool requirement when the task depends on a standalone binary such as Docker or jq rather than a governed language ecosystem.
tasks: docker:proof: requirements: tools: docker: ">=27"Choose native prerequisites when the task needs a host-native bundle like compilers or platform build tools that do not belong under a language toolchain.
tasks: install: requirements: native: - node-native-build-toolsDuplication boundaries
Duplicate ownership is invalid contract drift, not extra safety.
- Reject
toolchains.nodeplusruntimes.nodeortools.nodeon the same contract. - Reject
toolchains.pythonplusruntimes.pythonon the same contract, and rejecttoolchains.python.package_managers.poetryplustools.poetrywhen Poetry ownership is explicit. - Reject toolchain-provided components such as
rustfmtwhen they are also modeled as standalone tools. - Keep one owner for the selected capability and remove the duplicate location instead of carrying parallel truth.
Fulfillment behavior
Capability truth and fulfillment truth are separate on purpose.
The key distinction is this: version says what the repo needs, while fulfillment says whether ota may do anything about it on the selected path.
Use the two examples below as the default decision point: inspect-only when you want diagnosis, explicit run-path fulfillment when you want ota to provision through the declared owner.
ota validatevalidates contract shape only.ota doctordiagnoses missing or mismatched toolchains without mutation.ota up --dry-runshows the exact fulfillment action before execution.- Supported
fulfillment.modevalues today are onlynoneandrun. - Use
fulfillment.mode: nonewhen ota should only diagnose/check the toolchain truth and never provision it on the selected path. ota upand selectedota runmay provision only whenfulfillment.mode: runis declared and policy permits it.- Use
fulfillment.mode: runonly when the selected repo path should let ota own fulfillment through the declared source. - Default fulfillment should be
mode: none, so inspection commands never silently download or mutate.
Use mode: none when you want Ota to validate and diagnose the declared capability but never provision or mutate it on the selected path.
toolchains: rust: version: "1.94.0" components: - rustfmt fulfillment: mode: noneUse mode: run only when the selected path should let Ota fulfill the declared toolchain through its canonical source instead of leaving that step manual.
toolchains: rust: version: "1.94.0" components: - rustfmt fulfillment: source: rustup mode: runCurrent shipped examples
These examples use only the canonical public model.
Treat this section as a quick pattern catalog, not a copy-paste dump.
Each snippet shows the minimum truthful shape for a shipped owner so authors, agents, and crawlers can quickly see where a capability belongs and what Ota owns there.
A fully governed Rust example with version, profile, components, and targets owned by the Rust toolchain rather than scattered across shell setup.
toolchains: rust: version: "1.94.0" profile: minimal components: - rustfmt - clippy targets: - x86_64-unknown-linux-muslA Node example that keeps runtime truth, pnpm ownership, and fulfillment source together so dependency hydration and execution can build on one owner.
toolchains: node: version: "24.15.0" package_managers: pnpm: "10.33.4" fulfillment: source: corepack mode: runJava owns the JDK capability here, while Maven stays a standalone tool because it is not modeled as a Java-owned package-manager lane today.
toolchains: java: version: "21" fulfillment: source: sdkman mode: run tools: maven: "*"Python owns the interpreter and the uv package-manager lane here, which is the canonical shape when the repo relies on uv-managed Python setup.
toolchains: python: version: "3.12" package_managers: uv: "*" fulfillment: source: uv mode: runA simple Go example where the toolchain owns both version truth and run-path fulfillment through the Go source.
toolchains: go: version: "1.24" fulfillment: source: go mode: runRuby owns both the interpreter and Bundler lane here, so repos do not need to hide Bundler hydration behind raw bundle install shell glue.
toolchains: ruby: version: "3.3.11" package_managers: bundler: "2.5" fulfillment: source: ruby mode: runA minimal .NET shape where the SDK capability and its fulfillment path are both explicit, keeping the repo off ad hoc installer commands.
toolchains: dotnet: version: "9.0" fulfillment: source: dotnet mode: runUse an orchestrator block like this when repo execution should run through a declared manager such as mise instead of embedding trust and install commands in tasks.
orchestrators: mise: kind: mise required: true config_files: - mise.toml activation: trust: true prepare: install: trueUse this when repo tasks should run through devbox and ota should own the selected-path install step instead of shell glue.
orchestrators: devbox: kind: devbox required: true config_files: - devbox.json prepare: install: trueUse this when repo tasks should run through devenv and the contract only needs mediated execution in the current slice.
orchestrators: devenv: kind: devenv required: true config_files: - devenv.nixWhat this is not
- not a second task language
- not a place for arbitrary setup commands
- not a replacement for services or launch sources
- not the right home for Docker itself
- not the right home for OS-native build-tool bundles
Current shipped slice
- top-level
toolchains, top-levelorchestrators, and task-scopedrequirements.toolchainsare supported - Rust, Node, Java, Python, Go, Ruby, and .NET are the shipped toolchain names today
ota doctordiagnoses missing toolchains and orchestrators without mutation, and Ruby can govern Bundler throughtoolchains.ruby.package_managers.bundler- for
toolchains.rubywithfulfillment.mode: run, ota currently uses the selected Ruby to hydrate the declared Bundler lane structurally instead of treatingbundle installas shell glue - selected
ota run/ workflowota uppaths can provision shipped fulfillment sources plusfulfillment.source: misewhenfulfillment.mode: runis declared and policy allows it - shipped orchestrator kinds are currently
mise,devbox, anddevenv;activation.trustis currentlymise-only, andprepare.installis currently supported formiseanddevbox - command-backed
prepare.kind: dependency_hydrationandprepare.kind: tool_bootstrapcan useexecution.orchestrator.mode: exec; mixed/nativeprepare.kind: sequencecannot in the current slice - duplicate ownership is invalid and fails validation
- Ota's own contract now uses this model instead of shell-based managed-surface setup