# ota Command Reference 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](json-output-reference.md). For the shipped assist workflow and refusal rules, see [assist-workflow.md](assist-workflow.md). For canonical exit-code behavior, see [exit-codes.md](exit-codes.md). For service behavior across commands, see [service-behavior.md](service-behavior.md). For platform shell behavior, see [shell-semantics.md](shell-semantics.md). For text rendering and plain-mode behavior, see [output-style.md](output-style.md). For visual identity tokens and CLI/docs branding, see [brand-style.md](brand-style.md). For docs clarity rules and command-UX wording standards, see [docs-clarity-spec.md](docs-clarity-spec.md). For compatibility boundaries in the active version, see [compatibility-surface.md](compatibility-surface.md). For extension execution staging, see [extension-execution-boundary.md](extension-execution-boundary.md). For provider-neutral CI shells and receipt/annotation wiring, see [ci-pipeline-workflow.md](ci-pipeline-workflow.md). For hosted validation and PR-gating guidance, see [hosted-validation-workflow.md](hosted-validation-workflow.md). For the official GitHub Actions wrapper, see [github-action-workflow.md](github-action-workflow.md). Doctor first, contract second. ## Recommended onboarding flow 1. `ota doctor` 2. if the repo does not yet have `ota.yaml`, preview with `ota detect --dry-run .` 3. review the first write with `ota init --dry-run .` 4. choose an explicit first write with `ota init .` or `ota detect --write .` 5. `ota validate` 6. `ota up --dry-run` 7. `ota up` 8. `ota run ` or `ota proof runtime --workflow ` ## Global ```bash ota --help ota --version ota --version --json ota --debug ota --plain ota --concise ota --verbose ota --file /path/to/ota.yaml ``` Repo commands that read an existing `ota.yaml` can also target a monorepo member with: ```bash ota --member [PATH] ``` ota currently ships these commands: - `ota doctor` - `ota explain` - `ota up` - `ota run ` - `ota init` - `ota env` - `ota execution plan` - `ota execution topology` - `ota proof runtime` - `ota ci projection` - `ota ci github render` - `ota ci github check` - `ota ci github sync` - `ota assist declare-readiness` - `ota assist declare-service` - `ota assist bind-task` - `ota assist declare-env` - `ota assist add-task` - `ota assist wire-setup` - `ota assist normalize` - `ota detect` - `ota validate` - `ota tasks` - `ota workflows` - `ota services` - `ota diff` - `ota check` - `ota annotations` - `ota json validate` - `ota agents` - `ota clean` - `ota extensions` - `ota policy` - `ota policy init` - `ota policy review` - `ota uninstall` - `ota self-update` / `ota upgrade` - `ota skills install` - `ota workspace init` - `ota workspace detect` - `ota workspace validate` - `ota workspace tasks` - `ota workspace list` - `ota workspace execution plan` - `ota workspace run ` - `ota workspace check` - `ota workspace doctor` - `ota workspace explain` - `ota workspace up` - `ota workspace refresh` - `ota workspace diff` - `ota workspace status` - `ota workspace receipt` Start here: ```bash ota doctor ota detect --dry-run . ota init --dry-run ota up ota run ci ``` Workspace: ```bash 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 ` - `OTA_FILE` - explicit file `PATH` - an explicitly supplied directory `PATH` is treated as the contract boundary - upward discovery from the current directory when no `PATH` is supplied When the discovered `ota.yaml` is a declared monorepo member contract, ota now loads the merged member contract automatically from that member path. `ota detect` is different. Its `PATH` is a repo root to inspect. Global output modifiers: - `--concise`: reduce high-noise text output while preserving decisions and actions - `--verbose`: preserve full explanatory text output - `--json`: unaffected by `--concise`/`--verbose` - `--debug` emits command-phase tracing to stderr Current progress behavior: - quiet blocking commands show a delayed spinner in interactive terminals - `ota doctor` and `ota check` keep their own check/progress handling - `ota run` keeps streaming/progress-focused behavior instead of the shared spinner - `ota up` uses the shared spinner by default; `ota up --stream` opts into raw live provisioning, service-start, and setup output - `ota workspace doctor` uses the shared spinner - `ota workspace status` uses the shared spinner - `ota workspace doctor --json` still uses the shared spinner on stderr in interactive terminals, while stdout remains valid JSON - `ota workspace doctor --json --progress-json` switches that doctor lane from spinner updates to live NDJSON workspace progress on stderr while stdout remains the final JSON report - `ota workspace status --json --progress-json` switches that status lane from spinner updates to live NDJSON workspace progress on stderr while stdout remains the final JSON report, and each event carries the repo drift state in `tail` - `ota workspace list --json` also uses the shared spinner on stderr in interactive terminals, while stdout remains valid JSON - `ota workspace validate`, `ota workspace tasks`, `ota workspace list`, `ota workspace detect`, and `ota workspace init` use the shared spinner when they are waiting on work - successful interactive commands may print a best-effort update notice when a newer release exists, and the notice says `A newer \`ota\` release is available: vX.Y.Z` and points to `ota self-update` or `ota upgrade` Hosted validation guidance: - use `ota validate --json` and `ota doctor --json` for repo gating - use `ota workspace validate --json`, `ota workspace doctor --json`, and `ota workspace explain --json` for workspace gating and remediation planning - `ota validate --json` keeps legacy `warnings[]` strings and also exposes additive `warning_details[]` entries with stable advisory codes for governance-aware tooling; selected dependency-plane boundary advisories also carry additive provenance describing which backend selection lane won on the parent and dependency task - use `ota workspace tasks --json` and `ota workspace list --json` for workspace inventory, task availability, and preflight readiness summaries - do not mutate contracts during hosted validation ## Current exit semantics - `0`: success, ready state, or warning-only diagnosis - `1`: invalid contract, blocking readiness issue, protected write failure, or general command failure - `2`: CLI usage or argument parsing error - `ota run`: preserves child task exit codes on task failure - `ota up`: preserves provisioning, service-start, and setup child exit codes when those commands fail The canonical registry is in [exit-codes.md](exit-codes.md). ## `--debug` `--debug` emits command-phase tracing to stderr. Current intent: - help humans and agents understand which path or mode a command resolved - keep normal stdout stable - avoid persistent trace output or verbose default output - use the trace channel for multi-step commands like `ota up`, `ota run`, `ota workspace up`, `ota workspace refresh`, `ota workspace diff`, `ota workspace status`, `ota workspace run`, `ota doctor`, `ota detect`, `ota diff`, and `ota explain` ## `ota validate` Validate an ota contract. ```bash ota validate [PATH] ota validate --json [PATH] ota validate --member api [PATH] ``` Current behavior: - resolves `ota.yaml` using `--file`, `OTA_FILE`, or an explicit directory boundary - when `--member` is set, loads the root contract, merges the declared member override, and validates the merged contract - when a root contract declares `workspace.type: monorepo`, `ota validate` also validates each declared merged member contract - parses the contract - applies semantic validation - emits advisory warnings when authoring choices are valid but likely misleading, such as `depends_on` crossing execution boundaries or isolated cache paths that are not wired to the tool's effective `/workspace/...` path - includes provider-specific target examples for remote target validation errors: - `daytona`: `sandbox-dev` - `ssh` / `tsh`: `user@host` - `kubectl`: `pod/ota-dev` - exits `0` on success and non-zero on failure Text output: - success: `VALID ` followed by next-step guidance into `ota doctor` and `ota tasks --use` - failure: validation or load error text JSON output: - success: `ok`, `path`, `summary.error_count`, `summary.warn_count`, and `warnings` - failure: `ok`, `path`, `summary.error_count`, `summary.warn_count`, `warnings`, and either `errors` or `error` ## `ota tasks` List tasks from a validated contract. ```bash ota tasks [PATH] ota tasks --json [PATH] ota tasks --all [PATH] ota tasks --safe [PATH] ota tasks --unsafe [PATH] ota tasks --via native [PATH] ota tasks --via container [PATH] ota tasks --member api [PATH] ota tasks --member api --member web --json [PATH] ota tasks --safe --use [PATH] ota tasks --unsafe --use [PATH] ``` Current behavior: - runtime-proof output publishes a terminal `proof_verdict`: `passed`, `passed_with_unproven_boundaries`, or `failed`; use it with `proof_scope` and `not_proved` so a narrow green proof is not misread as broader repo completion - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota tasks` lists root tasks and grouped summaries for each declared member - when `--member` is set, lists tasks from the merged member contract - repeated `--member` values list tasks for those members in the provided order - hides `internal: true` tasks by default and includes them only when `--all` is set - prints tasks in deterministic order - resolves the execution form for the current OS - renders structured `launch` sources additively when a task uses command or packaged-container launch instead of shell `run` / `script` - includes task metadata when present - includes task `env` and `inputs` when present - includes declared task `effects` when present so repo mutation, network dependency, and out-of-repo state changes stay explicit - includes task `description` and optional `notes` when present, where `notes` carries purpose and extra guidance - includes an `agent` summary when the contract declares one - includes variant summaries when variants are declared - `--use` expands each task with human and agent run commands, closure-aware agent policy, command preview, safety posture, declared effects, dry-run JSON, receipt follow-up guidance, plus task description and required inputs when present; plain `ota tasks` remains the full declaration view for dependencies, services, conditions, hooks, notes, and related metadata - `--safe` and `--unsafe` are mutually exclusive filters over the effective safe set (`safe_for_agent: true` plus `agent.safe_tasks`) - `--via ` filters to tasks runnable through the selected backend lane - `--all` includes orchestration tasks marked `internal: true`; those entries carry `internal: true` in JSON output - task JSON keeps `use.human` and `use.agent` as selected-mode compatibility projections, while `use.modes[]` is the canonical human/agent mode matrix; it names supported and unavailable Container/Native planes explicitly and includes Remote only when the task advertises it Text output: - header: `TASKS ` - when agent guidance is present, the shared `AGENT` block is grouped into `Overview`, `Execution`, and `Boundary` sections with counted wrapped lists and writable-path root/exception collapsing - each task may include `kind`, `os`, `category`, `depends_on`, `safe_for_agent`, and variant count - each task may include `Launch` when the resolved execution source is structured `launch` - each task may include `Prepare` when the resolved execution source is a first-class `prepare` body, including ordered `prepare.kind: sequence` summaries - each task may include `env`, `inputs`, `effects`, `requires_services`, and `requires_artifacts`; generated artifact producer/consumer truth remains declared under top-level `artifacts` - each task may include `Description` and `Notes`, where `Notes` can describe purpose and usage - each task includes `Human Run`, with every advertised mode in stable `Container`, `Native`, then `Remote` order; the selected lane is marked `(Default)` - aggregate tasks inherit a mode only when every concrete task in their dependency closure supports that backend on the current platform; the aggregate itself never needs a duplicate command body merely to make its runnable modes discoverable - `Container` and `Native` remain visible when unavailable, with an explicit unsupported status instead of a fabricated command or silent omission - each task includes `Agent Run`; it prints `ota run --agent` only when the full dependency closure is effectively agent-callable, otherwise it names the review-required task or closure - each task includes `Agent Policy`, which distinguishes declared `safe_for_agent` truth from the effective closure-aware decision - each task includes a short execution preview Common operator lanes: - `ota tasks --safe`: show only effective agent-safe tasks - `ota tasks --unsafe`: show only non-safe tasks - `ota tasks --safe --use`: show safe tasks plus canonical run commands, safety posture, and machine-readable follow-up commands - `ota tasks --unsafe --use`: show non-safe tasks plus canonical run commands, review posture, and machine-readable follow-up commands - `ota tasks --via native`: filter to tasks runnable through native execution - `ota tasks --via container`: filter to tasks runnable through container execution Agent discoverability: - `ota tasks --safe --use` is the canonical task-discovery surface before agent execution - use it to find the effective safe task surface, the canonical `ota run --agent` command, selected mode hints, safety posture, and matching dry-run / receipt follow-up commands - once a task is selected from that surface, use `ota run --agent` when the execution itself should enforce the declared agent-safe boundary JSON output: - success: `ok`, `path`, `tasks` - `agent` is included when the contract declares agent guidance - monorepo root summaries include grouped per-member results in `members` - repeated `--member` values return grouped per-member results in `members` - each task includes the resolved execution plus optional `selected_variant_os` and `variants` - each task may include additive `effects` with declared `writes`, `workspace_writes`, `network`, and `external_state` - each task may include additive `prepare`; sequence prepares keep a nested `steps[]` tree, while dependency-hydration prepares expose `medium`, `source_kind`, `cwd`, `manager`, and `mode` - each task may include additive `launch` when the resolved execution source is structured command or packaged-container launch - failure: `ok`, `path`, and either `errors` or `error` ## `ota workflows` List declared workflows from a validated contract. ```bash ota workflows [PATH] ota workflows --json [PATH] ota workflows --member api [PATH] ota workflows --member api --member web --json [PATH] ``` Current behavior: - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota workflows` lists root workflows and grouped summaries for each declared member - when `--member` is set, lists workflows from the merged member contract - repeated `--member` values list workflows for those members in the provided order - prints declared workflows in deterministic order - resolves workflow surface exposes through the selected run task so the output shows the same URL surfaces `ota doctor`, `ota up`, and `ota execution plan` would target - keeps workflow discovery read-only; it does not prepare or run the workflow Text output: - header: `WORKFLOWS ` - 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 ` and `Proof: ota proof runtime --workflow ` - workflow `services` summaries include both `workflows..services.required` and transitive task-level `requires_services` from the selected prepare/setup/run dependency closure - each workflow may include `intent`, `description`, `notes`, `prepare`, `setup`, `run`, `services`, `run_launch`, `readiness_checks`, `readiness_probes`, `readiness_surfaces`, `signal_readiness_checks`, `signal_readiness_probes`, `signal_readiness_surfaces`, `exposes`, and the per-entry `default` flag - when no workflows are declared, the text output says so explicitly and points users back to `ota tasks` or contract authoring instead of ending empty JSON output: - success: `ok`, `path`, `default`, `workflows` - monorepo root summaries include grouped per-member results in `members` - repeated `--member` values return grouped per-member results in `members` - each workflow includes the resolved workflow summary plus a `default` boolean - failure: `ok`, `path`, and either `errors` or `error` Agent workflow discoverability: - use `ota workflows --json` when you need machine-readable workflow posture before selecting one - use `ota tasks --safe --use` alongside it when the workflow decision depends on which safe task lanes are actually callable underneath that workflow ## `ota services` List declared services from a validated contract. ```bash ota services [PATH] ota services --json [PATH] ota services --member api [PATH] ota services --member api --member web --json [PATH] ``` Current behavior: - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota services` lists root services and grouped summaries for each declared member - when `--member` is set, lists services from the merged member contract - repeated `--member` values list services for those members in the provided order - prints declared service fields in deterministic order - services are not direct task entrypoints; they are managed by `ota doctor` and `ota up` Text output: - header: `SERVICES ` - each service may include `required`, `provider`, `depends_on`, `start`, `stop`, `healthcheck`, `timeout`, and a management note - when no services are declared, the text output says so explicitly and points users back to `ota doctor` or contract authoring instead of ending empty JSON output: - success: `ok`, `path`, `services` - monorepo root summaries include grouped per-member results in `members` - repeated `--member` values return grouped per-member results in `members` - failure: `ok`, `path`, and either `errors` or `error` ## `ota env` Inspect resolved environment requirements from a validated contract. ```bash ota env [PATH] ota env --json [PATH] ota env --workflow docker-build [PATH] ota env --task test [PATH] ota env --member api --workflow docker-build --task test [PATH] ``` Current behavior: - validates the contract first - when `--member` is set, inspects the merged member contract - when `--workflow` is set, inspects the selected workflow instead of assuming `workflows.default` - when the selected workflow declares `env.profile`, env resolution prepends that workflow-owned profile truth before reporting values - when `--task` is set, includes the effective execution env for that task alongside the contract env view - when `--task` is set, any `tasks..requirements.env` entries are treated as required for that selected task view even when the same top-level `env.vars.` entry is optional in the repo-wide contract - resolves values in the same precedence order as task execution - reports declared env source status alongside the env-variable view - shows the winning source for each contract env entry - declared source provenance applies uniformly across curated `dotenv`, `properties`, `json`, `yaml`, and `toml` env sources - reports missing required env and invalid allowed values - stays read-only - uses the shared declared-source loader, so parse failures, structure failures, and normalized-key collisions are reported with the same source-scoped truth as execution and doctor Text output: - header: `ENV ` - includes a readiness status line, optional `Workflow` / `Profile` lines, a short overview, a `Declared env sources` section when sources exist, a `Rendered env artifacts` section when the selected workflow profile materializes files, and separate `Contract env` / `Execution env` sections when task-specific execution env is present - each env entry may include `kind`, `required`, `value`, `source`, `source kind`, `source path`, `source status`, `status`, `allowed`, `default`, and `Next` - each declared source may include `kind`, `path`, `label`, `must_exist`, `status`, `detail`, and `Next` - each rendered env artifact may include `path`, `kind`, `exists`, and `includes` - missing or invalid contract env entries point to a specific fix rather than guessing Example: ```text ENV ./ota.yaml Ready: yes Declared env sources - properties app.properties label=properties:app.properties status=loaded - json env/runtime.json label=json:env/runtime.json must_exist=true status=loaded Contract env - DISCORD_TOKEN required=true value=*** source=properties:app.properties source_kind=properties source_path=app.properties source_status=loaded status=resolved - DOCS_SITE_BASE_URL required=true value=https://docs.internal.example source=org policy status=resolved - RELEASE_CHANNEL required=false value=stable source=default status=resolved allowed=[stable, canary] Execution env - OTA_WORKSPACE value=/workspace source=execution status=task - CI value=true source=task status=task ``` JSON output: - success: `ok`, `path`, `summary`, `sources`, `env` - success with workflow scope also includes `workflow`, optional `profile`, and optional `rendered_artifacts` - success with task scope also includes `task` - `summary` includes contract, declared-source, task, resolved, missing, and invalid counts - failure: `ok`, `path`, `task` and `workflow` when relevant, and `error` ## `ota execution plan` Inspect the resolved execution context without provisioning, starting services, or running tasks. ```bash ota execution plan [PATH] ota execution plan --json [PATH] ota execution plan --mode container --ephemeral [PATH] ota execution plan --member api [PATH] ota execution plan --workflow backend [PATH] ``` Current behavior: - validates the contract first - when `--member` is set, inspects the merged member contract - when `--workflow` is set, plans the selected workflow instead of assuming `workflows.default` - when the selected workflow declares `run.task`, planning resolves that canonical runtime path first; if the workflow only declares `setup.task`, planning uses that setup path as the fallback - when the selected workflow also declares `prepare.task`, planning reports that host file-prep phase inside the additive workflow summary but does not use it as the concrete execution `task` - reuses the same backend and lifecycle resolution path as `ota run` and `ota up` - when named contexts use `execution.contexts..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 ` - status line: `RESOLVED` - optional `Workflow` section when the repo declares workflows and planning targets one explicitly - `Resolved` section with selected backend, lifecycle, image, engine candidates, target, and target strategy - selected `Task` when a workflow run task, or setup-only fallback, is the concrete planning source; `prepare` remains additive workflow context, not runtime identity - `Contract` section with the same compact contract identity used by receipts - `Execution` section when the contract declares execution intent - `Overrides` section when `--mode`, `--lifecycle`, or `--ephemeral` changed the resolved result JSON output: - success: `ok`, `path`, `contract`, `member` when relevant, additive `workflow` and `task` when workflow planning selected a canonical path, `contract_identity`, `declared_execution`, optional `workflow_env_artifacts`, `resolved`, and `overrides` - `workflow_env_artifacts` reports workflow-owned rendered env files such as rendered dotenv artifacts, including their `path`, `kind`, `profile`, current `exists` state, included env names, and consuming task/service lanes - failure: `ok`, `path`, `member` when relevant, and either `errors` or `error` ## `ota execution topology` Inspect the declared execution topology without provisioning, starting services, or running tasks. ```bash ota execution topology [PATH] ota execution topology --json [PATH] ota execution topology --member api [PATH] ``` Current behavior: - validates the contract first - when `--member` is set, inspects the merged member contract - stays read-only - reports the contract identity, declared execution surface, shared backends, reusable readiness probes, reusable runtime surfaces, services, normalized runtime listeners, and task target bindings exactly as the repo declares them - when tasks use structured `launch`, surfaces render that launch source additively instead of flattening it back into shell-only text - when runtimes attach reusable `surfaces`, shows both the declared top-level surfaces and the normalized listener truth that those attachments produced - task-target readiness probes surface their current reachability plane explicitly; top-level probes report whether they resolve from the invoking command host or from one named observer task plane - does not resolve effective readiness state or start anything; this is topology inspection, not execution planning Text output: - header: `EXECUTION TOPOLOGY ` - `Overview` section with project and topology counts - `Execution` section when the contract declares execution intent - `Shared Backends`, `Readiness Probes`, `Surfaces`, `Services`, and `Tasks` sections with runtime/listener/launch/target detail when present JSON output: - success: `ok`, `path`, `contract`, `member` when relevant, `contract_identity`, `declared_execution`, `shared_backends`, `readiness_probes`, `surfaces`, `services`, and `tasks` - top-level `readiness_probes` entries include literal URL or target source details plus the declared HTTP/TCP request contract fields that belong to that probe - top-level `surfaces` entries include declared surface kind, port, optional label, optional purpose, optional visibility, optional path, and optional readiness contract - task entries may include additive `launch` when the task uses structured command or container launch - task runtime entries may include `backend_binding`, `readiness`, `attached_surfaces`, additive `surface_attachments`, and normalized `listeners` - task target entries may include `activation_mode`, `override_input`, `url`, and typed `service` references - failure: `ok`, `path`, `member` when relevant, and either `errors` or `error` ## `ota proof lifecycle` Prove one bounded, manager-owned service lifecycle transaction without copying start, readiness, or teardown shell commands into the workflow. ```bash ota proof lifecycle --workflow smoke [PATH] ota proof lifecycle --workflow smoke --service database [PATH] ota proof lifecycle --workflow smoke --mode container [PATH] ota proof lifecycle --json --workflow smoke [PATH] ota proof lifecycle --json --archive --workflow smoke [PATH] ``` Current behavior: - requires the selected workflow to declare `proof.lifecycle.services[]` - refuses an explicit `--service` outside that declared lifecycle scope - evaluates declared replay-input pins and active replay-input policy over the exact workflow prerequisite-plus-assertion closure before creating a lifecycle transaction, running a task, observing or starting a service, or executing the assertion; JSON refusal carries `execution_started: false` plus the hard-pin and policy evidence - refuses with `replay_input_policy_unavailable` before lifecycle work when the active policy source cannot be loaded - executes the selected workflow's prerequisite closure before acquiring lifecycle ownership - refuses a concurrent lifecycle transaction for the same repository before it can observe manager state or acquire a cleanup lease - observes manager-owned initial state, refuses a pre-existing active service, and acquires a cleanup lease only after a current inactive observation. A service declaring `boundary_terminated` instead runs inside a fresh runner-owned ephemeral container session; Ota attests that boundary absent before start and does not claim a host-wide manager state - starts dependency services in declared order, checks declared readiness, and runs the optional finite assertion after readiness; assertion service requirements reuse the transaction-owned services rather than issuing another start command - JSON emits a typed assertion result with its terminal state and exit code when available; failed or interrupted assertions include runner-captured stdout/stderr tails bounded to 8 KiB per stream after declared secret values are redacted. Diagnostic output does not widen lifecycle proof into application-output proof - attempts teardown in reverse order and requires a positive manager inactive observation before releasing each lease. For an isolated boundary, it removes the exact session in the finalizer and re-observes its absence before emitting `boundary_terminated`; a successful remove command alone is insufficient. Failed isolated cleanup is runner-attested incomplete finalization, not manager-derived inactivity. Exact session removal proves the runner-owned session cannot retain a process, not that a host manager or broader application output is stopped - emits `passed_with_unproven_boundaries` for a successful transaction because lifecycle state transitions do not prove broader application output or repo completion; lifecycle proof never emits a bare `passed` - `--archive` writes local content-addressed transaction evidence. Its filename, semantic snapshot reference, contract identity, complete resolved service closure, declared teardown authority, terminal finalization, and effective execution scope are verified before Ota reports the archive. An isolated-boundary record and archive scope also carry Ota's exact engine/session identity, which must match before `boundary_terminated` is accepted. It remains outside claim assurance, replay, and CI projection Use it for a contract-owned contributor smoke that must prove Ota starts and cleans up only the service state it owns. It is not a replacement for an application-output or deployment proof. ## `ota proof runtime` Prove that one selected runtime path can become ready, capture the canonical execution artifacts, and tear the runtime back down. ```bash ota proof runtime [PATH] ota proof runtime --workflow app [PATH] ota proof runtime --ready-timeout 10m --workflow app [PATH] ota proof runtime --mode container --persistent [PATH] ota proof runtime --member api --workflow backend [PATH] ota proof runtime --json --workflow app [PATH] ota proof runtime --json --archive --workflow app [PATH] ota proof runtime --workflow app --negative-control postgres-unavailable [PATH] ``` Current behavior: - validates the contract first - evaluates declared replay-input pins and active replay-input policy before creating `.ota/proof`, writing topology or Doctor artifacts, or spawning the child runtime. The evaluated closure includes post-readiness seam observers and the selected negative-control task; JSON refusal carries `execution_started: false` plus the hard-pin and policy evidence - refuses with `replay_input_policy_unavailable` before proof artifacts or child execution when the active policy source cannot be loaded - when `--member` is set, proves the merged member contract from the monorepo root - when `--workflow` is set, proves that selected workflow path; otherwise it uses the effective default workflow or the default task path when the repo has no workflows - captures the canonical runtime-proof artifacts under `.ota/proof//` (and under the selected member prefix when `--member` is set): - `topology.json` - `doctor.json` - `up.log` - reuses the existing execution and diagnosis boundaries instead of inventing a parallel proof implementation: the topology artifact is the same `ota execution topology --json` surface, the doctor artifact is the same `ota doctor --json` surface, and the up log is the repo-level preparation report for the selected path - supports `--archive` with `--json` to write an immutable, content-addressed terminal proof record under `.ota/proof/archives/`; it binds the terminal proof JSON to an archived semantic contract snapshot, clean Git source identity when available, the resolved execution scope (`workflow`, primary task, backend, provider, lifecycle, and target), and explicit `replay_posture: witness_only`; later assurance verifies the referenced snapshot's content identity before admitting the proof archive - treats `.ota/proof//` as a mutable working bundle only. Its topology, doctor, and log paths may support diagnosis, but only the `--archive` record is a replay-grade proof witness - honors the selected path's declared readiness timing policy when a workflow/task surface defines startup timing such as `start_period`, `interval`, `timeout`, and `retries` - when the selected run task is a service launcher that exits successfully before the runtime is actually ready, such as `docker compose up -d`, proof keeps probing until the declared readiness budget expires instead of treating the launcher exit itself as a readiness failure - supports `--ready-timeout ` to cap the runtime-proof readiness wait budget with explicit values such as `90s`, `5m`, or `1h` - supports `--negative-control ` for a workflow-declared finite control task; Ota runs the selected task only after the ordinary proof path and its declared green seam obligation have passed. The task must exit non-zero and write a matching transaction-bound failure attestation for the contract-declared expected failure; only then does Ota validate the control and promote that exact dependency evidence to `fault_tested` - runs declared `workflows..proof.seam_observations[]` after readiness and before teardown; Ota injects one opaque marker into the declared producer task, never the finite observer. The observer must recover it through the declared dependency and write a runner-owned transaction attestation; only a verified attestation becomes `exercised` dependency evidence - renders `Dependency Evidence` before proof boundaries when Ota has runner-derived service reachability or caller-side attempt evidence; the render keeps its level, origin, and authority class visible instead of collapsing it into the proof verdict - uses the same backend/lifecycle override rules as `ota doctor` and `ota up` - attempts repo-scoped runtime cleanup after capturing artifacts so proof does not leave persistent Ota-managed runtime state behind - surfaces cleanup failures explicitly as a proof failure instead of silently dropping them Use it when you need clean-machine proof that one declared front door actually becomes operational, or when you want one archiveable readiness lane for CI and case-study adoption work. Text output: - header: `PROOF ` - `Workflow` and `Mode` lines first - success output includes a compact `Steps` section, final `READY` line, and `Artifacts` paths - blocked output keeps the shared `NOT READY` / `BLOCKED` status language, shows the failing proof phase, and surfaces one primary `Why` / `Next` lane without duplicating the full doctor report - blocked proof output may also include `Likely cause` when ota can derive a higher-confidence runtime-drift hint from captured proof logs - timeout output uses `TIMEOUT` when runtime-proof wait budget is exhausted and keeps the proof phase at `readiness` - interruption output uses `INTERRUPTED` and phase `interrupted` when runtime proof is terminated by a signal (for example CI cancellation) JSON output: - success or blocked proof output: `ok`, `path`, `mode`, optional `workflow`, `phase`, shared `summary`, optional `artifacts`, optional `workflow_env_artifacts`, optional advisory `likely_cause`, and optional cleanup `error` / `next` - `workflow_env_artifacts` uses the same rendered-artifact summary as `ota execution plan`, so CI and agents can see which workflow-owned env file was materialized and which task/service lanes consume it during proof - `--archive` adds `archive.identity` and `archive.path`; the archive identity is the SHA-256 of the stored record, so consumers can verify the exact terminal proof they select - after an archived runtime proof, `ota doctor --json` publishes a workflow `claim_assurance[]` `proof_breadth` record. It is `supported` only when a content-addressed proof archive matches the current semantic contract, clean source identity, resolved execution scope, and `replay_posture: witness_only`; a matching failed proof is `contradicted`, while missing, stale, source-mismatched, or scope-mismatched evidence remains `unknown` - contract load or validation failures still use the standard validation failure surface instead of inventing a second invalid-contract payload ## `ota ci projection` and `ota ci github` Render a dedicated, Ota-owned reusable GitHub Actions workflow after `ota.yaml` has become the reviewed execution authority. Use it to remove duplicated bootstrap and verification shell from CI without letting Ota take over triggers, permissions, secrets, runners, environments, or deployment jobs. ```bash ota ci github render --workflow verify --target-os linux ota ci github render --workflow verify --target-os linux --output .github/workflows/ota-governance.yml --json ota ci github check --workflow verify --target-os linux \ --output .github/workflows/ota-governance.yml \ --caller .github/workflows/ci.yml ota ci github sync --workflow verify --target-os linux \ --output .github/workflows/ota-governance.yml \ --caller .github/workflows/ci.yml ota ci projection --workflow verify --mode container --target-os linux --json ota ci github render --workflow verify --mode container --target-os linux ``` Current behavior: - `render` is pure: it prints deterministic reusable-workflow YAML and never writes the optional `--output` target. - `ota ci projection` is the provider-neutral source of truth. It evaluates the full selected workflow agent closure, including declared prepare, setup, run, and attach task roots, and any declared proof assurance before rendering; policy-denied or review-required lanes fail projection with the same canonical reason the runner would enforce. `--mode` selects its native, container, or remote plane; provider adapters consume that exact projection rather than reconstructing contract authority. - The projection carries `run_execution` as `finite_task` or `service_runtime`. Generated adapters prepare every lane with `ota up`; they execute a finite run task through `ota run --agent` after preparation, while service and proof lanes retain their one authoritative runtime path. - Omitting `--mode` resolves the selected task's effective contract default. `--target-os` is explicit projection truth and must match the intended CI operating system. Supplying an unavailable mode for that target is refused before rendering. A denied projection remains inspectable in JSON with its evaluated identity, governance decision, and refusal basis. - The generated workflow owns checkout, immutable adapter revisions, `ota-run/setup` with `source: contract`, provider-adapter setup for contract-required toolchains, selected `ota validate`, `ota doctor --workflow`, safe-surface discovery, agent dry-run, and either agent execution plus receipt archival or, for a proof-required lane, one authoritative `ota proof runtime` execution. Each declared, unique `agent.refusal_canaries` control renders as its own provider check using Ota's `--expect-refusal` exit semantics rather than provider-shell assertions. Each check maps one stable canonical V11.5 `merge_check_id` to a scope-qualified provider name such as `ota.refusal-canary.task.publish (linux/container)`, so native and container or OS lanes do not collapse into one GitHub check. A proof claim never bypasses agent admission. - The human-owned caller must invoke the generated path and pass its exact `ota_projection_identity` and `ota_target_os`. It may also choose `ota_runner`; the renderer's `--runner` value is only the reusable workflow's default. Ota checks the projection identity rather than reconstructing execution commands from caller YAML. - The generated workflow verifies identity through `ota ci projection --expect-identity`, not a provider-shell assertion. Runner selection remains provider-owned; target OS is bound explicitly. Ota refuses the projection before rendering when any executable member of the selected workflow closure or its resolved execution context does not support that target OS and mode. `projection.toolchains[]` is provider-neutral contract truth and each entry carries its selected `execution_scopes`. The GitHub adapter provisions only `native`-scoped toolchains; container and remote scopes remain owned by their declared execution boundaries. It renders immutable `actions/setup-go` for `source: go`, translating a supported lower-bound range (for example `>=1.26` or `>=1.26,<1.27`) into Go's release selector (`1.26`), and immutable `actions/setup-node` for `toolchains.node` fulfilled through `source: corepack`, preserving the declared Node semver selector while disabling setup-node's provider cache until Ota activates the declared package manager, immutable `ruby/setup-ruby` for `toolchains.ruby` fulfilled through `source: ruby`, immutable `actions/setup-python` plus `astral-sh/setup-uv` for `toolchains.python` fulfilled through `source: uv`, and immutable `actions/setup-dotnet` for `toolchains.dotnet` fulfilled through `source: dotnet`, translating supported lower-bound Python and .NET ranges to concrete provider releases. It refuses a required toolchain source or range it cannot provision rather than silently assuming the selected runner image provides it. - `check` fails for a missing, stale, manually changed, or unowned generated workflow, or when the caller points at a different semantic projection. - `sync` is explicit and atomic. It creates a missing output or replaces only a file carrying the Ota ownership marker. It never writes the caller, so update that provider-owned file first when a contract change produces a new projection identity. - `--output` and `--caller` must be normalized repo-relative `.yml` or `.yaml` paths under `.github/workflows`. Ota rejects absolute or traversal paths and refuses to use the caller as its managed output. The first cut projects one declared workflow with `run.task`. It intentionally does not generate a full CI workflow or infer provider policy. Existing workflow YAML remains useful detection evidence, but it cannot silently rewrite `ota.yaml`. JSON output carries `ok`, `operation`, `projection.identity`, selected workflow/task, runner, `merge_check_ids`, required `toolchains`, adapter `provider_checks`, `proof_required`, `render_identity`, `binding_identity`, managed output path, caller path, and whether `sync` mutated its owned file. `render_identity` is adapter-scoped; `binding_identity` also binds the parsed human-owned caller reference. Failures carry a stable `code`, including `managed_output_unowned`, `managed_output_stale`, and `caller_projection_reference_mismatch`. ## `ota assist declare-readiness` Declare or refine structured readiness for one existing task runtime service or one existing managed service. ```bash ota assist declare-readiness --task [--style spring-http|http|tcp] [PATH] ota assist declare-readiness --service [--style spring-http|http|tcp|compose-health] [PATH] ota assist declare-readiness --member api --task [PATH] ota assist declare-readiness --json --task [PATH] ota assist declare-readiness --write --task [PATH] ``` Use it when the runtime surface already exists and you want Ota to propose the readiness contract instead of hand-authoring it. Current behavior: - defaults to preview mode and shows assumptions, the exact readiness block, and the next validation commands - `--write` applies the proposed readiness mutation and revalidates the updated contract before returning success - supports `--task` for `tasks..runtime.readiness` - supports `--service` for `services..readiness` - supports `--member` through the existing merged monorepo contract path while writing only to the selected member overlay file - supports `spring-http`, `http`, and `tcp` styles for task readiness - supports `spring-http`, `http`, `tcp`, and `compose-health` styles for managed service readiness - `--style compose-health` is service-only and requires a compose-managed service (`services..manager.kind: compose`) - task targeting can infer from existing runtime and listener truth when that choice is unique - managed service targeting requires explicit `--style` unless the service already has a structured readiness kind that assist is refining - refuses when the target is ambiguous, unknown, missing the runtime/service surface needed for a truthful readiness declaration, or when the requested style conflicts with the selected listener protocol - text preview shows both current and proposed readiness when an existing readiness block would be replaced - `--json` emits the stable assist proposal/apply result shape described in [assist-operations.md](assist-operations.md) Examples: ```bash 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](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. ```bash ota assist declare-service --name --manager compose|host --port [PATH] ota assist declare-service --name --manager compose --compose-file docker-compose.yml --style tcp [PATH] ota assist declare-service --name --producer-repo --producer [:listener] [PATH] ota assist declare-service --member api --name --manager compose --port [PATH] ota assist declare-service --json --name --manager host --port [PATH] ota assist declare-service --write --name --manager compose --port [PATH] ``` Use it when the right next step is to create or refine a managed `services.` block instead of hand-authoring manager, endpoint, and readiness YAML. Current behavior: - defaults to preview mode and shows assumptions, the exact service block, and the next validation commands - `--write` applies the proposed service mutation and revalidates the updated contract before returning success - `--name` is required and identifies the managed service block under `services` - `--manager compose|host` chooses the manager kind for a new service and can refine an existing manager - `--producer-repo ` plus `--producer [:listener]` creates or refines a producer-owned `services..producer` block when the service is canonically owned by another repo in the same `ota.workspace.yaml` - `--endpoint`, `--address`, and `--port` control the selected endpoint projection; when safe, ota defaults the endpoint to `host` and the address to `127.0.0.1` - `--required true|false` sets the service requirement flag explicitly - `--style spring-http|http|tcp` adds or replaces structured readiness anchored to the selected endpoint - `--style compose-health` is valid for compose-managed services and anchors readiness to compose container health state (`readiness.kind: compose_health`) - `--compose-file`, `--compose-service`, and `--manager-name` refine compose-managed service metadata - producer-owned service previews remove conflicting local manager, endpoint, and readiness truth in favor of the canonical workspace producer binding - compose-managed previews default `manager.name` to `local` and `manager.service` to the declared service name when those values are otherwise absent - supports `--member` through the existing merged monorepo contract path while writing only to the selected member overlay file - refuses when the requested service shape is ambiguous or under-specified, such as a new service without an explicit manager kind - `--json` emits the stable assist proposal/apply result for this service declaration Examples: ```bash 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](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..targets.` binding to a producer task runtime. ```bash ota assist bind-task --task --target --to [:listener] [PATH] ota assist bind-task --task --target --to : --address-view topology|host|internal [PATH] ota assist bind-task --task --target --to : --activation ensure_ready [PATH] ota assist bind-task --member api --task --target --to : --write [PATH] ota assist bind-task --json --task --target --to [PATH] ``` Use it when the producer task runtime already exists and the correct next move is to wire one consumer target edge truthfully instead of hand-authoring `targets`. Current behavior: - defaults to preview mode and shows assumptions, the exact target block, and the next validation commands - `--write` applies the proposed `tasks..targets.` mutation and revalidates it before returning success - `--to ` works only when the producer exposes exactly one declared service listener or the existing target already pins one safe listener - `--to :` 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 ` selects a producer task from another declared monorepo member - `--address-view` and `--activation` refine the shipped target contract directly instead of hiding those fields behind heuristics - preserves an existing `override_input` unless a new one is supplied - refuses when the consumer task, producer task, or selected listener does not exist, or when assist cannot pick one listener safely - `--json` emits the stable assist proposal/apply result for this target-binding change Examples: ```bash 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](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. ```bash ota assist declare-env --name [--required true|false] [--secret true|false] [--default ] [PATH] ota assist declare-env --name PATH [--prepend ...] [--append ...] [PATH] ota assist declare-env --source-kind dotenv|properties|json|yaml|toml --source-path [--must-exist true|false] [PATH] ota assist declare-env --task --name --value [PATH] ota assist declare-env --member api --task --name --value --write [PATH] ota assist declare-env --json --source-kind dotenv --source-path .env.local [PATH] ``` Use it when the contract already knows which env surface should exist and the next safe move is one reviewed env mutation instead of broad contract inference. Current behavior: - defaults to preview mode and shows assumptions, the exact env block or task-local value, and the next validation commands - `--write` applies the proposed env mutation and revalidates it before returning success - root env requirements target `env.vars.` with `required`, `secret`, `default`, `allowed`, `prepend`, and `append` - declared env sources target one curated `env.sources[]` entry with `kind`, `path`, and optional `must_exist` - task-local env targets only one explicit `tasks..env. = ` write - `prepend` and `append` are allowed only for `PATH` - `secret: true` may not be combined with a new default value - supports `--member` through the merged monorepo contract path while writing only to the selected member overlay file - `--json` emits the stable assist proposal/apply result for this env mutation Examples: ```bash 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](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. ```bash ota assist add-task --name --run "" [PATH] ota assist add-task --name --script "" [PATH] ota assist add-task --name --kind sandbox [PATH] ota assist add-task --name --kind service --run "" --listener --protocol http|tcp --port [PATH] ota assist add-task --member api --name --run "" --write [PATH] ota assist add-task --json --name --run "" [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.`. Current behavior: - defaults to preview mode and shows assumptions, the exact new `tasks.` block, and the next validation commands - `--write` applies the proposed task creation and revalidates the updated contract before returning success - creates only new tasks in this slice; it refuses when the selected task name already exists in the effective contract - supports `command`, `service`, `setup`, `check`, and `sandbox` task kinds - requires `--run` or `--script` for every kind except `sandbox`, which uses the bounded starter body `echo sandbox` when no body is supplied - `--kind setup` only applies to the canonical `--name setup` task and defaults `internal: true` when you do not override it - `--kind service` requires `--listener`, `--protocol`, and `--port`, and currently declares one fixed listener plus a matching fixed host projection without adding readiness - supports `--member` through the merged monorepo contract path while writing only to the selected member overlay file - refuses service-only listener inputs on non-service task kinds - `--json` emits the stable assist proposal/apply result for this task creation change Examples: ```bash 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](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`. ```bash ota assist wire-setup --run "" [PATH] ota assist wire-setup --script "" [PATH] ota assist wire-setup --copy-from --copy-to [PATH] ota assist wire-setup --run "" --service [--service ...] [PATH] ota assist wire-setup --member api --run "" --write [PATH] ota assist wire-setup --json --script "" [PATH] ``` Use it when the contract needs one truthful `tasks.setup` declaration or when `setup.requires_services` should define which managed services must start before setup runs. Current behavior: - defaults to preview mode and shows assumptions, the exact `tasks.setup` block, and the next validation commands - `--write` applies the proposed setup mutation and revalidates the updated contract before returning success - `--run`, `--script`, and `--copy-from --copy-to ` set the setup body explicitly; a new setup task requires one of them - `--copy-from` / `--copy-to` writes a cross-platform `action.kind: copy_if_missing` setup task for file-template setup such as `.env.example` to `.env` - `--service ` sets `setup.requires_services` in the provided order as the pre-setup service phase - `--clear-services` removes `setup.requires_services` - `--internal true|false` refines `tasks.setup.internal` directly - supports `--member` through the existing merged monorepo contract path while writing only to the selected member overlay file - preserves unrelated existing `tasks.setup` fields instead of rewriting the whole task - refuses when a new setup task has no explicit body, when no actual setup change was requested, or when a named managed service does not exist - `--json` emits the stable assist proposal/apply result for this setup wiring change Examples: ```bash 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](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. ```bash ota assist normalize --task --into setup [PATH] ota assist normalize --member api --task --into setup --write [PATH] ota assist normalize --json --task --into setup [PATH] ``` Use it when the contract already has one setup-like task under the wrong task name and the right next step is to move that existing declaration into `tasks.setup` instead of hand-editing both the old and new task entries. Current behavior: - defaults to preview mode and shows assumptions, the current task block, the proposed canonical `tasks.setup` block, and the next validation commands - `--write` applies the normalization and revalidates the updated contract before returning success - the current shipped scope is one intent only: `--into setup` - removes the original `tasks.` entry and writes the moved task under `tasks.setup` - normalizes the moved task to `internal: true` so setup stays an `ota up` support task by default - supports `--member` only when the selected task is declared in that member overlay file; it refuses inherited root tasks because member overlays cannot delete those safely in this shipped slice - refuses when `tasks.setup` already exists, when the selected task does not exist, or when the selected task is already `setup` - `--json` emits the stable assist proposal/apply result for this normalization change Examples: ```bash 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](assist-workflow.md) when you need the fuller operator guide, refusal cases, or monorepo/member behavior. ## `ota diff` Compare two ota contracts semantically. ```bash ota diff ./before/ota.yaml ./after/ota.yaml ota diff ./repo-a ./repo-b ota diff --json ./before/ota.yaml ./after/ota.yaml ``` Current behavior: - compares two repo or workspace contracts as normalized semantic contract truth instead of raw YAML structure - also accepts archived receipt JSON or archived `.ota/contracts/...` snapshot JSON as either side of the comparison when you want to compare current truth against archived run truth - reports added, missing-in-target, and changed assumption keys in deterministic order - text output now includes explicit input identity for each side, so archived receipt snapshots are visible as first-class diff inputs instead of only being implied by the path - remains read-only - exits `0` when the comparison succeeds, even if differences exist - surfaces load and parse errors clearly - public operator guide: [`Semantic Snapshots and Correlation`](https://ota.run/docs/reference/semantic-snapshots-and-correlation) for archived semantic truth, snapshot inspection, and receipt-to-receipt drift correlation - local core spec: [semantic-snapshots-and-correlation.md](semantic-snapshots-and-correlation.md) Text output: - header: `DIFF -> ` - `MATCH` or `DIFFERENT` - explicit `Inputs` overview with per-side path, semantic input kind, and resolved snapshot path when a receipt-side diff resolves through `.ota/contracts/...` - readiness impact summary - grouped added, missing-in-target, and changed paths - additive category and risk labels per changed assumption - policy-section changes may include provenance labels - summary counts at the end JSON output: - success: `ok`, `base`, `target`, `summary`, `changes` - `changes[]` now comes from normalized semantic assumptions and can also include additive `category` and `risk` - policy-section changes may include `provenance` - failure: `ok`, `base`, `target`, and `error` Use this when you want to compare contract states before writing changes or to review the impact of a proposed edit in CI. ## `ota explain` Explain readiness findings as an ordered remediation plan. ```bash ota explain ./repo ota explain --json ./repo ota explain --member api ./repo ``` Current behavior: - requires an existing `ota.yaml` - diagnoses the contract first - turns grouped findings into an ordered remediation plan - prioritizes preview-first and contract-authoring actions ahead of later runtime follow-ups when several fixes are available - stays read-only and deterministic - prints a compact overview with step counts at the end If the repo does not yet have `ota.yaml`, start with `ota doctor`, then use `ota detect --dry-run .`, `ota detect --contract .`, and `ota init --dry-run .` before coming back to `ota explain`. Text output: - `Plan` section with ordered remediation steps - `Why` and `Next` lines for each step - `Provenance` lines when ota can trace the diagnosis source for that step - `Overview` counts at the end JSON output: - success: `ok`, `path`, `summary`, `actions`, `steps` - `actions` is the ordered grouped remediation plan; each action includes `order`, `action_key`, `action_title`, `severity`, `count`, `why`, and `next` - `actions` may also include shared `provenance` when the grouped action maps back to one diagnosis source - `steps` keeps the finding-level detail; each step includes `order`, `code`, `severity`, `summary`, `why`, and `next` - `steps[].code` is the stable finding/advisory identity surface for machine consumers; advisory-backed steps carry the contract advisory code directly instead of re-deriving it from rendered summary text - steps may also include `provenance` and `provenance_key` - failure: `ok`, `path`, and `error` ## `ota annotations` Render ota doctor findings as CI annotations or provider-neutral log lines. ```bash ota annotations --mode doctor --format github --input ./doctor.json ota annotations --mode workspace-doctor --format plain --input ./workspace-doctor.json ota annotations --mode doctor --format markdown --input ./doctor.json ota annotations --mode receipt-diff --format markdown --input ./receipt-diff.json ota doctor --json | ota annotations --mode doctor --format github --input - ``` Current behavior: - reads ota JSON from a file or from stdin when `--input -` is used - emits one primary blocker line when `summary.primary_blocker` is present - does not repeat that same primary blocker as a second finding line - emits one line per remaining finding - ignores `finding_groups` and stays one-annotation-per-finding by default - maps `severity: error` to `::error` or `ERROR` and all other severities to `::warning` or `WARNING` - `--format markdown` renders a compact summary block for step summaries or PR comments with status, counts, the primary blocker when present, and remaining findings - `--mode receipt-diff` expects `ota receipt --json --baseline ...` diff output and currently supports `--format markdown` only - scopes workspace findings with the repo name and path so annotations stay actionable - labels additive `Code:`, `Provenance:`, and `Next:` segments when those fields are present in the input JSON - serves as the canonical binary entrypoint for repo-local and CI annotation adapters Text output: - `ERROR: ...`, `WARNING: ...`, or `NOTICE: ...` for primary blockers depending on their severity - `ERROR: ...` and `WARNING: ...` for findings - primary-blocker and finding annotations append `Code: ` when the input JSON includes one - markdown output uses `## `, `Status`, `Counts`, optional `Primary blocker`, and `Findings` sections instead of one-line annotations - receipt-diff markdown output uses `Baseline source`, `Compare`, `Drift`, `Counts`, optional `Gate`, optional `Primary blocker`, and compact `Introduced` / `Resolved` sections JSON output: - none; this is a rendering command, not a contract reader ## `ota json validate` Validate a JSON payload against a published Ota schema with optional assertion gates. Payload can come from a command (`-- <command>`) or an existing artifact (`--input <file|->`). ```bash ota json validate \ --schema run-preview.json \ --allow-exit 0 \ --allow-exit 1 \ --assert-eq dry_run=true \ --assert-in preview_status='["RUNNABLE","RUNNABLE WITH WARNINGS","BLOCKED"]' \ -- ota run ci --dry-run --json . ota json validate \ --schema run-preview.json \ --input ./repo-run-preview.json \ --assert-type plan.actions:array ``` Current behavior: - with `--input <path>`, reads JSON from the file; with `--input -`, reads JSON from stdin - without `--input`, executes the provided command after `--` and captures JSON from stdout (falling back to stderr when stdout is empty) - accepts one or more allowed exit codes (`--allow-exit`); defaults to `0` when omitted - in `--input` mode, uses synthetic exit code `0` for exit-map assertions - when `--write-payload` is set, writes the captured/read payload before validation and assertions - validates payload shape against schemas under `docs/spec/json-schemas/`, including relative `$ref` resolution used by published Ota JSON schemas - supports optional assertion gates for CI: - `--assert-eq path=value` - `--assert-in path=[...]` - `--assert-type path:type` (`string|array|object|number|boolean`) - `--assert-non-empty-string path` - `--assert-exit-map path=0:[...];1:[...]` - is the canonical first-party gate for preview JSON contract checks in smoke CI Text output: - success: `validated <payload-source> against <schema-name>` - failure: command execution, payload parse, schema mismatch, or assertion mismatch details JSON output: - none; this is a validation/guard command ## `ota extensions` List staged extension descriptors declared in `ota.yaml`. ```bash ota extensions [PATH] ota extensions --json [PATH] ota extensions --member api [PATH] ota extensions --run demo-check [PATH] ota extensions --publish release-upload [PATH] ``` Current behavior: - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota extensions` lists root descriptors and grouped member results for each declared member - when `--member` is set, lists descriptors from the merged member contract - repeated `--member` values list descriptors for those members in the provided order - `ota extensions --run <name>` executes one explicitly named, allowlisted descriptor in the current repo or member context - `ota extensions --publish <name>` executes one explicitly named, allowlisted `export_provider` descriptor in the current repo or member context - execution currently accepts `kind: check_provider` descriptors with `api_version: 1` - execution currently accepts `kind: export_provider` descriptors with `api_version: 1` - execution also accepts `kind: backend_provider` descriptors for remote execution when named by `execution.backends.remote.provider` - backend providers receive a structured JSON request and must return a structured JSON response; the request is delivered on stdin and mirrored in `OTA_BACKEND_PROVIDER_REQUEST_JSON` for shell adapters - the seam is useful for external adapter contracts such as check providers, export targets, and execution backends that should be discoverable without being hidden in shell scripts Text output: - header: `EXTENSIONS <path>` - each descriptor may include `kind`, `command`, `api_version`, `description`, and `config` - the report is read-only unless `--run <name>` is set - when no descriptors are staged, the text output says so explicitly and points users back to `ota doctor` or adding `extensions` to the contract JSON output: - success: `ok`, `path`, `extensions` - monorepo root summaries include grouped per-member results in `members` - repeated `--member` values return grouped per-member results in `members` - `--run <name>` returns the executed descriptor, `exit_code`, and captured `stdout`/`stderr` - `--publish <name>` returns the executed descriptor, `exit_code`, and captured `stdout`/`stderr` - failure: `ok`, `path`, and either `errors` or `error` ## `ota run` Run a validated task. ```bash ota run <task> [PATH] ota run <task> --dry-run [PATH] ota run <task> --dry-run --json [PATH] ota run <task> --stream [PATH] ota run <task> --member api [PATH] ota run <task> --member api --member web [PATH] ota run <task> --mode native [PATH] ota run <task> --mode container --ephemeral [PATH] ota run <task> --mode remote [PATH] ota run <task> --skip-deps [PATH] ota run <task> --effect-override network:broad=allow [PATH] ota run <task> --memory 4GiB [PATH] ota run <task> --agent [PATH] ota run <task> --reason "release requested" [PATH] ota run <task> [PATH] --base-url http://localhost:8080 ``` Current behavior: - validates the contract first - when `--member` is set, resolves the merged member contract from the monorepo root - repeated `--member` values run the task across those members in the provided order - `--mode`, `--lifecycle`, and `--ephemeral` can select a contract-advertised execution mode for one invocation; ota refuses an unadvertised backend instead of forcing a host-owned task into a container or remote runtime - explicit execution options are capability requests, not advisory hints: ota refuses unsupported mode, lifecycle, host-port, memory, or dependency overrides before task startup; native and remote tasks without a managed shared backend reject `--ephemeral` / `--persistent` rather than pretending those lifecycles changed host or provider execution - dry-run and real execution use the same option-admission decision; refused preview JSON carries `execution_started: false`, the requested override, and a typed primary blocker - `--skip-deps` is a local execution override that skips `tasks.<name>.depends_on` for the requested task only - `--skip-deps` is rejected when the requested task has no declared `depends_on` - `--agent` enforces the declared agent-safe boundary before execution starts: ota refuses the run when the requested task is outside the safe set or when a declared-safe task still reaches an unsafe dependency / aggregate / hook closure - `--reason <text>` attaches operator intent when the selected task path crosses a heavier audited execution boundary; the current shipped slice records that reason only when ota derives `crossing_required` for the selected lane instead of attaching free-form narration to every run - `--effect-override <effect>=<allow|warn|deny>` temporarily overrides one effect-governance decision for this invocation only; supported selectors are `network`, `network:broad`, `network:dependency_hydration`, `network:container_image_hydration`, `network:service_readiness`, `network:integration_test`, `network:tool_bootstrap`, and `external_state:<token>` - task inputs are declared in `tasks.<name>.inputs` and are passed as `--kebab-case value` flags - when a task input overlaps an ota command flag name such as `mode` or `jobs`, put the ota command flag before the task and the task input after the task - task inputs are exposed to the task process as `OTA_INPUT_<NAME>` env variables - `default` values are applied when the caller omits an input - `required: true` makes an input mandatory unless a default exists - `allowed` limits the accepted values for that input - task inputs only apply to the task you invoked, not its dependencies - if every declared input has a default, you can omit all input flags - `--dry-run` is the read-only repo run preview surface: it resolves the selected task path, env, toolchains, native prerequisites, dependencies, and execution plan without running setup, dependencies, task processes, or containers - dry-run also evaluates declared replay-input identity pins. A missing or mismatched `expected_identity` returns the typed `replay_input_identity_missing` or `replay_input_identity_mismatch` preflight result before Ota presents the lane as runnable; real execution blocks on the same condition before task startup and retains the expected identity plus the observed identity when the input was readable in the receipt's `evaluated_inputs[]` - when the active org policy declares `policies.replay_inputs.identity`, dry-run and Doctor also publish the canonical `replay_input_policy` decision. `ota run` and `ota up` refuse before task startup, dependency hydration, or native prerequisite provisioning on `deny` or `review`; receipts retain the same selected subject, applicable rule identities, task-qualified input records, coverage, and reasons. An unavailable observation fails closed. Hard-pin failures remain unconditional and retain the active policy record. Unselected lanes preserve optional-pin behavior. - `--dry-run` prints `RUN PREVIEW`, uses the execution-preview vocabulary `RUNNABLE` / `RUNNABLE WITH WARNINGS` / `BLOCKED`, and shows `Mode: dry-run (no write)` plus the selected execution path, requirements, and planned actions - repo-level `--json` normally requires `--dry-run`; the one execution-free exception is a contract-declared refusal canary: `ota run --agent --expect-refusal --json <task>` emits the runner-authored `refused_as_expected` or `refusal_not_observed` result without starting the task - preview JSON includes additive `governance`, a compact selected-lane summary with `safety_posture`, `review_required`, closure-aware effective safety, effective `default_mode`, runnable mode commands, selected effect surface, and the next durable receipt command - preview JSON includes additive `plan.dependency_steps[]`, so automation can inspect each planned task step's selected backend, context, parent task, and backend-selection source instead of inferring dependency-plane inheritance from the task names alone - by default, interactive terminals stream raw child output live, while non-interactive text runs buffer output into the final report for a cleaner failure/success surface - `--stream` forces raw live child output in text mode when you want the old firehose behavior explicitly - agent refusals are ota-authored execution outcomes, not generic task failures: text output renders `AGENT EXECUTION REFUSED`, receipts use blocked status, and no task process or dependency path starts before the refusal returns - `--expect-refusal` is the negative-control form of that same boundary. It requires `--agent` and a matching `agent.refusal_canaries` target; a refusal is exit `0`, while admission is exit `1`. It never accepts task inputs or starts the selected lane. - when the requested task is a service runtime with declared readiness, `--stream` also shows live readiness probe progress while ota is still trying to prove startup - for service runtimes with declared readiness, ota now treats the declared startup readiness budget as authoritative: if the configured `start_period` / `interval` / `timeout` / `retries` budget is exhausted before the runtime endpoint becomes reachable, startup fails instead of waiting indefinitely for the workload to exit or for the user to interrupt it - startup failure output now includes the final readiness-budget summary, including attempts used, timeout per attempt, interval, start period, and the last probe failure - backend-configuration failures now point through `ota execution plan` first so the selected execution path can be inspected before you change contract execution settings or retry the task - declared env-source failures now point through `ota env --task <name>` first so source status and precedence stay visible before you repair files or rerun the task - when the selected task path uses `requirements.toolchains`, ota treats that toolchain as the owner for the selected path instead of describing its owned capabilities as standalone `runtimes` or `tools` - toolchain run-path fulfillment is opt-in: `toolchains.<name>.fulfillment.mode: none` keeps `ota run` on diagnosis/check-only behavior, while `fulfillment.mode: run` lets ota attempt run-path provisioning for the selected toolchain only - when toolchain fulfillment fails, run output names the selected toolchain, the chosen fulfillment source, the declared requirement slice ota checked, and the rerun lane instead of reducing the failure to a generic tool install error - `ota run <task> --dry-run --json` exposes additive `toolchains[]` evidence for the selected preview path, including provider, backend, target OS, version, fulfillment mode, owned runtime, and any owned tools/components/targets ota selected on that path - when ota actually runs provider fulfillment commands on an executed path, the corresponding receipt-backed toolchain evidence records `fulfilled` plus additive `commands[]` - on failure, text output keeps `Why` and `Next` first, then appends a compact `RUN SUMMARY` block with `Status` first for quick scanning, followed by the selected mode, container image when relevant, target when one exists, and task - on non-interactive text success, large task output is shown as a bounded excerpt before the compact `RUN SUMMARY` - on non-interactive text failure, task output is shown as a bounded excerpt with a `--stream` rerun hint before the compact `RUN SUMMARY` - on `Ctrl-C` during ephemeral container runs, ota still attempts to remove the repo-owned container created for that invocation and surfaces cleanup failures in the final summary - before starting a new ephemeral container run, ota reclaims repo-owned orphaned ephemeral containers for the same repo, uses bounded retries when reclaim resolves stale host-publication holders, and can reclaim legacy running ephemerals without `dev.ota.owner_pid` when they are the conflicting holder - persistent container runs reconcile shape before reuse and recreate when projection/publication drift would make runtime endpoint metadata stale - Compose attachment namespace drift also counts as persistent execution-shape drift, so changing `attachments.compose` recreates the persistent backend instead of reusing a container bound to the old Compose network family - service tasks with projected listeners classify post-readiness exits as service-stop failures (including `interrupted`) so summaries and receipts stay truthful across both ephemeral and persistent lifecycle modes - active repo execution ownership is now tracked in `.ota/state/active-executions.json` instead of a single whole-run lock, so compatible runs can coexist when their execution ownership does not conflict - the current shipped conflict rule is ownership-shaped: duplicate long-running service-task ownership still blocks, and so do shared host-managed service ownership, shared Compose project ownership, shared persistent backend-family ownership, and shared deterministic env-file materialization ownership; finite task paths can still run alongside an active service owner when they do not claim the same owned resources - shared logical write paths are now compared on effective ownership namespace, not path text alone: when two container contexts declare the same repo-relative `attachments.isolated_paths` entry but resolve to different Ota-managed dependency-isolation volume families, ota allows them to coexist; raw repo-worktree writes or unresolved shared namespaces still conflict - execution-conflict reporting now carries typed reason identities such as `active_execution_present`, `host_service`, `compose_project`, `persistent_backend_family`, `env_materialization_path`, and `service_task` instead of reducing the failure to owner detail text alone - failure receipts now also publish an `execution_conflict.reasons[]` object derived from that same ownership truth while keeping the existing `blocked[]` compatibility lane - stale active-execution records are pruned by owner PID before conflict checks, so interrupted or crashed ota processes do not leave a permanent fake-active barrier behind - when `--skip-deps` is used, receipts and run summaries mark the override explicitly and point back to rerunning without it when you need to validate the full declared task flow - on success, text output includes the compact `RUN SUMMARY` block with `Status` first for quick scanning, followed by the selected mode, container image when relevant, target when one exists, and task - `--receipt` adds the full execution receipt when you need the detailed trail - when replay-input policy refuses admission, `--receipt` still emits the blocked preconditions receipt with policy decision, coverage, rule identity, typed reasons, and observed input posture; generic `ota receipt` does not reconstruct that earlier decision - replay admission includes recursive `after_success`, `after_failure`, and `after_always` hook closures before the parent task starts; an invalid active policy source refuses with typed `replay_input_policy_unavailable` evidence rather than behaving like no policy Example: ```yaml 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 ``` ```bash ota run api-automation-tests ota run api-automation-tests --base-url http://localhost:8080 --suite-mode contract-drift ota run version:bump --version minor ota run version:bump --version 0.2.0 ota run version:bump --version major ota run dev --host-port 4000 ota run dev --memory 4GiB ota run build --skip-deps ``` - resolves task dependencies before execution - when `tasks.<name>.when.checks` is declared, ota evaluates those checks before dependency/service startup and skips the task when any condition check fails or times out - `when.checks` supports declared `precondition` (with `run`), `file`, and `changed_files` checks; probe-driven checks are not valid execution conditions - `--skip-deps` suppresses that dependency execution for the requested task only; required service acquisition, hooks, and the selected task body still run - if the task body exits successfully, runs `after_success` hooks in declared order - if the task body exits with a failure, runs `after_failure` hooks in declared order - runs `after_always` hooks after either outcome when the task body was actually attempted - hook task failures affect the final `ota run` exit code for the parent task - resolves the best matching task variant for the current OS when variants are declared - executes either `run` or `script` - supports mode-aware task branches under `tasks.<name>.execution.modes` - when `tasks.<name>.execution.default_mode` is declared and `--mode` is omitted, `ota run` uses that mode as the default execution plane - when `--mode` is set, `ota run` uses the matching mode branch for the task - if the selected mode has no declared task branch, `ota run` falls back to the task-level execution body and task-level execution settings - selected mode branches can override task `context`, `lifecycle`, `env`, `run`/`script`, and `runtime` - resolves task execution backend from: - `tasks.<name>.execution.default_mode` when set - selected mode branch context when `tasks.<name>.execution.modes.<mode>.context` is set - `tasks.<name>.context` when set - `execution.default_context` - legacy `execution.preferred` / `execution.backends` - for container tasks, runs through the first available configured container engine CLI, falling back to `docker` when no engines are listed - for container tasks, `execution.lifecycle: ephemeral` uses a fresh container - for container tasks, `execution.lifecycle: persistent` reconciles a named container: ota reuses it when the resolved execution shape is equivalent and recreates it when image/publication/isolation shape drifts - supports remote execution when the resolved task/context backend declares `provider` and `target` - current shipped remote providers are `daytona`, `ssh`, `tsh`, and `kubectl` - remote target guidance: - `daytona`: `sandbox-dev` - `ssh` / `tsh`: `user@host` - `kubectl`: `pod/ota-dev` - passes `execution.backends.remote.cwd` to the provider CLI when set - runs in the effective target contract directory - applies configured environment values, approved policy env values, and task input env variables - when the selected native task path references `requirements.native` entries with `activation.kind`, ota resolves the declared native activation environment first; Windows `visual_studio_dev_shell` and generic `command` activations both apply to the real task body instead of assuming the current terminal was already activated correctly - when `tasks.<name>.runtime.listeners` declare host projection, ota also injects runtime endpoint env values before process start when the projection is known: - `OTA_PUBLIC_URL` - `OTA_PUBLIC_HOST` - `OTA_PUBLIC_PORT` - `OTA_PUBLIC_URL_<LISTENER>` - when multiple listeners are projected, exactly one projected listener must set `project.host.primary: true`; ota uses that listener for `OTA_PUBLIC_URL` and summary endpoint rendering - for container listeners with `project.host.port.mode: auto`, `execution.lifecycle: ephemeral` pre-reserves a host port before spawn and retries bounded host-port conflicts; `execution.lifecycle: persistent` resolves the reconciled container's published host mapping before exec - `--host-port <port>` overrides one run's projected host/public port on the selected primary projected listener without changing the internal bind port; ota updates runtime env, summary output, and receipts to the overridden public URL - `--host-port` is valid when the selected primary listener uses `project.host.port.mode: fixed` and the execution path is either container execution or native structured `docker compose up` with explicit `project.publication.compose.service` ownership - `--host-port` is rejected for `project.host.port.mode: auto`, tasks without projected host listeners, ambiguous multi-listener projections without one primary listener, native compose publications without `project.publication.compose.service`, and native compose engines other than `docker` - stream-mode endpoint banners such as `External:` and `Internal:` are printed only after ota itself confirms the projected endpoint; workload logs like `ready` or framework-local URLs are not treated as authoritative host-reachability proof - if Docker is running through Colima, published ports may be reachable inside the Colima VM but not on macOS localhost; when this happens, ota keeps the endpoint banner withheld and the interrupted pre-confirmation path calls out the Colima boundary explicitly - `--memory <size>` overrides one run's requested container memory (examples: `512MiB`, `2GiB`, `4TiB`) - `--memory` is valid only when the selected task resolves to container execution - when the selected container context declares `container.resources.memory.minimum`, ota rejects `--memory` values below that minimum before task execution starts - when no `--memory` override is passed, ota uses `container.resources.memory.default` when declared; if only `minimum` is declared ota requests that minimum; otherwise the engine default applies - for container-backed `runtime.kind: service` tasks, ota now captures container termination state before ephemeral teardown and reports post-readiness service stops as first-class failures (including explicit OOM classification when the engine reports it) - prints task progress and advisory notes on stderr when output is streaming - prints a summary in text output, and emits an execution receipt on stderr after task output when `--receipt` is set - execution receipts include backend, remote `provider` / `target` / optional `cwd` when relevant, lifecycle, container image when relevant, resolved container memory when requested, acquired paths, env sources, step summary data, resolved runtime listener endpoints, optional `service_termination` details for post-readiness service stops, and optional `host_service_cleanup` evidence when ota attempted host-managed service shutdown during cleanup; text receipts also print the winning env source for each resolved value - task-backed execution receipts also carry additive `dependency_steps[]`, so the archived run path preserves each executed dependency step's selected backend, optional context, parent task, and backend-selection source instead of only the flattened dependency order - returns the child process exit code Use this when the contract is already the source of truth and you want deterministic task execution. Agent execution: - use `ota tasks --safe --use` first to discover the effective safe task surface - then run the selected task with `ota run <task> --agent` - `--agent` is the enforcement lane: ota refuses unsafe requested tasks and unsafe reachable task closures instead of treating safety as review-only guidance - without `--agent`, declared tasks still run as the normal human/operator execution lane ## `ota doctor` Diagnose repo readiness from a validated contract. ```bash ota doctor [PATH] ota doctor --mode native [PATH] ota doctor --mode container [PATH] ota doctor --container --persistent [PATH] ota doctor --remote --ephemeral [PATH] ota doctor --json [PATH] ota doctor --fix --dry-run [PATH] ota doctor --fix [PATH] ota doctor --member api [PATH] ota doctor --member api --member web --json [PATH] ``` - Current behavior: - when no contract exists, reports `Contract missing`, shows any trustworthy repo and host signals under `Repo Signals` across mainstream and long-tail detector-supported stacks, including repo type, dependency/build tools, likely runnable tasks, services, and host tool availability, and keeps the next step compare-first with `ota detect --dry-run`, `ota detect --contract`, and `ota init --dry-run` - the human summary now makes the top-level state explicit as `READY`, `READY WITH WARNINGS`, or `BLOCKED` - `ota --version` now exposes build identity when available, including the git commit and dirty marker for source builds, so released and unreleased binaries do not masquerade as the same version string - `ota --version --json` exposes the same identity in machine-readable form (`semver`, `source_build`, `commit`, `dirty`) plus `schema_version` and additive `contract_capabilities[]` entries (`id`, `introduced_in`) for CI and contract-compatibility provenance checks - when `metadata.ota.minimum_version` rejects a contract, ota now reports the contract minimum, current binary identity, detected unsupported contract feature when one is known, and the next install/rebuild step instead of only saying the binary is too old - validates the contract first when one is present - when a root contract declares `workspace.type: monorepo`, plain `ota doctor` diagnoses the root contract and grouped summaries for each declared member - when `--member` is set, diagnoses the merged member contract - repeated `--member` values diagnose those members in the provided order - prints the highest-priority blocker first in the human-readable output so the fastest next action is visible immediately - when findings are warning-only, still surfaces one highest-priority primary finding before grouped detail so the next safe action is visible without scanning the whole report - doctor now surfaces selected task-path side-effect signals: network-dependent paths stay explicit, and selected tasks that mutate external systems surface warnings before execution - environment blockers now point through `ota env` first so operators can inspect precedence before changing shell values, policy env, or declared sources - unverifiable required services now route into `ota assist declare-readiness` when only the probe is missing, or `ota assist declare-service` when the managed service shape itself still lacks a start path - missing-file precondition failures now point to `ota up` / `ota run setup` when `tasks.setup` already exists, or to `ota assist wire-setup` when the repo still needs a contract-first setup path - when a contract has no tasks, doctor now keeps that path preview-first too: it suggests `ota detect --dry-run` before any detect write, while still offering `ota assist add-task` when the right fix is clearly one explicit task - checks configured env requirements, declared checks, and service healthchecks in native mode - reports warning findings for missing or mismatched pinned `tasks.<name>.replay_inputs[]` so operators can repair deterministic replay inputs before invoking an execution lane; execution remains the hard preflight boundary - publishes additive workflow `claim_assurance[]` proof-breadth records when a workflow declares runtime proof. The same immutable archive matching rule applies: current semantic contract, clean source identity, resolved execution scope, and `replay_posture: witness_only` are all required for `supported`; a matching failed archive is `contradicted` and all other cases are `unknown` - when the selected or default workflow task closure declares `tasks.<name>.requirements`, doctor scopes runtime, tool, env, and precondition-check diagnosis to that setup/run dependency path instead of treating unrelated task-specific prerequisites as repo-global truth - selected-path `requirements.env` entries become missing-env blockers for that diagnosis even when the same top-level `env.vars.<name>` entry is optional outside the selected task/workflow path - when the selected path resolves through `requirements.toolchains`, doctor diagnoses the selected toolchain/provider/components/targets as one owned surface and does not restate owned capabilities such as `cargo` or `rustfmt` as standalone selected-path tools - doctor never provisions toolchains: `toolchains.<name>.fulfillment.mode: none` and `fulfillment.mode: run` both stay diagnosis-only here; duplicate ownership between `toolchains`, `runtimes`, and `tools` fails early as an invalid contract instead of degrading into an advisory finding - org-policy version/provisioning reasoning now sees the selected toolchain-owned runtime lane too, so `toolchains.rust` can participate in approved-version and approved-source findings without re-declaring `runtimes.rust` - `ota doctor --json` now exposes additive `toolchains[]` entries for the selected workflow/task path, including the provider, effective backend, target OS, version, fulfillment mode, and the owned runtime/tool capabilities ota checked on that path - agent-safe tasks that declare broad `effects.network` or `effects.external_state` produce warnings in doctor output instead of leaving those execution risks implicit behind a safe-task label - when a structured `AGENTS.md` or `CLAUDE.md` managed block drifts from the declared `agent.safe_tasks`, `agent.verify_after_changes`, `agent.writable_paths`, or `agent.protected_paths`, doctor reports agent-boundary drift instead of assuming agent guidance docs are still aligned - first-class `prepare.kind: dependency_hydration` lanes keep their network truth visible, but ota does not emit the generic agent-safe dependency-hydration warning when that bounded lane is already modeled on the typed hydration surface - first-class `prepare.kind: tool_bootstrap` lanes keep contract-owned tool installation visible too; ota can now distinguish typed tool bootstrap from both repo dependency hydration and broad network execution - when one selected tool requirement resolves to `tools.<name>.acquisition`, doctor names that activation lane directly instead of reducing the fix to a generic install hint; Corepack-managed `pnpm` is the first shipped provider path - when a selected task declares `requirements.native`, doctor runs the selected platform precondition check and reports an OS-specific native prerequisite finding instead of a vague shell-check failure - probe-backed checks and workflow readiness probes execute directly inside ota; they do not depend on repo-local helper commands such as `curl` or `node` - checks required execution backends for the selected `--mode` and resolved contexts - `ota doctor` now accepts the same execution-selector family shape as the other mode-bearing repo commands: `--mode`, backend shorthands (`--native`, `--container`, `--remote`), `--lifecycle`, and lifecycle shorthands (`--persistent`, `--ephemeral`) - `--mode native` diagnoses host/native readiness; `--mode container` diagnoses selected container context requirements - when a lifecycle override is selected, doctor keeps that lifecycle on its reported execution identity and rerun guidance instead of silently collapsing container diagnosis back to ephemeral - context diagnostics use the resolved named-context shape after `extends` merge, while legacy shorthand remains supported for one-context contracts - warns on suspicious remote target shape: - `ssh` / `tsh` targets without `user@host` - `kubectl` targets not starting with `pod/` - checks runtime and tool presence on `PATH` - for contract-backed repos, when Ota-owned local artifacts are git-backed but `.ota/state/`, `.ota/contracts/`, `.ota/receipts/`, or `.ota/proof/` is not ignored, reports a fixable repo-hygiene finding - `--fix --dry-run` previews deterministic safe fixes without writing files - `--fix` applies only supported deterministic safe fixes for repos with a valid `ota.yaml`; current scope is `.gitignore` hygiene for `.ota/state/`, `.ota/contracts/`, `.ota/receipts/`, and `.ota/proof/` - when no `ota.yaml` exists yet, `ota doctor --fix` does not propose repo-hygiene mutations and instead points operators to preview-first onboarding with `ota detect --dry-run` or `ota init --dry-run` - in container mode, runtime and tool findings are evaluated against the selected container image instead of the host PATH - in container mode, ota also uses safe non-mutating installability probes for the shipped mutating provisioning adapters when policy-backed provisioning is declared - in container mode, `apt` findings distinguish pinned-version unavailable, package unavailable, and apt-index/source failures when the backend evidence supports that classification - in container mode, host-bound env, check, and service healthchecks are omitted so container diagnosis does not mix execution contexts - when `services.<name>.readiness` is used, readiness probes run in the declared context and use the matching endpoint projection for reporting - `ota doctor --mode remote` probes runtime/tool requirements in each executable remote context - shows any inert top-level `extensions` entries in the human-readable report so adapter metadata is visible without execution - warns when a required service has no healthcheck, because readiness cannot be verified - honors `services.<name>.timeout` when a service healthcheck is declared - warns when `execution.lifecycle: ephemeral` is declared and clarifies that current isolation applies to `ota run <task>` and the setup step inside `ota up`; diagnosis, healthchecks, and full repo cleanup are not ephemeral yet, and `--ephemeral` remains the shorthand for a fresh task-execution path when supported - reports contract drift as warning findings when repo signals no longer match the declared contract, and still preserves the most important blocker first - tags contract-drift findings with repo-contract ownership and provenance so consumers can distinguish stale contract truth from host or service failures - warns when `.devcontainer/devcontainer.json` advertises a Node image that does not satisfy the repo contract's declared `runtimes.node` requirement - warns when `.devcontainer/devcontainer.json` bootstraps with a different Node package manager than the repo contract declares - warns when `agent.writable_paths` includes sensitive lockfile, env/config, runtime-topology, CI, or repo-contract paths when they exceed the declared `agent.posture`; narrow intentional exceptions can still be acknowledged with `agent.exceptions.sensitive_writes` - reports an error when no `tasks` are declared, because the contract is not operational for `ota run` - runs configured checks - orders findings by severity - includes an `agent` summary when the contract declares one - may include a `provisioning` plan when the contract declares runtimes or tools and policy provides approved provisioning sources - prints the reason and next action for each finding Text output: - header: `DOCTOR <path>` - status line: `READY` or `NOT READY` - `Execution` includes a `Mode:` line in text output so the selected diagnosis context is explicit - when agent guidance is present, the shared `AGENT` block is grouped into `Overview`, `Execution`, and `Boundary` sections with counted wrapped lists and writable-path root/exception collapsing - summary includes repo verdict and agent verdict before per-finding details - grouped finding sections include `Provenance:` when the grouped findings share one diagnosis source - with `--concise`, findings keep severity + summary + `Next`, while `Why` detail is omitted JSON output: - `ok` - `path` - `agent` when the contract declares agent guidance - `fix` when `--fix` is requested, including planned/applied action status and any write failures - `findings` - findings may also include `provenance` / `provenance_key` when ota can trace the diagnosis back to the repo contract, org policy, or repo signals - monorepo root summaries include grouped per-member results in `members` - repeated `--member` values return grouped per-member results in `members` Warnings can still produce `READY`. Errors produce `NOT READY`. ## `ota init` Create a starter ota contract for a repo that does not yet have one. ```bash ota init [PATH] ota init --bootstrap [PATH] ota init --pack <node|python|ruby|go|rust|dotnet|php-composer|java-maven|java-gradle> [PATH] ota init --pack node --package-manager <npm|pnpm|yarn|bun> [PATH] ota init --pack python --test-runner <pytest|unittest> [PATH] ota init --packs ota init --dry-run [PATH] ota init --json [PATH] ``` Current behavior: - inspects the repo using the detection engine - writes by default - `--bootstrap` writes the fuller detected starter contract when it is safe to do so - `--pack <node|python|ruby|go|rust|dotnet|php-composer|java-maven|java-gradle>` skips detector-led starter selection and seeds an explicit conventional starter contract pack, including short task `description` fields on the seeded starter tasks - `--pack node --package-manager <npm|pnpm|yarn|bun>` keeps pack mode explicit while swapping the conventional Node starter setup hydration and script command bodies to the selected package manager - `--pack python --test-runner <pytest|unittest>` keeps pack mode explicit while swapping the conventional Python test entrypoint to the selected runner - `--packs` lists the built-in starter packs, what they seed, the exact `ota init --pack ...` selection command, the safe dry-run preview command to use next, and any explicit starter knobs exposed by that pack - when no stronger project identity is inferred, `--bootstrap` can fall back to the repo directory name for `project.name` - supports preview mode with `--dry-run` - refuses to run when `ota.yaml` already exists - can initialize both detected repos and blank repos - keeps JSON output stable while using text output to guide review, write, and first validation steps - in `detected` mode, plain `ota init` writes the smallest valid starter contract for the repo - in `detected` mode, `ota init --bootstrap` can include lower-confidence fields when they are needed to capture the fuller starter contract - when detector-led init has strong ecosystem signals, it normalizes shipped starter truth onto current first-class surfaces instead of leaving obvious setup and task bodies in raw shell: supported Node, Ruby, Java, and .NET starters now prefer `toolchains.*`, `prepare.kind: dependency_hydration` for `setup`, and `command` for simple finite task execution where ota can model the body directly - when standard env source files already exist, detector-led init can declare them as explicit `env.sources` in the starter contract: `.env.local`, `.env`, `src/main/resources/application.properties`, `src/main/resources/application.yml`, `src/main/resources/application.yaml`, `appsettings.json`, and `appsettings.Development.json`; explicit `--pack` mode does not infer env sources from repo files - runtime support for declared `env.sources` also includes curated `yaml` and `toml`; detector-led init auto-infers the explicit standard dotenv, Spring properties/yaml, and .NET JSON files listed above, but does not yet auto-suggest standard TOML paths - when `project.name` is still missing in bootstrap mode, ota falls back to the repo directory name rather than leaving the contract invalid - low-confidence fields remain excluded from plain `ota init` writes - canonical detected tasks can include short `description` fields so the starter contract teaches the task-authoring pattern immediately instead of only relying on notes - confident detected tasks may include a `notes` field that points to the matching `ota run <task>` command - when the detected tasks are confident enough, the starter contract now keeps a derived `agent` block and review notes even when writable-path inference is still partial; ota now combines broader common app/source directories with detector-backed nested project roots and a bounded stack-aware source-root scan so custom code roots can surface in `agent.writable_paths` without falling back to `.`, while detector-backed control files such as manifests and lockfiles now surface explicitly in `agent.protected_paths` and operational directories such as `config`, `database`, `migrations`, `manifests`, `deploy`, and `infra` stay out of the default starter allowlist - starter contracts now also carry `agent.inferred_boundary.reviewed: false` plus provenance for the inferred writable and protected paths, so the boundary is visible as inferred state rather than silent starter magic; detector-led init uses `detect:...` provenance, while explicit pack mode uses `init:...` provenance for the starter defaults it owns - those starter `agent.notes` now explicitly tell authors to review `agent.writable_paths` and `agent.protected_paths`, then set `agent.inferred_boundary.reviewed: true` before trusting automation - starter contracts protect `ota.yaml` by default; if a future starter intentionally grants contract-authoring authority with writable `ota.yaml`, it should also carry `agent.exceptions.sensitive_writes: [ota.yaml]` instead of leaving that sensitivity implicit - detector-led `ota init --dry-run` now renders the same explicit `Agent boundary` outcome as detect preview, so repos without a safe inferred task see why the starter omits `agent` instead of having to infer that omission from the YAML alone Choosing an init path: - use `ota init --dry-run` when detector-led init should shape the first draft from repo signals - use `ota detect --contract` before detector-led `ota init` when you want the exact starter text without annotations or pack commentary - use detector-led init when you want ota to carry existing declared-source candidates such as `.env.local`, `.env`, `src/main/resources/application.properties`, `src/main/resources/application.yml`, `src/main/resources/application.yaml`, `appsettings.json`, or `appsettings.Development.json` into `env.sources` - use plain `ota init` only after comparing that detector-led starter against `ota detect --contract` - use `ota init --packs` when you want to compare the explicit starter catalog first - use `ota init --pack <name> --dry-run` when you want an explicit conventional starter without detector-led selection - use `ota init --pack node --package-manager <name> --dry-run` when the repo is intentionally npm-, pnpm-, yarn-, or bun-based and you want the starter to match that package-manager boundary from the first write, including first-class setup hydration for every shipped Node manager lane - use `ota init --pack python --test-runner <name> --dry-run` when the repo is intentionally `pytest`- or `unittest`-driven and you want the starter to reflect that test command directly - use `ota init --pack ruby --dry-run` when the repo is intentionally Ruby/Bundler-based and you want the starter to seed first-class Bundler dependency hydration plus the default `bundle exec rake test` loop, with Bundler version governance under `toolchains.ruby.package_managers.bundler` - use `ota init --pack rust --dry-run` when the repo is intentionally Rust-first and you want `toolchains.rust` ownership plus first-class Cargo hydration for `setup`, with `cargo build` / `cargo test` as the honest first draft - use `ota init --pack dotnet --dry-run` when the repo is intentionally .NET-first and you want `toolchains.dotnet` ownership plus first-class `dotnet_restore` hydration for `setup`, with the standard `dotnet build` / `dotnet test` loop as the honest first draft - use `ota init --pack php-composer --dry-run` when the repo is intentionally Composer-managed PHP and `composer install` plus reuse of an existing `scripts.test` entry is the honest first draft you want to review - use `ota init --pack java-maven --dry-run` when the repo is intentionally Maven-based and you want `toolchains.java` ownership plus first-class Maven hydration for `setup`, preferring `mvnw` when the wrapper already exists - use `ota init --pack java-gradle --dry-run` when the repo is intentionally Gradle-based and you want `toolchains.java` ownership plus first-class Gradle hydration for `setup`, preferring `gradlew` when the wrapper already exists - explicit packs seed short task `description` fields so the authoring pattern is visible immediately Examples: ```bash # detector-led path ota detect --contract ota init --dry-run ota init ota up --dry-run # pack-led path ota init --packs ota init --pack node --dry-run ota init --pack node --package-manager yarn --dry-run ota init --pack python --dry-run ota init --pack python --test-runner unittest --dry-run ota init --pack ruby --dry-run ota init --pack go --dry-run ota init --pack rust --dry-run ota init --pack dotnet --dry-run ota init --pack php-composer --dry-run ota init --pack java-maven --dry-run ota init --pack java-gradle --dry-run ``` Modes: - `blank`: starter contract derived from minimal repo context - `detected`: starter contract derived from detected repo signals - `pack`: starter contract derived from an explicit built-in starter pack - `catalog`: starter-pack discovery output from `ota init --packs` Text output: - dry-run header: `INIT <path>` - write success: `WROTE <path>` - includes `Mode: blank` or `Mode: detected` - `pack` mode also includes `Pack: <name>`, optional `Options: ...` when the selected starter pack supports explicit knobs, plus an explicit pack-policy note - explicit pack mode can also include an advisory note with `Why`, weighted `Signals`, `Selected signals`, `Strength`, `Gap`, and `Next` rows when strong repo signals disagree with the selected pack; ota does not auto-switch or merge detector output into the pack - `--packs` renders `INIT PACKS catalog`, one entry per pack, the exact `ota init --pack ...` command, any starter-specific option rows, and a `Next:` line with the matching `ota init --pack ... --dry-run .` preview command plus why that preview is the right next move - successful init writes now use explanatory `Next:` steps instead of bare commands: validate the written contract, inspect the runnable task surface, review readiness with doctor, then preview preparation with `ota up --dry-run` - `blank` mode explicitly warns that the starter contract is minimal coverage only - `detected` mode write output explicitly calls out the write policy and any excluded low-confidence fields - includes inferred-field annotations with field type, source, signal, confidence, and task-scoped agent metadata when applicable JSON output: - `ok` - `path` - `written` - `mode` - optional `pack` when explicit pack mode is used - optional `pack_options` when explicit pack mode selected a starter-specific knob such as Node package manager or Python test runner - optional `pack_advisory` when explicit pack mode disagrees with strong detected repo signals; it includes the selected pack, suggested pack, distinct-signal scores, score gap, normalized signal markers, weighted signal details for both the suggested and selected pack, and a safe dry-run follow-up command - `config` - `inferred` - `packs` when `mode` is `catalog` and ota is listing the built-in starter packs instead of previewing one contract; each entry includes `name`, `summary`, `when`, the exact `command`, a safe `next` preview command, optional starter `options`, explicit `does_not_infer` boundaries, and the seeded runtimes, tools, checks, and tasks - failure responses can include `next` when ota can point to one safe follow-up command ## `ota agents` Generate or sync a repo-local `AGENTS.md` from the current contract. Use this after `ota doctor`, `ota explain`, or `ota up` when you want the same repo contract to produce reviewable agent guidance for humans and coding agents. ```bash ota agents [PATH] ota agents --review [PATH] ota agents --confirm --dry-run [PATH] ota agents --confirm [PATH] ota agents --write [PATH] ota agents --json [PATH] ota agents --write --output AGENTS.md [PATH] ``` Current behavior: - keeps the contract-first boundary workflow inside `ota.yaml`: `ota agents --review` inspects the current writable/protected path boundary and provenance, `ota agents --confirm --dry-run` previews the exact `reviewed: true` mutation, and `ota agents --confirm` writes that confirmation into the contract before any `AGENTS.md` sync - derives `AGENTS.md` from the repo contract’s `agent` block when one is present - doctor treats the structured managed block as governed boundary evidence, so stale `safe_tasks`, `verify_after_changes`, `writable_paths`, and `protected_paths` content surfaces as agent-boundary drift instead of silently staying outdated - when the default workflow is declared, the generated default-workflow summary now carries the explicit `prepare`, `setup`, and `run` command forms instead of collapsing host file prep into setup implicitly - when the repo contract does not declare `agent`, preview mode now behaves like a blocked agent-boundary sync surface instead of a generic scaffold preview: it reports `Agent contract missing`, shows compare-first next steps through `ota detect --dry-run` and `ota init --dry-run`, and surfaces any trustworthy inferred repo signals plus inferred starter agent boundaries under `Repo Signals` - `ota agents --write` now refuses when the repo contract still lacks `agent`, so Ota does not write generic guidance that looks more authoritative than the authored contract - renders an explicit `Bootstrap` section when `agent.bootstrap.ota` is present, including the approved shell and PowerShell install commands for `ota` - preserves existing `AGENTS.md` content and appends or refreshes an ota-managed block instead of overwriting user-authored guidance - skips the write if the existing file already contains the generated AGENTS content - keeps the generated file lightweight by using short provenance (`Generated from ... by \`ota agents\`.`) instead of an Ota copyright or license banner - renders a `Managed block:` label in text output so the ota-owned section is explicit and shows each task list item together with its `ota run ...` command form - text preview points directly at the missing boundary and the next safe authoring lane instead of only previewing generated markdown when the contract still lacks `agent` - writes to `AGENTS.md` by default when `--write` is set - accepts `--output` to write elsewhere - keeps output deterministic and reviewable Text output: - header: `AGENTS <path>` - `--review` uses `AGENTS REVIEW <path>`, reports whether the boundary is `REVIEW REQUIRED` or `REVIEWED`, and shows `Boundary sync` as `blocked until review`, `update needed`, or `in sync` - `--confirm --dry-run` uses `AGENTS CONFIRM <path>` with `PREVIEW` and shows the exact reviewed-boundary contract preview before any write - `--confirm` uses `AGENTS CONFIRM <path>` and reports whether the boundary was just confirmed or whether no confirmation write was needed because the boundary was already reviewed or already declared as confirmed - when `agent` exists, preview mode shows the generated markdown content together with the write and verification next steps - when `agent` is missing, preview mode shows a blocked boundary-sync diagnosis with `Target`, `Primary Blocker`, `Next`, and `Repo Signals` - write mode reports whether the target was written or already in sync and points back to `ota doctor` - reviewed boundaries that are already synced end with `Boundary is already synced.` plus a plain Next hint to run `ota doctor`; reviewed boundaries that still need sync keep a two-step next lane for `ota agents --write` and `ota doctor` JSON output: - `ok` - `path` - `output` - `written` - `content` - failure responses can include `next` when ota can point to one safe follow-up command ## `ota check` Run configured checks from a validated contract. ```bash ota check [PATH] ota check --json [PATH] ota check --member api [PATH] ota check --member api --member web --json [PATH] ``` Current behavior: - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota check` runs root checks and grouped check summaries for each declared member - when `--member` is set, runs checks from the merged member contract only - repeated `--member` values run checks for those members in the provided order - when the selected workflow declares explicit readiness checks or probes, runs that workflow readiness surface - when the selected or default workflow task closure declares explicit `tasks.<name>.requirements.checks`, `ota check` also runs those prerequisite checks without broadening into runtime, tool, or env diagnosis - otherwise runs the repo-wide configured checks surface - command-backed checks keep their existing shell execution model - probe-backed checks and workflow readiness probes run the named `readiness.probes` target directly inside ota - does not perform runtime, tool, or env diagnosis - does not execute tasks Text output: - header: `CHECK <path>` - status line: `READY` or `NOT READY` - when agent guidance is present, the shared `AGENT` block uses grouped `Overview`, `Execution`, and `Boundary` sections with counted wrapped lists and writable-path root/exception collapsing JSON output: - `ok` - `path` - additive `agent` can appear when the contract declares agent boundaries - `findings` - additive `toolchains[]` evidence can appear for the selected workflow path even though `ota check` itself still stays on the configured readiness/check surface instead of broadening into toolchain diagnosis or fulfillment - monorepo root summaries include grouped per-member results in `members` ## `ota baseline record` and `ota baseline promote` Use these commands for any declared `artifacts.<name>.replay` authority lane. The artifact keeps its `kind`, so an existing `generated_source` can gain replay authority without duplicate output ownership. ```bash ota baseline record --artifact recorded-baseline --json ota baseline promote --artifact recorded-baseline \ --attestation .ota/replay-baselines/recorded-baseline/attestation-<sha>.json --json ``` `record` executes the declared producer, archives its execution receipt, and writes a content-addressed attestation of the complete generated output set, exact task scope, resolved backend, lifecycle when one applies, and the clean Git source identity verified before and after the producer runs. Declared baseline outputs and Ota-owned `.ota` runtime state are excluded from that check; every other tracked or untracked source mutation refuses recording. `record` does not update a baseline or select the newest recording. Every attestation also carries the producer's V11.16 execution-boundary graph and its asserted target and derivation-input closure identities. When no material prerequisite adapter observed a graph, Ota records the canonical empty graph with `unknown` posture rather than omitting that provenance. `promote` verifies the selected attestation against its successful archived receipt and current contract identity, then atomically writes only the contract-declared portable authority manifest. That manifest embeds the selected attestation, retaining reviewable source, scope, receipt, and boundary provenance without requiring local archive retention. `ota doctor` evaluates the selected replay-consumer closure against that portable authority before a replay task starts. For a `generated_source` artifact, a consumer that explicitly depends on the producer is ordinary generated lineage execution, not replay; dedicated `replay_baseline` artifacts always require authority. A missing, revoked, malformed, or identity-mismatched authority emits `OTA_REPLAY_BASELINE_UNAVAILABLE`; record and explicitly promote a reviewed replacement instead of editing a digest or letting replay regenerate the baseline. `consumption: read_only` requires an ephemeral runner-owned container boundary across the selected closure. Ota mounts a run-scoped snapshot stored outside the writable workspace; it does not treat `verify_unchanged` as read-only enforcement. Use `consumption: verify_unchanged` when native execution must remain available: Ota verifies the authority before the task and re-checks it afterward. A changed output fails with `replay_artifact_mutation_detected`; this detects a write after it occurred and does not claim it was prevented. Command-capable typed preparation remains declarative in `ota.yaml`; Ota projects it through the same strict container boundary instead of requiring a shell replacement. Use this when a fixture, generated store, or model baseline must change through an intentional recording lane. Review the producer output and selected attestation; never hand-edit a digest to make replay pass. ## `ota receipt` Capture the current repo readiness scan as a read-only receipt artifact for CI, archival use, or baseline comparison. Use this when you want a stable handoff between local readiness and CI history: - `ota receipt --json --archive` leaves a durable readiness artifact behind a CI run - `ota receipt --json --archive --promote-baseline` marks one known-good receipt as the repo's explicit baseline - `ota receipt --json --baseline promoted` compares the current repo state against that reviewed baseline instead of whatever happened to run last - `ota receipt --workflow frontend --json --archive` captures one declared workflow's readiness lane as its own archived receipt/baseline stream instead of mixing it with another workflow's history - `ota receipt --snapshot latest` reads the archived normalized contract truth directly from the latest matching archived receipt instead of routing that inspection through compare mode ```bash ota receipt [PATH] ota receipt --json [PATH] ota receipt --mode container [PATH] ota receipt --container --persistent [PATH] ota receipt --remote --ephemeral [PATH] ota receipt --workflow frontend [PATH] ota receipt --archive [PATH] ota receipt --archive --promote-baseline [PATH] ota receipt --baseline promoted [PATH] ota receipt --baseline latest [PATH] ota receipt --baseline ./baseline-receipt.json [PATH] ota receipt --baseline latest --fail-on-new-blockers [PATH] ota receipt --snapshot latest [PATH] ota receipt --snapshot promoted [PATH] ota receipt --snapshot ./.ota/contracts/sha256-....json [PATH] ota receipt --snapshot ./.ota/receipts/repo-receipt-....json [PATH] ota receipt --history [PATH] ota receipt --member api [PATH] ``` Current behavior: - resolves `ota.yaml` using `--file`, `OTA_FILE`, or upward discovery - `--member <name>` captures the merged monorepo member contract instead of the root contract - validates the contract first - runs repo readiness diagnosis in the selected execution context - `--workflow <name>` captures one declared workflow's readiness selection instead of the repo's default readiness path, including workflow-owned env-profile rendering and readiness selection - `ota receipt` now accepts the same execution-selector family as `ota doctor`: `--mode`, backend shorthands (`--native`, `--container`, `--remote`), `--lifecycle`, and lifecycle shorthands (`--persistent`, `--ephemeral`) - includes repo contract drift findings from the same `ota detect` comparison path used by `ota doctor` - captures the current repo state as an execution receipt with one `readiness` step - when a lifecycle override is selected, the receipt preserves that selected lifecycle, image, target, and rerun path instead of falling back to the default doctor container lifecycle - receipt JSON now includes additive `receipt.toolchains[]` evidence for the selected readiness path, mirroring the same provider/backend/target OS/fulfillment shape used by `ota run <task> --dry-run --json` and `ota up --json` - when a recorded execution path actually ran provider fulfillment commands, `receipt.toolchains[]` can also include additive `fulfilled` and `commands[]` evidence there - never provisions, runs tasks, starts services, or writes repo state - `--json` returns a repo receipt artifact with `mode: "receipt"` - receipt JSON always includes a normalized `receipt.contract_snapshot_hash`; `--archive` also materializes that normalized snapshot under `.ota/contracts` - `latest`, `promoted`, and archived baseline/snapshot selection stay workflow-scoped when `--workflow` is selected, so one workflow's receipt history cannot silently replace another's - receipt JSON also includes an additive `receipt.assumption_set_hash` derived from the canonical extracted assumption map, so automation can fingerprint semantic contract meaning separately from whole-snapshot identity - `--archive` writes the JSON receipt to `.ota/receipts` and keeps the newest 50 archives - `--archive --promote-baseline` also writes `.ota/receipts/repo-baseline.json`, pointing at the archived receipt as the repo's explicit promoted baseline - `--history` lists archived repo receipts from `.ota/receipts` newest first without loading or validating the current contract; explicit paths must be a repo directory or an `ota.yaml` file - `--baseline promoted` compares the current receipt against the explicit promoted baseline pointer under `.ota/receipts/repo-baseline.json` - `--baseline latest` compares the current receipt against the newest valid archived repo receipt for the same contract under `.ota/receipts` - `--baseline <file>` compares the current receipt against an explicit repo receipt JSON file - `--snapshot latest` resolves the newest valid archived repo receipt for the same contract and reads the archived normalized contract snapshot referenced by that receipt - `--snapshot promoted` resolves the explicit promoted repo baseline pointer under `.ota/receipts/repo-baseline.json` and reads the archived normalized contract snapshot - `--snapshot <receipt.json>` accepts an archived repo receipt JSON file and follows `receipt.contract_snapshot_ref` - `--snapshot <snapshot.json>` accepts an archived normalized snapshot JSON file directly - `--snapshot` is read-only and does not rerun doctor, archive a new receipt, or mutate repo state - compare mode is read-only and does not archive or mutate repo state; it exits `0` when the comparison itself succeeds, even if the current or baseline receipt is not ready - when the selected baseline receipt carries `receipt.contract_snapshot_ref`, compare mode also diffs the archived normalized contract snapshot against the current normalized contract truth and returns additive `contract_changes[]`, `likely_related_changes[]`, and `summary.comparison.contract_snapshot_changed` in JSON output - compare mode also returns additive `summary.comparison.correlation` so automation can distinguish `likely_related`, `possibly_related`, and `no_clear_correlation` without inferring correlation posture from array presence alone; `possibly_related` is reserved for coarse same-family overlap, while unrelated drift stays `no_clear_correlation` - public operator guide: [`Semantic Snapshots and Correlation`](https://ota.run/docs/reference/semantic-snapshots-and-correlation) for the higher-level snapshot, diff, and correlation workflow - `--fail-on-new-blockers` requires `--baseline` and exits `1` when the diff introduces one or more new `severity: error` findings relative to the baseline Text output: - header: `RECEIPT <path>` - prints the receipt steps, compact contract identity, summary, env sources, policy lines, and blocked items when present - `--archive --promote-baseline` adds `Baseline:` and `Promoted:` summary lines so the operator can see which archive became the explicit repo baseline - `--history` switches the text header to `RECEIPT HISTORY <path>` and lists archived receipt files with their archived time, archived status, contract path, and any preserved execution identity fields such as context, backend, target, provider, lifecycle, and cwd; malformed archived files are skipped and surfaced under `Skipped Archives` - `--baseline` switches the text header to `RECEIPT DIFF <path>` and reports the baseline source plus provenance such as the selection path, promoted time, contract identity, introduced findings, resolved findings, and unchanged findings when there are no newly introduced or resolved changes - `--baseline` also preserves execution identity on both sides when present, including archived/current `status`, `backend`, `context`, `target`, `provider`, `lifecycle`, and `cwd` - `--baseline` includes the advisory correlation posture inside the `Drift:` overview line so operators can see whether new blocker findings look likely related, possibly related, or have no clear contract-change correlation - `--fail-on-new-blockers` adds a `Gate:` overview line showing whether the current diff passed or was blocked by newly introduced blockers - `--snapshot` switches the text header to `RECEIPT SNAPSHOT <path>` and prints the archived snapshot source, selection kind, archive/snapshot paths, whole-snapshot hash, assumption-set hash, assumption count, any preserved contract identity, and the pretty-printed normalized semantic contract JSON itself JSON output: - `ok` - `path` - `mode: "receipt"` - `archive_path` (when `--archive` is set) - `promoted_baseline.path`, `promoted_baseline.archive_path`, and `promoted_baseline.promoted_at` (when `--archive --promote-baseline` is set) - `summary` mirroring the receipt summary with `error_count`, `warn_count`, `info_count`, and `step_count` - `receipt`, including additive `receipt.contract_identity` with declared project, selected metadata, execution intent, compact contract counts, and optional `receipt.workflow_env_artifacts` when the selected/default workflow owns rendered env artifacts - `findings` - `--history` switches `mode` to `history` and returns `summary.archive_count`, `summary.invalid_archive_count`, an `archives` array for valid archived receipts, and `invalid_archives` when malformed archive files were skipped - `--snapshot` switches `mode` to `snapshot` and returns additive `source`, `selection_kind`, `selection_path`, `archive_path`, `archived_at`, `promoted_at`, `snapshot_hash`, `snapshot_path`, `contract`, and the normalized archived `snapshot` - `--baseline` keeps `mode: "diff"` and returns additive `summary.comparison.correlation`, `baseline.assumption_set_hash`, and `current.assumption_set_hash` alongside the existing archive-backed snapshot-drift evidence - each history archive may preserve `status`, `backend`, `context`, `target`, `provider`, `lifecycle`, and `cwd` when that execution identity existed in the archived receipt - `--baseline` switches `mode` to `diff` and returns `baseline`, `current`, `summary`, `introduced`, `resolved`, and `unchanged`, with additive provenance fields on `baseline` - diff `summary` also carries a compact `comparison` block so wrappers can show baseline/current identity labels plus readiness drift without reconstructing it from the full baseline/current sections - `--fail-on-new-blockers` adds `gate.rule`, `gate.passed`, and `gate.new_blocker_count` to diff JSON when the compare gate is active - when that compare gate blocks, diff JSON also carries the first blocking summary, next step, and provenance so CI summaries and PR comments do not need to scrape the full `introduced` array Current non-goals: - mutating repo state - replacing `ota doctor` as the full readiness explanation surface - separate receipt storage outside the explicit `.ota/receipts` archive directory - monorepo multi-member roll-up beyond the selected resolved contract target - multi-rule diff gating beyond the explicit `--fail-on-new-blockers` compare gate ## `ota up` Prepare a repo for use with minimal prior knowledge. ```bash ota up [PATH] ota up --json [PATH] ota up --stream [PATH] ota up --dry-run [PATH] ota up --dry-run --json [PATH] ota up --mode container --ephemeral [PATH] ota up --effect-override network:broad=allow [PATH] ota up --workflow verify --replay-baseline promoted [PATH] ota up --member api [PATH] ota up --member api --member web [PATH] ota up --agent [PATH] ota up --reason "release approved" [PATH] ``` Current behavior: - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota up` prepares the root contract and grouped member summaries for each declared member - when `--member` is set, prepares the merged member contract - repeated `--member` values prepare those members in the provided order - `--agent` enforces the declared agent-safe task boundary before setup or workflow execution starts; ota refuses the selected workflow path when any selected prepare/setup/run/attach task sits outside the safe set or reaches an unsafe task closure - `--reason <text>` attaches operator intent when the selected workflow crosses a heavier audited execution boundary; repo-target `ota up --json` mirrors the resulting ota-authored crossing record at both `governance.crossing` and `receipt.crossing` - runs inherited or overridden setup in the effective member directory - runs blocking precondition checks - when the selected or default workflow task closure declares `tasks.<name>.requirements`, `ota up` evaluates and provisions that merged prerequisite surface before setup instead of unrelated task-local quickstart or packaged-runtime requirements elsewhere in the repo - `ota up --dry-run` now lists selected toolchains explicitly: `fulfillment.mode: none` means ota will diagnose/check that toolchain on the selected path without provisioning it, while `fulfillment.mode: run` means ota may provision that selected toolchain on the selected run path if the chosen fulfillment source and policy allow it - mixed-backend workflows now keep selected prerequisites on their own execution boundary during `ota up` preflight, so a native run task is diagnosed on the host while a container setup task is diagnosed in the selected container lane instead of flattening both into one doctor mode - selected toolchain preview lines stay toolchain-owned: when a declared toolchain owns the selected ecosystem path, `ota up --dry-run` describes that provider-owned toolchain requirement instead of pretending owned capabilities are standalone setup tools, and the preview now names the owned runtime capability alongside the provider so `toolchains.node` reads as Node via Corepack rather than implying that Corepack itself is the runtime; when `toolchains.node` declares `package_managers`, those activation lanes stay attached to the same selected toolchain - policy-backed version/provisioning previews now include selected toolchain-owned runtime lanes too, and selected toolchain-owned tool lanes when the provider projects them there, so a repo can show governed install paths without reintroducing duplicate `runtimes` or `tools` ownership - `ota up --json` and preview/final execution receipts now expose additive `receipt.toolchains[]` evidence so the selected workflow path's toolchain/provider/backend decisions stay machine-readable alongside readiness findings - when ota actually runs provider fulfillment commands on the selected run path, the final receipt can also include additive `fulfilled` and `commands[]` evidence for that toolchain entry - when one selected tool requirement resolves to `tools.<name>.acquisition`, `ota up` can run that activation lane before setup when it is safe and selected; shipped paths now cover both Corepack-managed tools and explicit shell-command acquisition - on a selected native Corepack path, ota activates every declared package manager before direct task commands run, so `yarn` or `pnpm` resolves to the contract-owned version rather than an ambient global shim - when the selected workflow setup or run task references `requirements.native` entries with `activation.kind`, ota resolves that native activation environment first; Windows `visual_studio_dev_shell` and generic `command` activations both apply to the affected native task bodies instead of assuming the current terminal was pre-activated - when policy-backed provisioning is selected for a tool that also has an acquisition path, `ota up --dry-run` names the provisioning action and explicitly marks the selected acquisition activation as skipped - when a selected workflow declares `prepare.task`, `ota up` runs that native host file-prep action before pre-setup services or setup; this phase is for deterministic file preparation such as `copy_if_missing`, not long-lived runtime or service work - when a default workflow declares `setup.task`, `ota up` uses that task as the preparation phase; otherwise it falls back to repo `setup` - when blocking preconditions fail and the selected workflow declares a setup task, ota runs that setup phase early and then re-checks readiness - when the effective execution mode is container, policy-backed provisioning adapters run inside that container instead of on the host - explicit or effective container-backed `ota up` stays container-authoritative; if no supported container engine is available, ota stops in preconditions instead of falling back to host provisioning - when provisioning fails, `ota up` now surfaces a higher-level backend diagnosis for every shipped adapter while still preserving the raw backend stdout/stderr in the failure output - when the initial provisioning stderr is too generic to classify safely, `ota up` reuses the read-only installability probe for that adapter to refine the diagnosis without hiding the original backend output - when container/Linux provisioning uses `apt`, ota also classifies supported provisioning failures as pinned-version unavailable, package unavailable, or apt-index/source failures - execution-plane precondition failures, backend startup failures, and provisioning failures now point through `ota execution plan` first so the selected backend, lifecycle, image, or target path is visible before you edit execution settings or retry `ota up` - `--dry-run` reuses the same contract path, member targeting, backend selection, lifecycle selection, and provisioning plan resolution as `ota up`, but does not mutate repo or execution state - runs selected workflow service startup through the declared service owner before and after setup as declared - starts required services, and required-service dependencies, in declared dependency order - verifies required service healthchecks before setup and treats them as readiness gates - stops in the `services` phase when required-service readiness still fails - runs the selected workflow setup task if one exists, using the configured execution backend when present - activates the selected workflow run task when it has a declared service runtime - for service-runtime workflow run tasks, default `ota up` behavior uses detached readiness proof semantics: start the run task, wait for readiness, then tear down proof-owned run execution before returning `READY` - `ota up --detach` keeps that proved run task alive intentionally after readiness confirms - `ota up --attach` keeps `ota up` attached to the workflow run task process instead of detached proof behavior - `--ready-timeout <duration>` overrides readiness wait budget for detached service-runtime proof behavior - when setup binds to a named context that uses `extends`, `ota up` uses the merged context backend/lifecycle/image shape - can override execution mode and lifecycle for the selected workflow setup/run phase with `--mode`, `--lifecycle`, or the shorthand `--ephemeral` - explicit workflow execution overrides use the same fail-closed admission as `ota run`; if the selected task path cannot enforce a requested mode or lifecycle, `ota up` refuses before prepare/setup/run execution instead of treating the request as advisory - `ota up --dry-run --json` carries `execution_started: false`; option-admission blockers identify the requested flag and selected task before any workflow phase starts - `--effect-override <effect>=<allow|warn|deny>` temporarily overrides one effect-governance decision for this `ota up` invocation only using the same selectors as `ota run` - `--replay-baseline <latest|promoted|archive-path>` resolves an archived repo receipt baseline before execution, runs the selected workflow lane, and publishes replay posture from the execution-authored receipt as `replay_verified`, `replay_failed`, or `replay_unavailable` - replay execution is intentionally narrower than general receipt comparison today: it is only supported for mutating single-repo `ota up` lanes and is rejected for `--dry-run`, `--stream`, `--member`, and monorepo-root aggregate `ota up` - the current workflow-task backend path supports native, container, and the shipped remote providers - prints a lifecycle note on stderr when the selected workflow task uses backend-backed execution - reruns readiness diagnosis - still runs service start commands, service healthchecks, and diagnosis on the host today - returns `READY` or `NOT READY` - reports the phase where execution stopped: `preconditions`, `provisioning`, `activation`, `services`, `setup`, `run`, or `post-up diagnosis` - reports `provisioning` when early setup ran but the repo is still not ready - includes setup exit code details when the `setup` task fails - includes service start exit code details when a required service start command fails - keeps child output compact by default and surfaces failed service/setup output inside the final report - `--stream` opts into raw live child output for provisioning, required service `start` commands, and the `setup` task - `--stream` is text-only and is only supported for mutating `ota up` - prints a summary in text output, emits an execution receipt when `--receipt` is set, and includes `summary` plus a `receipt` object in repo-target JSON output; the compact `UP SUMMARY` now leads with `Status` for faster scan time, and monorepo aggregate JSON keeps grouped `members` results instead of inventing a top-level receipt - when the execution receipt carries follow-up guidance, text output appends that shared `Next:` block after `UP SUMMARY`, and the same receipt-backed lane stays on the repo-target JSON `receipt.next` surface so repo and workspace preparation flows end the same way - `--dry-run` prints `UP PREVIEW`, shows the selected execution backend, lifecycle, container image when relevant, a real named target when one would exist, the setup task, the actions ota would attempt, the actions ota would skip because current state already satisfies them, the compact contract identity, and the first actionable readiness finding when one exists - `--dry-run` keeps the shared readiness verdict in JSON `status` / `summary.verdict`, but the preview text and additive JSON `preview_status` now make the execution meaning explicit as `RUNNABLE`, `RUNNABLE WITH WARNINGS`, or `BLOCKED` - `ota run <task> --dry-run` now diagnoses the selected task dependency path with the same precondition truth surface used by `ota doctor` / `ota up`, so selected runtime, tool, and toolchain mismatches block preview instead of being silently described as runnable - `--dry-run` never provisions, starts services, runs setup, or writes repo files - `--receipt` is only for mutating `ota up`; it conflicts with `--dry-run` - replay baselines are honest only for what the archived witness actually carried: matching `semantic_contract_snapshot` acquits contract drift, while matched `declared_replay_input` records remain narrowing evidence and keep replay hermeticity at `partly_ambient` - the detailed preview contract lives in [up-preview.md](up-preview.md) This is the onboarding command. It is intentionally narrower than a general-purpose environment orchestrator. Agent execution: - use `ota up --agent` when the selected workflow path should be enforced against the declared agent-safe boundary before setup or runtime work starts - combine it with `--workflow <name>` when one specific workflow is intended: `ota up --workflow <name> --agent` - use `ota workflows --json` first when you need machine-readable workflow posture before selecting that lane ## `ota self-update` Update the installed ota binary. ```bash ota self-update ota self-update --version v0.1.3 ota self-update --channel stable ota upgrade ota upgrade --version v0.1.3 ota upgrade --channel stable ``` Current behavior: - `ota self-update` and `ota upgrade` are aliases - `--version` pins a specific release - `--channel` currently accepts `stable` and `latest` - `stable` resolves the latest stable release tag - `latest` resolves the newest release entry, including prereleases if present - `--version` overrides the channel when both are set - when the chosen target matches the installed binary, the command exits successfully and prints the up-to-date banner instead of reinstalling - on Windows, when `ota` is currently running, the downloaded binary is staged and applied after the current process exits; a successfully scheduled replacement reports `pending` and exits successfully instead of surfacing a `Copy-Item` stack trace ## `ota policy` Show the active policy pack, its source, and the resolved path. ```bash ota policy [PATH] ota policy --json [PATH] ota policy --file /path/to/ota.yaml ota policy --file /path/to/ota.yaml --json ``` Current behavior: - resolves the policy pack using the same precedence ota uses for repo commands - shows the effective policy content and where it came from - accepts `OTA_POLICY` as a local file path or `http(s)://` URL override - falls back to the nearest ancestor `.ota/org-policy.yaml` - falls back again to the nearest ancestor `ota.workspace.yaml` `workspace.policy` when present - remains read-only Text output: - header: `POLICY <path>` - `Policy source:` shows where ota loaded the policy from - `Policy path:` shows the resolved policy file path or URL - effective policy content when one is loaded - when a policy pack is loaded, `Next:` points to `ota policy review` for boundary inspection and `ota doctor` for readiness with that active policy applied - when no policy pack is found, the text output says so explicitly and points users back to repo readiness or `.ota/org-policy.yaml` JSON output: - `ok` - `path` - `policy_source` and `source` - `policy_path` - `policy` - failure responses include `error` Use this when you need to confirm which org policy ota actually applied before a run or diagnosis. ## `ota policy init` Create a conservative starter org policy pack. ```bash ota policy init [PATH] ota policy init --preset required-sections [PATH] ota policy init --preset provisioning [PATH] ota policy init --preset agent [PATH] ota policy init --dry-run [PATH] ota policy init --json [PATH] ota policy init --dry-run --json [PATH] ``` Current behavior: - writes by default - refuses to overwrite an existing policy pack - defaults to `.ota/org-policy.yaml` under the current directory when no path is given - accepts a repo root, a `.ota/` directory, or an explicit `.ota/org-policy.yaml` target path - supports explicit starter presets: `required-sections`, `provisioning`, and `agent` - writes the minimal valid starter today: `policies: {}` - stays conservative and does not infer org rules or add provisioning approvals automatically - `required-sections` starts with a small required-section policy (`runtimes` and `tasks`) - `provisioning` scaffolds empty `provisioning` and `adapter_bootstrap` maps plus inline example guidance - `agent` starts with agent-safety and `AGENTS.md` export requirements enabled Text output: - write header: `POLICY INIT <path>` - preview header: `POLICY INIT PREVIEW <path>` - `Preset:` is shown when a preset is selected - preview shows the starter policy pack YAML without writing it - write output confirms the written path and points back to `ota policy` - overwrite refusal stays explicit and non-mutating JSON output: - `ok` - `path` - `written` - `mode` (`policy`) - optional `preset` - `config` - failure responses include `error` - overwrite refusals may include `next` Use this when a team needs a valid `.ota/org-policy.yaml` scaffold without guessing policy intent or hand-authoring the starter shape. ## `ota policy review` Review the policy-vs-contract boundary and approved policy sources. ```bash 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: ```bash ota policy review ``` Current behavior: - resolves the active policy pack using the same precedence as `ota policy` - focuses only on policy-authority findings, approved provisioning sources, and adapter bootstrap sources - stays read-only - points repo-owned conflicts back to `ota.yaml` - points governance-owned conflicts back to `.ota/org-policy.yaml` Text output: - header: `POLICY REVIEW <path>` - `Policy` is the context block and shows the active source plus resolved policy path or URL - `Overview` rolls up the policy findings by severity - policy findings use operator-shaped summaries and action-specific `Next:` steps instead of pointing back to `ota policy review` - when no policy pack is found, the text output says so explicitly and points users back to `ota policy` JSON output: - `ok` - `path` - `policy_source` - `policy_path` - `summary` - `finding_groups` - `policy` - `findings` Use this when you need to understand what policy ota enforced, why a repo-contract request is outside the approved policy boundary, or whether the org policy pack itself needs to change. ## `ota skills install` Install the canonical Ota skill for an agent tool after the `ota` binary is available. ```bash ota skills install --agent codex ota skills install --agent claude ota skills install --agent codex --json ``` Current behavior: - installs the first-party `ota` skill from the `ota-run/skills` distribution repository - requires network access to fetch the current distribution copy - requires `--agent codex` or `--agent claude` so Ota writes to the correct tool-specific skill directory - writes Codex installs to `$CODEX_HOME/skills/ota` when `CODEX_HOME` is set, otherwise `~/.codex/skills/ota` - writes Claude Code installs to `~/.claude/skills/ota` - stages and validates the complete skill tree before replacing an existing install - keeps skill lifecycle ownership in the CLI instead of installer scripts JSON output: - `ok` - `skill` - `agent` - `path` Use this after installing or updating Ota when you want Codex or Claude Code to use Ota-specific contract authoring and repo-readiness guidance. ## `ota completion` Show how to enable shell completion for ota. ```bash ota completion --setup ota completion --remove ota completion check ota completion bash ota completion bash --script ota completion zsh ota completion fish ota completion powershell ota completion elvish ``` Current behavior: - `ota completion --setup` detects the current shell when possible and installs ota's managed hook into the shell profile or completion file idempotently - `ota completion --remove` detects the current shell when possible and removes ota's managed hook plus any managed zsh support file idempotently - `ota completion <shell> --setup` installs the managed hook for one explicit shell without relying on auto-detection - `ota completion <shell> --remove` removes the managed hook for one explicit shell without relying on auto-detection - `ota completion check` verifies the detected shell, the current ota binary path, the target profile or completion file, any managed zsh completion file, and whether the managed hook is present or needs refresh - `ota completion <shell>` prints the manual shell setup ota expects for that shell; for zsh it includes both the `_ota` completion file and the `.zshrc` loader - `ota completion <shell> --script` prints the exact raw registration script clap generates for that shell so users can inspect the shell-side function directly - zsh setup writes a managed `_ota` completion file under `~/.config/ota/zsh/_ota` and loads that exact file through the shell completion path instead of relying on late runtime `compdef` registration alone - once the shell has sourced that setup, `ota <TAB>` completes commands first and keeps global flags after them in zsh - once the shell has sourced that setup, `ota run <TAB>` completes task names only when one shared invocation can satisfy the selected repo/member target set, and shells that support candidate help can also show each task description when the contract declares one - once the shell has sourced that setup, `ota run <task> <TAB>` completes shared task input flags and any constrained values that remain valid across the selected repo/member target set - once the shell has sourced that setup, `ota env --task <TAB>` completes task names from the active repo or selected monorepo member, using the same task-description metadata when available - once the shell has sourced that setup, `ota extensions --run <TAB>` and `ota extensions --publish <TAB>` complete declared extension names for the active repo or selected member target - once the shell has sourced that setup, `ota receipt --baseline <TAB>` completes `latest`, `promoted`, and archived receipt JSON files from the active repo's `.ota/receipts` - once the shell has sourced that setup, `--member <TAB>` completes monorepo member names from the active repo contract - once the shell has sourced that setup, `ota workspace run <TAB>` completes task names only when one shared invocation can satisfy the currently available workspace repos, with shared task descriptions when the participating repos agree on that description - once the shell has sourced that setup, `ota workspace run <task> <TAB>` completes shared task input flags and any constrained values that remain valid across the currently available workspace repos - once the shell has sourced that setup, `ota workspace doctor --repo <TAB>`, `ota workspace explain --repo <TAB>`, and `ota workspace list --repo <TAB>` complete declared workspace repo names - when no repo contract is available, shell completion falls back to static command and flag suggestions - the auto-installed hook is managed between `# >>> ota completion >>>` and `# <<< ota completion <<<` markers so rerunning setup updates or reuses the same block instead of appending duplicates - `ota completion --remove` only strips ota's managed block and managed zsh support file; it does not try to edit unrelated shell completion setup - users should reload or re-source their shell after upgrading ota so the shell-side glue and the installed binary stay in sync Use this when you want contract-aware shell suggestions instead of memorizing task names and task input flags. Automatic setup: ```bash ota completion --setup ota completion --remove ota completion zsh --setup ota completion zsh --remove ``` Verification and inspection: ```bash ota completion check ota completion bash --script ``` Manual setup examples: `bash` ```bash ota completion bash # >>> ota completion >>> if command -v ota >/dev/null 2>&1; then source <(COMPLETE=bash ota) fi # <<< ota completion <<< ``` `zsh` ```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` ```fish ota completion fish # >>> ota completion >>> if type -q ota COMPLETE=fish ota | source end # <<< ota completion <<< ``` `PowerShell` ```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` ```elvish ota completion elvish # >>> ota completion >>> if (has-external-command ota) { eval (E:COMPLETE=elvish ota | slurp) } # <<< ota completion <<< ``` Troubleshooting: - `zsh`: if completions still do not appear after setup, reopen the shell or confirm `ota completion check` shows both `Hook: present` and a `Completion file:` line for the managed `_ota` file; `ota completion --remove` gives you a clean reinstall path - `bash`: if completions still do not appear after setup, reopen the shell or source the profile again with `. ~/.bashrc` - `ota completion check` should report `Hook: present`; if it reports `missing` or `needs update`, rerun `ota completion --setup` - `ota completion <shell> --script` lets you inspect the exact raw registration script when the shell-side behavior itself looks wrong ## `ota uninstall` Remove ota from this laptop. ```bash ota uninstall ``` Current behavior: - removes the installed ota binary from the current machine - on Windows, schedules removal of the running executable through a detached helper because the live `ota.exe` cannot delete itself while it is executing - on Unix-like systems, removes the current executable directly when possible - does not touch repo state, contracts, or workspace state Text output: - success: `removed ota from <path>` or `scheduled ota removal from <path>; Windows will delete the running executable after this command exits. Open a new terminal to verify \`ota\` is gone.` - already removed: `ota was already removed from <path>` Use this when you want to remove ota from the machine itself, not when you want to clean a repo. - on success, the command runs the installer for the chosen release target Use this when: - you already have ota installed and want to update it in place Use-case: - a developer sees the update notice after `ota doctor` and runs `ota self-update` JSON output: - `ok` - `path` - `status` - `phase` - `findings` - `service` when a service-start failure occurs - `task` when a task failure occurs - `exit_code` when a child command failure occurs - monorepo root and repeated `--member` summaries include grouped per-member results in `members` - contract load/validation failures return the same failure envelope as `ota validate --json` (`ok`, `path`, and either `errors` or `error`) ## `ota clean` Clean persistent execution state for a repo. ```bash ota clean [PATH] ota clean --member api [PATH] ota clean --member api --member web [PATH] ota clean --stale ota clean --stale --dry-run ota clean --stale --json ``` Current behavior: - validates the contract first - when a root contract declares `workspace.type: monorepo`, plain `ota clean` reports the root cleanup result and grouped member cleanup results - when `--member` is set, targets those merged member contracts in the provided order - removes current contract-derived Ota-managed persistent containers and dependency-isolation volumes - rediscovers and removes drifted Ota-managed persistent containers and dependency-isolation volumes for the same repo via ownership labels (`dev.ota.managed`, cleanup kind/lifecycle labels, and repo ownership token) - repo identity for cleanup is anchored by `.ota/state/ownership-id` (not `project.name`) - tracks repo-used container engines in `.ota/state/managed-engines` so drift cleanup can still query a previously used engine after contract engine changes - scopes discovery to relevant engines (current contract targets plus recorded repo-used engines) and does not fail because an unrelated installed engine is unavailable - when no relevant engine evidence exists for the repo, falls back to best-effort discovery across locally available container engines and only fails if none of those discovery probes succeed - fails explicitly when discovery for a relevant engine fails; it does not downgrade to `No cleanup needed` - reports ownership-ambiguous Ota-managed state as skipped (not removed) when repo ownership cannot be proven - `ota clean --stale` does not require `ota.yaml`; it scans available local container engines for exited ota-managed containers from any repo - stale cleanup uses ota ownership labels first and falls back to legacy `ota-*` container names for older persistent backends - if a local container engine cannot answer `ps`, stale cleanup continues with other available engines and only fails when none of them can be queried - `ota clean --stale --dry-run` previews stale containers without removing them - `ota clean --json` emits structured repo/member/workspace cleanup counters on success and structured cleanup failure details on failure; active execution cleanup barriers now classify separately from engine/resource failures instead of falling back to a generic error string - `ota clean --stale --json` emits the matched engines, containers, and cleanup counts for automation - `ota clean --stale` has its own exit-code contract and is separate from repo-scoped `ota clean` - remote backends do not currently define cleanup semantics; they report `No cleanup needed` - reports `No cleanup needed` only when no owned cleanup target is found and no relevant-engine discovery failed - repo-scoped `ota clean` now treats any live active-execution registry entry as a cleanup barrier; it fails with an execution-conflict report instead of stopping host services or tearing down persistent state underneath an active run - does not stop services or perform workspace-wide cleanup ## `ota detect` Infer a starting contract from repo state. ```bash ota detect --dry-run [PATH] ota detect --json --dry-run [PATH] ota detect --contract [PATH] ota detect --write [PATH] ota detect --json --write [PATH] ota detect --merge --dry-run [PATH] ota detect --merge --apply FIELD [PATH] ota detect --merge --apply-all [PATH] ota detect --merge [PATH] ota detect --rewrite --dry-run [PATH] ota detect --rewrite --yes [PATH] ota detect [PATH] ``` Current detect sources: - `package.json` - `pnpm-workspace.yaml` - `pnpm-lock.yaml` - `yarn.lock` - `bun.lock` / `bun.lockb` - `package-lock.json` - `npm-shrinkwrap.json` - `.nvmrc` - `.node-version` - `.devcontainer/devcontainer.json` - `devbox.json` - `AGENTS.md` - `CLAUDE.md` - `.tool-versions` - `mise.toml` - `devenv.nix` - `pyproject.toml` - `Pipfile` - `uv.lock` - `requirements.txt` - `setup.cfg` - `.python-version` - `.java-version` - `.sdkmanrc` - `go.mod` - `Cargo.toml` - `rust-toolchain.toml` - `rust-toolchain` - `settings.gradle(.kts)` - `build.gradle(.kts)` - `gradle/wrapper/gradle-wrapper.properties` - `pom.xml` - `mvnw` - `.mvn/wrapper/maven-wrapper.properties` - `composer.json` - `.ruby-version` - `Gemfile` - `global.json` - `*.sln` / `*.csproj` / `*.fsproj` - `mix.exs` - `rebar.config` - `build.zig` - `dub.json` / `dub.sdl` - `fpm.toml` - `shard.yml` - `elm.json` - `cpanfile` / `Makefile.PL` - `*.hxml` - `docker-compose.yml` / `docker-compose.yaml` - `compose.yml` / `compose.yaml` For Docker Compose service inference, ota currently derives: - `provider` at high confidence - `start` / `stop` at high confidence - declared `healthcheck.test` at high confidence For external `AGENTS.md` / `CLAUDE.md`, ota currently admits only narrow structured shapes: - `safe_tasks`, `verify_after_changes`, `writable_paths`, and `protected_paths` lists as medium-confidence agent-boundary evidence - labeled command bullets under explicit command sections such as `## Commands`, `## Individual Commands`, `## Common Commands`, `## Build/Test Commands`, or `## Build & Development Commands`, including nearby bounded variants such as `## Quick Reference Commands` - bounded structured command tables inside those explicit command sections as low-confidence task guidance for narrow canonical lanes such as `build`, `check`, `lint`, `test`, `docs`, and `compile`; ota currently admits exact `| Task | Command |` tables and adjacent `| Command | description |` forms when it can still recover a stable canonical task name from the command itself or from a bounded task label such as `Run all unit tests`, `Run type checking`, or `Format code` - placeholder-heavy command templates stay out of detect evidence even inside those structured sections; commands that still carry metavars such as `<PROJECT>`, `<tests>`, or obvious `path/to/...` scaffolding are guidance prose, not runnable task truth - when a structured boundary-doc command already exposes a stable canonical task identity such as `pnpm run format:diff` or `pnpm run test`, ota prefers that command-owned task identity over a prose alias from the table label Ota-generated/self-origin agent docs stay excluded from detect evidence, and broader prose is not promoted into executable contract truth. Dry-run behavior: - `ota detect` is read-only by default - prints a candidate `ota.yaml` - prints per-field provenance - prints per-field confidence - when curated standard env source files exist, includes inferred `env.sources` entries for `.env.local`, `.env`, `src/main/resources/application.properties`, `src/main/resources/application.yml`, `src/main/resources/application.yaml`, `appsettings.json`, and `appsettings.Development.json` - when `ota.yaml` already exists, text output leads with the existing-contract comparison and drift review before the inferred contract details - existing-contract add/update lines include the detector source and confidence for the proposed value - when `ota.yaml` already exists and only drift is present, text output says there are no additive detected changes and points users at merge vs rewrite review - text preview now renders an explicit `Agent boundary` outcome: `Inferred` when safe tasks were inferred, `Partially inferred` when only writable/protected boundary defaults were inferred, and `Omitted` when ota intentionally withholds the starter `agent` block because no safe task was inferred - PowerShell-script detection now emits `runtimes.pwsh` for `pwsh`-based repos instead of the legacy `runtimes.powershell` key, so the inferred contract and later runtime probes stay aligned with the detected task/tool surface - does not write anything Contract preview behavior: - `ota detect --contract` prints the exact starter contract that `ota init` would write - `ota detect --contract` omits annotations and comparison output - `ota detect --contract` is text output only Current write behavior: - `ota detect --write` writes a conservative detect-write candidate: high-confidence detected fields plus a narrow starter-owned subset ota can model structurally without broadening into a full starter rewrite - `ota detect --write` remains conservative even when `ota init` can write a valid starter - versioned `pnpm`/`yarn` package-manager-backed `package.json#engines.node` is high confidence for detect write, merge, rewrite, ownership metadata, and drift comparison, and is written as canonical `toolchains.node` Corepack ownership so a detected Node contract does not silently omit the runtime or generate legacy split ownership - detected Go, Ruby, and .NET runtime/tool lanes now converge to canonical toolchain ownership on write/merge (`toolchains.go`, `toolchains.ruby`, and `toolchains.dotnet` with structured fulfillment instead of legacy split ownership), and detected `tools.bundler` ownership folds into `toolchains.ruby.package_managers.bundler` instead of remaining split - Docker Compose service `start`, `stop`, and declared `healthcheck.test` commands are high confidence and can be written with the inferred service block - detect preview, exact starter preview, and detect write now keep the same derived starter `agent` block that init uses, while detect-owned field metadata remains scoped to actually inferred fields and writable-path inference can include broader common directories plus bounded custom source roots - detect write metadata now distinguishes direct high-confidence writes from conservative promotion: written contracts record `metadata.ota.detect.field_admission` with `direct` for detector-owned fields and `promoted` for the narrow starter-owned fields admitted by the conservative detect-write lane - validates the generated contract before writing - refuses to overwrite an existing `ota.yaml` - when no `ota.yaml` exists yet, preview guidance stays compare-first: `ota detect --contract` for exact detected text, `ota init --dry-run` for the conservative starter path, then `ota detect --write` for the first detected write - after a successful first detect write, text output uses explanatory `Next:` steps: validate the written contract, inspect the runnable task surface, review readiness with doctor, then preview preparation with `ota up --dry-run` Current merge-preview behavior: - `ota detect --merge --dry-run` is a review-only mode - it requires an existing `ota.yaml` - it does not write - it reuses the comparison preview instead of applying changes, including stale contract fields that no longer match repo reality - JSON comparison entries carry stable ownership/provenance labels; add/update entries also carry direct detector source and confidence - task drift in text output is grouped by task name instead of raw dotted paths - when both kinds are present, task drift splits command removals from `safe_for_agent` removals - task drift text starts with a compact summary showing affected task count and removal counts by kind - with `--concise`, task drift collapses to one line per affected task with removal counts instead of listing every command - there is no standalone `ota drift` command yet; drift review stays on `ota detect --merge --dry-run`, and operator-facing trust/readiness drift stays on `ota doctor` - CI verification drift stays on verification-oriented workflow evidence only; deploy, release, publish, sync, and similar workflows no longer become canonical verification truth just because they contain one verifier step - declared verifier aggregates can now be satisfied by exact coverage split across more than one verification-oriented workflow file instead of forcing one workflow file to restate the whole aggregate Current merge-write behavior: - `ota detect --merge` requires an existing `ota.yaml` - it applies only `high` confidence missing fields - `ota detect --merge --apply FIELD` applies only the selected high-confidence detected changes and leaves the rest of `ota.yaml` unchanged - `ota detect --merge --apply-all` applies all eligible high-confidence detected changes and leaves the rest of `ota.yaml` unchanged - inferred `env.sources` additions participate in the same high-confidence merge/apply path and are never auto-loaded at runtime unless they are declared in the contract - it does not overwrite conflicting existing values - it validates the merged contract before writing - it is additive only in the current implementation - on mixed repos, lower-confidence fields can still appear in `comparison` without being written - if nothing eligible can be added, it returns success with `written: false` and leaves `ota.yaml` unchanged - after a successful merge write, text output uses explanatory `Next:` steps: validate the updated contract first, then review any remaining add-only drift with `ota detect --merge --dry-run`, review rewrite-only drift with `ota detect --rewrite --dry-run` when the current contract is stale, and only drift-free merges hand into the same task/doctor/preparation lane used by first writes Current rewrite behavior: - `ota detect --rewrite` targets existing contracts only and is destructive - `ota detect --rewrite --dry-run` previews replacement without writing - `ota detect --rewrite --yes` replaces the existing `ota.yaml` with the regenerated detected contract - rewrite creates a timestamped backup file (`ota.yaml.bak-<timestamp>`) before writing - rewrite validates the regenerated contract before replacing the existing file - after a successful rewrite, text output uses explanatory `Next:` steps: validate the rewritten contract, inspect the runnable task surface, review readiness with doctor, then preview preparation with `ota up --dry-run` Example dry-run annotations for detected Compose services: ```text --- Annotations: ✦ Field: services.db.provider Type: service Value: docker-compose Source: docker-compose.yml#services.db Signal: config Confidence: high ``` Annotation metadata stays additive but stable: - `Type:` currently renders one of `project`, `runtime`, `tool`, `env`, `service`, `check`, `task`, `agent`, or `field` - `Signal:` currently renders one of `config`, `script`, `lockfile`, `file`, `template`, or `convention` - task-shaped annotations can also include `Agent Safe:` (`yes`, `no`, `unknown`) and `Agent Signal:` (`verification_candidate`, `bootstrap_candidate`) Current precedence is conservative: - higher confidence beats lower confidence - when confidence is equal, more repo-specific runtime sources win before generic version-manager aggregation - when confidence is equal for project names, `package.json` wins over conflicting Python or Go manifest names - when confidence is equal for package-manager tools, `package.json#packageManager` wins over conflicting `.tool-versions` values - repo-owned `mise.toml#tools.*` runtime and Node package-manager truth wins over conflicting `.tool-versions` values, but still yields to more specific repo-local markers such as `.nvmrc`, `.python-version`, or `package.json#packageManager` - `.devcontainer/devcontainer.json` can contribute high-confidence runtime/toolchain truth from known devcontainer features (`node`, `python`, `go`, `github-cli`, `kubectl-helm-minikube`) plus Node package-manager truth from string, object, or array `postCreateCommand` / `updateContentCommand` entries, but those hints remain lower precedence than repo-local runtime markers, `package.json#packageManager`, and `mise.toml` - `devbox.json` can contribute high-confidence `tools.devbox` ownership and `shell.scripts.*` task inference - `devenv.nix` currently contributes only high-confidence `tools.devenv` presence; ota does not yet infer broader Nix environment truth from it in this slice - when `package.json#packageManager` is present for versioned `pnpm` or `yarn`, `package.json#engines.node` is treated as high-confidence `toolchains.node` runtime truth; without a package manager signal, it stays conservative - when `package.json#packageManager` is absent, known repo-local Node package-manager markers such as workspace files and lockfiles can determine the tool and task command prefix conservatively - verifier-style inferred tasks (for example `test`, `lint`, `typecheck`, `check`, `verify`, `fmt`) are marked with `safe_for_agent: true` only when the inferred lane still looks like a bounded verification command; orchestration-flavored lanes such as `docker:test` stay unsafe-by-default, watch/dev/serve variants stay unsafe-by-default because they are usually long-running, and other inferred tasks stay unsafe-by-default - GitHub Actions `run:` lines that still contain `${{ ... }}` interpolation are excluded from command-truth inference instead of being promoted into repo task bodies - `Pipfile` can contribute `python` runtime inference and `pipenv` tool inference conservatively - `uv.lock` can contribute `uv` tool inference conservatively - `requirements.txt` can contribute `pip` tool inference conservatively - `setup.cfg` can contribute project name and `python` runtime inference conservatively - `mise.toml` can contribute high-confidence runtime and Node package-manager inference from repo-owned `[tools]` entries - `.devcontainer/devcontainer.json` can contribute high-confidence Node runtime inference from the declared image, conservative runtime/tool presence from known devcontainer features, and Node package-manager inference from string, object, or array `postCreateCommand` / `updateContentCommand` entries - `devbox.json` can contribute high-confidence `devbox` tool inference plus `devbox run <script>` tasks from declared `shell.scripts` - `devenv.nix` can contribute high-confidence `devenv` tool presence conservatively - for example, `.nvmrc`, `.node-version`, `.python-version`, `.java-version`, `.sdkmanrc`, `go.mod`, `rust-toolchain.toml`, and `rust-toolchain` win over conflicting `.tool-versions` runtime values Write behavior: - `ota detect --write` writes only the conservative detect-write candidate, not the full detected starter contract - validates the projected contract before writing - refuses to overwrite an existing `ota.yaml` - when `ota.yaml` already exists, points the user at `ota detect --merge --dry-run` and `ota detect --rewrite --dry-run` - fails if the high-confidence projection is not sufficient - JSON failure responses can include `next` when ota can point to one safe follow-up command This is intentionally conservative. Review mode comes first, write mode second. ## `ota workspace init` Create a starter workspace contract from existing repo contracts. ```bash ota workspace init [PATH] ota workspace init --json [PATH] ``` Current behavior: - infers workspace repos by scanning common local repo roots (top-level plus containers like `apps/`, `services/`, `repos/`, `packages/`) - includes only repos that already have `ota.yaml` - skips candidate repos that do not yet have `ota.yaml` - when no `ota.workspace.yaml` exists yet, preview-first onboarding is compare-first: review `ota workspace detect --dry-run` against `ota workspace init --dry-run` before any first write - `ota workspace init` writes `ota.workspace.yaml` by default - `ota workspace init --bootstrap` can auto-provision missing repo contracts from detected repo signals before writing `ota.workspace.yaml` - `--write` remains a compatibility alias for the write path - writes `ota.workspace.yaml` - refuses to overwrite an existing `ota.workspace.yaml` - when no repos are available to bootstrap, points to `ota init <repo-path>`, `ota detect --dry-run <repo-path>`, then back to `ota workspace detect --dry-run` and `ota workspace init --dry-run` before any workspace write - when overwrite is refused, points to `ota workspace validate` and `ota workspace doctor` - successful writes now hand directly to `ota workspace validate`, `ota workspace up --dry-run`, and `ota workspace up` - supports JSON for machine-readable write outcomes Text output: - preview: compare-first `Next:` guidance into `ota workspace detect --dry-run` or the explicit write path - write: `WORKSPACE INIT WRITE <path>` with the same post-write lane into `ota workspace validate`, `ota workspace up --dry-run`, and `ota workspace up` JSON output: - success: `ok`, `path`, `written`, `mode`, `config`, `included`, `missing_contract` - failure: `ok`, `path`, `written`, `mode`, `error`, optional `next` ## `ota workspace detect` Infer workspace contract shape and additive merge candidates. ```bash ota workspace detect [PATH] ota workspace detect --write [PATH] ota workspace detect --dry-run [PATH] ota workspace detect --merge [PATH] ota workspace detect --merge --dry-run [PATH] ota workspace detect --rewrite --dry-run [PATH] ota workspace detect --rewrite --yes [PATH] ota workspace detect --json [PATH] ``` Current behavior: - infers workspace repos by scanning common local repo roots (top-level plus containers like `apps/`, `services/`, `repos/`, `packages/`) - includes only repos that already have `ota.yaml` - skips candidate repos that do not yet have `ota.yaml` - default mode is preview - when no `ota.workspace.yaml` exists yet, preview-first onboarding is compare-first: review `ota workspace detect --dry-run` against `ota workspace init --dry-run` before `ota workspace detect --write` - `--write` writes `ota.workspace.yaml` only for first contract creation - `--merge` requires an existing `ota.workspace.yaml` and adds only missing discovered repo entries under `repos` - merge is additive-only and does not overwrite existing repo entries - `--rewrite --dry-run` previews full replacement of an existing `ota.workspace.yaml` - `--rewrite --yes` fully replaces existing `ota.workspace.yaml` with regenerated detected workspace contract - rewrite creates a timestamped backup file (`ota.workspace.yaml.bak-<timestamp>`) before writing - when no repo contracts are found, points to `ota init <repo-path>`, `ota detect --dry-run <repo-path>`, then back to `ota workspace detect --dry-run` and `ota workspace init --dry-run` before any workspace write - successful writes, merges, and rewrites now hand directly to `ota workspace validate`, `ota workspace up --dry-run`, and `ota workspace up` - supports JSON for machine-readable preview/write outcomes ## `ota workspace validate` Validate an ota workspace contract. ```bash ota workspace validate [PATH] ota workspace validate --json [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or an explicit directory boundary - parses the workspace contract - validates the workspace shape - validates each present referenced repo contract through the workspace contract - validates `repos.<name>.workflow` against the referenced repo contract when the workspace pins a non-default repo workflow - allows missing repo paths only when `repos.<name>.source` is declared Text output: - header: `WORKSPACE VALIDATE <path>` - success: `VALID` plus next steps into `ota workspace doctor`, `ota workspace up`, and `ota workspace tasks` - failure: validation or load error text JSON output: - success: `ok`, `path`, `summary.error_count` - failure: `ok`, `path`, `summary.error_count`, and either `errors` or `error` ## `ota workspace tasks` List workspace repo tasks in dependency order. ```bash ota workspace tasks [PATH] ota workspace tasks --json [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or an explicit directory boundary - validates workspace shape and present repo contracts - preserves workspace dependency order in output - lists non-internal task declarations for each acquired repo contract, including task descriptions and declared `after_success`, `after_failure`, and `after_always` hook relationships when the repo contract declares them - carries structured task `launch` additively when one repo task uses command or packaged-container launch instead of shell `run` / `script` - reports non-acquired repos with `acquired: false` and empty task lists - does not execute tasks Text output: - header: `WORKSPACE TASKS <path>` - each repo includes required/optional status, acquisition status, dependency list, and task summaries JSON output: - `ok` - `path` - `summary` with `repo_count`, `acquired_count`, and `task_count` - `repos` - each repo includes: `name`, `path`, `contract_path`, `required`, `acquired`, `depends_on`, `tasks` - each task includes: `name`, `kind`, optional `description`, one execution body field (`run` or `script`) or additive `launch`, `depends_on`, `requires_services`, `requires_artifacts`, `after_success`, `after_failure`, `after_always` ## `ota workspace list` List workspace repos, contract presence, and lightweight readiness status without running workspace doctor. ```bash ota workspace list [PATH] ota workspace list --status ready [PATH] ota workspace list --status not-ready [PATH] ota workspace list --repo <name> [PATH] ota workspace list --json [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace shape for deterministic repo ordering - lists all declared repos (or filters by `--repo` / `--status`) - reports acquisition state per repo (`ACQUIRED` vs `NOT ACQUIRED`) - reports lightweight readiness status per repo (`READY` vs `NOT READY`), and when `repos.<name>.workflow` is declared the readiness lane follows that selected repo workflow instead of the repo default - shows execution metadata and env provenance when the repo contract declares it - reports contract presence per repo (`contract_present`) - for missing contracts in text output, embeds a repo-specific setup hint using `ota init <repo-path>` Text output: - header: `WORKSPACE LIST <path>` - each repo includes required/optional status, acquisition status, readiness status, path, contract path state, dependencies, and execution metadata with env provenance when present - each repo shows acquisition on the summary line, readiness on a dedicated `Status:` line, and execution metadata in a compact `Execution:` block with env provenance when present JSON output: - `ok` - `path` - `summary` mirroring the receipt summary with `error_count`, `warn_count`, `info_count`, and `step_count` - `repos` - each repo includes: `name`, `path`, `contract_path`, `contract_present`, `required`, `acquired`, `status`, `depends_on` ## `ota workspace execution plan` Inspect the resolved execution context for each workspace repo without running anything. ```bash ota workspace execution plan [PATH] ota workspace execution plan --json [PATH] ota workspace execution plan --repo api [PATH] ota workspace execution plan --mode container --ephemeral [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure and keeps repo ordering deterministic - when `repos.<name>.workflow` is declared, plans that selected repo workflow instead of silently assuming the repo default path - reuses the same per-repo backend validation boundary as `ota execution plan` - reports one execution plan per selected workspace repo - supports `--repo` filtering for focused inspection - supports `--mode`, `--lifecycle`, and `--ephemeral` overrides across the selected repos - fails the command when any selected repo cannot produce a runnable execution plan - still preserves each repo’s required/optional declaration in the report instead of flattening workspace metadata - never mutates repo state Text output: - header: `WORKSPACE EXECUTION PLAN <path>` - status line: `READY` or `NOT READY` - optional `Overrides` section when backend or lifecycle is forced - each repo includes required/optional status, path, contract path, acquired state, optional selected `Workflow` / `Task`, and either resolved execution details or an honest `Why` / `Next` - when a repo contract loads, the report also includes the compact `Contract` block and declared `Execution` block for that repo - a final `Summary` block reports resolved and unresolved repo counts JSON output: - `ok` - `path` - `mode: "execution-plan"` - `summary` with `repo_count`, `resolved_count`, `unresolved_count`, `required_unresolved_count`, `not_acquired_count`, and `missing_contract_count` - `repos` - each repo includes: `name`, `path`, `contract_path`, `required`, `acquired`, `status`, additive `workflow` and `task` when workflow planning resolves through a canonical path (including repo-default workflow selection), optional `contract_identity`, optional `declared_execution`, optional `resolved`, optional `error`, and optional `next` - `overrides` appears only when execution overrides are supplied Current non-goals: - running setup or task commands - provisioning missing repos automatically - hiding unrunnable execution choices behind a synthetic success state - inventing one workspace-wide execution backend when repo contracts disagree ## `ota workspace run` Run one task across workspace repos in dependency order. ```bash ota workspace run <task> [PATH] ota workspace run <task> --json [PATH] ota workspace run <task> --json --progress-json [PATH] ota workspace run <task> --jobs 4 [PATH] ota workspace run <task> --stream [PATH] ota workspace run <task> [PATH] --base-url http://localhost:8080 ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure and repo contracts - acquires missing repos declared with `repos.<name>.source` before execution - executes the task for each repo in workspace dependency order - can run independent repos concurrently when `--jobs` is greater than `1` - blocks downstream repos when a dependency repo did not complete successfully - captures per-repo stdout/stderr in default mode - `--stream` opts into raw child output (text only, currently requires `--jobs 1`) - `--json --progress-json` emits live NDJSON workspace progress events on stderr while keeping the final workspace run JSON report on stdout - optional repo task failures do not fail the overall workspace status - task inputs are declared in `tasks.<name>.inputs` and are passed as `--kebab-case value` flags - task inputs are exposed to each repo task process as `OTA_INPUT_<NAME>` env variables - `default` values are applied when the caller omits an input - `required: true` makes an input mandatory unless a default exists - `allowed` limits the accepted values for that input - task inputs only apply to the targeted repo task, not its dependencies - if every declared input has a default, you can omit all input flags Example: ```yaml 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 ``` ```bash ota workspace run api-automation-tests ota workspace run api-automation-tests --base-url http://localhost:8080 --suite-mode contract-drift ota workspace run version:bump --version 0.2.0 ``` - prints a summary in text output, emits an execution receipt when `--receipt` is set, and a `receipt` object in JSON output - the workspace receipt includes additive `receipt.contract_identity` with workspace name/type and compact workspace repo/policy counts Text output: - header: `WORKSPACE RUN <task> <path>` - status line: `READY` or `NOT READY` - per-repo status includes `required/optional`, task name, findings, and optional exit details - after `WORKSPACE RUN SUMMARY`, ota appends the same receipt-backed `Next:` lane used by repo-level execution output when a safe follow-up exists JSON output: - `ok` - `path` - `task` - `summary` - `receipt` - `repos` - each repo includes: `name`, `path`, `contract_path`, `required`, `ok`, `status`, `task`, `findings`, additive `next` / `next_steps`, and optional `exit_code`/`stdout`/`stderr` ## `ota workspace check` Run configured checks across workspace repos in dependency order. ```bash ota workspace check [PATH] ota workspace check --json [PATH] ota workspace check --json --progress-json [PATH] ota workspace check --jobs 4 [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure and referenced repo contracts - evaluates repo checks in workspace dependency order - when `repos.<name>.workflow` is declared, checks that selected repo workflow instead of assuming the repo default path - can check independent repos concurrently when `--jobs` is greater than `1` - preserves deterministic repo ordering in text and JSON output even when checks run concurrently - downgrades findings for optional repos to warnings - `--progress-json` emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-check JSON report on stdout Text output: - header: `WORKSPACE CHECK <path>` - status line: `READY` or `NOT READY` - summary roll-up includes repo verdict and agent verdict before the `Overview` count block at the bottom of the report - each repo includes required/optional status, contract path, and findings rendered through the shared grouped finding UX - when one repo has several findings, ota also surfaces that repo's primary next action before the grouped finding list so the operator does not have to choose the first move by hand - with `--concise`, repo `Path`/`Contract` and finding `Why` detail are omitted; summary + `Next` remain JSON output: - `ok` - `path` - `summary` with `repo_count`, `ready_count`, `not_ready_count`, `verdict`, `agent_verdict`, `error_count`, `warn_count`, and `info_count` - `summary.primary_blocker` may be present with the workspace headline blocker, including the repo name that currently owns it - each repo may include additive `primary_blocker` with that repo's current highest-priority `severity`, `summary`, `why`, and `next` - `summary.primary_blocker.code` and `repos[].primary_blocker.code` are additive and only appear when the blocker comes from a finding with explicit stable identity - `repos` ## `ota workspace doctor` Diagnose workspace repo readiness from an ota workspace contract. ```bash ota workspace doctor [PATH] ota workspace doctor --json [PATH] ota workspace doctor --jobs 4 [PATH] ota workspace doctor --repo <name> [PATH] ota workspace doctor --status ready|not-ready [PATH] ota workspace doctor --severity error|warn|info [PATH] ota workspace doctor --stream [PATH] ota workspace doctor --json --progress-json [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure - evaluates repos in dependency order - can diagnose independent repos concurrently when `--jobs` is greater than `1` - preserves deterministic repo ordering in text and JSON output even when diagnosis runs concurrently - evaluates each referenced repo through its own `ota.yaml` - when `repos.<name>.workflow` is declared, diagnoses that selected repo workflow instead of assuming the repo default path - reports missing-but-acquirable repos as not yet acquired - keeps workspace logic above repo diagnosis instead of duplicating it - downgrades findings for optional repos to warnings - rejects required repos that depend on optional repos - supports repo/status/severity filtering for focused diagnosis views - `--stream` is text-only and emits repo completion updates while the final report is being built - `--progress-json` emits the same live workspace progress events as compact NDJSON on stderr while preserving the final doctor JSON report on stdout Text output: - header: `WORKSPACE DOCTOR <path>` - status line: `READY` or `NOT READY` - when the workspace is blocked, a primary blocker appears immediately under the readiness status - summary roll-up includes repo verdict and agent verdict before the `Overview` count block at the bottom of the report - each repo includes required/optional status, contract path, and findings rendered through the shared grouped finding UX - with `--concise`, repo `Path`/`Contract` and finding `Why` detail are omitted; summary + `Next` remain JSON output: - `ok` - `path` - `summary` mirroring the workspace doctor roll-up with `repo_count`, `ready_count`, `not_ready_count`, `verdict`, `agent_verdict`, `error_count`, `warn_count`, and `info_count` - `summary.primary_blocker` may be present with the workspace headline blocker, including the repo name that currently owns it - each repo may include additive `primary_blocker` with that repo's current highest-priority `severity`, `summary`, `why`, and `next` - `summary.primary_blocker.code` and `repos[].primary_blocker.code` are additive and only appear when the blocker comes from a finding with explicit stable identity - repo execution metadata may include env provenance for inherited workspace policy values - `repos` Current non-goals: - passing a repo URL directly on the CLI without a workspace contract ## `ota workspace explain` Explain workspace readiness findings as an ordered remediation plan. ```bash ota workspace explain [PATH] ota workspace explain --json [PATH] ota workspace explain --repo api [PATH] ``` Current behavior: - diagnoses the workspace first - exposes one top-level ordered workspace plan before the per-repo drill-in - keeps the same grouped remediation actions and detailed steps under each repo - stays read-only and deterministic - prints a summary with repo and step counts at the end Text output: - one top-level `Plan` section with explicit repo ownership for each grouped action - one section per workspace repo - ordered remediation `Plan` steps under each repo - an `Overview` count block at the end JSON output: - success: `ok`, `path`, `summary`, top-level `actions`, and `repos` - each top-level action identifies the owning `repo`, `path`, `contract_path`, `required`, and the grouped action fields - each repo report includes `summary`, grouped `actions`, and detailed `steps` - failure: `ok`, `path`, and either `errors` or `error` The `summary` object on success mirrors the top-level receipt summary and includes `error_count`, `warn_count`, `info_count`, and `step_count`. ## `ota workspace up` Prepare every repo in an ota workspace contract. ```bash ota workspace up [PATH] ota workspace up --json [PATH] ota workspace up --json --progress-json [PATH] ota workspace up --jobs 4 [PATH] ota workspace up --quiet [PATH] ota workspace up --stream [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure - clones missing repos declared with `repos.<name>.source` before repo-level prepare - runs the existing repo-level `up` flow for each referenced repo - when `repos.<name>.workflow` is declared, prepares that selected repo workflow instead of assuming the repo default workflow - can prepare independent repos concurrently when `--jobs` is greater than `1` - respects declared workspace repo dependency order - blocks downstream repos when a dependency does not become ready - aggregates per-repo status, phase, findings, and exit details - captures repo child stdout and stderr per repo so text and JSON output remain deterministic - emits live repo progress on stderr in text mode so users can see queued/running/completed state while buffered output is still being collected - `--json --progress-json` emits the same live workspace progress as NDJSON on stderr while the final workspace-up JSON report remains on stdout; each event now carries additive `phase` and `stage_family` so machine consumers can classify acquisition vs prepare/result lanes without inferring from status text alone - `--quiet` suppresses live progress output and prints only the final workspace report - optional repo failures do not fail the overall workspace result - defaults to sequential execution because `--jobs` defaults to `1` - `--stream` opts into raw live child process output instead of buffered per-repo output - `--stream` is text-only and currently requires `--jobs 1` - does not pull or update repos that already exist locally - prints a summary in text output, emits an execution receipt when `--receipt` is set, and a `receipt` object in JSON output - the workspace receipt includes additive `receipt.contract_identity` with workspace name/type and compact workspace repo/policy counts Text output: - header: `WORKSPACE UP <path>` - status line: `READY` or `NOT READY` - each repo includes required/optional status, phase, findings, exit details, and captured stdout/stderr when present - after `WORKSPACE UP SUMMARY`, ota appends the same receipt-backed `Next:` lane used by repo-level execution output when a safe follow-up exists JSON output: - `ok` - `path` - `summary` mirroring the workspace doctor roll-up with `repo_count`, `ready_count`, `not_ready_count`, `error_count`, `warn_count`, and `info_count` - `receipt` with additive `next_steps` when the workspace follow-up lane can be split into ordered machine-readable steps - each repo may include additive `next` and `next_steps` for that repo's current follow-up lane - `repos` Current non-goals: - passing a repo URL directly on the CLI without a workspace contract - host or workstation provisioning beyond workspace bootstrap plus repo readiness - GitHub API integration or non-git acquisition modes ## `ota workspace refresh` Refresh existing repos in an ota workspace contract without cloning missing ones. ```bash ota workspace refresh [PATH] ota workspace refresh --json [PATH] ota workspace refresh --json --progress-json [PATH] ota workspace refresh --jobs 4 [PATH] ota workspace refresh --dry-run [PATH] ota workspace refresh --quiet [PATH] ota workspace refresh --stream [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure - refreshes repos that already exist locally and have a declared source - leaves missing repos alone so `ota workspace up` remains the bootstrap path - can refresh independent repos concurrently when `--jobs` is greater than `1` - respects declared workspace repo dependency order - blocks downstream repos when a dependency does not become ready - aggregates per-repo status, phase, findings, and exit details - captures repo child stdout and stderr per repo so text and JSON output remain deterministic - emits live repo progress on stderr in text mode so users can see queued/running/completed state while buffered output is still being collected - `--json --progress-json` emits the same live workspace progress as NDJSON on stderr while the final workspace-refresh JSON report remains on stdout; each event now carries additive `phase` and `stage_family` so machine consumers can distinguish refresh/setup progress from generic status strings - `--quiet` suppresses live progress output and prints only the final workspace report - optional repo failures do not fail the overall workspace result - defaults to sequential execution because `--jobs` defaults to `1` - `--stream` opts into raw live child process output instead of buffered per-repo output - `--stream` is text-only and currently requires `--jobs 1` - `--dry-run` previews the refresh commands without changing repo state - `--force` force-fetches and hard-resets refreshed repos to the declared source or `--ref` override - `--prune` prunes stale remote-tracking refs during refresh - `--ref <branch|tag|sha>` overrides the source ref used for refresh - refresh target precedence is: explicit `--ref`, then declared `source.ref`, then the repo's current upstream branch - when none of those targets exist, ota refuses before preview or apply instead of falling through to a vague `git pull` failure - refresh failures now distinguish wrong remote target (`source.ref` / `--ref`), remote access/auth problems, and generic local git-state failures so the follow-up lane stays specific - prints a summary in text output, emits an execution receipt when `--receipt` is set, and a `receipt` object in JSON output Text output: - header: `WORKSPACE REFRESH <path>` or `WORKSPACE REFRESH PREVIEW <path>` for `--dry-run` - preview mode prints `Mode: dry-run (no write)` - status line: `READY`, `NOT READY`, or `NOT ACQUIRED` for normal refresh; preview mode does not claim readiness - each repo includes required/optional status, phase, findings, exit details, and captured stdout/stderr when present JSON output: - `ok` - `path` - `mode`: `refresh` for normal refresh, `preview` for `--dry-run` - `summary` from the shared workspace execution receipt shape, always including `error_count`, `warn_count`, `info_count`, and `step_count`, and optionally including `repo_count`, `ready_count`, and `not_ready_count` when ota recorded the workspace roll-up - `receipt` - `repos` Current non-goals: - cloning missing repos - passing a repo URL directly on the CLI without a workspace contract - host or workstation provisioning beyond workspace bootstrap plus repo readiness - GitHub API integration or non-git acquisition modes ## `ota workspace diff` Compare local workspace repos against their declared source state without mutating anything. ```bash ota workspace diff [PATH] ota workspace diff --json [PATH] ota workspace diff --json --progress-json [PATH] ota workspace diff --jobs 4 [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure - compares each acquired repo’s local git state against the declared source ref or upstream branch - reports `MATCH` when a repo is clean and aligned, `DIRTY` when the worktree has local changes, `DIFFERENT` when commit counts differ, `MISSING` when the repo is absent, and `UNRESOLVED` when git state cannot be compared safely - can compare independent repos concurrently when `--jobs` is greater than `1` - never mutates repo state - `--json` returns a workspace diff roll-up with `mode: "diff"` - `--progress-json` emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-diff JSON report on stdout; those events use `status` for the repo diff verdict such as `MATCH`, `DIRTY`, or `UNRESOLVED`, and `tail` for the machine `drift_kind` - text and JSON now carry an additive top-level lifecycle `next` lane when ota can name the safest refresh or acquisition follow-up directly - text output now makes the comparison provenance explicit on each `Target:` line when ota is using declared `source.ref` versus upstream-branch fallback - when drift is being compared against upstream-branch fallback instead of declared `source.ref`, the repo-level follow-up lane now says that explicitly and suggests declaring `source.ref` when the workspace should own the target - per-repo JSON items can also carry additive `next` and `next_steps` so automation can read the repo-owned follow-up lane without reparsing findings - per-repo JSON also carries additive `drift_kind` so automation can distinguish local dirtiness, commit divergence, missing repo, missing contract, target ambiguity, and unresolved comparison directly - per-repo JSON also carries additive `target_source` so automation can tell whether the comparison target came from declared `source.ref` or from the repo's upstream branch - text and JSON summaries now also break the collapsed `Missing` and `Unresolved` buckets into explicit missing-contract and target-unavailable subcounts when present - differences do not fail the command; the command succeeds and surfaces drift in the report Current non-goals: - refreshing or mutating repo state - cloning missing repos automatically ## `ota workspace status` Compact workspace status combines readiness and drift without mutating repo state. ```bash ota workspace status [PATH] ota workspace status --json [PATH] ota workspace status --json --progress-json [PATH] ota workspace status --jobs 4 [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure - reads repo readiness and local git drift for each workspace repo - when `repos.<name>.workflow` is declared, readiness is evaluated through that selected repo workflow - reports readiness and drift together so you can scan one operational summary - can compare independent repos concurrently when `--jobs` is greater than `1` - never mutates repo state - `--json` returns a workspace status roll-up with `mode: "status"` - `--progress-json` emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-status JSON report on stdout; each event uses `tail` for the repo drift state such as `MATCH`, `DIRTY`, or `UNRESOLVED`, and now also publishes additive `phase` and `stage_family` so machines can tell this is a status/receipt reporting lane without recovering that intent from `command` plus status text alone - text and JSON now carry an additive top-level lifecycle `next` lane when ota can name the safest doctor, refresh, or acquisition follow-up directly - text output now makes the comparison provenance explicit on each `Target:` line when ota is using declared `source.ref` versus upstream-branch fallback - when drift is being compared against upstream-branch fallback instead of declared `source.ref`, the repo-level follow-up lane now says that explicitly and suggests declaring `source.ref` when the workspace should own the target - per-repo JSON items can also carry additive `next` and `next_steps` so automation can read the repo-owned follow-up lane without reparsing findings - per-repo JSON also carries additive `drift_kind` so automation can distinguish local dirtiness, commit divergence, missing repo, missing contract, target ambiguity, and unresolved comparison directly - per-repo JSON also carries additive `target_source` so automation can tell whether the comparison target came from declared `source.ref` or from the repo's upstream branch - text and JSON summaries now also break the collapsed `Missing` and `Unresolved` buckets into explicit missing-contract and target-unavailable subcounts when present - readiness findings and drift findings are surfaced in the same report Text output: - header: `WORKSPACE STATUS <path>` - each repo includes required/optional status, combined readiness and drift status, path, contract path, source metadata, and local git comparison details when present - a summary block reports readiness and drift roll-ups in one place JSON output: - `ok` - `path` - `mode: "status"` - `summary` with readiness counts and drift counts - `repos` - each repo includes: `name`, `path`, `contract_path`, `required`, `acquired`, `ready`, `readiness_status`, `drift_status`, `branch`, `head`, `target_ref`, `ahead`, `behind`, `dirty`, and `findings` Current non-goals: - mutating repo state - cloning missing repos automatically - cross-repo dependency scheduling - passing a repo URL directly on the CLI without a workspace contract - host or workstation provisioning - a workspace-only bootstrap engine that bypasses repo contracts - GitHub API integration or non-git acquisition modes ## `ota workspace receipt` Capture the current workspace scan as a read-only receipt artifact for CI or archival use. ```bash ota workspace receipt [PATH] ota workspace receipt --json [PATH] ota workspace receipt --json --progress-json [PATH] ota workspace receipt --jobs 4 [PATH] ota workspace receipt --archive [PATH] ``` Current behavior: - resolves `ota.workspace.yaml` using `--file`, `OTA_FILE`, or upward discovery - validates workspace structure - reads repo readiness and local git drift for each workspace repo without mutating anything - captures the combined workspace state as an execution receipt with one step per repo - can inspect independent repos concurrently when `--jobs` is greater than `1` - never clones, fetches, resets, or writes repo state - `--json` returns a workspace receipt roll-up with `mode: "receipt"` - `--progress-json` emits the same live workspace progress events as compact NDJSON on stderr while preserving the final workspace-receipt JSON report on stdout; because receipt reuses the same scan as `workspace status`, those events use `tail` for the repo drift state such as `MATCH`, `DIRTY`, or `UNRESOLVED`, and now also publish additive `phase` and `stage_family` so machine consumers can keep receipt/status reporting semantics separate from prepare or task-execution progress - the workspace receipt includes additive `receipt.contract_identity` with workspace name/type and compact workspace repo/policy counts - `--archive` writes the JSON receipt to `.ota/receipts` and keeps the newest 50 archives - the receipt records the same readiness, drift, and findings scan so CI or agents can archive it deterministically Text output: - header: `WORKSPACE RECEIPT <path>` - each receipt step shows the repo name, readiness status, and drift status - the summary block mirrors the execution receipt counts JSON output: - `ok` - `path` - `mode: "receipt"` - `archive_path` (when `--archive` is set) - `summary` mirroring the receipt summary with `repo_count`, `ready_count`, `not_ready_count`, `error_count`, `warn_count`, `info_count`, and `step_count` - `receipt` - `repos` Current non-goals: - mutating repo state - cloning missing repos automatically - cross-repo dependency scheduling - passing a repo URL directly on the CLI without a workspace contract - host or workstation provisioning