feat(joinir): Phase 286 P2.4.1 - Pattern8 normalizer 実装 + Fail-Fast 化

## 概要
Pattern8 (BoolPredicateScan) を Plan line で完走させる。
stub normalizer を完全実装に置き換え、legacy fallback を禁止。

## Step 0: Fixture 簡約
- `?:` ternary → `if/else` に変更(PoC 安定化)

## Step 1: Router Fail-Fast 化
- 文字列判定 `e.contains("[normalizer/pattern8]")` を削除
- extract が Some → normalize/lower 失敗は即 Err(fallback 禁止)
- extract が None → 次の extractor へ(legacy 含む)

## Step 2: Pattern8 normalizer 実装
- CFG: preheader → header(PHI) → body → found/step → after
- found: return false(早期脱出)
- after: return true(ループ完走)
- compose::cleanup() で 2 つの Return exit をマージ

## 検証結果
- Integration: exit 7 
- Plan 完走: route=plan strategy=extract pattern=Pattern8 MATCHED
- Regression: quick 154 PASS, 0 FAILED

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 03:17:48 +09:00
parent 064cae169e
commit 832d018046
3 changed files with 318 additions and 26 deletions

View File

@ -248,20 +248,9 @@ fn try_plan_extractors(
let log_msg = format!("route=plan strategy=extract pattern={}", entry.name);
trace::trace().pattern("route", &log_msg, true);
// Phase 286 P2.4: Catch normalization errors for PoC fallback
// Pattern8 PoC returns error from normalizer to trigger legacy fallback
match lower_via_plan(builder, domain_plan, ctx) {
Ok(result) => return Ok(result),
Err(e) if e.contains("[normalizer/pattern8]") => {
// Pattern8 PoC: normalization not yet supported, continue to legacy Pattern8
if ctx.debug {
trace::trace().debug("route", &format!("Pattern8 Plan PoC: normalization failed ({}), continuing to legacy Pattern8", e));
}
// Continue to next extractor (or legacy patterns)
continue; // Explicit continue to skip "extraction returned None" log
}
Err(e) => return Err(e), // Real errors propagate
}
// Phase 286 P2.4.1: Fail-Fast - extract 成功 → normalize/lower 失敗は即 Err
// 構造的 Fail-Fast: 文字列判定なし、extract が Some なら fallback 禁止
return lower_via_plan(builder, domain_plan, ctx);
} else {
// Extraction returned None - try next extractor
if ctx.debug {