| 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 |
| metadata | {"internal":true} |
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:
- Preserve source in
references/imported-plan.md.
- 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.
- Run
git status --porcelain. If empty, continue silently to the next step.
- If non-empty, present the dirty list to the user.
- 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."
- 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
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:
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 .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):
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:
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:
- Existing destination
plan.md, when it contains a resumed explicit value.
- 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:
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":
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:
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:
if [[ ! -f "$PROJECT_PATH/state.md" ]]; then
echo "Error: Project missing state.md: $PROJECT_PATH/state.md" >&2
exit 1
fi
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.
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:
-
Resolve the gate for this skill:
oat gate resolve <this-skill> --json
If the command returns JSON null, no gate is configured; the skill is complete.
-
Export the resolved project path into the command shell:
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.
-
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.
-
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.
-
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.
-
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.
-
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
hostDetectionCommands 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.