# Phase 282: Router Shrinkage (pattern番号の症状ラベル化)

Status: Active SSOT / ✅ P0–P9a complete (2025-12-23)

Goal:
- pattern番号（Pattern1/2/…）を “症状ラベル（テスト名）” に縮退させ、router の責務を「抽出の配線」へ収束させる。
- CFG 構築は `Frag/ExitKind` 合成 SSOT（`compose::*` + `emit_frag()`）へ一本化する。

## SSOT References

- Frag/emit SSOT: `docs/development/current/main/design/edgecfg-fragments.md`
- Composition SSOT: `src/mir/builder/control_flow/edgecfg/api/compose/mod.rs`
- JoinIR overview: `docs/development/current/main/joinir-architecture-overview.md`
- Plan line（Pattern6/7）: `docs/development/current/main/phases/phase-273/README.md`
- Composition adoption（Pattern6/7）: `docs/development/current/main/phases/phase-281/README.md`

## Problem

pattern番号が router の分岐点として肥大化すると、以下が起きる：
- “認識（extract）” と “CFG構築（lower）” が混ざる
- 分岐の追加が雪だるま式に増える（実質、2つ以上のコンパイラを育てる）
- 収束先（SSOT）が曖昧になり、バグ修正の導線が折れる

## Target Shape (SSOT)

router の役割をここまで縮退させる：
- **やる**: Extractor の列挙、Ok(None)/Err の境界管理、Plan/JoinIR の入口選択
- **やらない**: CFG構築のディテール、terminator 生成、推測 fallback

CFG構築は以下に収束させる：
- Plan line: `Extractor → DomainPlan → Normalizer → CorePlan → Lowerer → emit_frag()`
- JoinIR line: `cf_loop → (必要な抽出) → Frag composition → emit_frag()`

## P0 (docs-only) — SSOT 固定

- router の責務/禁止事項を明文化（by-name hardcode禁止、silent fallback禁止）
- "pattern番号の意味" を SSOT として定義（テスト名/症状ラベル）
- 入口リンクを `10-Now.md` に固定

### Router Responsibilities SSOT

**Router がやること**:
1. Extraction 戦略の列挙（Plan-based, JoinIR table-based）
2. Extractor を優先順で呼び出し（Plan line → JoinIR table）
3. Ok(None)/Err の境界管理（Fail-Fast原則）
4. Entrypoint の選択（Plan line vs JoinIR line）
5. Routing 決定のログ出力（debug mode のみ、既存 trace 機構）

**Router がやらないこと**（禁止事項）:
1. CFG 構築（block 割り当て、PHI 挿入、terminator 生成）
2. Pattern 固有の lowering ロジック（Normalizer/Lowerer に委譲）
3. Silent fallback（エラーは明示的に）
4. By-name hardcode（関数名マッチング禁止、debug 以外）
5. Mock path fallback（test 専用パターンの本番使用禁止）

**Pattern 番号 = 症状ラベル**（Phase 280 SSOT positioning）:
- ✅ 正しい用途: テスト名（`loop_if_phi.hako` → Pattern3_WithIfPhi）、debug ログ
- ❌ 禁止: CFG 分岐（`if pattern == 6 then ...`）、アーキテクチャ SSOT（Frag composition が SSOT）

**Detection 戦略**（Phase 282 P3-P7 migration 完了）:
- **ExtractionBased** (全Pattern統一): extract_*() 成功 → match（SSOT 単一）
  - Pattern1-5: Phase 282 P3-P7 で ExtractionBased 移行完了
  - Pattern6/7: Phase 273 Plan-based (extract_*_plan)
  - Pattern8/9: Phase 259/270 で既に ExtractionBased
  - pattern_kind: **safety valve のみ**（O(1) perf guard、検出ロジックではない）

### Pattern Detection SSOT Table (Phase 282 P3-P7 Complete)

**全Pattern統一ルール**:
1. **SSOT = extract**: Extraction 関数が検出の唯一の真実（pattern_kind ではない）
2. **Safety valve**: pattern_kind は O(1) 早期reject のみ（検出ロジックに使わない）
3. **Re-extract**: lower() は必ず再extract して SSOT 強制（can_lower 通過を信じない）

| Pattern | SSOT Entrypoint | Safety Valve | Re-extract | Phase |
|---------|-----------------|--------------|------------|-------|
| **Pattern1** | `extractors/pattern1.rs::extract_*` | `pattern_kind==Minimal` | ✅ | P282 P3 |
| **Pattern2** | `extractors/pattern2.rs::extract_*` | `pattern_kind==Basic` | ✅ | P282 P4 |
| **Pattern3** | `extractors/pattern3.rs::extract_*` | `pattern_kind==WithIfPhi` | ✅ | P282 P5 |
| **Pattern4** | `extractors/pattern4.rs::extract_*` | `pattern_kind==Carrier` | ✅ | P282 P6 |
| **Pattern5** | `extractors/pattern5.rs::extract_*` | `pattern_kind==InfiniteEarlyExit` | ✅ | P282 P7 |
| **Pattern6** | `pattern6_scan_with_init.rs::extract_scan_with_init_plan()` | (none, Plan-based) | ✅ | P273 P1 |
| **Pattern7** | `pattern7_split_scan.rs::extract_split_scan_plan()` | (none, Plan-based) | ✅ | P273 P2 |
| **Pattern8** | `pattern8_scan_bool_predicate.rs` (can_lower) | (none, ExtractionBased) | ✅ | P259 |
| **Pattern9** | `pattern9_accum_const_loop.rs` (can_lower) | (none, ExtractionBased) | ✅ | P270 |

**pattern_kind の正しい使い方**:
- ✅ **O(1) guard**: `if pattern_kind != Expected { return false }` (perf最適化)
- ✅ **Debug logging**: `pattern_kind={:?}` (診断情報)
- ❌ **検出ロジック**: `if pattern_kind == X then lower_X()` (禁止、SSOT違反)
- ❌ **CFG 分岐**: `match pattern_kind { ... }` (禁止、extract が SSOT)

**移行完了状態** (Phase 282 P8):
- すべての Pattern が extract_*() を SSOT として使用
- pattern_kind は早期reject の補助にすぎない
- lower() は必ず re-extract して SSOT 強制（二重検証）

**SSOT 参照**:
- Frag composition: `src/mir/builder/control_flow/edgecfg/api/compose/mod.rs`
- Plan line: `src/mir/builder/control_flow/plan/normalizer.rs`
- Terminator 生成: `emit_frag()` (Phase 267)

### Ok(None)/Err Boundary Rules

**Fail-Fast 原則**:
- `Ok(None)`: Extraction がパターンに一致しなかった（次を試す）
- `Err(String)`: Extraction が一致したが検証失敗（即座に fail）
- **禁止**: Close-but-unsupported → Ok(None)（Err で返すべき）

**例**:

```rust
// ✅ 正しい: 一致しない
extract_scan_with_init_plan() → Ok(None)  // 次のパターンを試す

// ✅ 正しい: 一致したが非サポート
extract_scan_with_init_plan() → Err("P1 scope: reverse scan not supported")

// ❌ 禁止: Silent fallback
extract_scan_with_init_plan() → Ok(None) for unsupported cases
// （非サポートケースを Ok(None) で返してはいけない）
```

**境界の位置**（router.rs 実装）:
- Plan line（lines 310-337, 341-368）: extract → match → Normalize → Verify → Lower
- JoinIR table（lines 376-382）: can_lower → lower
- No match（lines 384-395）: Ok(None) を caller に返す（Err ではない）

### Entrypoint Table

| Entrypoint | Entry Condition | Patterns | SSOT Downstream |
|------------|----------------|----------|-----------------|
| **Plan line** | extract_*_plan() が Ok(Some) | Pattern6/7 | Normalizer → CorePlan → Lowerer → emit_frag |
| **JoinIR table** | LOOP_PATTERNS 反復 | Pattern1-5,8-9 | cf_loop 抽出 → Frag composition → emit_frag |
| **No match** | すべての extract が Ok(None) | (none) | Err を caller に返す |

**Plan line の責務**（Phase 273 SSOT）:
1. **Extraction**: extract_*_plan()（pure、builder 不要）
2. **Normalization**: PlanNormalizer（pattern 知識の展開）
3. **Verification**: PlanVerifier（fail-fast 検証）
4. **Lowering**: PlanLowerer（block/value 割り当て + Frag composition）
5. **Emission**: emit_frag()（terminator SSOT）

**JoinIR table の責務**（Phase 194+ table-driven）:
1. **Detection**: can_lower()（pattern により structure-based / extraction-based が混在）
2. **Extraction**: cf_loop 抽出
3. **収束先**: Frag composition → emit_frag（内部パイプライン詳細は最小化）

**収束先の統一**（Phase 282 Goal）:
- すべての entrypoint が最終的に `emit_frag()` に収束（terminator 生成の SSOT）
- CFG 構築の詳細は router が持たない（抽出の配線のみ）

## P1 (code, minimal) — 配線の可視化

## Follow-up (design-first): Return as ExitKind SSOT

移行期間中に一番 “ズレやすい” のは `return` まわりなので、pattern 個別実装へ散らさず、
`ExitKind` + `compose::*` / `emit_frag()` に収束させる方針を別フェーズで扱う。

- 入口: `docs/development/current/main/30-Backlog.md`（Phase 284）

## P9a (refactor, behavior-preserving) — Extractor 重複削減（Common helpers）

Pattern1–5 の extractor が持っていた “再帰カウント/検出/条件ヘルパ” の重複を、pure helper に集約する。

- 追加: `src/mir/builder/control_flow/joinir/patterns/extractors/common_helpers.rs`
- 方針:
  - **SSOT=extract** は維持（判定は各 pattern extractor の責務）
  - helper は pure（builder 触らない）・**silent fallback を作らない**
  - Pattern3 の “if-else PHI” は専用ロジックが多いため、段階的に扱う（P9b 以降）

- router に “経路ログ” を追加（既定OFF、debugのみに限定）
- pattern番号ではなく “entrypoint（Plan/JoinIR）” をログの主語にする

## Acceptance (Phase 282)

- router の責務が docs で SSOT 化されている
- router の変更が「extractor配線」のみになっている（CFG構築の詳細を持たない）
- 既存の VM/LLVM smokes に退行がない
