---
Status: SSOT
Scope: JoinIR → PlanFrag → CorePlan への移行道筋（仕様不変で完了と言える条件）
Related:
- docs/development/current/main/10-Now.md
- docs/development/current/main/30-Backlog.md
- docs/development/current/main/design/coreplan-skeleton-feature-model.md
- docs/development/current/main/design/post-phi-final-form-ssot.md
- docs/development/current/main/design/effect-classification-ssot.md
- docs/development/current/main/design/exitkind-cleanup-effect-contract-ssot.md
- docs/development/current/main/phases/phase-29ae/README.md
---

# CorePlan Migration Roadmap (SSOT)

目的: “JoinIR を CorePlan で組み立てる” 状態へ、仕様不変（release既定）で段階的に収束するための道筋を 1 枚に固定する。

## 0. 前提（守ること）

- 既定挙動不変（release の意味論/エラー文字列/恒常ログを変えない）
- silent fallback 禁止（strict/dev では Freeze/Fail-Fast で検出可能にする）
- by-name ハードコード禁止（構造条件・SSOT境界で解く）
- JoinIR integration gate（SSOT）を常に緑維持:
  - `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`

## 1. “CorePlan で組み立てる” の定義

ここでの完了は「pattern列挙が消えた」ではなく、以下が成立していること。

- 構造SSOTは `CorePlan`（emit/merge は CorePlan/Frag 以外を再解析しない）
- `Facts → Planner → (DomainPlan) → CorePlan → lowerer/emit → merge` が主経路
- `DomainPlan` は意図/recipe として残ってもよいが、最終の verify/emit 契約は CorePlan 側で固定されている

## 2. すでに固めた SSOT（再発防止の土台）

- Skeleton/Feature: `docs/development/current/main/design/coreplan-skeleton-feature-model.md`
- post-phi final form: `docs/development/current/main/design/post-phi-final-form-ssot.md`
- effect classification: `docs/development/current/main/design/effect-classification-ssot.md`
- cleanup contract: `docs/development/current/main/design/exitkind-cleanup-effect-contract-ssot.md`

## 3. 移行タスクの流れ（安全順）

### Step A: CorePlan 語彙の “穴” を埋める（lowerer/verifier）

狙い:
- `CorePlan` の構造ノードが単独でも lower/verify できる（未対応分岐を減らす）

典型:
- `CorePlan::If` / `CorePlan::Exit` の lowerer 対応
- verifier の不変条件を “局所検証” で完結させる

現状:
- ✅ Phase 29am（P0–P3）で “最低限 lower/verify できる語彙” を前倒しで固定済み

### Step B: Facts を Skeleton+Feature の SSOTへ寄せる

狙い:
- Pattern1/2/4/5 のような “complete pattern” の増殖を止める
- `LoopSkeleton + ExitMap + ValueJoin + ...` の合成で表現できる状態に寄せる

やらない:
- Facts から emit/merge を助けるための再解析を前提にした “不足した Facts” を作る

直近の入口（SSOT）:
- Phase 29an: `docs/development/current/main/phases/phase-29an/README.md`

### Step B.5: 合成入口（composer）を 1 箇所に固定（未接続）

狙い:
- Facts→CorePlan 合成の入口を “1ファイル” に集約し、以後の実装を合成側へ閉じ込める

入口:
- Phase 29ao: `docs/development/current/main/phases/phase-29ao/README.md`

### Step C: Planner を「骨格の一意化→特徴付与→Freeze」へ

狙い:
- CandidateSet は骨格の一意化に集中（0/1/2+ → None/Some/Freeze）
- feature は “別パターン” ではなく合成の材料として付与

### Step D: Normalizer を “合成だけ” にする

狙い:
- `(Skeleton, FeatureSet, DomainIntent)` → `CorePlan` の純変換に収束
- join 入力（post-phi）と effect/cleanup 契約を壊さない

### Step E: 入口から legacy fallback を段階的に 0 へ

狙い:
- `single_planner` の legacy extractor fallback を削減し、planner-first を主にする
- JoinIR 側 wrapper/router を薄くし、PlanFrag 側 SSOT に寄せる

注意:
- “落ちる” を `Ok(None)` で隠さない（対象っぽいのに一意化できない場合は Freeze）

## 4. 完了（Done）の判定（設計上）

最低限の Done:
- JoinIR 回帰 SSOT が緑: `phase29ae_regression_pack_vm.sh`
- 回帰対象（現状の gate: pattern2/6/7 + phase1883 + phase263）が “CorePlan 合成” 経路で通る

強い Done（段階2）:
- Facts が complete pattern を増やさず Skeleton+Feature に寄っている
- DomainPlan は scan/split/predicate 等の “意図” 以外は CorePlan 合成へ吸収されている
- Freeze taxonomy が運用でぶれず、strict/dev の診断が安定タグで追える

## 5. 次の実装順（SSOTの “安全順”）

この順で進めると、仕様不変を保ったまま “CorePlan で組み立てる” へ近づける。

1. Phase 29ao P1: composer の allocation 境界を固定（bridge で DomainPlan を生成、未接続）
2. Phase 29ao P2: bridge→Normalizer で CorePlan を得る導線（未接続のまま、unit test で固定）
3. Phase 29ao P3+: ExitMap/Cleanup/ValueJoin を CorePlan/Frag SSOT に寄せて “pattern を feature 合成” へ
4. Phase 29ao closeout: 代表回帰（phase29ae pack）が “CorePlan 合成” 経路で通る範囲を増やし、fallback を縮退
