This document defines the V5 policy-pack target contract for ota.
Policy packs are org-scoped rules that apply consistently across multiple repos without changing each repo’s source contract shape.
Policy packs let a platform team define shared standards once and apply them deterministically across repos.
They are intended to support:
- required contract sections
- required files
- safer agent task execution
- org-level template and convention enforcement
- audit-friendly machine output
- mutation controls for sensitive operations
The canonical policy pack lives at:
.ota/org-policy.yamlToday, ota resolves the org policy pack in this order:
- the explicit
OTA_POLICYfile path or HTTP(S) URL override, when set .ota/org-policy.yamlin the nearest ancestor directory of the repo contract path- the
workspace.policypath declared in the nearest ancestorota.workspace.yaml, when set
That means:
- a single policy pack can apply to multiple repos inside one workspace tree
- a repo can inherit an org policy from a parent directory
OTA_POLICYgives operators an explicit override when they need a different file path or hosted URL- the canonical policy pack still lives at
.ota/org-policy.yaml, so shared org rules have one deterministic place to live today workspace.policygives a workspace an explicit shared policy file or hosted URL when the workspace wants one
If there is no matching OTA_POLICY, ancestor policy file, or workspace policy source, ota simply
keeps running with repo-local contract behavior.
Example override:
OTA_POLICY=/path/to/custom-org-policy.yaml ota doctorOTA_POLICY=https://config.example.com/custom-org-policy.yaml ota doctorUse this when you want ota to read an explicit policy file path or hosted URL instead of the
nearest ancestor .ota/org-policy.yaml or workspace policy source.
The current implementation supports:
- an explicit
OTA_POLICYoverride that can be a local file path or an HTTP(S) URL - the nearest ancestor
.ota/org-policy.yaml - a workspace policy source declared in
ota.workspace.yaml
The precedence is:
- explicit environment override
- nearest ancestor
.ota/org-policy.yaml - workspace policy source, if the workspace declares one
If none of those sources exist, ota continues without org policy.
That order keeps the most local repo policy first while still letting a workspace declare one shared policy source when it needs to.
One separately configured remote policy source remains future work.
policies:
required_sections:
- runtimes
- tasks
- agent
required_files:
- AGENTS.md
version_policy:
runtimes:
node:
approved_versions:
- "22"
tools:
pwsh:
platforms:
windows:
approved_versions:
- "7.6.0"
env:
values:
DOCS_SITE_BASE_URL: https://docs.internal.example
RELEASE_CHANNEL: stable
agent:
require_safe_tasks: true
require_writable_paths: true
claim_assurance:
agent_safety:
minimum_status: supported
required_coverage:
- task_body
- ci
on_insufficient: deny
replay_inputs:
identity:
tasks:
replay:
on_insufficient: deny
workflows:
offline_replay:
on_insufficient: review
effects:
mode: strict
tasks:
network: warn
dependency_hydration: allow
external_state_default: warn
safe_tasks:
network: deny
dependency_hydration: allow
external_state_default: warn
external_state:
docker: deny
exports:
require_agents_md: trueFor a team that wants a quick rollout, the practical path is:
- preview a starter pack with
ota policy init --dry-run - write
.ota/org-policy.yamlwithota policy init - start with a small set of required sections and files
- run
ota doctorin one repo and compare the output before and after - expand policy only after the first rules are easy to understand
ota policy init is deliberately conservative: it writes the minimal valid starter
policies: {} and does not guess provisioning approvals or org intent.
When the policy owner wants a stronger starting point, ota policy init also supports explicit
presets:
--preset required-sectionsto start withruntimesandtasks--preset provisioningto scaffold empty provisioning and adapter-bootstrap maps with inline examples--preset agentto require safe tasks, writable-path intent, andAGENTS.md
Example policy pack:
policies:
required_sections:
- runtimes
- tasks
required_files:
- AGENTS.md
agent:
require_safe_tasks: trueBefore the policy pack exists, ota doctor only reports repo-local readiness.
After the policy pack is added, a repo missing tasks or AGENTS.md will show an org policy finding like:
◉ ERROR Repo does not satisfy org policy pack
Why: `./.ota/org-policy.yaml` requires missing contract sections: tasks and missing files: AGENTS.md
Next: add the missing items or update `./.ota/org-policy.yaml`
That makes the value visible immediately:
- the repo contract stays local and explicit
- the org policy stays shared and reusable
ota doctorbecomes the review point for both
required_sectionsdefines contract sections that every governed repo must provide.required_filesdefines files that every governed repo must keep at the repo root or under the governed repo directory.strict_versionstells ota whether already-installed runtime and tool versions must also comply with policy instead of only satisfying the repo contract.version_policydefines explicit approved runtime and tool versions.- when
strict_versions: false,version_policy.runtimes.<name>.approved_versionsconstrains the repo contract version for that runtime. - when
strict_versions: false,version_policy.tools.<name>.approved_versionsconstrains the repo contract version for that tool. - when
strict_versions: true, the sameversion_policyrules also constrain the resolved installed versions thatota doctorandota runobserve. version_policy.*.platforms.<os>overrides approved versions forlinux,macos, orwindows.env.valuessupplies approved shared env values for vars the repo contract already declares inenv.vars.env.valuesdoes not create new repo requirements by itself; it only helps satisfy declared env vars.agent.require_safe_tasksrequires agent-visible execution surfaces to be explicitly marked safe.agent.claim_assuranceis an opt-in requirement over V11.14's canonical claim-assurance record. Keys are claim families such asagent_safety; each rule sets a minimum assurance status, optional required coverage, andon_insufficient: deny|review. It never changes the underlying assurance result: policy consumessupported,contradicted, orunknownafter Ota has evaluated it.agent.require_writable_pathsrequires writable-path intent to be declared instead of assumed.replay_inputs.identity.tasks.<name>requires every replay input in that task's dependency closure to declare a matchingexpected_identity.replay_inputs.identity.workflows.<name>applies the same requirement to the complete selected workflow closure. Reachable task rules remain cumulative with the workflow rule.- each replay-input identity rule declares
on_insufficient: deny|review. Insufficient coverage means the governed closure has no declared replay inputs or at least one declared input lacks anexpected_identity. reviewis refusing in the current policy model on human, agent, and CI execution surfaces. It records that authorization is required but does not treat a reason string or audited crossing record as authorization.- an unavailable preflight observation and a missing, unreadable, or mismatched declared pin are
always denied before native provisioning, dependency hydration, or task startup. Policy cannot
weaken the existing
expected_identitypreflight boundary toreview, and the blocked receipt retains the active policy evidence. - task rules apply when their subject is reachable in the selected task or workflow closure, so a
parent lane cannot bypass policy on a governed dependency. Unknown selectors remain contextual
policy findings in Doctor and block governed execution without changing
ota validate. - each selected contract target loads one policy snapshot before admission. Agent safety, claim assurance, replay policy, Doctor, provisioning, proof, receipts, and CI projection consume that snapshot; detached proof execution receives a private temporary copy instead of rediscovering ambient local or remote policy.
effects.modecontrols the fallback decision when no explicit rule matches:compatibilityfalls back towarn,strictfalls back todeny.effects.typed.rulesselects canonical typed effects by kind, action, resolved resource, optional canonical bounds, derivation posture, task, and workflow. Rule IDs are unique canonical lowercase tokens. Every match accumulates; specificity and document order cannot discard a broader deny.- typed resource selectors use one explicit branch:
match: exactbinds a complete canonical namespace, resource ID posture, and PostgreSQL schema;match: namespace_patternuses*only at explicitly wildcarded namespace positions;match: anymatches any resolved PostgreSQL resource. Omitted pattern dimensions match absence, not any value. - the shared evaluator composes typed rules, typed mode fallback, and existing coarse effect
decisions through
deny > warn > allow.--effect-overrideremains limited to coarse selectors and cannot target or weaken a typed rule. - the decision records whether policy came from
OTA_POLICY, repository policy, or workspace policy ascaller_selected,repository_controlled, orworkspace_controlled. Matching content does not upgrade source authority. Independently administered policy is not implemented. effects.tasksgoverns declaredeffects.network/effects.network_kind/effects.external_stateon any selected task closure.effects.tasks.networkcontrols broad network lanes for selected task paths.effects.tasks.dependency_hydrationcontrols lockfile-backed hydration lanes (effects.network_kind: dependency_hydration) for selected task paths.effects.tasks.container_image_hydrationcontrols registry-backed Compose image-pull lanes (effects.network_kind: container_image_hydration) for selected task paths.effects.tasks.service_readinesscontrols finite endpoint assertions against declared repo-managed services (effects.network_kind: service_readiness) for selected task paths.effects.tasks.integration_testcontrols live, staging, or remote-backed verification lanes (effects.network_kind: integration_test) for selected task paths.effects.tasks.external_state_defaultsets the default decision for selected-task external-state targets when no target-specific override is declared.effects.tasks.external_state.<target>overrides external-state decisions per target token.- prefer the same shipped canonical tokens in policy and task contracts so effect governance stays
reusable across repos; for example use
docker,postgres,redis,s3,cloudflare, orkubernetesinstead of repo-local aliases likedocker_compose,postgresql, ork8s effects.safe_tasksgoverns the same effect lanes for agent-safe task closures and falls back toeffects.taskswhen the safe-task scope does not declare a more specific rule.effects.safe_tasks.networkcontrols broad network lanes for safe-task paths.effects.safe_tasks.dependency_hydrationcontrols lockfile-backed hydration lanes (effects.network_kind: dependency_hydration) for safe-task paths.effects.safe_tasks.container_image_hydrationcontrols registry-backed Compose image-pull lanes (effects.network_kind: container_image_hydration) for safe-task paths.effects.safe_tasks.service_readinesscontrols finite endpoint assertions against declared repo-managed services (effects.network_kind: service_readiness) for safe-task paths.effects.safe_tasks.integration_testcontrols live, staging, or remote-backed verification lanes (effects.network_kind: integration_test) for safe-task paths.effects.safe_tasks.external_state_defaultsets the default decision for safe-task external state targets when no target-specific override is declared.effects.safe_tasks.external_state.<target>overrides external-state decisions per target token (for exampledocker,postgres).- valid effect decisions are
allow,warn, anddeny. exports.require_agents_mdrequires repo-side agent guidance to be present when the policy pack says so.
This policy denies one exact production PostgreSQL schema-mutation resource:
policies:
effects:
mode: compatibility
typed:
rules:
- id: deny_production_schema_mutation
selector:
kind: database_schema_mutation
actions: [apply_migration_set, reset_schema, rollback_migration_set]
resource:
match: exact
engine: postgresql
namespace:
authority: dns:example.org
environment: production
tenant: platform
account: primary
schema: public
decision: denyTyped selector collections are canonical semantic sets: list every value once in ascending lexical
order. In a namespace_pattern, an omitted optional dimension matches only an absent resource
dimension, while "*" matches any present value; it never turns a missing identity dimension into
a match.
ota run <task> --dry-run --json exposes the exact non-secret effect_policy_decision. A denied
real execution returns OTA_EFFECT_POLICY_DENIED before setup, environment rendering, services,
dependencies, provider contact, or repository mutation. This proves only that Ota's selected lane
was refused by the recorded policy decision. Provider mutation, effect-refusal canaries, positive
effect receipts, archives, and assurance remain disabled.
Policy packs are intended to be:
- deterministic
- explicit
- additive to repo contracts
- visible in diagnosis
- non-mutating by default
The policy pack does not replace ota.yaml.
It constrains and interprets it at the org layer.
ota doctor reads the explicit OTA_POLICY file path or HTTP(S) URL when set, otherwise it reads .ota/org-policy.yaml
from the nearest ancestor when it exists, validates the file shape, and reports a finding if:
- the policy pack cannot be read or parsed
- required sections declared by the policy pack are missing from the repo contract
- required files declared by the policy pack are missing from the repo root
- declared runtime/tool versions violate
policies.version_policy - resolved installed runtime/tool versions violate
policies.version_policywhilestrict_versions: true - effect governance resolves to
denyfor any selected task/safe-task effect lane or external-state target policies.adapter_bootstrapis malformed
ota doctor remains read-only. It does not mutate repo contracts or apply policy remediation automatically.
ota run and ota up also accept --effect-override <effect>=<allow|warn|deny> for one
invocation when policy owners need an explicit, auditable temporary decision.
ota run stays non-mutating unless the selected backend or execution context opts into
fulfillment: run. In that case, ota may use policy-approved provisioning to repair missing or
policy-noncompliant runtime/tool versions on the actual run path.
Policy packs are for:
- repo readiness governance
- org-wide standards
- policy-aware diagnosis
- audit and compliance support
They are not for:
- a general-purpose workflow engine
- arbitrary org RBAC design
- ticketing or approval orchestration
- hidden mutation behavior
- waiver lifecycle management
- fleet-wide reporting or retention