# Phase 29ai P0: LoopFacts SSOT + Single Planner skeleton — Instructions Status: Ready for execution Scope: Plan/Frag 導線の SSOT 化(docs-first, 仕様不変) ## Goal Plan/Frag の入口を「pattern 名で分岐」から「Facts(事実)をSSOTとして構築→単一PlannerでPlan化」へ寄せる。 - 外部API: `build_plan(...) -> Result, Freeze>` の 1 本に収束 - 分岐は `PlanKind`(enum)内部に閉じ込め、pattern 名を漏らさない ## Non-goals - 仕様変更(挙動変更) - 大規模な既存置換(このP0は骨格とSSOTを作るだけ) - fixture/smoke の新設(既存の quick / regression pack で担保) ## Target Architecture (SSOT) ``` src/mir/builder/control_flow/plan/ facts/ # 解析のみ(AST/CFG から Facts) mod.rs loop_facts.rs # LoopFacts / 収集API scan_shapes.rs # step/cond の形(enum) normalize/ # Facts の表現ゆれを正規化(純変換) mod.rs canonicalize.rs planner/ # Facts → Plan(純変換、分岐はここに閉じ込める) mod.rs build.rs # build_plan_from_facts emit/ # Plan → Frag(生成のみ) mod.rs ``` ## API Contract ### Facts layer - `try_build_loop_facts(...) -> Result, Freeze>` - `Ok(Some(facts))`: facts が揃った - `Ok(None)`: 対象外(既存経路へ) - `Err(freeze_tag)`: 契約違反(Fail-Fast) ### Planner layer - `build_plan_from_facts(facts: CanonicalLoopFacts) -> Result` - `PlanKind` は内部 enum で表現(例: `ScanWithInit`, `SplitScan`, `GeneralLoop`) ## Implementation Steps (P0) 1) `facts/` と `planner/` の骨格を追加(未使用でもOK) 2) “入口SSOT” の関数だけ用意(中身は `Ok(None)` でも良い) 3) 既存の pattern 実装は触らない(P0は並走の足場だけ) 4) docs を更新(Phase 29ai README に P0 入口/目的を明記) ## Verification (SSOT) - `cargo build --release` - `./tools/smokes/v2/run.sh --profile quick` - `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` ## Acceptance Criteria - 既存挙動は不変(回帰パック/quick が緑) - Facts/Planner の SSOT 骨格ができ、次Pで “pattern吸収” の受け口が明確