@open-agent-toolkit/cli 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/assets/docs/cli-utilities/configuration.md +66 -11
  2. package/assets/docs/reference/oat-directory-structure.md +22 -21
  3. package/assets/docs/workflows/projects/dispatch-ceiling.md +123 -0
  4. package/assets/docs/workflows/projects/implementation-execution.md +27 -9
  5. package/assets/docs/workflows/projects/index.md +1 -0
  6. package/assets/docs/workflows/projects/lifecycle.md +1 -1
  7. package/assets/public-package-versions.json +4 -4
  8. package/assets/skills/oat-project-implement/SKILL.md +125 -31
  9. package/assets/skills/oat-project-plan/SKILL.md +28 -28
  10. package/assets/skills/oat-project-quick-start/SKILL.md +28 -27
  11. package/dist/commands/config/index.d.ts.map +1 -1
  12. package/dist/commands/config/index.js +53 -13
  13. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  14. package/dist/commands/project/dispatch-ceiling/index.js +126 -28
  15. package/dist/config/dispatch-ceiling-preset.d.ts +54 -0
  16. package/dist/config/dispatch-ceiling-preset.d.ts.map +1 -0
  17. package/dist/config/dispatch-ceiling-preset.js +32 -0
  18. package/dist/config/json.d.ts +2 -0
  19. package/dist/config/json.d.ts.map +1 -0
  20. package/dist/config/json.js +29 -0
  21. package/dist/config/oat-config.d.ts +9 -2
  22. package/dist/config/oat-config.d.ts.map +1 -1
  23. package/dist/config/oat-config.js +26 -18
  24. package/dist/config/resolve.d.ts.map +1 -1
  25. package/dist/config/resolve.js +5 -2
  26. package/dist/config/sync-config.d.ts.map +1 -1
  27. package/dist/config/sync-config.js +2 -1
  28. package/dist/providers/ceiling/registry.d.ts +53 -0
  29. package/dist/providers/ceiling/registry.d.ts.map +1 -0
  30. package/dist/providers/ceiling/registry.js +77 -0
  31. package/package.json +3 -2
@@ -160,13 +160,69 @@ oat config describe activeIdea
160
160
 
161
161
  ## Dispatch ceiling resolution
162
162
 
163
- `oat config get workflow.dispatchCeiling.<provider>` shows only the effective
164
- config value. Implementation workflows should use the project-aware resolver
165
- instead:
163
+ The dispatch ceiling is set as a **provider-neutral preset** (or per-provider
164
+ advanced values) and compiled at write time into concrete per-provider values.
165
+ Runtime dispatch reads only the concrete values — never the preset label.
166
+
167
+ For the full conceptual model — presets, the compile/resolve flow, and how
168
+ enforcement differs for Codex, Claude, and unsupported providers — see
169
+ [Dispatch Ceiling](../workflows/projects/dispatch-ceiling.md).
170
+
171
+ ### Config keys
172
+
173
+ Three keys control the ceiling, all under `workflow.dispatchCeiling`:
174
+
175
+ | Key | Values | Purpose |
176
+ | ------------------------------------------- | --------------------------------------- | --------------------------------------------------- |
177
+ | `workflow.dispatchCeiling.preset` | `balanced`, `maximum`, `cost-conscious` | Convenience preset; compiles to concrete values |
178
+ | `workflow.dispatchCeiling.providers.codex` | `low`, `medium`, `high`, `xhigh` | Concrete Codex ceiling (set by preset or Advanced) |
179
+ | `workflow.dispatchCeiling.providers.claude` | `haiku`, `sonnet`, `opus` | Concrete Claude ceiling (set by preset or Advanced) |
180
+
181
+ **Presets compile to:**
182
+
183
+ | Preset | Codex | Claude |
184
+ | ---------------- | -------- | -------- |
185
+ | `balanced` | `high` | `sonnet` |
186
+ | `maximum` | `xhigh` | `opus` |
187
+ | `cost-conscious` | `medium` | `sonnet` |
188
+
189
+ **Advanced (no preset):** set `providers.codex` and/or `providers.claude`
190
+ individually. No `preset` key is stored.
191
+
192
+ **No ceiling:** leave `oat_dispatch_ceiling` unset; implementer subagents run at
193
+ provider defaults.
194
+
195
+ ### How enforcement works
196
+
197
+ OAT applies the ceiling where the provider exposes a reliable mechanism. Other
198
+ providers receive it as advisory.
199
+
200
+ | Provider | Mechanism | Enforcement mode |
201
+ | -------- | ------------------------- | -------------------------- |
202
+ | Codex | Pinned role variants | `enforced` |
203
+ | Claude | Task `model` parameter | `enforced` |
204
+ | Others | None (informational only) | `advisory` / `unsupported` |
205
+
206
+ **Verify-on-upgrade:** when the requested tier exceeds the current orchestrator
207
+ tier (an upgrade request), the resolver sets `verifyOnDispatch: true`. The skill
208
+ confirms the actual model used after dispatch. If the provider did not honor the
209
+ upgrade, the enforcement log reads `advisory — provider did not honor upgrade;
210
+ ran <tier>` instead of `enforced`.
211
+
212
+ ### Clean break
213
+
214
+ The previous flat keys (`workflow.dispatchCeiling.codex` and
215
+ `workflow.dispatchCeiling.claude`) are removed. There is no migration path — set
216
+ the new keys.
217
+
218
+ ### Using the resolver
219
+
220
+ Implementation workflows should use the project-aware resolver rather than
221
+ reading config keys directly:
166
222
 
167
223
  ```bash
168
224
  oat project dispatch-ceiling resolve --provider codex --json
169
- oat project dispatch-ceiling resolve --provider claude --json
225
+ oat project dispatch-ceiling resolve --provider claude --orchestrator-tier sonnet --json
170
226
  ```
171
227
 
172
228
  The resolver checks effective config first, then project `state.md`
@@ -199,8 +255,9 @@ Workflow preference keys live under the `workflow.*` namespace:
199
255
  - `workflow.reviewExecutionModel` — `subagent`, `inline`, or `fresh-session`. Default final-review execution model in `oat-project-implement`. `subagent` and `inline` run automatically. `fresh-session` is a soft preference: the skill prints guidance to run the review in another session but still offers escape hatches to `subagent` or `inline` if you change your mind. When unset, the skill prompts.
200
256
  - `workflow.autoReviewAtHillCheckpoints` — boolean. Automatically run the extra lifecycle review when a HiLL checkpoint is reached. This does not control Tier 1 per-phase `oat-reviewer` gates, which run after each phase in Tier 1 regardless of this setting. When unset, the skill prompts.
201
257
  - `workflow.autoNarrowReReviewScope` — boolean. Auto-narrow re-review scope to fix-task commits only in `oat-project-review-provide`. When unset, the skill prompts.
202
- - `workflow.dispatchCeiling.codex` — `low`, `medium`, `high`, or `xhigh`. Maximum Codex reasoning effort OAT may select through deterministic pinned implementer/reviewer variants. Provider default effort is informational for base/unpinned roles and is not treated as this ceiling.
203
- - `workflow.dispatchCeiling.claude` — `haiku`, `sonnet`, or `opus`. Maximum Claude model tier OAT may select for provider-native subagent dispatch. Claude has no separate per-dispatch effort axis, so the effort axis remains `not-applicable`.
258
+ - `workflow.dispatchCeiling.preset` — `balanced`, `maximum`, or `cost-conscious`. Convenience preset that compiles to per-provider values at write time. Setting this key is the recommended way to configure the ceiling.
259
+ - `workflow.dispatchCeiling.providers.codex` — `low`, `medium`, `high`, or `xhigh`. Concrete Codex ceiling. Set automatically when a preset is selected; also settable directly for Advanced (no preset) configurations. Provider default effort is informational for base/unpinned roles and is not treated as this ceiling.
260
+ - `workflow.dispatchCeiling.providers.claude` — `haiku`, `sonnet`, or `opus`. Concrete Claude ceiling. Set automatically when a preset is selected; also settable directly for Advanced configurations. Claude has no separate per-dispatch effort axis, so the effort axis remains `not-applicable`.
204
261
 
205
262
  ### Three-layer resolution
206
263
 
@@ -224,19 +281,17 @@ oat config set workflow.reviewExecutionModel subagent --user
224
281
  oat config set workflow.autoReviewAtHillCheckpoints true --user
225
282
  oat config set workflow.autoNarrowReReviewScope true --user
226
283
  oat config set workflow.designMode selective --user
227
- oat config set workflow.dispatchCeiling.codex high --user
228
- oat config set workflow.dispatchCeiling.claude sonnet --user
284
+ oat config set workflow.dispatchCeiling.preset balanced --user
229
285
 
230
286
  # Shared repo: team decision for this repo
231
287
  oat config set workflow.createPrOnComplete false --shared
232
288
  oat config set workflow.designMode collaborative --shared
233
- oat config set workflow.dispatchCeiling.codex high --shared
234
- oat config set workflow.dispatchCeiling.claude sonnet --shared
289
+ oat config set workflow.dispatchCeiling.preset balanced --shared
235
290
 
236
291
  # Repo-local: personal override for this repo (default when no flag)
237
292
  oat config set workflow.hillCheckpointDefault every
238
293
  oat config set workflow.designMode draft
239
- oat config set workflow.dispatchCeiling.codex medium
294
+ oat config set workflow.dispatchCeiling.providers.codex medium # Advanced: per-provider override
240
295
  ```
241
296
 
242
297
  Default (no flag) targets `.oat/config.local.json` for workflow keys. Pass at most one of `--user`, `--shared`, or `--local`. Structural keys (`projects.root`, `worktrees.root`, `git.*`, `documentation.*`, `archive.*`, `tools.*`) are still shared-only regardless of flag.
@@ -92,27 +92,28 @@ Legacy `.oat/active-project` / `.oat/projects-root` / `.oat/active-idea` files m
92
92
 
93
93
  Current schema keys:
94
94
 
95
- | Key | Type | Default | Description |
96
- | ------------------------------------------- | ---------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
97
- | `version` | `number` | `1` | Schema version |
98
- | `worktrees.root` | `string` | `".worktrees"` | Root directory for git worktrees (repo-relative or absolute) |
99
- | `projects.root` | `string` | `".oat/projects/shared"` | Default root directory for OAT projects |
100
- | `localPaths` | `string[]` | - | Gitignored directories to sync between main repo and worktrees. Supports glob patterns. Managed via `oat local add/remove`. |
101
- | `documentation.root` | `string` | - | Root directory containing documentation source files (e.g., `apps/docs/docs`) |
102
- | `documentation.tooling` | `string` | - | Documentation framework identifier (`mkdocs` or `fumadocs`) |
103
- | `documentation.config` | `string` | - | Path to the documentation framework config file (e.g., `mkdocs.yml`, `next.config.js`) |
104
- | `documentation.index` | `string` | - | Path to the docs surface entry point (e.g., `index.md` for Fumadocs, `mkdocs.yml` for MkDocs). Set by `oat docs init` and updated by `oat docs generate-index`. |
105
- | `documentation.requireForProjectCompletion` | `boolean` | `false` | When `true`, OAT project completion gates require documentation to be updated |
106
- | `git.defaultBranch` | `string` | `"main"` | Default branch for PR creation. Auto-detected during `oat init` via `gh repo view` or `origin/HEAD`. Used by `oat-project-pr-final` and `oat-project-pr-progress`. |
107
- | `workflow.autoReviewAtHillCheckpoints` | `boolean` | unset | When `true`, completing a HiLL checkpoint automatically runs the extra lifecycle review. Does not control Tier 1 per-phase `oat-reviewer` gates. Can be overridden per-project via `oat_auto_review_at_hill_checkpoints` in `plan.md` frontmatter. Legacy `autoReviewAtCheckpoints` remains a fallback. |
108
- | `workflow.dispatchCeiling.codex` | `string` | unset | Maximum Codex reasoning effort OAT may select through deterministic pinned implementer/reviewer variants (`low`, `medium`, `high`, or `xhigh`). Codex provider default effort is informational for base/unpinned roles and is not treated as this ceiling. |
109
- | `workflow.dispatchCeiling.claude` | `string` | unset | Maximum Claude model tier OAT may select for provider-native subagent dispatch (`haiku`, `sonnet`, or `opus`). Claude has no separate per-dispatch effort axis. |
110
- | `archive.s3Uri` | `string` | - | Base S3 URI for repo-scoped archived project sync, for example `s3://bucket/oat-archive` |
111
- | `archive.s3SyncOnComplete` | `boolean` | `false` | When `true`, `oat-project-complete` uploads the archived project to the configured S3 archive after local archive succeeds |
112
- | `archive.summaryExportPath` | `string` | - | Repo-relative directory where completion exports `summary.md` as a dated snapshot like `20260401-<project-name>.md` for durable tracked reference |
113
- | `archive.wrapUpExportPath` | `string` | - | Repo-relative directory where `oat-wrap-up` writes dated reports like `20260413-wrap-up-past-week.md`; when unset, the skill falls back to `.oat/repo/reference/wrap-ups/` |
114
- | `archive.awsProfile` | `string` | - | Optional AWS named profile forwarded as `AWS_PROFILE` to every `aws` invocation in archive flows (`oat-project-complete` S3 sync, `oat project archive sync`). Overrides ambient shell `AWS_PROFILE` / `AWS_DEFAULT_PROFILE` when set. |
115
- | `archive.awsRegion` | `string` | - | Optional AWS region forwarded as `AWS_REGION` to every `aws` invocation in archive flows. Overrides ambient shell `AWS_REGION` / `AWS_DEFAULT_REGION` when set. |
95
+ | Key | Type | Default | Description |
96
+ | ------------------------------------------- | ---------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
97
+ | `version` | `number` | `1` | Schema version |
98
+ | `worktrees.root` | `string` | `".worktrees"` | Root directory for git worktrees (repo-relative or absolute) |
99
+ | `projects.root` | `string` | `".oat/projects/shared"` | Default root directory for OAT projects |
100
+ | `localPaths` | `string[]` | - | Gitignored directories to sync between main repo and worktrees. Supports glob patterns. Managed via `oat local add/remove`. |
101
+ | `documentation.root` | `string` | - | Root directory containing documentation source files (e.g., `apps/docs/docs`) |
102
+ | `documentation.tooling` | `string` | - | Documentation framework identifier (`mkdocs` or `fumadocs`) |
103
+ | `documentation.config` | `string` | - | Path to the documentation framework config file (e.g., `mkdocs.yml`, `next.config.js`) |
104
+ | `documentation.index` | `string` | - | Path to the docs surface entry point (e.g., `index.md` for Fumadocs, `mkdocs.yml` for MkDocs). Set by `oat docs init` and updated by `oat docs generate-index`. |
105
+ | `documentation.requireForProjectCompletion` | `boolean` | `false` | When `true`, OAT project completion gates require documentation to be updated |
106
+ | `git.defaultBranch` | `string` | `"main"` | Default branch for PR creation. Auto-detected during `oat init` via `gh repo view` or `origin/HEAD`. Used by `oat-project-pr-final` and `oat-project-pr-progress`. |
107
+ | `workflow.autoReviewAtHillCheckpoints` | `boolean` | unset | When `true`, completing a HiLL checkpoint automatically runs the extra lifecycle review. Does not control Tier 1 per-phase `oat-reviewer` gates. Can be overridden per-project via `oat_auto_review_at_hill_checkpoints` in `plan.md` frontmatter. Legacy `autoReviewAtCheckpoints` remains a fallback. |
108
+ | `workflow.dispatchCeiling.preset` | `string` | unset | Convenience preset compiled at write time to concrete per-provider ceilings (`balanced` = codex `high` / claude `sonnet`; `maximum` = codex `xhigh` / claude `opus`; `cost-conscious` = codex `medium` / claude `sonnet`). Stored only when a preset is chosen; runtime reads compiled `providers.*`, never the preset label. |
109
+ | `workflow.dispatchCeiling.providers.codex` | `string` | unset | Maximum Codex reasoning effort OAT may select through deterministic pinned implementer/reviewer variants (`low`, `medium`, `high`, or `xhigh`). Codex provider default effort is informational for base/unpinned roles and is not treated as this ceiling. |
110
+ | `workflow.dispatchCeiling.providers.claude` | `string` | unset | Maximum Claude model tier OAT may select for provider-native subagent dispatch (`haiku`, `sonnet`, or `opus`). Claude has no separate per-dispatch effort axis. The flat keys `workflow.dispatchCeiling.codex` and `workflow.dispatchCeiling.claude` were removed (no migration). |
111
+ | `archive.s3Uri` | `string` | - | Base S3 URI for repo-scoped archived project sync, for example `s3://bucket/oat-archive` |
112
+ | `archive.s3SyncOnComplete` | `boolean` | `false` | When `true`, `oat-project-complete` uploads the archived project to the configured S3 archive after local archive succeeds |
113
+ | `archive.summaryExportPath` | `string` | - | Repo-relative directory where completion exports `summary.md` as a dated snapshot like `20260401-<project-name>.md` for durable tracked reference |
114
+ | `archive.wrapUpExportPath` | `string` | - | Repo-relative directory where `oat-wrap-up` writes dated reports like `20260413-wrap-up-past-week.md`; when unset, the skill falls back to `.oat/repo/reference/wrap-ups/` |
115
+ | `archive.awsProfile` | `string` | - | Optional AWS named profile forwarded as `AWS_PROFILE` to every `aws` invocation in archive flows (`oat-project-complete` S3 sync, `oat project archive sync`). Overrides ambient shell `AWS_PROFILE` / `AWS_DEFAULT_PROFILE` when set. |
116
+ | `archive.awsRegion` | `string` | - | Optional AWS region forwarded as `AWS_REGION` to every `aws` invocation in archive flows. Overrides ambient shell `AWS_REGION` / `AWS_DEFAULT_REGION` when set. |
116
117
 
117
118
  All `documentation.*` keys are managed via `oat config get/set` and are set automatically by `oat docs init`.
118
119
  The `git.defaultBranch` key is auto-detected during `oat init` and can be overridden via `oat config set git.defaultBranch <branch>`.
@@ -0,0 +1,123 @@
1
+ ---
2
+ title: Dispatch Ceiling
3
+ description: "How OAT's provider-neutral dispatch ceiling works — presets, the compile/resolve flow, and how enforcement differs for Codex, Claude, and unsupported providers."
4
+ ---
5
+
6
+ # Dispatch Ceiling
7
+
8
+ The **dispatch ceiling** is the most capable tier OAT is allowed to use when it dispatches subagents (phase implementers and reviewers) during `oat-project-implement`. It is a **provider-neutral OAT intent** — your declaration of "don't go above this," decoupled from which provider you happen to be running. OAT then applies that intent through whatever mechanism each provider actually exposes.
9
+
10
+ This page explains the model, the why, and — most importantly — how enforcement differs across providers. For the raw config keys see [Configuration](../../cli-utilities/configuration.md); for how dispatch runs at execution time see [Implementation Execution](implementation-execution.md).
11
+
12
+ ## Two principles
13
+
14
+ 1. **Presets are convenience only — they compile to concrete values at _write_ time.** A fuzzy label like `balanced` is never read at dispatch; it is expanded into concrete per-provider values the moment you set it.
15
+ 2. **Enforcement capability is computed at dispatch, never stored.** Whether a ceiling is _enforced_, _advisory_, or _unsupported_ is a property of the provider × runtime, so OAT recomputes it on every run rather than persisting a value that would go stale.
16
+
17
+ ## Setting a ceiling
18
+
19
+ Set it via `oat config set` (repo / user / local) or answer the planning/implementation preflight prompt. Three shapes:
20
+
21
+ | Choice | What gets written |
22
+ | ------------------------------------------------------ | ---------------------------------------------------------------------- |
23
+ | **Preset** — `balanced` / `maximum` / `cost-conscious` | The preset label (provenance) **and** the compiled per-provider values |
24
+ | **Advanced** — set providers directly | `providers.*` only (no `preset` key) |
25
+ | **No ceiling** | Nothing — each provider runs at its normal/inherited behavior |
26
+
27
+ The fixed preset table:
28
+
29
+ | Preset | Codex | Claude |
30
+ | -------------- | -------- | -------- |
31
+ | Balanced | `high` | `sonnet` |
32
+ | Maximum | `xhigh` | `opus` |
33
+ | Cost-conscious | `medium` | `sonnet` |
34
+
35
+ `cost-conscious` deliberately keeps Claude at `sonnet` (no `haiku` reviewers by default) so review quality stays trustworthy.
36
+
37
+ Config keys:
38
+
39
+ - `workflow.dispatchCeiling.preset`
40
+ - `workflow.dispatchCeiling.providers.codex` (`low` \| `medium` \| `high` \| `xhigh`)
41
+ - `workflow.dispatchCeiling.providers.claude` (`haiku` \| `sonnet` \| `opus`)
42
+
43
+ ```bash
44
+ # A preset compiles immediately to concrete per-provider values:
45
+ oat config set workflow.dispatchCeiling.preset balanced --shared
46
+ # → stores preset: balanced AND providers: { codex: high, claude: sonnet }
47
+
48
+ # Or set providers directly (advanced — no preset key stored):
49
+ oat config set workflow.dispatchCeiling.providers.codex high --shared
50
+ ```
51
+
52
+ Project state can persist the compiled answer as `oat_dispatch_ceiling` (`preset?` + `providers` + `source`) so a planning/preflight choice carries into implementation.
53
+
54
+ > The earlier flat `workflow.dispatchCeiling.codex` / `.claude` keys were removed. This was a clean break with no migration — set the new keys above.
55
+
56
+ ## How it resolves at dispatch
57
+
58
+ Before dispatching a subagent, the orchestrator calls:
59
+
60
+ ```bash
61
+ oat project dispatch-ceiling resolve --provider <provider> --role <implementer|reviewer> --preflight --json
62
+ ```
63
+
64
+ The resolver:
65
+
66
+ 1. reads the concrete `providers.<provider>` value (config precedence, then project state) — **never the preset label**;
67
+ 2. looks up that provider's **adapter** in the provider ceiling registry;
68
+ 3. returns a per-provider result: `{ value, mode, mechanism, dispatchArgs }`.
69
+
70
+ `mode` is the honest enforcement status, computed right there and never persisted:
71
+
72
+ - **enforced** — the provider has a real mechanism and OAT dispatched the requested control.
73
+ - **advisory** — the ceiling is recorded as intent, but the provider can't deterministically enforce it (or an above-orchestrator request couldn't be honored).
74
+ - **unsupported** — no adapter mechanism exists for that provider.
75
+
76
+ ## Per-provider behavior
77
+
78
+ The same ceiling intent produces different — but honest — behavior per provider, because each adapter declares its own mechanism.
79
+
80
+ | | **Codex** | **Claude** | **Unsupported provider** |
81
+ | ----------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------- | -------------------------- |
82
+ | Can OAT enforce? | Yes | Yes | No (no adapter yet) |
83
+ | Mechanism | Pinned variant files | Per-call Task `model` argument | None |
84
+ | Where it lives | Sync-time committed `.codex` role variants (`oat-phase-implementer-high`, `oat-reviewer-high`, …) | Passed at dispatch time — **no files** | — |
85
+ | Axis | effort (`low < medium < high < xhigh`) | model tier (`haiku < sonnet < opus`) | — |
86
+ | `dispatchArgs` | `{ variant: "oat-reviewer-high" }` | `{ model: "sonnet" }` | `null` |
87
+ | `mode` | `enforced` | `enforced` | `advisory` / `unsupported` |
88
+ | If no ceiling set | base/unpinned role (provider default) | inherits the orchestrator's model | normal behavior |
89
+
90
+ ### Why the mechanisms differ
91
+
92
+ - **Codex** dispatches through **pinned, sync-time role variants**. Per-call reasoning-effort proved unreliable in practice, so OAT generates committed `oat-phase-implementer-{low..xhigh}` / `oat-reviewer-{low..xhigh}` role files and dispatches the variant matching the resolved effort.
93
+ - **Claude** uses the **per-call Task `model` parameter**, which is reliable and bidirectional (a Sonnet orchestrator can dispatch an Opus subagent and vice-versa) and overrides agent frontmatter — so OAT simply passes `model` at dispatch and needs no variant files.
94
+ - **Unsupported providers** (any without a registered adapter) resolve to `unsupported` with `dispatchArgs: null`. The resolve command **returns cleanly and never blocks** — the ceiling is recorded as intent and applied if/when an adapter ships, while the provider runs at its own capabilities.
95
+
96
+ A **provider adapter registry** is what lets these genuinely different mechanisms sit behind one resolver, so the lifecycle skills consume `dispatchArgs` without ever branching on provider.
97
+
98
+ ## Cap vs target: implementer vs reviewer
99
+
100
+ The ceiling means slightly different things for the two dispatch roles:
101
+
102
+ - **Implementer** runs at `min(preferred, ceiling)` — the ceiling is a **cap**. A simple phase may run below it.
103
+ - **Reviewer** runs **at** the ceiling — a **target** — so quality gates are deterministic.
104
+
105
+ Both providers honor this distinction (Codex selects the matching variant; Claude passes the matching model).
106
+
107
+ ## Verify-on-upgrade
108
+
109
+ Only a request for a tier **above** the orchestrator's current tier risks a silent plan/entitlement fallback. So the adapter verifies the actually-dispatched model **only** on that upgrade path; capping down or staying lateral needs no check. OAT never logs `enforced` unless the requested control was actually honored.
110
+
111
+ ## Dispatch logs
112
+
113
+ OAT logs the honest enforcement state per dispatch, for example:
114
+
115
+ ```text
116
+ Dispatch ceiling: high (codex, enforced — variant oat-reviewer-high)
117
+ Dispatch ceiling: sonnet (claude, enforced — Task model arg)
118
+ Dispatch ceiling: balanced (cursor, unsupported — no adapter; informational)
119
+ ```
120
+
121
+ ## Summary
122
+
123
+ You declare intent once (a preset, explicit per-provider values, or nothing). Under Codex it becomes deterministic pinned variants; under Claude it becomes a per-call Task `model` argument; under any other provider it is recorded as advisory and that provider runs normally. The logs tell you honestly whether the ceiling was `enforced`, `advisory`, or `unsupported` — one provider-neutral knob that enforces where it can and degrades gracefully where it can't.
@@ -12,7 +12,7 @@ This page covers how `oat-project-implement` actually runs a plan: tier selectio
12
12
  - **When to use:** you have a plan ready and want to understand what happens during `oat-project-implement`.
13
13
  - **Unit of dispatch:** one phase at a time (not one task). A phase implementer executes all tasks in the phase, commits per task, and returns a single summary.
14
14
  - **Two tiers, one lock:** capability detection picks Tier 1 (native subagents) or Tier 2 (inline) at start. The tier is locked for the whole run — no mid-run downgrades.
15
- - **Dispatch ceiling:** implementation resolves an OAT-owned, provider-aware ceiling before work starts. Codex uses effort values (`low`, `medium`, `high`, `xhigh`); Claude uses model tiers (`haiku`, `sonnet`, `opus`).
15
+ - **Dispatch ceiling:** implementation resolves an OAT-owned, provider-neutral ceiling before work starts. A ceiling is set as a preset (`balanced`, `maximum`, `cost-conscious`) or per-provider advanced values; runtime dispatch reads only the compiled concrete values. Codex uses effort values (`low`, `medium`, `high`, `xhigh`); Claude uses model tiers (`haiku`, `sonnet`, `opus`).
16
16
  - **Runtime dispatch:** each phase uses the lowest available model/effort/control that can confidently complete the work, capped by the resolved dispatch ceiling, unless `plan.md` includes an explicit Dispatch Profile override.
17
17
 
18
18
  ## Execution model
@@ -37,7 +37,7 @@ The selected tier is reported to the user and locked for the remainder of the ru
37
37
 
38
38
  ### Dispatch ceiling preflight
39
39
 
40
- Before phase work starts, `oat-project-implement` resolves and prints the dispatch ceiling for the current provider.
40
+ Before phase work starts, `oat-project-implement` resolves and prints the dispatch ceiling for the current provider. For the conceptual model and per-provider enforcement (Codex vs Claude vs unsupported), see [Dispatch Ceiling](dispatch-ceiling.md).
41
41
 
42
42
  The compiled resolver is the source of truth:
43
43
 
@@ -47,25 +47,43 @@ oat project dispatch-ceiling resolve --provider codex --preflight --json
47
47
 
48
48
  Resolution order:
49
49
 
50
- 1. `workflow.dispatchCeiling.<provider>` from effective config
50
+ 1. `workflow.dispatchCeiling.providers.<provider>` from effective config
51
51
  2. `oat_dispatch_ceiling` in project `state.md` frontmatter
52
52
  3. Interactive implementation preflight prompt
53
53
  4. Non-interactive unresolved state blocks before work starts
54
54
 
55
+ The ceiling is set via a preset or Advanced per-provider values. Runtime dispatch reads the compiled concrete provider values only — never the preset label. If no ceiling is configured, the interactive preflight prompt offers the preset choices; non-interactive mode blocks.
56
+
57
+ **Preset options (interactive prompt):**
58
+
59
+ | Option | Codex | Claude |
60
+ | -------------- | ---------------- | ---------------- |
61
+ | Balanced | `high` | `sonnet` |
62
+ | Maximum | `xhigh` | `opus` |
63
+ | Cost-conscious | `medium` | `sonnet` |
64
+ | Advanced | per-provider | per-provider |
65
+ | No ceiling | provider default | provider default |
66
+
55
67
  For Codex, provider default effort is displayed when available but is not treated as the OAT ceiling. Provider default only explains base/unpinned role behavior.
56
68
 
57
69
  ```text
58
- Codex dispatch ceiling: high
59
- Source: project state
60
- Codex provider default effort: medium
70
+ Dispatch ceiling: high (codex, enforced — variant oat-phase-implementer-high)
71
+ Source: project state | Preset: balanced
72
+ Provider default effort: medium
61
73
  Note: OAT will use pinned subagent variants up to high. Base/unpinned roles resolve through the provider default.
62
74
  ```
63
75
 
76
+ **Enforcement modes** (from resolver):
77
+
78
+ - `enforced` — the adapter compiled concrete dispatch args and the provider accepted them (Codex: pinned variants; Claude: Task `model` parameter).
79
+ - `advisory` — the provider is supported but no value resolved, or an upgrade request was not honored by the provider.
80
+ - `unsupported` — the provider has no registered adapter; the ceiling is informational only. Dispatch follows provider defaults.
81
+
64
82
  In non-interactive mode, an unresolved ceiling blocks before any implementation work:
65
83
 
66
84
  ```text
67
85
  BLOCKED: Codex dispatch ceiling is unresolved in non-interactive mode.
68
- Set workflow.dispatchCeiling.codex in .oat/config.json or oat_dispatch_ceiling in project state.
86
+ Set workflow.dispatchCeiling.providers.codex in .oat/config.json or oat_dispatch_ceiling in project state.
69
87
  ```
70
88
 
71
89
  Dry-run reports unresolved ceiling and planned behavior without writing project state.
@@ -92,7 +110,7 @@ Model and effort are separate axes. Each axis logs one of these states:
92
110
 
93
111
  In Codex, implementation and fix dispatch classify a preferred effort (`low`, `medium`, `high`, or `xhigh`) and select `min(preferred, resolved_ceiling)`. The selected effort maps to the matching pinned role: `oat-phase-implementer-low`, `oat-phase-implementer-medium`, `oat-phase-implementer-high`, or `oat-phase-implementer-xhigh`. Reviewer dispatch uses the reviewer variant matching the resolved ceiling (`oat-reviewer-low|medium|high|xhigh`) for deterministic quality gates. Base/unpinned Codex roles are provider-default fallbacks; they should be logged as `provider-default`, not as inherited parent-session ceiling.
94
112
 
95
- In Claude Code, subagent model selection is a model axis when available and is capped by `workflow.dispatchCeiling.claude` or project `oat_dispatch_ceiling`. The separate effort axis is `not-applicable`.
113
+ In Claude Code, subagent model selection is a model axis when available and is capped by `workflow.dispatchCeiling.providers.claude` (or the compiled concrete value from a preset) or project `oat_dispatch_ceiling`. The separate effort axis is `not-applicable`.
96
114
 
97
115
  Dispatch logs use a consistent structured block so provider behavior is comparable without flattening the model and effort axes:
98
116
 
@@ -175,7 +193,7 @@ Tier is never silently downgraded. If a Tier 1 dispatch has a transient failure,
175
193
  When escalation re-dispatches at a stronger control, the ladder is provider-specific:
176
194
 
177
195
  - **Codex:** `selected:low -> selected:medium -> selected:high -> selected:xhigh`, capped by the resolved Codex dispatch ceiling.
178
- - **Claude Code:** `selected:haiku -> selected:sonnet -> selected:opus`, capped by the resolved Claude dispatch ceiling.
196
+ - **Claude Code:** `selected:haiku -> selected:sonnet -> selected:opus`, capped by the resolved Claude dispatch ceiling (compiled from preset or `workflow.dispatchCeiling.providers.claude`).
179
197
 
180
198
  Escalation re-dispatches still count against the bounded retry budget; escalation changes the dispatch control, it does not grant extra retry attempts.
181
199
 
@@ -33,6 +33,7 @@ This sub-section is the deep technical surface for how tracked OAT projects exec
33
33
  - [Lifecycle](lifecycle.md) - End-to-end flow from discovery through completion.
34
34
  - [Design Modes](design-modes.md) - How full design balances collaborative, selective collaborative, and draft-and-review interaction.
35
35
  - [HiLL Checkpoints](hill-checkpoints.md) - Human-in-the-Loop Lifecycle configuration and approval behavior.
36
+ - [Dispatch Ceiling](dispatch-ceiling.md) - Provider-neutral ceiling model: presets, the compile/resolve flow, and how enforcement differs for Codex, Claude, and unsupported providers.
36
37
  - [Artifacts](artifacts.md) - What lives in `state.md`, `discovery.md`, `plan.md`, `implementation.md`, and related files.
37
38
  - [Project Splitting](splitting.md) - How broad discoveries or brainstorms become coordination parents and child projects.
38
39
  - [State Machine](state-machine.md) - Lifecycle and review status transitions across a project.
@@ -136,7 +136,7 @@ flowchart LR
136
136
 
137
137
  During the Design step, `oat-project-design` asks how to work through the document unless a mode was selected by argument, environment, or `workflow.designMode`. The three full-design choices are collaborative, selective collaborative, and draft-and-review.
138
138
 
139
- Before a plan is marked ready for implementation, planning resolves the current provider's dispatch ceiling from `workflow.dispatchCeiling.<provider>` or project `state.md` frontmatter. If no ceiling is configured and the session is interactive, planning asks once and stores the answer as `oat_dispatch_ceiling`; non-interactive planning leaves it unresolved so implementation preflight can fail before work starts with setup instructions.
139
+ Before a plan is marked ready for implementation, planning resolves the current provider's dispatch ceiling from `workflow.dispatchCeiling.providers.<provider>` (compiled from a preset or set directly) or project `state.md` frontmatter. If no ceiling is configured and the session is interactive, planning asks once and stores the answer as `oat_dispatch_ceiling`; non-interactive planning leaves it unresolved so implementation preflight can fail before work starts with setup instructions.
140
140
 
141
141
  ### Quick lane
142
142
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.1.11",
3
- "docs-config": "0.1.11",
4
- "docs-theme": "0.1.11",
5
- "docs-transforms": "0.1.11"
2
+ "cli": "0.1.13",
3
+ "docs-config": "0.1.13",
4
+ "docs-theme": "0.1.13",
5
+ "docs-transforms": "0.1.13"
6
6
  }