Implementation Execution
This page covers how oat-project-implement actually runs a plan: tier selection, phase-level subagent dispatch, runtime dispatch selection, the review + fix loop, plan-declared parallelism with worktree fan-in, and dry-run.
Quick Look
- When to use: you have a plan ready and want to understand what happens during
oat-project-implement. - 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.
- 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.
- 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). - 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.mdincludes an explicit Dispatch Profile override.
Execution model
Tier selection
At skill start, oat-project-implement detects whether the host supports native subagent dispatch for oat-phase-implementer and oat-reviewer.
- Claude Code / Cursor: native subagent dispatch → Tier 1.
- Codex multi-agent: Tier 1 if
spawn_agentis allowed without authorization, or after an explicit single prompt at skill start if authorization is required. Codex subagent dispatch should use self-contained scope packets with fresh context; do not assume pinned OAT roles can also inherit the full parent thread. - Authorization declined or agents do not resolve: Tier 2 (inline). The orchestrator reads
.agents/agents/oat-phase-implementer.mdand.agents/agents/oat-reviewer.mdas reference and executes that process itself.
The approval decision covers both phase implementation and checkpoint review for the run. The orchestrator should not drift into a mixed mode based on conversational emphasis alone; if Tier 1 was not approved, stay inline throughout unless the user explicitly requests mixed execution.
The selected tier is reported to the user and locked for the remainder of the run:
[preflight] Checking subagent availability…
→ oat-phase-implementer + oat-reviewer: available
→ Selected: Tier 1 — SubagentsDispatch ceiling preflight
Before phase work starts, oat-project-implement resolves and prints the dispatch ceiling for the current provider.
The compiled resolver is the source of truth:
oat project dispatch-ceiling resolve --provider codex --preflight --jsonResolution order:
workflow.dispatchCeiling.<provider>from effective configoat_dispatch_ceilingin projectstate.mdfrontmatter- Interactive implementation preflight prompt
- Non-interactive unresolved state blocks before work starts
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.
Codex dispatch ceiling: high
Source: project state
Codex provider default effort: medium
Note: OAT will use pinned subagent variants up to high. Base/unpinned roles resolve through the provider default.In non-interactive mode, an unresolved ceiling blocks before any implementation work:
BLOCKED: Codex dispatch ceiling is unresolved in non-interactive mode.
Set workflow.dispatchCeiling.codex in .oat/config.json or oat_dispatch_ceiling in project state.Dry-run reports unresolved ceiling and planned behavior without writing project state.
Runtime dispatch selection
Tier selection decides whether OAT uses native subagents or inline fallback. Runtime dispatch selection is separate: it decides which provider-specific model and effort controls to use for a specific phase when the host exposes those axes.
The default rule is conservative: use the lowest available model and/or effort that can confidently complete the phase. Escalate before dispatch when the phase is high-risk, broad, cross-cutting, or when retry evidence suggests the current control is underpowered.
The orchestrator considers, in order:
- A valid
## Dispatch Profileoverride row inplan.md, if present and the host can honor it. - The phase's files, risk, requirements, and recent review/fix-loop evidence.
- The host's actual control surface by axis.
Model and effort are separate axes. Each axis logs one of these states:
selected:<value>— the host exposes the axis and the orchestrator chose a value.provider-default— Codex base/unpinned role follows configured/provider default effort.inherited— the host exposes the axis and the orchestrator deliberately defers to the parent session.not-applicable— this host/API has no meaningful per-dispatch concept for that axis.host-auto— exceptional; the host uses that axis internally but the orchestrator cannot read or pin it.
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.
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.
Dispatch logs use a consistent structured block so provider behavior is comparable without flattening the model and effort axes:
OAT Dispatch: Phase p01 implementation
Host: Claude Code
Model axis: selected:sonnet
Effort axis: not-applicable
Dispatch target: oat-phase-implementer
Rationale: multi-file integration with mock wiring; sonnet is the lowest sufficient Claude model.
OAT Dispatch: Phase p02 implementation
Host: Codex
Preferred effort: high
Dispatch ceiling: medium
Selected effort: medium
Ceiling source: repo config
Provider default effort: high
Model axis: inherited
Effort axis: selected:medium
Dispatch target: oat-phase-implementer-medium
Rationale: shared TypeScript/config substrate; high preferred due to integration risk, capped by configured ceiling.
OAT Dispatch: Phase p03 review
Host: Codex
Dispatch ceiling: high
Selected effort: high
Ceiling source: project state
Provider default effort: medium
Model axis: inherited
Effort axis: selected:high
Dispatch target: oat-reviewer-high
Rationale: reviewer runs at the configured ceiling for deterministic quality gate behavior.
OAT Dispatch: Phase p04 implementation
Host: Other
Model axis: host-auto
Effort axis: host-auto
Dispatch target: host default
Rationale: host does not expose readable or pinnable dispatch controls; rationale maps to standard effort.Phase and review scope packets include dispatch context when the orchestrator has resolved it: model_axis, effort_axis, dispatch_ceiling, ceiling_source, provider_default_effort, and dispatch_rationale.
Dispatch Profile overrides
plan.md should omit ## Dispatch Profile by default. Missing dispatch rows are normal, because runtime selection has fresher phase context and host capability information at execution time.
Add Dispatch Profile rows only when the user has an explicit constraint or preference, such as "use high reasoning effort for the security implementation phase" or "keep documentation-only phases on the lowest tier." Override rows should include a rationale explaining why runtime selection should not decide on its own.
Per-phase loop
For each phase in the plan (whether sequential or inside a parallel group):
- Select runtime dispatch control for the phase and log the chosen control plus rationale.
- Dispatch the selected implementer role with a Phase Scope block (project path, phase id, artifact paths, commit convention, workflow mode, and dispatch context when known). In Codex,
effort_axis=selected:low|medium|high|xhighusesoat-phase-implementer-low|medium|high|xhigh. Baseoat-phase-implementermeans provider-default/unpinned fallback. - Receive the summary:
DONE | DONE_WITH_CONCERNS | NEEDS_CONTEXT | BLOCKED.BLOCKEDstops the run and surfaces the blocker to the user.
- Dispatch the selected reviewer role with a Review Scope block (phase id, commit range, optional files-changed hint, and dispatch context). The commit range is authoritative; the file list is only orientation metadata. In Codex, pass this as a self-contained packet with
fork_context: falseand use theoat-reviewer-<ceiling>variant. In Claude Code, cap any selected model by the Claude ceiling and recordeffort_axis=not-applicable. If the reviewer does not conclude on the first wait, poll once more, then send a concise "return now with current findings" nudge before falling back inline for that phase. - Parse the verdict: zero Critical + zero Important findings →
pass; otherwisefail. - On fail, run the bounded fix loop (see below).
- Update artifacts (
implementation.md,plan.mdreview row,state.md) and make the mandatory bookkeeping commit. - HiLL checkpoint if the phase id is listed in
oat_plan_hill_phases.
Bounded fix loop
On a fail verdict:
- Read
oat_orchestration_retry_limitfromstate.mdfrontmatter (default2, range0–5). - For each retry: re-dispatch the implementer in
fixmode with the review artifact and findings, then re-dispatch the reviewer. - On
pass→ exit the loop; the phase disposition becomesmerged(sequential) ormerged(parallel, after fan-in). - On retries exhausted:
- Sequential mode: STOP the run with phase id, unresolved findings, and review artifact path.
- Parallel group mode: mark the phase
excluded, do not merge its worktree, continue the remaining phases in the group, and report it in Outstanding Items.
Tier is never silently downgraded. If a Tier 1 dispatch has a transient failure, the orchestrator retries exactly once; a second failure is treated the same as fix-loop exhaustion for that phase.
Escalation termini
When escalation re-dispatches at a stronger control, the ladder is provider-specific:
- Codex:
selected:low -> selected:medium -> selected:high -> selected:xhigh, capped by the resolved Codex dispatch ceiling. - Claude Code:
selected:haiku -> selected:sonnet -> selected:opus, capped by the resolved Claude dispatch ceiling.
Escalation re-dispatches still count against the bounded retry budget; escalation changes the dispatch control, it does not grant extra retry attempts.
Plan-declared parallelism
Phases whose task file sets do not overlap may execute concurrently. Declare this in plan.md frontmatter:
oat_plan_parallel_groups: [['p02', 'p03'], ['p04', 'p05']]- Each inner array is a group of phases that run concurrently — one worktree per phase.
- Phases not listed in any group run sequentially in plan order.
- Groups themselves run sequentially — group
[p02, p03]merges before group[p04, p05]starts. - Empty or missing field → fully sequential, no worktrees created, behavior identical to today's
oat-project-implement.
How a parallel group runs
- Bootstrap worktrees via
oat-worktree-bootstrap-auto, one per phase, branch name{project-name}/{pNN}.- The bootstrap checks inherited git cleanliness before the all-scope provider sync sweep.
- If that sync leaves
.oat/sync/manifest.jsonor provider directories dirty, bootstrap commits only existing or tracked sync-managed paths (.oat/sync/manifest.json,.claude,.cursor,.codex) aschore: run syncand reportssync_commit: pass | fail | skip. - If any bootstrap fails, cancel successful worktrees and degrade the entire group to sequential inline execution.
- Concurrent dispatch of
oat-phase-implementerinto each worktree (Tier 1 only — Tier 2 cannot run concurrently and also degrades to sequential). - Wait for terminal verdicts (
passorfailed) across every phase in the group. - Fan-in reconciliation in plan order: for each passing phase,
git merge --no-ff {project-name}/{pNN}. Integration verification (pnpm test && pnpm lint && pnpm type-check) runs after each merge. - Failed phases are excluded — their worktrees are preserved and logged in
implementation.mdOutstanding Items. Passing phases still merge (partial merge-back, not atomic). - Worktree cleanup runs for merged phases; preserved for excluded phases.
- Bookkeeping commit + HiLL checkpoint check after the group finishes.
Merge-conflict handling
When a merge produces a conflict:
git merge --no-ffis aborted.git cherry-pickof the phase's commits is attempted.- If cherry-pick also conflicts, an inline conflict-resolution subagent is dispatched via the Task tool. The orchestrator never reads conflicted files itself — that context belongs in a fresh subagent.
- The subagent reads conflicted files and project artifacts (
plan.md,design.md,spec.md), applies a resolution, runs integration verification, and returns:RESOLVED→ merge is committed; orchestrator proceeds.UNRESOLVABLEorVERIFICATION_FAILED→ STOP the run with phase id, conflicting files, worktree path, and the subagent's reasoning summary.
The orchestrator does not proceed past a broken merge.
Validating plan metadata
Before dispatching, oat-project-implement invokes the validator CLI:
oat project validate-plan --project-path "${PROJECT_PATH}"The command enforces:
oat_plan_parallel_groupsis either missing/empty (treated as fully sequential) or a non-empty nested array of phase ID strings.- Every referenced phase id exists in the plan body.
- No phase id appears in more than one group.
- No singleton groups (each group must contain at least 2 phases).
- Frontmatter YAML parses cleanly — malformed frontmatter fails with exit 1.
Non-zero exit stops the run. The skill does not re-implement validation in prose — the CLI is the single source of truth.
Dry-run mode
Run with --dry-run to preview a run without dispatching anything:
oat-project-implement --dry-runDry-run:
- Performs tier selection and plan validation.
- Builds the execution schedule (singleton phases + parallel groups in plan order).
- Prints the planned dispatches and worktree layout.
- Exits 0 without dispatching subagents, creating worktrees, or modifying files.
Use dry-run as a sanity check after editing oat_plan_parallel_groups to confirm the schedule matches your intent.
Resumption
On re-invocation after a partial run:
- Read
implementation.mdfor the most recent orchestration-runs entry. - Compare phase counts against the plan's phase list; phases not covered by any run are the resume targets.
- Read
state.mdforoat_current_taskand cross-check with git log. - If a phase committed implementer output but has no review verdict recorded, the reviewer is re-dispatched for that phase's current HEAD.
- If un-cleaned worktrees remain from a prior parallel group, the orchestrator lists them and asks whether to resume or clean up.
First-ever invocations skip resumption detection.
State and artifact updates
After each phase (or parallel group) completes, oat-project-implement updates:
implementation.md— appends a### Run Nentry between the<!-- orchestration-runs-start -->markers with tier, dispatch rationale, phase outcomes, parallel groups, and outstanding items.plan.md— updates the reviews table lifecycle (pending→passedorfixes_added→fixes_completed→passed).state.md— updatesoat_current_task,oat_last_commit,oat_project_state_updated, and persistsoat_orchestration_retry_limitif the user overrode the default.
Legacy oat_execution_mode: subagent-driven in existing projects is silently ignored and removed on the next bookkeeping write.
Related
- Lifecycle — where implementation sits in the full project flow.
- Artifacts —
plan.mdfrontmatter contract, includingoat_plan_parallel_groups. - HiLL Checkpoints — orthogonal pause semantics; fires after a phase or group completes and merges.
- CLI Reference —
oat project validate-planand other commands.