---
name: oat-project-import-plan
version: 1.4.7
description: Use when you have an external markdown plan to execute with OAT. Preserves the source plan and normalizes it into canonical plan.md format.
argument-hint: '<path-to-plan.md> [--provider codex|cursor|claude] [--project <name>]'
oat_gateable: true
disable-model-invocation: true
user-invocable: true
allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
---

# Import External Plan

Import a markdown plan from an external coding provider and normalize it into OAT project artifacts.

Provider native plan mode uses this same path: provider-plan-via-import
preserves the provider plan first, and provider plan dispatch readiness
inherits the same import workflow contract below, including complete ladder
adoption, the project named ceiling, the `Shared Phase Gate Review Setup Contract`,
and the rule that readiness and completion follow only after a durable review
disposition. It does not add a separate provider-plan prompt.

## Prerequisites

- External plan exists as a local markdown file.
- OAT repository scaffolding is available.

## Mode Assertion

**OAT MODE: Plan Import**

**Purpose:** Preserve the original plan and generate a runnable canonical `plan.md` for OAT execution.

**BLOCKED Activities:**

- No destructive edits to the imported source file.
- No implementation code changes.

**ALLOWED Activities:**

- Creating/updating project artifacts.
- Plan normalization into OAT task structure.
- Updating project state metadata for import mode.

**Self-Correction Protocol:**
If you catch yourself:

- Mutating source plan content in-place → STOP; copy source first.
- Producing prose-only plan without runnable tasks → STOP and normalize to `pNN-tNN` tasks.

**Recovery:**

1. Preserve source in `references/imported-plan.md`.
2. Regenerate canonical `plan.md` in OAT structure.

## Progress Indicators (User-Facing)

When executing this skill, provide lightweight progress feedback so the user can tell what’s happening after they confirm.

- Print a phase banner once at start using horizontal separators, e.g.:

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  OAT ▸ IMPORT PLAN
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

- Before multi-step work, print step indicators, e.g.:
  - `[0/8] Checking inherited git state...`
  - `[1/8] Resolving project + source plan…`
  - `[2/8] Preserving imported source…`
  - `[3/8] Normalizing plan to OAT task structure…`
  - `[4/8] Updating plan metadata…`
  - `[5/8] Running import-aware plan review…`
  - `[6/8] Updating project state + dashboard…`
  - `[7/8] Ensuring implementation tracker + committing…`
  - `[8/8] Running configured gate…`

## Process

### Step 0 (Preflight): Inherited Git State

Before scaffolding, surface the working tree state so unrelated changes don't get carried into the project workflow's bookkeeping commits.

1. Run `git status --porcelain`. If empty, continue silently to the next step.
2. If non-empty, present the dirty list to the user.
3. If `.oat/sync/manifest.json` or paths under `.claude/`, `.cursor/`, `.codex/` appear in the list, note: "These are generated by `oat sync` (often by `pnpm run worktree:init` or `oat-worktree-bootstrap-auto`) and are typically safe to commit as `chore: run sync`."
4. Offer three choices via `AskUserQuestion`:
   - **Commit now** (recommended when only sync output is dirty) — stage and commit. For sync-only diffs, default the message to `chore: run sync`; otherwise ask the user for the commit message.
   - **Proceed anyway** — start the project workflow with the dirty state acknowledged.
   - **Abort** — exit the skill so the user can clean up manually.

> **Tool availability is not the same as interactivity.** If `AskUserQuestion` is unavailable but chat is available, present the three choices as a plain chat message and wait for the user's reply. Only fall back to "Proceed anyway" when `OAT_NON_INTERACTIVE=1` is set or there is no user-response channel at all.

Do not advance past this gate without an explicit choice.

### Step 0.5: Resolve Active Project

```bash
PROJECT_PATH=$(oat config get activeProject 2>/dev/null || true)
PROJECTS_ROOT="${OAT_PROJECTS_ROOT:-$(oat config get projects.root 2>/dev/null || echo ".oat/projects/shared")}"
PROJECTS_ROOT="${PROJECTS_ROOT%/}"
```

If no valid active project exists:

- Use `--project` if provided, else ask user.
- Resolve `TARGET_PROJECT_PATH="${PROJECTS_ROOT}/{project-name}"`.
- If `TARGET_PROJECT_PATH/state.md` exists, set:
  ```bash
  oat config set activeProject "$TARGET_PROJECT_PATH"
  PROJECT_PATH="$TARGET_PROJECT_PATH"
  ```
- Otherwise create an import-mode scaffold (which sets active project by default):
  ```bash
  oat project new "{project-name}" --mode import
  PROJECT_PATH="$TARGET_PROJECT_PATH"
  ```

### Step 1: Resolve and Validate Source Plan Path

Inputs:

- source path from `$ARGUMENTS`
- optional provider hint from `--provider`

If source path is not provided, discover likely recent plans first. The discovery script checks both provider plan directories and this repository's external plan directory by default:

- `.oat/repo/reference/external-plans/` — the durable destination for imported provider/external plans, as documented in the repo reference guide (`.oat/repo/reference/AGENTS.md`). External plans are durable reference material and stay under `reference/`, never under the active `pjm/` operational layer.

```bash
bash .agents/skills/oat-project-import-plan/scripts/find-recent-provider-plans.sh --hours 24
```

Optional: extend discovery roots via `OAT_PROVIDER_PLAN_DIRS` (colon-separated):

```bash
export OAT_PROVIDER_PLAN_DIRS="$HOME/custom-plans:$HOME/tmp/provider-plans"
```

Then ask user to either:

- choose one of the listed files (by number), or
- provide a manual file path.

Validation rules:

- File must exist.
- File extension must be `.md` (or user explicitly confirms nonstandard markdown extension).
- File must contain non-empty content.

### Step 2: Preserve Imported Source

Create references directory if missing:

```bash
mkdir -p "$PROJECT_PATH/references"
cp "{source-path}" "$PROJECT_PATH/references/imported-plan.md"
```

Never overwrite an existing source snapshot without user confirmation.
If already present, write timestamped copy:

- `references/imported-plan-YYYY-MM-DD-HHMM.md`

### Step 2.5: Snapshot Explicit Phase-Review Setting Before Plan Normalization

Before any template-based create, update, replacement, or normalization of
`"$PROJECT_PATH/plan.md"`, inspect these sources in precedence order:

1. Existing destination `plan.md`, when it contains a resumed explicit value.
2. The preserved imported source, when it contains an imported explicit value
   and the destination did not contain the key.

Snapshot the key presence of `oat_phase_review_gate` as a separate boolean;
presence is not truthiness and is authoritative regardless of validity. When
present, snapshot the complete explicit value as the exact YAML frontmatter
entry, including its full nested mapping or scalar form. Preserve enabled,
disabled, selected-phase, `null`, and malformed-for-contract values verbatim;
do not normalize, validate, or reconstruct the value while taking the snapshot.

If both sources contain the key, the resumed destination value wins so an
import cannot silently replace a project-level choice. Resumed and imported
settings therefore preserve their complete explicit value across
normalization. Explicit presence must not trigger a target probe or re-prompt,
even when the preserved value is `null` or malformed for the phase-review
contract.

### Step 3: Normalize Into Canonical OAT plan.md

Create/update `"$PROJECT_PATH/plan.md"` using `.oat/templates/plan.md` and map imported content into the canonical structure. Apply `oat-project-plan-writing` invariants after mapping:

Restore the exact snapshot into the resulting `plan.md` frontmatter as part of
the first normalized plan write, before any later frontmatter rewrite and
before Step 4.25 invokes the shared setup contract. Carry the snapshot
losslessly through every subsequent plan update. When the key was explicitly
present, its complete value must still be present exactly as captured; do not
probe, re-prompt, validate, or replace it here. When the key was absent, do not
invent one before the shared setup contract runs.

- `## Phase N`
- `### Task pNN-tNN` (stable task IDs)
- Step structure (RED/GREEN/Refactor/Verify/Commit)
- Required sections: `## Reviews`, `## Implementation Complete`, `## References`
- Review table preservation rules (never delete existing rows)

Normalization rules:

- Preserve original intent and ordering from source.
- Generate stable task IDs per `oat-project-plan-writing` format (`pNN-tNN`).
- Where source lacks test/verify details, add explicit TODO-style placeholders with clear expected output.
- Keep tasks executable and atomic.

Dispatch Profile import handling:

- Preserve recognizable OAT-format `## Dispatch Profile` rows only when
  they use phase named ceilings at or below the project ceiling.
- Preserve foreign model or effort hints as rationale/context in the relevant
  task or phase text. Do not turn them into enduring exact model-family or
  effort preferences.
- Do not generate Dispatch Profile recommendation rows during import.

### Step 4: Update Plan Metadata

Set frontmatter in `"$PROJECT_PATH/plan.md"`:

- `oat_status: in_progress`
- `oat_ready_for: null`
- `oat_phase: plan`
- `oat_phase_status: in_progress`
- `oat_plan_source: imported`
- `oat_import_reference: references/imported-plan.md`
- `oat_import_source_path: {source-path}`
- `oat_import_provider: {codex|cursor|claude|null}`
- `oat_template: true`

These values are the interruption-safe pre-review state. Here,
`oat_template: true` keeps the normalized plan owned by the current import
workflow until its review disposition is durable. If the skill pauses, is
interrupted, or cannot resolve dispatch before Step 4.6, persist and commit this
state. `oat-project-next` must route it back to the same planning workflow and
cannot advance it to implementation.

### Step 4.1: Adopt Complete Ladders and Record the Named Ceiling

Invoke the `Complete Dispatch Ladder Adoption Contract` from
`oat-project-plan-writing`. If the effective ladder is missing or incomplete,
show the complete bundled recommendation and ask for its owning scope before
running exactly one of:

```bash
oat config adopt dispatch-matrix --shared
oat config adopt dispatch-matrix --local
oat config adopt dispatch-matrix --user
```

Adoption preserves explicit cells. Re-run the resolver and completeness check.
An incomplete or missing ladder after adoption blocks readiness; do not
overwrite explicit cells or infer provider defaults. Non-interactive import
also blocks on a missing or incomplete ladder.

The selected scope owns only reusable ladders. A project-specific active policy
or ceiling must not be written to user `~/.oat/config.json`. Resolve or ask
for the project named ceiling, then persist only the maximum constraint in
`"$PROJECT_PATH/state.md"`:

```yaml
oat_dispatch_policy:
  mode: managed
  policy: high
  source: project-state
```

The named `High` tier is a maximum, not an exact family preference:
configured `Economy`, `Balanced`, and `High` candidates remain
available at or below it. An optional phase Dispatch Profile may narrow the
maximum. Use the canonical
`oat project dispatch-ceiling choices --format markdown` prompt when no
project ceiling resolves. `Uncapped` and `Inherit Host Defaults` remain
explicit modes; `Leave Unresolved` is not implementation-ready.

### Step 4.25: Configure Optional Phase Gate Review

After normalization has produced stable phase IDs and before Step 4.5 starts
the import-aware plan artifact review, invoke the `Shared Phase Gate Review Setup
Contract` from `oat-project-plan-writing`. Provider native plan mode uses this
same import step and inherits its result.

If `plan.md` already contains an explicit `oat_phase_review_gate`, preserve it
through the shared contract without probing, prompting, or mutation. Resumed or
imported explicit values remain authoritative without re-prompting. Otherwise
let the contract probe qualifying targets and offer all phases, selected
phases, or disabled. If the probe fails, no target qualifies, or the user
declines, leave Phase gate review disabled and continue with the contract's concise
status output.

This Phase gate review setup is independent from HiLL checkpoints. Do not read or
change HiLL fields here, and do not add a provider/model `--target` to any
lifecycle command.

### Step 4.5: Run Import-Aware Plan Artifact Review Loop

Before dispatching the artifact reviewer, invoke the `Managed Dispatch
Readiness and Review Contract` from `oat-project-plan-writing`:

```bash
oat project dispatch-ceiling resolve --provider "$ACTIVE_PROVIDER" --role reviewer --preflight --json
```

If managed resolution or the complete ladder is unresolved, return to Step
4.1, adopt the recommendation in the selected ownership scope, and re-run the
resolver. Do not set `oat_ready_for: oat-project-implement` while either
contract is unresolved.

Invoke the shared `Auto Artifact-Review Loop` from `oat-project-plan-writing` with target `plan` before advancing project state or handing off to implementation.

Required payload:

- `target: plan`
- `type: artifact`
- `scope: plan`
- `artifact_path: "$PROJECT_PATH/plan.md"`
- `oat_output_mode: structured`
- `import_aware: true`
- `review_note: "Review for canonical OAT plan conformance, executable completeness, stable task IDs, required sections, review-row preservation, and verification clarity. Preserve the imported source's intent and ordering; do not rewrite the author's plan goals or product decisions unless required for OAT conformance or completeness."`

Apply the shared loop exactly:

- Resolve `workflow.autoArtifactReview.plan`; only an explicit `false` skips the loop.
- Resolve `oat_orchestration_retry_limit` from project state, defaulting to `2`.
- Review in the current planning parent by deliberate inheritance by default.
  Do not launch a managed child unless launcher-owned evidence identifies that
  parent as unknown or below the resolved reviewer ceiling.
- For that exception only, apply the shared concrete target contract. A Codex
  materialized variant must first be launched as the exact native `agent_type`;
  only a recorded actual pre-start role-selection rejection permits a fresh
  child pinned to the resolved model and effort. Claude uses the exact
  resolver-returned `providers.claude.dispatchArgs.model` value. Cursor
  launches the exact resolver-returned
  `providers.cursor.dispatchArgs.variant` native reviewer variant first;
  Cursor model strings remain opaque inside the mapping and resolver. Only a
  pre-start native role-selection rejection permits another route.
- After acceptance, poll, nudge, or continue only through the existing reviewer
  handle. A terminal timeout blocks or escalates without another launch.
  Replacement eligibility is limited to explicit pre-start rejection.
- Run an exception inline only with verified equivalent current-host model and
  effort controls. Default inherited review runs in the planning parent. If
  neither route applies, fail closed before artifact review.
- Apply Critical and Important artifact-local fixes when unambiguous and limited to canonical conformance/completeness; offer Medium and Minor fixes instead of silently applying them.
- Re-dispatch after rewrites until clean or the retry bound is exhausted.
- Update the `plan` artifact row in the `## Reviews` table to `passed` when clean. If residual findings remain, preserve the row and surface the residual findings before downstream handoff.

### Step 4.6: Record Review Disposition and Mark Plan Complete

Before changing readiness, durably record the review outcome in `plan.md`:

- When review ran, update the `plan` review row in the `## Reviews` section to
  the outcome reached by Step 4.5. Use `passed` only for a clean result. If
  residual findings remain, retain their actual non-passed status and add a
  concise residual-finding disposition in the same section.
- When `workflow.autoArtifactReview.plan` is explicitly `false`, record the
  explicit skip in the `## Reviews` section as
  `Plan artifact review: skipped (workflow.autoArtifactReview.plan=false)`.
  Do not claim that the plan passed review.

The review row or explicit skip must be written to `plan.md`; chat or status
output alone is not durable. Only after that write succeeds, atomically update
the plan frontmatter:

- `oat_status: complete`
- `oat_ready_for: oat-project-implement`
- `oat_phase_status: complete`
- `oat_template: false`

If dispatch remains unresolved, review execution fails closed, or the outcome
cannot be recorded, leave the Step 4 pre-review values unchanged and commit
them before stopping. Never expose a partially reviewed imported plan to
`oat-project-implement`. Provider-plan-via-import inherits this exact boundary.

### Step 5: Update Project State

Set `"$PROJECT_PATH/state.md"` frontmatter:

- `oat_workflow_mode: import`
- `oat_workflow_origin: imported`
- `oat_phase: plan`
- `oat_phase_status: complete`
- `oat_current_task: null`
- `oat_project_state_updated: "{ISO 8601 UTC timestamp, e.g. 2026-03-10T14:30:00Z}"`

### Step 5.5: Ensure Active Project Pointer

Import mode must leave the imported project as active for immediate execution.

Validate target project before writing pointer:

```bash
if [[ ! -f "$PROJECT_PATH/state.md" ]]; then
  echo "Error: Project missing state.md: $PROJECT_PATH/state.md" >&2
  exit 1
fi
```

```bash
oat config set activeProject "$PROJECT_PATH"
oat state refresh
```

If `activeProject` in local config already exists with a different path, treat this as a project switch and note it in output.

### Step 6: Ensure Implementation Artifact Exists

If missing, scaffold from template:

- `.oat/templates/implementation.md` → `"$PROJECT_PATH/implementation.md"`

Initialize pointer to first plan task ID.

### Step 6.5: Commit Import Artifacts

After the import-aware plan artifact review, project state sync, dashboard refresh, and implementation tracker setup, stage and commit the changed import artifacts before handing off to implementation or stopping.

```bash
git add "$PROJECT_PATH/references/"
for path in \
  "$PROJECT_PATH/plan.md" \
  "$PROJECT_PATH/implementation.md" \
  "$PROJECT_PATH/state.md"; do
  [ -e "$path" ] && git add "$path"
done
git diff --cached --quiet || git commit -m "chore(oat): update imported plan artifacts for {project-name}"
```

### Gate Execution

Before reporting this skill as complete, run the configured gate as the final step after artifact review, state sync, dashboard refresh, and the import artifact commit:

1. Resolve the gate for this skill:

   ```bash
   oat gate resolve <this-skill> --json
   ```

   If the command returns JSON `null`, no gate is configured; the skill is complete.

2. Export the resolved project path into the command shell:

   ```bash
   export PROJECT_PATH
   ```

   If the resolved command invokes `oat gate review`, the configured review command must already include `--project "$PROJECT_PATH"` and must not include `--target <id>`. A valid reusable shape is `oat gate review --project "$PROJECT_PATH" ...`. If the declaration is missing, stop and migrate the stored gate command; do not inject or append arguments at execution time.

3. Resolve the current planning parent's model identity from session context.
   When that identity is non-empty and the resolved configured command invokes
   `oat gate review`, export
   `OAT_GATE_PRODUCER_IDENTITY=<model>:declared` for that command invocation.
   For a non-review configured command or unavailable current identity, ensure
   `OAT_GATE_PRODUCER_IDENTITY` is unset. Do not persist the value or alter the
   configured command.

4. Execute the resolved command exactly as configured and unchanged. Capture
   stdout, stderr, the exit code, and the structured JSON result. A zero exit
   code means the review passed its threshold, but it does not by itself
   authorize artifact receipt or complete the handoff.

5. Review-artifact handoff:
   - Parse the structured gate result. An exit code or artifact path alone never authorizes `oat-project-review-receive`.
   - Invoke receive only when all three conditions hold: `status` is `ok` or `blocked`, the envelope explicitly sets `receiveEligible: true`, and a non-null `handoff` confirms the artifact was corroborated.
   - `receiveEligible: false` is a hard stop even when `artifactPath` is present. Never receive `targeting_correlation_failed`; correct the project/run routing and run a new gate.
   - Keep `artifact_validation_failed` outside receive until the artifact is corrected and the gate successfully revalidates it. Treat `review_failed`, unknown statuses, null handoffs, and contradictory eligibility fields as operational failures.
   - `blocked` exits nonzero but is receive-eligible; `ok` exits zero and still requires durable receive disposition. Route by structured status and eligibility, not by exit code.

6. If the command exits nonzero, use `description` to orient the next steps and handle `onFailure`:
   - `block`: read gate feedback, remediate, and re-run the gate up to `maxAttempts` attempts (default `2`). If attempts are exhausted, escalate to the human with accumulated feedback and append that feedback to `implementation.md`. Treat a launch failure, missing CLI, or no eligible runtime as escalation-biased and do not spend it as a remediation attempt.
   - `prompt`: surface the gate failure and ask the human how to proceed.
   - `warn`: record the gate failure and continue.

7. Runtime selection note: the review-only declaration carries producer
   identity, not reviewer runtime identity. By default, `oat gate review` and
   `oat gate cross-provider-exec` resolve the current host from built-in
   `hostDetectionCommand`s and avoid the same runtime when no exact target is
   supplied. Reusable lifecycle skill-gate commands must not include
   `--target <id>` so independent review stays provider-neutral. Use explicit
   targets only for manual/debug commands or deliberate local/user-specific
   overrides; do not hardcode provider/model targets in bundled skill guidance
   or shared lifecycle gate examples.

### Step 7: Output Next Action

Report:

- source imported path
- normalized phases/tasks count
- Dispatch Profile handling: preserved/mapped/omitted; foreign hints kept as rationale when not explicit constraints
- first task ID
- active project pointer path
- dashboard refresh status
- next options:
  - `oat-project-implement` (sequential by default; parallel when `oat_plan_parallel_groups` is declared)

## Success Criteria

- ✅ Imported markdown preserved at `references/imported-plan.md`.
- ✅ Canonical `plan.md` generated with OAT task structure.
- ✅ `plan.md` metadata marks `oat_plan_source: imported`.
- ✅ `plan.md` records the import-aware plan artifact review row unless `workflow.autoArtifactReview.plan` was explicitly disabled.
- ✅ `state.md` marks `oat_workflow_mode: import`.
- ✅ `implementation.md` is present and resumable.
- ✅ `oat_plan_hill_phases` left unset in frontmatter (deferred to `oat-project-implement` Step 2.5).
- ✅ `## Planning Checklist` items left unchecked (HiLL configuration deferred to implementation).
- ✅ `activeProject` in `.oat/config.local.json` points to the imported project.
- ✅ `.oat/state.md` has been refreshed locally after pointer update; it is not staged or committed.
- ✅ Changed import artifacts are committed before handoff or pause.
- ✅ Configured gate has run, and only a corroborated, receive-eligible artifact has been handed off to `oat-project-review-receive` before it is treated as consumed.
