Dispatch Policy
OAT dispatch policy separates reusable provider choices from project-specific constraints:
- A candidate ladder is an ordered provider column stored in user, shared, or repo-local config. Each named tier contains one or more exact candidates.
- A named ceiling is a project or phase maximum such as
balancedorhigh. It is not an enduring model-family or effort preference. - A phase target is one exact configured candidate selected at invocation time at or below the named maximum. Optional nested work resolves separately.
In short:
- What this page is for: choosing the dispatch policy, the highest model tier (Economy, Balanced, High, or Frontier) that OAT may use when it starts agents to implement and review each phase, or no cap, or leaving model choice to the provider; and deciding where the list of candidate models (the ladder) is stored.
- Who needs it: anyone starting a quick, spec-driven, lite, brief, goal, or imported project, because planning asks you to choose a policy; and team leads who manage model cost.
- Know first: there is no default policy, and a policy set in any config file overrides the one each project chose. A tier is a cap: phase agents may run below it, but per-phase and final code reviews run on the last model listed in the capped tier. Codex and Claude enforce the cap; Cursor pins the model but cannot confirm which one ran; other providers treat it as a suggestion.
- Decide: Which tier for which work, How each provider applies the cap, and Where the ladder lives. Most other sections are the detailed resolution contract.
The CLI command remains oat project dispatch-ceiling resolve for compatibility.
Legacy workflow.dispatchCeiling.* and oat_dispatch_ceiling values remain
readable, but new projects use ordered candidates plus oat_dispatch_policy.
For raw config keys, see Configuration. For the root-owned phase-agent loop, see Implementation Execution.
To pick a tier for a project, start with Which tier for which work and the rest of Choosing policy and ladder ownership near the end of this page.
How each provider applies the cap
The cap is the named ceiling: the highest tier a project or phase may use. A provider is the agent tool running OAT. How firmly the cap holds depends on the provider:
- Codex: enforced. OAT launches each phase on an agent role that
oat syncgenerated, pinned to one model and effort. - Claude: enforced. OAT launches a generated role pinned to one model and effort, or passes the exact model on each call.
- Cursor: pinned, not verified. OAT launches a generated role pinned to the mapped model, but Cursor can substitute a different model without an error, for example when your plan or account cannot use the pinned one or the pin has a typo. OAT records the model it requested, not the model that ran.
- Other providers: advisory. OAT has no way to pin a model there, so the cap is only a suggestion.
The full rules are in Provider Enforcement and Cursor evidence authority.
Named Policy Choices
| Choice | Mode | Named maximum | Eligible configured tiers |
|---|---|---|---|
Economy | managed | economy | Economy |
Balanced | managed | balanced | Economy, Balanced |
High | managed | high | Economy, Balanced, High |
Frontier | managed | frontier | Economy, Balanced, High, Frontier |
Uncapped | managed | none | Any configured candidate OAT can resolve |
Inherit Host Defaults | inherit | none | OAT does not select provider controls |
A named High ceiling therefore keeps configured Economy, Balanced, and High
candidates eligible and available. It does not pin Sol, opus, one Cursor
string, or one effort value. (Model names on this page, such as Sol, Luna,
Astra, Opus, Sonnet, and Fable, are provider models listed in the ladder that
ships with OAT; they are examples, and your own ladder may list different
models.) The project root, meaning the main agent session running the project,
chooses one exact candidate it judges sufficient for the phase.
Uncapped is explicit managed state. It is not represented by omitted policy
state. Unresolved is a planning or preflight deferral and cannot begin
implementation.
Ownership and Adoption
Who chose the bundled ladder. OAT ships a recommended ladder, the bundled
recommendation. OAT's maintainers choose which models go in each tier and in
what order, using provider documentation, vendor benchmarks and their own
model-selection guidance, and they refresh it as providers release new models
(see Updating Model Guidance).
OAT has not compared these models on its own workloads, so treat the ladder as
the maintainers' recommended starting point, not a measured ranking. Your team
does not have to use it as shipped. You can adopt it and then edit any cell in
the config file that owns it (see Config Shapes), or write
your own cells without adopting. Rerunning adoption later replaces edited cells
unless you pass --keep-existing.
Adopt the complete bundled recommendation into one explicit owning scope:
# Team-owned, tracked repo configuration
oat config adopt dispatch-matrix --shared
# Checkout-specific repo configuration
oat config adopt dispatch-matrix --local
# Personal defaults across repositories
oat config adopt dispatch-matrix --userWith no scope flag, oat config adopt dispatch-matrix writes repo-local config
(.oat/config.local.json), the same as --local. The examples on this page
pass a flag so the owning scope is always explicit.
Adoption replaces populated cells from the bundled recommendation in the
chosen scope, fills missing cells, and retains extra custom tiers when the
bundled provider remains a tier map. A bundled provider-level scalar replaces
the whole provider, including custom tiers. It warns about replaced or
removed cells and reports their paths. Use --dry-run to preview. For
fill-only setup, --keep-existing preserves populated cells; if it fills
nothing, both config and version remain unchanged. When it fills missing
cells but preserves divergent cells, the version also stays unchanged. Planning always uses
--keep-existing after showing the complete recommendation and asking which
scope should own it. If the resulting effective ladder remains incomplete,
planning blocks. The version identifies the bundle last written by adoption;
it does not prove the effective ladder matches it. OAT resolves the effective
ladder for dispatch targets and runtime disclosure.
Upgrading to a newer recommendation version
A new bundled recommendation does not change an adopted config by itself. To update the chosen scope, first preview its affected cells:
oat config adopt dispatch-matrix --user --dry-runRun the same command without --dry-run to replace bundled cells. Use
--shared or --local instead when that scope owns the ladder. Add
--keep-existing to fill only missing cells and preserve existing choices.
Version 2026-10-01.1 is the current bundled recommendation. It prefers
GPT-6 Luna and GPT-6.1 Sol in Codex, then Sol 6.1 xhigh, Astra high, and Astra xhigh in
Codex Frontier. OAT's maintainers added the Astra entries as a preference:
public benchmarks show task-dependent gains for Astra over GPT-6 Sol, but OAT
has not tested Astra on its own workloads, and the maintainers' separate
model-selection review has not yet confirmed the choice. Sonnet 5.5 medium replaces Sonnet 5 medium
in Claude Economy; the older generation stays supported for explicit cells.
Opus 5.5 low/medium/high remains in Claude High.
The Claude low option suits bounded intelligent recon in Balanced or High;
medium remains the implementation starting point under High. GPT-6 Sol and GPT-5.6 targets remain
supported for explicit Codex and Cursor configurations. Cursor's preferred
ladder interleaves verified GPT-5.6 and Opus 5.5 mappings: Balanced adds
Opus 5.5 low above Terra high, High orders Sol medium, Opus low, Sol high,
Opus medium, then Opus high, and Frontier places Opus high and xhigh between
Sol max and Fable 5.1. Grok 4.6 medium replaces Grok 4.5 high at the bottom of
Balanced, and Fable 5.1 thinking high replaces Fable 5 as Frontier's terminal
target. OAT's maintainers chose this ordering using vendor benchmarks and their
model-selection guidance; it is not the result of comparing models on OAT's own
workloads. Grok 4.5 and Fable 5 stay supported for explicit configurations.
Rerunning adoption replaces the bundled cells in the chosen scope; use
--keep-existing to preserve explicit older cells. For the complete
maintenance procedure, see
Updating Model Guidance.
The terminal Fable target may require model access from the executing provider. The adopting organization is responsible for confirming its applicable retention policy. OAT does not determine model access or organizational retention eligibility; recommendation membership and catalogue presence are configuration data, not an eligibility decision.
The terminal entries in that version are chosen, not incidental. Because the
final candidate in a tier is the target its implementation-phase self-review
pins, reordering a tier changes who reviews it even when the membership is
untouched. Cross-family independence for the external phase gate and the
lifecycle gate comes from gates.execTargets instead, which is configured
separately and is not drawn from this ladder.
Curating a ladder down has a consequence in the other direction. The
subagent-orchestration guidance names a route per task class, and a route you
prune becomes unreachable: the guidance still recommends it while the resolver
rejects it as unconfigured. A ladder whose economy tier drops its mechanical
route leaves mechanical work with nowhere to go but a higher tier. Keep at least
one reachable rung for each task class the project uses, and prefer more than
one candidate per tier, since a tier holding a single entry cannot express a
choice.
Before offering adoption, planning runs oat config list --json once and treats
its output as the effective boundary across shared, repo-local, user, and
bundled-default precedence. A complete effective ladder skips adoption even
when the current project has not selected a policy or named ceiling. Adoption
is offered only when the resolved provider/tier cells are actually missing,
empty, malformed, or incomplete.
Ladder completeness and project-policy selection are separate checks.
oat project dispatch-ceiling resolve --json reports both:
unresolvedReason: policy | ladder | bothidentifies which side is missing;ladderCompleteness.completeevaluates every supported provider/tier cell; andladderCompleteness.missingCellsidentifies the exact cells adoption would fill.
The resolver preserves the merged effective matrix when only policy is
missing, so a missing project policy no longer looks like a missing reusable
ladder. Planning offers matrix adoption only for ladder or both, or when
ladderCompleteness.complete is false. With a complete ladder and only policy
unresolved, it proceeds directly to the project-specific policy choice.
The ownership boundary is deliberate:
| Source | Config location | Codex materialization output |
|---|---|---|
| Shared or repo-local project configuration | .oat/config*.json | Tracked project .codex view |
| Active-project sparse override | Project state.md | Tracked project .codex view |
| User configuration | ~/.oat/config.json | User ~/.codex view |
| Supported OAT catalogue | Bundled recommendation/catalog | Tracked project .codex view on sync |
Project-generated roles remain visible to version control. OAT does not auto-ignore them. User-generated roles remain outside the repository under the user home directory.
The reusable ladder and active project ceiling have separate ownership. A
project-specific policy or ceiling belongs in that project's state.md; do not
write it into user ~/.oat/config.json merely because the reusable ladder is
user-owned.
Config Shapes
An ordered candidate cell uses candidates:
{
"workflow": {
"dispatchCeiling": {
"providers": {
"codex": {
"balanced": {
"candidates": [
{
"harness": "codex",
"model": "gpt-6-luna",
"effort": "low"
},
{
"harness": "codex",
"model": "gpt-6-luna",
"effort": "medium"
},
{
"harness": "codex",
"model": "gpt-6-luna",
"effort": "high"
}
]
}
},
"claude": {
"balanced": { "candidates": ["sonnet"] }
},
"cursor": {
"balanced": {
"candidates": ["gpt-5.6-terra-high"]
}
}
}
}
}
}Each candidate can also be a fallback route. Route entries are attempted only through the resolver's bounded escalation level; a route does not change the named maximum.
Project state records only the active named maximum:
oat_dispatch_policy:
mode: managed
policy: high
source: project-stateDo not copy compiled providers targets into this shape. An optional plan
## Dispatch Profile row may narrow one phase to another named maximum at or
below the project maximum. Blank or auto uses the project value.
Managed uncapped and inherit/default shapes are explicit:
oat_dispatch_policy:
mode: managed
policy: uncapped
source: project-stateoat_dispatch_policy:
mode: inherit
source: project-stateComplete Bundled Recommendation
The bundled ladder is a curated subset of the supported targets:
- Codex: GPT-6 Luna at
lowthroughmaxacross Economy and Balanced; GPT-6.1 Sol atlowthroughhighin High andxhighin Frontier; then GPT-6 Astra athighandxhighin Frontier. Astra and Sol remain supported atlow,medium,high,xhigh, andmax, while GPT-6 Sol and GPT-5.6 variants remain available outside this preferred ladder. - Claude:
haikuand Sonnet 5.5 medium at Economy; Opus 5.5 low at Balanced, low/medium/high at High, and xhigh/max at Frontier; Fable 5.1 high ends Frontier. Sonnet 5 remains supported for explicit configurations. - Cursor: the preferred ladder uses approved Composer 2.5, GPT-5.6, Grok
4.6, Opus 5.5, and Fable 5.1 targets. The catalogue maps each flat ladder ID
to its separately verified frontmatter model. Sonnet 5.5 exact-ID pins
remain supported outside the ladder; existing families keep their bracket
selectors. Grok 4.6 (
lowthroughxhigh) and Fable 5.1 (lowthroughmax, thinking variants) were approved from a 2026-09-25 native desktop probe; targets outside the ladder remain available for explicit selection. Grok 4.7 stays outside the shipped catalogue pending its own approved mapping and per-pin evidence; exact-ID support does not admit arbitrary CLI IDs.
The final candidate in a named tier defines that tier's reviewer ceiling. Lower reviewer selection requires a separate reviewed contract; a normal reviewer does not use task candidate flags.
Exact Phase Resolution
Planning and implementation preflight resolve the active policy first:
oat project dispatch-ceiling resolve \
--provider codex \
--preflight \
--jsonBefore each managed capped phase or bounded fix continuation, the root requests
one exact configured candidate. It passes the recorded project or narrower
phase maximum through the
invocation-only --ceiling-tier option:
# Codex: exact model plus effort
oat project dispatch-ceiling resolve \
--provider codex \
--role implementer \
--ceiling-tier high \
--candidate-model gpt-5.6-terra \
--candidate-effort medium \
--task-class default-implementation \
--task-effort medium \
--report-scope p02 \
--report-action implementation \
--json
# Claude: exact model and effort candidate
oat project dispatch-ceiling resolve \
--provider claude \
--role implementer \
--ceiling-tier high \
--candidate-model claude-opus-5-5 \
--candidate-effort medium \
--task-class default-implementation \
--report-scope p02 \
--report-action implementation \
--json
# Cursor: exact opaque configured string
oat project dispatch-ceiling resolve \
--provider cursor \
--role implementer \
--ceiling-tier high \
--candidate-model gpt-5.6-sol-high \
--task-class default-implementation \
--report-scope p02 \
--report-action implementation \
--jsonUse the same classification flags and --report-action fix for a bounded fix.
Reviewer routes use neither --task-class nor --task-effort; the CLI rejects
classification flags for reviewers.
--ceiling-tier accepts economy, balanced, high, or frontier. It
overrides a layered active-policy ceiling for that resolver invocation only. It
does not modify user, shared, local, or project configuration.
Successful JSON reports:
- top-level
source: invocationfor the ephemeral maximum providers.<provider>.cellSourcefor the config layer that owns the selected candidate definitionselection.ceilingTier,selection.candidateTier, andselection.requestedCandidate- the exact provider-specific
dispatchArgs
The resolver rejects an above-ceiling candidate, an ambiguous route, malformed
ordering, a reviewer candidate request, or controls that cannot compile
exactly. For compatibility, omitting an exact candidate from a managed
named-cap implementation or fix still resolves successfully at the cap. When
report context is present, that path emits the coded
managed-capped-selection-skipped warning in human and JSON output. The root
must treat the warning as a dispatch-policy violation and select an exact
candidate before launch even though the command retains exit code 0.
Implementer and fix resolution has two mutually exclusive selection branches:
- Preferred selection: pass
--preferredfor legacy scalar ceilings or managedUncappedmodel-only compatibility. Do not include--candidate-modelor--candidate-effort. - Exact-candidate selection: pass
--candidate-modeland, where applicable,--candidate-effortfor a managed capped phase or fix, or for managedUncappedwith an explicit model/effort choice. Do not include--preferred.
Never combine the branches in one resolver invocation. The exact-candidate branch replaces, rather than supplements, preferred selection.
Claude effort capability evidence
An effort-pinned Claude candidate must establish the exact generation and that
generation's supported effort. Current directly recognized generations include
claude-fable-5-1, claude-fable-5, claude-opus-5-5, claude-sonnet-5-5, claude-sonnet-5,
claude-opus-4-7, claude-opus-4-6, and claude-sonnet-4-6.
Opus 5.5 and Sonnet 5.5 support low, medium, high, xhigh, and max; consult the
updating guide before adding
further generations or rungs. Retired Opus 5.0 and 4.8 are no longer directly
recognized for effort-pinned dispatch.
A family alias can also establish capability through its matching
ANTHROPIC_DEFAULT_<FAMILY>_MODEL pin. A recognized versioned model ID in that
pin supplies generation evidence. A custom provider model ID additionally
requires the matching <PIN>_SUPPORTED_CAPABILITIES declaration: effort
enables low, medium, and high, while xhigh_effort and max_effort add
those respective rungs. When a declaration is present, it is authoritative and
must include effort; it cannot claim a different family.
CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST takes precedence over family pins, so an
effort-pinned bare alias fails closed under host-managed routing. A bare alias
without a usable pin also fails closed because provider routing,
availableModels, or organization policy can substitute another generation.
Use a recognized versioned model ID when the host owns routing. Model-only
aliases remain compatible through the Agent API's per-call model argument. They
report the per-call effort axis as not-applicable because that API has no
per-call effort argument; this does not claim that Claude itself lacks effort.
Provider Enforcement
| Provider | Exact phase-agent or optional-child invocation | Failure behavior |
|---|---|---|
| Codex | Use providers.codex.dispatchArgs.variant as agent_type; otherwise launch a fresh child pinned to the returned model and effort | Block if neither exact route is usable |
| Claude | Launch providers.claude.dispatchArgs.variant for effort-pinned targets; pass the exact model for legacy targets | Block if the exact target cannot be applied |
| Cursor | Launch providers.cursor.dispatchArgs.variant as the exact native agent type first | Block rather than normalize or substitute |
| Other | Use a registered provider adapter when it can compile exact controls | Unsupported providers remain advisory |
Materialized Codex and Cursor roles exist before phase dispatch after
project/user sync. Cursor definitions carry supported-catalogue,
project-config, or user-config ownership. Project and supported variants
are tracked under .cursor/agents; user variants live under
~/.cursor/agents. Cleanup stays within the current owner boundary.
Reviewers use the final candidate at the configured review ceiling. Managed
Uncapped and explicit inherit/default behavior retain their documented base
reviewer behavior. A timeout retry preserves the same exact role or complete
Claude model payload or Cursor native variant.
Cursor evidence authority
Cursor selection, pin mapping, catalogue availability, and runtime identity are separate evidence layers:
- The candidate ladder and resolver use an opaque flat ID.
- The materialized definition uses the mapping's explicit frontmatter model, either a verified bracket selector or a separately verified exact ID. Sonnet 5.5 uses exact IDs; existing families keep their bracket mappings.
- Mapping-specific native-launch evidence authorizes the shipped mapping data.
An approved mapping may carry a probe record whose
submittedSelectormust equal the mapping'sfrontmatterModeland whoseresolvedModelmust equal itsladderModelId, so editing a mapping without re-probing fails its own test rather than inheriting an approval it was never granted. See Verifying Cursor Pins. oat doctorchecks current flat-ID catalogue availability, which can detect drift but cannot prove a definition pin.- The launcher records the selected variant and mapped model with
configuredprovenance. Runtime identity remainsnot-reportedwithout an independent observation.
Cursor can silently fallback when account, plan, or administration constraints prevent a requested definition pin. Native variant acceptance is therefore not runtime-model verification, and skills must not promote self-report or catalogue presence into observed identity.
Unresolvable selectors also fall back silently
Entitlement is not the only trigger. Cursor does not reject a malformed pin either; it substitutes a default for whichever selector component it cannot resolve, with no error or warning:
- An unknown family falls back to the account default model. Probing
claude-opus-9[effort=high]resolved tocursor-grok-4.5-high-fast. - An unknown effort falls back to that family's default rung. Probing
claude-opus-5[effort=ultra]resolved toclaude-opus-5-thinking-high.
The default rung is family-specific and is not always high — Opus 4.7
defaults to xhigh. A typo in a pinned selector therefore ships a
working-but-wrong model that silently tracks a vendor-controlled default, so
capability can change with no corresponding change in the repository.
OAT does not currently validate effort rungs at sync time; that check is planned but not yet built. Until it is, the probe runbook is the only guard.
Phase and Optional-Worker Layers
The phase implementer directly implements the phase tasks from one Phase Scope and:
- reads phase artifacts once and preserves dependency order;
- directly implements each planned task;
- creates and verifies one bounded commit per task; and
- runs phase-wide verification before returning to the root.
Optional nested workers or recon agents resolve their own exact candidates only when they provide a concrete benefit. They are not required for ordinary plan tasks and do not own phase commits or review dispatch.
Tasks run serially in the same worktree. Parallelism remains limited to plan-declared phase worktrees unless optional work has explicitly isolated write authority. See Implementation Execution for the full loop.
Dispatch Report V1 and Producer Provenance
Resolver calls that pass --report-scope and --report-action include a
dispatchReport object in JSON output, plus the additive dispatchStamp
string described below. Consumers must require
dispatchReport.schemaVersion: 1 before dispatch. The report keeps these
concerns separate:
| Report area | What it means |
|---|---|
policy | The resolved managed/inherit policy, its status, name, and source |
selection.ceilingTier / selection.ceilingTarget | The maximum allowed tier and its boundary target |
selection.requestedCandidate / candidateTier / candidateIndex | The exact candidate requested for this bounded task and its position |
selection.preferredValue | The legacy --preferred selection value, or null when that compatibility path was not used |
selection.exactSelectedTarget / route.target | The compiled provider target and actual invocation route |
classification | Caller-reported task class, applicable Codex preferred effort, and provenance source |
notices | Ordered coded warnings and advisories derived from the effective dispatch context |
A named policy or ceiling is never a substitute for the requested candidate or
exact selected target. Classification is provenance only: it records the
root's judgment but does not participate in candidate normalization or let OAT
judge whether that classification was correct. requestedControls records
what OAT put into the host payload. configuredDefaults records fallback
configuration and is explicitly not a runtime observation.
Managed named-cap implementation and fix reports can include two warning codes:
managed-capped-selection-skippedmeans no exact candidate or legacy preferred value was supplied, so compatibility behavior selected the cap.managed-capped-classification-missingmeans an exact candidate was supplied without--task-class.
Both warnings preserve resolved status and exit code 0. They do not apply to
policy-only preflight, reviewer, inherit, uncapped, unresolved, or
legacy-preferred routes. terminal-reviewer-eligibility is an advisory tied to
an effective Fable reviewer target, not proof of model access or organizational
retention eligibility.
gateInvocation is an immutable copy of configured gate controls.
runtimeIdentity is separate and stays not-reported until independently
observed or otherwise supported runtime evidence exists. Requested controls,
configured defaults, role-name parsing, and reviewer self-identification do not
become observed runtime identity.
Human output comes from formatDispatchReport(dispatchReport). Dispatch notes
also retain a parseable compatibility stamp for later review gates:
Dispatch: scope=p06-t03 action=implementation role=implementer producer=gpt-5.6-sol provenance=declared model_axis=selected:gpt-5.6-sol effort_axis=selected:high dispatch_policy=high dispatch_ceiling=high target=oat-phase-implementer-gpt-5-6-sol-highproducer is the runtime model slug when OAT can establish it; otherwise it is
unknown. provenance is declared, observed, inferred, or unknown.
Selected model and effort axes can remain exact even when runtime producer
identity is not reported.
Before launching an implementation, fix, or reviewer, surface the structured
notices in dispatchReport.notices and render the report. Structured notices
and runtime disclosure use the effective target returned by that resolver call,
never a target inferred from the bundled recommendation version.
The additive dispatchStamp field
JSON responses that carry dispatchReport also carry dispatchStamp, a single
canonical string produced by the same formatter that owns the grammar above:
{
"status": "resolved",
"dispatchReport": { "schemaVersion": 1 },
"dispatchStamp": "Dispatch: scope=p06-t03 action=implementation role=implementer ..."
}Field eligibility matches report eligibility exactly. The field is present when
and only when dispatchReport is present, so non-report resolver calls and
error envelopes never carry it. DispatchReportV1, its schema version, and the
stamp grammar are unchanged; the field is purely additive.
Eligibility tracks report presence, not dispatch authorization or exit code. A
status: blocked response that was asked for report context therefore carries
both dispatchReport and dispatchStamp while still exiting 1: the stamp
records what OAT resolved, and a blocked resolution is a resolved fact about an
unusable route rather than a missing report. Only a call without report context,
or an error envelope that never built a report, omits the field.
Consumers read dispatchStamp directly, validate that it is a non-empty string
beginning with the canonical Dispatch: prefix, and copy it byte-for-byte into
dispatch and audit metadata. Reformatting the report through
toDispatchStampRecord(dispatchReport) and formatDispatchStamp is an optional
corroboration where that library is already loaded; it is never the normal path,
and no out-of-tree shim is required. Callers must not reconstruct the stamp from
policy labels, role names, candidate strings, or target names.
A stamp carrying producer=unknown provenance=unknown is truthful, not
degraded: the resolver reports configured invocation, and those fields stay
unknown until an independent runtime observation exists.
Legacy Compatibility
The following remain readable during migration:
workflow.dispatchCeiling.preset- bare
workflow.dispatchCeiling.providers.<provider>values - project
oat_dispatch_ceiling --preferredresolver selection
Legacy presets (cost-conscious, balanced, maximum) compile to bare legacy
provider ceilings, not complete named candidate tiers. Their values can resemble
tier caps without supplying a ladder. They remain migration inputs, not evidence
that a new project should persist exact provider-family pins.
Choosing policy and ladder ownership
The dispatch policy decides how strong (and how expensive) the models that OAT
starts for each phase and review may be. A ladder is the configured list of
model and effort candidates for each provider, grouped into four tiers from
Economy to Frontier. The policy sets the highest tier a project may use.
Planning asks you for the policy and saves it in the project's state.md.
WARNING
A dispatch policy set in any config file (workflow.dispatchPolicy.*, or a
legacy plain value in workflow.dispatchCeiling.providers.<provider>)
overrides the policy every project chose for itself in state.md. To let each
project choose its own policy, leave those keys unset. Adopting a ladder is a
separate step from setting a policy.
| Policy | Choose it when | What you give up |
|---|---|---|
| Economy / Balanced | Routine or normal work where cost matters | The higher tiers |
| High / Frontier | Broad, security-sensitive, or consequential changes | Cost: implementation code reviews run on the cap tier, which can cost more |
| Uncapped | You trust the main session to choose a model per phase | Any tier ceiling on cost. You must choose it explicitly |
| Inherit | The provider's own model settings must stay untouched | All OAT model control: OAT passes no model or effort setting |
There is no default policy, and a missing policy never means Uncapped. If a project has none when implementation starts, an interactive run asks you and a non-interactive run stops. The agents that implement a phase may use a lower tier than the cap, but when a cap is set, the per-phase and final code reviews always use the last candidate listed in the cap tier. Planning-artifact reviews and gates choose their reviewers separately.
- If you are trying OAT for the first time, choose Balanced for the project.
- If cost matters most, choose Economy for routine work or Balanced for normal work.
- If the change is high-risk, choose High. Choose Frontier only if you want every implementation code review to run on Frontier's last candidate. For a reviewer from a different model family, add a gate instead.
- If the provider's own settings must stay untouched, choose Inherit.
Which tier for which work
The tier is a cap, not a target: for each phase the main session still picks
one candidate at or below it. A Dispatch Profile row is an optional per-phase
limit in plan.md that can lower the cap for one phase.
| Work | Tier to start with | What the tier limits | Cost and speed direction |
|---|---|---|---|
| Routine, bounded work: one component and a clear outcome, such as a bug fix | Balanced, or Economy if cost matters most | Phase agents use only Economy and Balanced candidates. Per-phase and final code reviews run on the last Balanced candidate | Lower cost; usually faster |
| Ambiguous or cross-cutting work: a broad change across several components | High | Economy through High candidates are eligible. Code reviews run on the last High candidate | Higher, because every implementation code review runs at High |
| Consequential or high-risk change: security-sensitive, or a missed defect costs more than the run | High. Choose Frontier only if every implementation code review should run on Frontier's last candidate | At Frontier all four tiers are eligible, and its last candidate may need model access your account lacks. For a reviewer from a different model family, add a gate | Higher at High; highest and usually slowest at Frontier |
| Cost-sensitive team | Economy for routine work and Balanced for normal work, set per project | Economy allows only Economy candidates. A Dispatch Profile row can lower one phase, but code reviews still run at the project cap | Lowest of the managed tiers |
OAT sets no prices and does not measure cost or speed. What a run costs depends on your provider plan and on the models in your ladder. The directions above assume a ladder ordered like the bundled one, where higher tiers hold larger models or higher effort settings, which usually cost more per run and take longer.
Where the ladder lives
oat config adopt dispatch-matrix writes the bundled ladder to the config scope
you choose, and to repo-local config if you pass no flag. Without
--keep-existing it replaces cells you already set, so preview with --dry-run
first.
- If you are rolling OAT out to a team, adopt the ladder with
--sharedso everyone uses the same candidates and the change is reviewed in Git. - If you are trying OAT alone, run
oat config adopt dispatch-matrix --user --dry-run, then rerun it without--dry-run. A shared or repo-local ladder in a repository still overrides your user ladder there. - If you are testing candidates in one checkout, use
--local. Afteroat sync, the agent roles generated from it still appear in the tracked.codex,.claude, and.cursorfolders, so checkgit statusbefore committing.
How firmly the cap holds depends on the provider. Codex and Claude enforce the model and effort that OAT selects. Cursor pins mapped candidates, but OAT cannot confirm at run time which model actually ran, so it records what it requested. Other providers treat the cap as a suggestion only. See Provider Enforcement.