# Pattern2 (Loop with Break) - JoinIR

## Scope / Criteria
- `loop(...) { ... break ... }` (break present, no continue/return)
- break condition is normalized to "break when <cond> is true"
- loop variable comes from header condition or loop(true) counter extraction
  - loop(true): `i = i + 1` + `substring(i, i + 1)` or `i = j + K` with `j = indexOf(..., i)` + `substring(i, ...)`

## LoopBodyLocal promotion
- SSOT entry: `pattern2::api::try_promote`
- Supported: A-3 Trim / A-4 DigitPos (promote LoopBodyLocal to carrier)
- ConditionOnly carriers are recalculated per iteration (no host binding)

## Trim (seg) minimal shape
- Example shape (A-3): `local seg = s.substring(i, i + 1)`
- Break guard: `if seg == " " || seg == "\\t" { break }`
- seg is read-only (no reassignment in the loop body)

## Derived slot minimal shape (seg)
- Example shape (Derived): `local seg = ""` then `if cond { seg = expr1 } else { seg = expr2 }`
- Break guard: `if seg == "" { break }` (seg used in break condition)
- seg is recomputed per-iteration (Select), no promotion
- Contract SSOT: `pattern2/contracts/derived_slot.rs`

## Carrier binding rules (Pattern2)
- `CarrierInit::FromHost` -> host binding required
- `CarrierInit::BoolConst(_)` / `CarrierInit::LoopLocalZero` -> host binding is skipped
- ConditionOnly carriers must not use `FromHost`

## Boundary hygiene (Phase 29af)
- Header PHI 対象: `carrier_info` の carriers（LoopState + ConditionOnly + LoopLocalZero）
- Exit reconnection 対象: LoopState のみ（ConditionOnly は exit_bindings に入れない）
- Host binding 対象: `CarrierInit::FromHost` のみ（BoolConst / LoopLocalZero は host slot 不要）
- Fail-Fast: exit_bindings の `carrier_name` 重複は禁止（debug_assert）
- Fail-Fast: `CarrierInit::FromHost` が `host_id=0` の場合は Err
- SSOT: `docs/development/current/main/phases/phase-29af/README.md`

## Out of scope
- multiple breaks / continue / return in the loop body
- reassigned LoopBodyLocal outside the derived-slot shape
- break conditions with unsupported AST shapes
- non-substring init for Trim promotion (e.g., `seg = other_call()`)

## Fail-Fast policy
- `PromoteDecision::Freeze` -> Err (missing implementation or contract violation)
- JoinIR lowering/merge contract violations -> Err

## `Ok(None)` meaning
- not Pattern2 (extractor returns None)
- promotion NotApplicable (continue Pattern2 without promotion)
