diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md
index e5ea22ea..f21c7e0f 100644
--- a/CURRENT_TASK.md
+++ b/CURRENT_TASK.md
@@ -21,6 +21,9 @@ JoinIR 回帰の integration gate を phase29ae pack に固定し、phase143_*
**2025-12-29: Phase 29aj P7 COMPLETE (Pattern8 planner-first)**
Pattern8 BoolPredicateScan の Facts→Planner-first を導入し、single_planner の extractor 依存を縮小。
+**2025-12-29: Phase 29aj P10 COMPLETE (single_planner unified shape)**
+single_planner を全パターンで planner-first → extractor フォールバックの共通形に統一(挙動不変)。
+
**2025-12-29: Phase 29aj P9 COMPLETE (phase286 pattern9 legacy isolation)**
phase286_pattern9_frag_poc を legacy pack (SKIP) に隔離し、JoinIR 回帰 SSOT を phase29ae pack に固定。
diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md
index a7e53a25..825f5719 100644
--- a/docs/development/current/main/10-Now.md
+++ b/docs/development/current/main/10-Now.md
@@ -2,10 +2,15 @@
## Current Focus: Phase 29aj(PlannerOutcome SSOT)
-Next: Phase 29aj P10(TBD)
+Next: Phase 29aj P11(TBD)
運用ルール: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ)
運用ルール: phase286_pattern9_* は legacy pack (SKIP) を使う
+**2025-12-29: Phase 29aj P10 完了** ✅
+- 目的: single_planner を全パターン planner-first 形に統一(挙動不変)
+- 実装: `src/mir/builder/control_flow/plan/single_planner/rules.rs`
+- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` PASS
+
**2025-12-29: Phase 29aj P9 完了** ✅
- 目的: phase286_pattern9_frag_poc を legacy pack (SKIP) に隔離して SSOT を固定
- 実装: `tools/smokes/v2/profiles/integration/joinir/phase286_pattern9_legacy_pack.sh` / `docs/development/current/main/phases/phase-29aj/README.md` / `docs/development/current/main/phases/phase-29ae/README.md`
diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md
index 40d6bb9a..38b6b1d9 100644
--- a/docs/development/current/main/30-Backlog.md
+++ b/docs/development/current/main/30-Backlog.md
@@ -19,8 +19,8 @@ Related:
- **Phase 29aj(candidate): PlannerOutcome observability SSOT**
- 入口: `docs/development/current/main/phases/phase-29aj/README.md`
- - 状況: P0/P1/P2/P3/P4/P5/P6/P7/P8/P9 ✅ 完了
- - Next: Phase 29aj P10(TBD)
+ - 状況: P0/P1/P2/P3/P4/P5/P6/P7/P8/P9/P10 ✅ 完了
+ - Next: Phase 29aj P11(TBD)
- 運用: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ)
- 運用: phase286_pattern9_* は legacy pack (SKIP) を使う
diff --git a/docs/development/current/main/phases/phase-29aj/P10-SINGLE_PLANNER-UNIFY-PLANNER_FIRST-SHAPE-INSTRUCTIONS.md b/docs/development/current/main/phases/phase-29aj/P10-SINGLE_PLANNER-UNIFY-PLANNER_FIRST-SHAPE-INSTRUCTIONS.md
new file mode 100644
index 00000000..d9102efa
--- /dev/null
+++ b/docs/development/current/main/phases/phase-29aj/P10-SINGLE_PLANNER-UNIFY-PLANNER_FIRST-SHAPE-INSTRUCTIONS.md
@@ -0,0 +1,56 @@
+# Phase 29aj P10: Unify single_planner planner-first shape
+
+Date: 2025-12-29
+Status: Ready for execution
+Scope: single_planner の分岐形を一本化(挙動不変の整形リファクタ)+ docs 更新
+Goal: 入口の形を最終形に寄せて「もう pattern 別 if を増やさない」状態にする
+
+## Objective
+
+- Pattern1/2/3/4/5/6/7/8/9 の順序 SSOT を維持しつつ、全パターンで
+ 1. planner_opt が該当 DomainPlan なら採用
+ 2. そうでなければ extractor にフォールバック
+ の共通形に統一する
+- Pattern1 guard と Pattern8 static box filter はそのまま維持
+- ログ・観測は不変(`route=plan strategy=extract pattern=...` 以外増やさない)
+
+## Non-goals
+
+- extractor の削除 / legacy の撤去
+- rule order SSOT を CandidateSet に移管
+- planner に ctx(pattern_kind/in_static_box)を渡す
+
+## Implementation Steps
+
+### Step 1: RuleKind を Pattern1..Pattern9 に統一
+
+File:
+- `src/mir/builder/control_flow/plan/single_planner/rules.rs`
+
+Changes:
+- RuleKind を Pattern1..Pattern9 の列挙に整理
+- rules テーブルは name/kind のみ維持
+- try_take_planner / fallback_extract の helper を追加して共通形に統一
+
+### Step 2: log_none の意味を SSOT 化
+
+- planner 採用時: log_none=false
+- extractor フォールバック時: log_none=true
+
+### Step 3: docs / CURRENT_TASK 更新
+
+Files:
+- `docs/development/current/main/phases/phase-29aj/README.md`
+- `docs/development/current/main/10-Now.md`
+- `docs/development/current/main/30-Backlog.md`
+- `CURRENT_TASK.md`
+
+## Verification
+
+- `cargo build --release`
+- `./tools/smokes/v2/run.sh --profile quick`
+- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
+
+## Commit
+
+- `git add -A && git commit -m "phase29aj(p10): unify single_planner planner-first shape"`
diff --git a/docs/development/current/main/phases/phase-29aj/README.md b/docs/development/current/main/phases/phase-29aj/README.md
index 8d5036f5..8adcb7b7 100644
--- a/docs/development/current/main/phases/phase-29aj/README.md
+++ b/docs/development/current/main/phases/phase-29aj/README.md
@@ -73,6 +73,13 @@ Goal: planner の facts/plan を 1 本の outcome に集約し、観測の SSOT
- 完了: phase286_pattern9_legacy_pack を追加し、運用ルールを docs に固定
- 検証: `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` / `./tools/smokes/v2/profiles/integration/joinir/phase286_pattern9_legacy_pack.sh` (SKIP)
+## P10: single_planner planner-first 形の統一(挙動不変)
+
+- 指示書: `docs/development/current/main/phases/phase-29aj/P10-SINGLE_PLANNER-UNIFY-PLANNER-FIRST-SHAPE-INSTRUCTIONS.md`
+- ねらい: 全パターンを planner-first → extractor フォールバックの共通形に統一
+- 完了: RuleKind と single_planner の分岐形を統一し、log_none の意味を SSOT 化
+- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
+
## Verification (SSOT)
- `cargo build --release`
diff --git a/src/mir/builder/control_flow/plan/single_planner/rules.rs b/src/mir/builder/control_flow/plan/single_planner/rules.rs
index 435fd6d7..b2e1a2ce 100644
--- a/src/mir/builder/control_flow/plan/single_planner/rules.rs
+++ b/src/mir/builder/control_flow/plan/single_planner/rules.rs
@@ -67,83 +67,17 @@ pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result