# Phase 133: Promoted Carrier Join ID (Loop Condition Promotion)

**Date**: 2025-12-15
**Status**: ✅ Done (P1 fix complete)
**Scope**: Loop内のcondition promotionにおいて、Trim promoted carrierにjoin_idが割り当てられていない問題を解決

---

## 背景

実アプリ由来のループパターン（JsonParserBox._skip_whitespace）から発見：

```nyash
loop(index < s.length()) {
  local char = s.substring(index, index + 1)
  if char == " " {          // ← promoted carrier が生成される
    count = count + 1
    index = index + 1
  } else {
    break                   // ← break時の join_id が未割り当て
  }
}
```

**エラー（修正前）**: `[phase229] promoted carrier 'is_char_match' has no join_id`

---

## 根本原因

- Trim系のcondition promotion (A-3) が返す `CarrierInfo` は promoted carrier を `carriers` に入れず `loop_var_name` 側に置く設計
- Pattern2 が `find_carrier()` で join_id を取ろうとして `[phase229] ... has no join_id` で落ちる
- 該当: `src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs:73` (コメント通り "carriers: Empty")

---

## 修正内容（P1: 箱理論アプローチ）

**方針**: Pattern2 は Trim(A-3) を自前で join_id 解決しない。Trim は既に `apply_trim_and_normalize()` (= TrimLoopLowerer) が SSOT で ConditionEnv に join_id を登録するので、そちらに責務を寄せて二重実装を消す。

**実装**: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
- `promote_and_prepare_carriers()` で Promoted が Trim 由来かチェック (`carrier_info.trim_helper().is_some()`)
- Trim の場合:
  - `promoted_pairs.push()` をスキップ
  - `DigitPosConditionNormalizer::normalize()` をスキップ
  - `[phase229]` の promoted_pairs 解決ループ自体を通らない
- Trim でない（DigitPos等）なら、現状の promoted_pairs + join_id 解決を維持

**結果**: "Trim を cond_promoter 側で promote したのに、Pattern2 側で join_id を要求して死ぬ" 構造矛盾が解消

---

## 検証

### P0: Fixture & Smoke Test 実装
- ✅ Fixture: `apps/tests/phase133_json_skip_whitespace_min.hako`
- ✅ Smoke: `tools/smokes/v2/profiles/integration/apps/phase133_json_skip_whitespace_llvm_exe.sh`
  - 現状: compile-only（`--dump-mir`）で `[phase229] ... has no join_id` が出ないことを固定

### P1: 修正完了
- ✅ MIR compilation: promoted carrier join_id エラーが消滅
- ✅ Phase 132 退行チェック: 3/3 PASS
- ⚠️ Note: Fixture の VM 実行エラー（substring on IntegerBox）は別問題として Phase 134+ で対応

**Acceptance Criteria (P1)**:
- `--dump-mir` で `[phase229] ... has no join_id` エラーが出ないこと ✅
- Phase 132 smoke test（退行チェック）: 3/3 PASS ✅

---

## 参考

- **Fixture**: `apps/tests/phase133_json_skip_whitespace_min.hako`
- **Smoke Test**: `tools/smokes/v2/profiles/integration/apps/phase133_json_skip_whitespace_llvm_exe.sh`
- **Phase 132**: Exit PHI value parity (Phase 133は promoted carrierの join_id SSOT統一を扱う)
- **修正コミット**: `b3c832b2` fix(joinir): Phase 133 P1 - Skip Trim promoted_pairs resolution
