Files
hakorune/docs/development/current/main/phase49-selfhost-joinir-depth2-design.md
nyash-codex 80e952b83a feat(joinir): Phase 54 SELFHOST-SHAPE-GROWTH - 構造軸育成 + 偽陽性観測
Phase 53 成果を踏まえ、構造シグネチャ軸を 5+ に育て、
偽陽性観測テストで name ガード縮小準備を整えた。

方針変更: 新ループ追加 → 構造軸育成 + 偽陽性率測定に焦点変更
- 理由: Phase 53 で selfhost P2/P3 実戦パターン追加済み
- 焦点: 既存ループに対する構造軸拡張 + 精度測定

主な成果:

1. 構造軸 5+ 達成:
   - carrier 数
   - carrier 型
   - Compare パターン
   - branch 構造
   - NEW: Compare op 分布 (count_compare_ops ヘルパー)

2. 偽陽性観測テスト追加:
   - test_phase54_structural_axis_discrimination_p2()
   - test_phase54_structural_axis_discrimination_p3()

3. 重要な発見 - 偽陽性率 ~50%:
   - P2: selfhost P2 が正しく検出されず (name ガード依存)
   - P3: selfhost P3 が Pattern4ContinueMinimal と誤検出 (構造的類似性)
   - 結論: 構造判定のみでは分離不十分、name ガード必須と判明

変更内容:

- shape_guard.rs (+80 lines):
  - count_compare_ops() 構造軸ヘルパー追加
  - detect_shapes() pub 化 (テストから呼び出し可能に)
  - SelfhostVerifySchemaP2/SelfhostDetectFormatP3 enum 追加 (将来用)

- normalized_joinir_min.rs (+110 lines):
  - 偽陽性観測テスト 2 個追加 (P2/P3 各1)
  - canonical shapes vs selfhost shapes 構造判定精度測定

- phase49 doc (+200 lines):
  - Phase 54 節完成版
  - 偽陽性分析結果記録
  - name ガード縮小方針明記

- enum 拡張対応:
  - bridge.rs (+8 lines)
  - normalized.rs (+8 lines)
  - ast_lowerer/mod.rs (+2 lines)

偽陽性観測結果 (2025-12-12):
- P2 構造判定: selfhost P2 検出失敗 → name ガード必須
- P3 構造判定: selfhost P3 が Pattern4 と誤判定 → 構造的類似性問題
- 総合: 偽陽性率 ~50% → 構造軸 5 本では不十分

次フェーズ方針 (Phase 55+):
- Phase 55-A: 条件複雑度軸追加 (BinOp/UnaryOp ネスト深度)
- Phase 55-B: 算術パターン軸追加 (Mul/Sub/Div 出現パターン)
- Phase 56: selfhost 実戦ループ追加 (6 本以上蓄積)
- Phase 57: 誤判定率 < 5% 達成後に name ガード縮小開始

name ガード撤去条件 (Phase 57):
- 構造軸 8+ 本確立
- selfhost P2/P3 各 6 本以上蓄積
- 誤判定率 < 5% 達成
- 複合的特徴量ベース判定実装

回帰テスト:  939 PASS, 0 FAIL (既存挙動不変)

Files Modified: 8 files
Lines Added: ~408 lines (net)
Implementation: Pure additive (feature-gated)

Phase 54 完了!構造軸育成・偽陽性観測基盤確立!
2025-12-12 17:12:58 +09:00

521 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 49-SELFHOST-NORM-DEPTH2: selfhost depth2 Normalized 設計メモ(コード変更なし)
## 1. Goal & Scope
- 目標: `.hako → Program/MIR JSON → JoinIR(Structured) → Normalized → MIR → VM/LLVM` の depth2 パイプラインを selfhost でも踏めるように設計を固める。
- フォーカスする selfhost ループPhase 183 の棚卸しを前提に「軽い P2/P3」を 2 本に固定):
- 対象A: `selfhost_token_scan_p2.hako` / 関数 `selfhost_token_scan_p2`P2 カウンタループ、break あり・continue なし・MethodCall なし)。
- 対象B: `selfhost_if_sum_p3.hako` / 関数 `selfhost_if_sum_p3`P3 if-sum: sum+count、条件は Compare のみ・MethodCall なし)。
- Out of Scope今回扱わない: P5/Trim 相当の heavy ループ、MethodCall 多用ループ、selfhost の他ループ。
## 2. 現状整理Status Snapshot
- Phase 183 時点: selfhost depth2 の代表ループは棚卸し済みだが、Normalized 経路や shape_guard は未整備。
- JsonParser 側: P1〜P4 代表形が canonical NormalizedPhase 41/48で安定、StepScheduleBox/shape_guard/normalized_bridge が揃っている。
- selfhost: Program/MIR JSON までは出せるが、JoinIR→Normalized→MIR への橋は未設計。まずは P2/P3 の軽量ループに限定して設計する。
## 3. ループ→Pattern/Shape マッピング表(確定)
| Loop 名(仮) | Pattern 想定 | Normalized shape 想定 | 必要キャリア | 特記事項 |
| --- | --- | --- | --- | --- |
| selfhost_token_scan_p2 | P2 corebreak あり/continue なし) | Pattern2 coreJsonParser skip_ws と同列) | ループ変数 + count | body-local/MethodCall なし |
| selfhost_if_sum_p3 | P3 if-sum minimal | Pattern3 if-sum minimal/multi | sum + count + ループ変数 | MethodCall なし、条件は Compare のみ |
## 4. depth2 パイプライン設計(責務メモ)
```
.hako (selfhost) → Program/MIR JSON selfhost front-end
→ JoinIR(Structured) JoinIR front-end / ast_lowerer・fixtures
→ Normalized normalized.rs + shape_guard
→ MIR normalized_bridge 直 or Structured 再構成フォールバック)
→ VM/LLVM 実行
```
- selfhost front-end: Program/MIR JSON を生成(既存 Stage-1/Stage-3
- JoinIR front-end: Program/MIR JSON → Structured JoinModule既存 ast_lowerer + 追加 selfhost fixtures
- Normalized: shape_guard で P2/P3 自動判定 → Structured→Normalized 変換。
- Bridge: canonical セットP1〜P4は direct Normalized→MIR を優先、非対応は Structured 再構成フォールバック。
- 実行: VM/LLVM は JsonParser と同じ経路を共用。
## 5. フィクスチャとテスト計画dev-only
- Program JSON:
- `docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_token_scan_p2.program.json`
- `docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3.program.json`
- Structured JoinModule helpernormalized::fixtures:
- `build_selfhost_token_scan_p2_structured_for_normalized_dev()`
- `build_selfhost_if_sum_p3_structured_for_normalized_dev()`
- テストnormalized_dev feature 下):
- Structured→MIR vs Structured→Normalized→MIR(direct) の VM stdout 比較を追加。
- shape_guard が誤判定した場合は Fail-Fast させ、対象ループ以外はスコープ外と明示。
## 6. Out of Scope / 次フェーズ送り
- heavy selfhost ループMethodCall 多用、P5/Trim 依存)。
- Normalized 最適化や verifier 拡張(設計のみ、実装は Phase 50+)。
- selfhost 以外の新規ループ適用。
## 7. Next steps49-B/50 に向けたメモ)
- normalized::fixtures に selfhost 用 helper を追加し、shape_guard に selfhost shape variant を足す。
- tests/normalized_joinir_min.rs に selfhost ループの比較テストを追加dev-only
- canonical 昇格は Phase 50 以降で検討(まずは dev 正規化を通すことに専念)。
## 8. Status updatePhase 50 反映)
- 対象ループを `selfhost_token_scan_p2` / `selfhost_if_sum_p3` に確定し、normalized_dev フィクスチャと Structured helper を追加済み。
- ShapeGuard に selfhost 用 shape を追加し、Structured→Normalized→MIR(direct) の dev 比較テストで Structured 直経路と一致するところまで実装完了canonical 化は後続フェーズ)。
## 9. Phase 51SELFHOSTNORMDEVEXTEND
Phase 50 の selfhost P2/P3 dev Normalized の足場を使い、selfhost 側でもう少し実戦寄りの形状を dev Normalized に追加する。
canonical 昇格は別フェーズで扱い、このフェーズでは dev-only のまま固定する。
### 追加対象dev-only
| ループ名 | 想定パターン | ねらい | キャリア/更新 | 備考 |
| --- | --- | --- | --- | --- |
| selfhost_token_scan_p2_accum | P2 corebreak あり/continue なし) | P2 で複数キャリア更新の安定化 | i + count + accacc += i, count += 1 | name ガード dev-only構造判定が安定したら撤去 |
| selfhost_if_sum_p3_ext | P3 if-sum family | then/else 両側更新の安定化 | i + sum + countthen: sum+=i,count+=1 / else: sum+=1 | name ガード dev-only構造判定が安定したら撤去 |
### 受け入れ条件Phase 51
- 上記 2 本が fixtures + shape_guard + dev 比較テストまで揃い、狙い撃ちテストが緑。
- normalized_dev 以外の挙動は不変canonical/既定経路に影響なし)。
## 10. Phase 52SELFHOSTSHAPESTRUCTSIGNATUREdev-only
Phase 5051 で入れた selfhost shape の name ガードを、可能な範囲で「構造判定structural signature」へ寄せる育成フェーズ。
このフェーズでは **name ガードの全面撤去は狙わず**、構造シグネチャで一次判定 → 曖昧な場合のみ dev-only name ガードで絞る二段階 detector を導入する。
### ねらい
- selfhost P2/P3 が JsonParser/canonical 群と混線しないためのガードを、by-name 依存から段階的に縮退させる。
- 将来の selfhost ループ追加Phase 53+)時に「構造で識別できる軸」を SSOT として固定する。
### 構造シグネチャ候補(一次判定)
#### Selfhost P2 core familyTokenScanP2 / TokenScanP2Accum
- Structured JoinModule で `loop_step` が存在し、tail-call で自分自身に戻る P2 ブレークループであること。
- `loop_step` のパラメータ数が **3〜4**`i` + host param + 1〜2 carriersで、body 内に `Select` が出ないこと。
- body の主要 Compute が `Compare`break/cond`Add` 系に限定され、外部/BoxCall が含まれないこと。
- **注意**: JsonParser `skip_ws_mini` と構造が近く、一次判定だけでは区別不能なケースがある。
#### Selfhost P3 if-sum familyIfSumP3 / IfSumP3Ext
- 現状の selfhost baseline は **P2-like skeletonnormalize_pattern2_minimal 委譲)** のままなので、一次判定は「P3 の理想形Select を含む if-sum」を要求しない。
- `loop_step` のパラメータ数が **4**`i` + host param + `sum` + `count`で、break 由来の `Ge` Compareparams 間)が存在すること。
- tail-call によるループ継続を持ち、body が純粋な算術更新のみで、外部/BoxCall が含まれないこと。
### 二段階 detector 方針
1) 上記の構造シグネチャで Selfhost family candidate を一次判定
2) 一次判定が他 shape と曖昧な場合のみ、**dev-only name ガードで最終確定**
name ガードは `normalized_dev` 限定・混線防止用途に閉じ、canonical/本番経路には持ち込まない。
### 撤去条件(次フェーズ)
- Phase 53+ で selfhost 形状のバリエーションが 3〜4 本以上に増え、構造軸carrier 数/Compare 配列/StepSchedule など)が安定したら、
P2/P3 それぞれで name ガードの適用範囲を縮小→最終撤去する。
## 11. Phase 53: SELFHOSTNORMDEVEXPANDdev-only バリエーション拡大)
Phase 5051 で selfhost P2/P3 dev Normalized の足場を構築し、Phase 52 で構造シグネチャ軸carrier 数、Compare 配列等)を導入した。
Phase 53 では **実戦寄りループを P2/P3 各 1〜2 本追加**し、構造シグネチャ軸を育成・name ガード適用範囲を縮小する。
### 追加対象ループdev-only
| ループ名 | 想定パターン | ソース箇所 | キャリア/更新 | 構造的特徴 |
| --- | --- | --- | --- | --- |
| **selfhost_args_parse_p2** | P2 corebreak あり/continue なし) | `apps/selfhost-runtime/runner.hako:20-33` | i + box_pref文字列更新| 1 キャリア、文字列比較多用、StringBox メソッドindexOf/substring |
| **selfhost_stmt_count_p3** | P3 if-sum family多分岐 | `apps/selfhost-runtime/mir_loader.hako:76-89` | i + 9 カウンタr/e/l/iff/lp/br/ct/tr/ex | 9 キャリア、多段 if-else9 分岐、MethodCallst.get/str |
### 選定理由
#### P2: selfhost_args_parse_p2
- **実戦的 P2**: コマンドライン引数パース(`--box-pref=` 等)
- **構造的差異**:
- 既存 P2token_scan, token_scan_accumは数値キャリアのみ
- **本ループ**: 文字列キャリアbox_pref+ StringBox MethodCallindexOf/substring/length
- **構造判定軸育成**: MethodCall 出現パターン、キャリア型多様性
- **name ガード必要性**: StringBox MethodCall が入るため、JsonParser P2 との混線は低い(構造一次判定で十分分離可能)
- **dev-only name ガード**: 最終確定のみ(構造判定が主軸)
#### P3: selfhost_stmt_count_p3
- **実戦的 P3**: MIR 文種別カウントReturn/Expr/Local/If/Loop/Break/Continue/Try/Extern
- **構造的差異**:
- 既存 P3if_sum, if_sum_extは 2〜3 キャリア・単純 if-sum
- **本ループ**: **9 キャリア**r/e/l/iff/lp/br/ct/tr/ex+ **9 分岐**(多段 if-else
- **構造判定軸育成**:
- キャリア数上限検証9 は P3 範囲内か?)
- 多段 if-else パターンSelect チェーン長)
- MethodCall 出現st.get/str
- **name ガード必要性**: MethodCall + 9 キャリアで JsonParser P3 と明確に分離
- **dev-only name ガード**: 構造判定優先、最終確定のみ
### 構造シグネチャ軸育成方針Phase 52 継続)
#### P2 family 構造軸(強化)
1. **キャリア数**: 1〜3 → **型多様性追加**Integer, String, mixed
2. **MethodCall 出現**: なし → **StringBox メソッド許容**indexOf/substring/length
3. **Compare 配列**: `Lt`/`Ge` 単一 → **複合条件(`Eq` 多用)**
#### P3 family 構造軸(強化)
1. **キャリア数上限**: 2〜4 → **9 キャリア検証**P3 範囲内確定)
2. **分岐数**: 単純 if-sum → **多段 if-else9 分岐)**
3. **MethodCall 出現**: なし → **JsonNodeBox メソッド許容**get/str
4. **Select チェーン長**: 構造的計測Normalized 時の Select 深度)
### 二段階 detector 実装方針Phase 52 継承)
```rust
// P2: selfhost_args_parse_p2 detector
fn is_selfhost_args_parse_p2(module: &JoinModule) -> bool {
// 1. 構造一次判定(優先)
if !has_p2_break_pattern(module) { return false; }
let carrier_count = count_carriers(module);
if carrier_count < 1 || carrier_count > 3 { return false; }
// StringBox MethodCall 許容indexOf/substring
let methodcalls = count_methodcalls(module);
if methodcalls > 5 { return false; } // 過剰な MethodCall は除外
// 2. dev-only name 最終確定(曖昧時のみ)
#[cfg(feature = "normalized_dev")]
if !function_name_matches("selfhost_args_parse_p2") { return false; }
true
}
// P3: selfhost_stmt_count_p3 detector
fn is_selfhost_stmt_count_p3(module: &JoinModule) -> bool {
// 1. 構造一次判定(優先)
if !has_p3_if_sum_pattern(module) { return false; }
let carrier_count = count_carriers(module);
if carrier_count < 2 || carrier_count > 10 { return false; } // 9 キャリア許容
// 多段 if-else パターン確認
let branch_count = count_if_else_branches(module);
if branch_count < 2 { return false; }
// JsonNodeBox MethodCall 許容get/str
let methodcalls = count_methodcalls(module);
if methodcalls > 10 { return false; } // 過剰な MethodCall は除外
// 2. dev-only name 最終確定(曖昧時のみ)
#[cfg(feature = "normalized_dev")]
if !function_name_matches("selfhost_stmt_count_p3") { return false; }
true
}
```
### name ガード適用範囲縮小条件
Phase 53 実装後、以下の条件で name ガードを撤去可能:
1. **構造軸が 5 軸以上安定**carrier 数/型/MethodCall 数/Compare 配列/分岐数)
2. **P2/P3 各 6 本以上の dev ループ蓄積**(バリエーション十分)
3. **誤判定率 < 5%**(構造一次判定の精度検証)
現状Phase 53 後):
- P2: 4 本token_scan, token_scan_accum, args_parse, +1 予定)
- P3: 4 本if_sum, if_sum_ext, stmt_count, +1 予定)
- 構造軸: 4 軸carrier 数/型/MethodCall/Compare
- **撤去条件未達** → name ガード継続dev-only
### 受け入れ基準Phase 53
- ✅ P2/P3 各 1〜2 本追加(合計 2〜4 本、最小 2 本)
- ✅ Program JSON + Structured builder 完備
- ✅ ShapeGuard 二段階判定実装(構造一次 + dev-only name 最終)
- ✅ dev VM 比較テスト追加(全 PASS
- ✅ 構造軸 4〜5 本確立carrier 数/型/MethodCall/Compare/分岐数)
- ✅ phase49 doc Phase 53 節完成SSOT
- ✅ 既存挙動不変normalized_dev 以外)
### Out of ScopePhase 54+
- **name ガード完全撤去**: Phase 54 以降で構造軸が十分安定してから
- **canonical 昇格**: Phase 55+ で検討dev 正規化安定後)
- **P4/P5 heavy ループ**: Phase 56+ で段階的追加
### 実装完了記録Phase 53
**実装日**: 2025-12-12
**追加内容**:
1. **Program JSON fixtures**: 2 個
- `selfhost_args_parse_p2.program.json` (P2: string carrier + 条件分岐)
- `selfhost_stmt_count_p3.program.json` (P3: 5 carriers + 多段 if-else)
2. **Structured builders**: 2 個fixtures.rs
- `build_selfhost_args_parse_p2_structured_for_normalized_dev()`
- `build_selfhost_stmt_count_p3_structured_for_normalized_dev()`
3. **ShapeGuard detectors**: 2 個shape_guard.rs
- `is_selfhost_args_parse_p2()` (二段階判定: P2 core family + name guard)
- `is_selfhost_stmt_count_p3()` (二段階判定: 2-10 carriers + name guard)
4. **dev VM 比較テスト**: 2 個normalized_joinir_min.rs
- `normalized_selfhost_args_parse_p2_vm_bridge_direct_matches_structured()`
- `normalized_selfhost_stmt_count_p3_vm_bridge_direct_matches_structured()`
**変更ファイル**:
- `phase49-selfhost-joinir-depth2-design.md` (+128 lines, Phase 53 節)
- `selfhost_args_parse_p2.program.json` (NEW, 60 lines)
- `selfhost_stmt_count_p3.program.json` (NEW, 150 lines)
- `fixtures.rs` (+48 lines, 2 builders)
- `shape_guard.rs` (+80 lines, 2 detectors + enum 拡張)
- `bridge.rs` (+8 lines, 2 shape handlers)
- `normalized.rs` (+10 lines, 2 roundtrip handlers)
- `ast_lowerer/mod.rs` (+2 lines, 2 entry point registrations)
- `normalized_joinir_min.rs` (+40 lines, 2 tests + imports)
**テスト結果**:
- ✅ normalized_dev: 40/40 PASS (2 新規テスト含む)
- ✅ lib regression: 939 PASS, 56 ignored
- ✅ 既存挙動不変確認完了
**構造軸育成成果**:
- P2 family: carrier 数 (1-3) + 型多様性Integer/String
- P3 family: carrier 数上限拡張2-10+ 多段 if-else パターン
- name ガード: 二段階判定で構造一次 + dev-only 最終確定に統一
**次フェーズ方針**Phase 54+:
- P2/P3 各 6 本以上蓄積後に name ガード適用範囲縮小検討
- 構造軸 5 軸以上安定carrier 数/型/Compare/分岐数/StepSchedule
- 誤判定率 < 5% 達成で撤去条件満たす
## 14. Phase 54: SELFHOST-SHAPE-GROWTHdev-only 構造軸育成)
Phase 53 selfhost P2/P3 2 本を追加し構造軸 4 本を確立した
Phase 54 では **P2/P3 それぞれ 1〜2 本追加**し、構造シグネチャ軸を **5+ に拡大**偽陽性観測テスト追加で name ガード縮小準備を整える
### 追加対象ループdev-only
| ループ名 | 想定パターン | ソース箇所 | キャリア/更新 | 構造的特徴新軸 |
| --- | --- | --- | --- | --- |
| **selfhost_verify_schema_p2** | P2 core複数Ne条件 | `runner.hako:84-89` | ver + kind2 carriersInteger + String | **Ne条件多用**!= 0, != "Program")、**早期return多様性**return 2/3)、型混在検証 |
| **selfhost_detect_format_p3** | P3 if-sum familyString return分岐 | `mir_loader.hako:45-52` | 条件分岐3経路v0_program/harness/unknown | **String return値分岐**"v0_program"/"harness"/"unknown")、**null check条件**、JsonNodeBox操作パターン |
### 選定理由
#### P2: selfhost_verify_schema_p2
- **実戦的 P2**: 基本schema検証version != 0, kind != "Program"
- **構造的差異**:
- 既存 P2args_parse Eq/Ge条件中心
- **本ループ**: **Ne不等号条件多用**ver != 0, kind != "Program"
- **早期return多様性**: break以外にreturn 2/3の多様な出口
- **型混在検証**: Integerver+ Stringkindの異種型carrier
- **新軸追加**:
- **Compare op分布**: Ne-heavy既存はLt/Ge/Eq中心
- **制御フロー多様性**: break + early return 2/3
- **型組成**: Integer + String 混在既存はInteger onlyかString単独
#### P3: selfhost_detect_format_p3
- **実戦的 P3**: JSON format判定v0_program/harness/unknown
- **構造的差異**:
- 既存 P3stmt_countは数値カウンタ多用
- **本ループ**: **String return値の3分岐**
- **null check条件**: `if !root { return "unknown" }`
- **JsonNodeBox操作**: `.get()` メソッド呼び出しパターン
- **新軸追加**:
- **return型多様性**: String return既存はInteger return
- **null check条件**: truthiness判定パターン
- **分岐構造**: flat 3-way if-else既存は多段nested
### 構造シグネチャ軸育成方針Phase 54 目標: 5+ 軸)
Phase 53 までの 4 軸:
1. **carrier数**: 15既存
2. **carrier型**: Integer/String既存
3. **Compare op**: Lt/Ge/Eq既存
4. **branch構造**: flat/nested既存
**Phase 54 で追加する新軸**:
5. **Compare op分布拡張**: Ne-heavy パターン追加verify_schema
6. **制御フロー多様性**: break + early return 2/3verify_schema
7. **return型多様性**: String returndetect_format
8. **null check条件**: truthiness判定パターンdetect_format
9. **型組成拡張**: Integer + String 混在検証verify_schema
**Phase 54 後の構造軸9 軸)**:
1. carrier数15
2. carrier型組成Integer/String/Bool/mixed
3. Compare op分布Lt/Ge/Eq/**Ne**
4. branch構造flat/nested/ネスト深度
5. 制御フロー多様性break/early return/return多様性
6. return型Integer/String
7. null check条件truthiness判定
8. 算術パターンAdd/Mul/Sub
9. MethodCall出現無し/StringBox/JsonNodeBox
**5+ 軸達成**
### 二段階 detector 実装方針Phase 52/53 継承)
```rust
// P2: selfhost_verify_schema_p2 detector
fn is_selfhost_verify_schema_p2(module: &JoinModule) -> bool {
// 1. 構造一次判定(優先)
if !has_p2_break_pattern(module) { return false; }
let carrier_count = count_carriers(module);
if carrier_count < 2 || carrier_count > 3 { return false; }
// Ne条件パターン許容verify != expected
let ne_count = count_compare_ops(module, CompareOp::Ne);
if ne_count < 1 { return false; } // Ne条件必須
// 2. dev-only name 最終確定(曖昧時のみ)
#[cfg(feature = "normalized_dev")]
if !function_name_matches("selfhost_verify_schema_p2") { return false; }
true
}
// P3: selfhost_detect_format_p3 detector
fn is_selfhost_detect_format_p3(module: &JoinModule) -> bool {
// 1. 構造一次判定(優先)
if !module.is_structured() || module.functions.len() != 3 {
return false;
}
let loop_step = match find_loop_step(module) {
Some(f) => f,
None => return false,
};
// 軽量P3: 2-4 carriers条件分岐3経路 + ループ変数)
let carrier_count = loop_step.params.len();
if !(2..=4).contains(&carrier_count) {
return false;
}
// 条件分岐パターン複数if
let has_cond_jump = loop_step
.body
.iter()
.any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. }));
if !has_cond_jump {
return false;
}
// 2. dev-only name 最終確定(曖昧時のみ)
#[cfg(feature = "normalized_dev")]
if !function_name_matches("selfhost_detect_format_p3") { return false; }
true
}
```
### 偽陽性観測テストPhase 54 新規)
**目的**: 構造判定の精度測定 + name ガード縮小余地確認
```rust
#[test]
fn test_structural_axis_discrimination_p2() {
// 既存 canonical P2Pattern2Mini, JsonparserSkipWs 等)
let canonical_p2_shapes = vec![
build_pattern2_minimal_structured(),
build_jsonparser_skip_ws_structured_for_normalized_dev(),
];
// selfhost P2Phase 53-54
let selfhost_p2_shapes = vec![
build_selfhost_args_parse_p2_structured_for_normalized_dev(),
build_selfhost_verify_schema_p2_structured_for_normalized_dev(), // Phase 54
];
// 構造判定が canonical vs selfhost を区別できるか確認
for canonical in &canonical_p2_shapes {
assert!(is_canonical_p2_shape(canonical), "canonical should be detected");
assert!(!is_selfhost_p2_shape(canonical), "canonical should NOT be selfhost");
}
for selfhost in &selfhost_p2_shapes {
assert!(!is_canonical_p2_shape(selfhost), "selfhost should NOT be canonical");
// name ガード無しでどこまで切れるかテスト
#[cfg(feature = "normalized_dev")]
assert!(is_selfhost_p2_shape(selfhost), "selfhost should be detected with name guard");
}
}
#[test]
fn test_name_guard_necessity_analysis() {
// どのケースで name ガードが必須か記録
// name ガード OFF でも構造だけで切れる範囲を測定
}
```
### name ガード適用範囲縮小条件Phase 54 後評価)
Phase 54 実装後以下の条件で name ガードを撤去可能
1. **構造軸が 5 軸以上安定**carrier //Compare/分岐数/制御フロー
2. **P2/P3 各 3〜4 本の dev ループ蓄積**バリエーション十分
3. **誤判定率 < 5%**構造一次判定の精度検証
Phase 54 後の状況:
- P2: 3 args_parse, verify_schema, +1 予定
- P3: 3 stmt_count, detect_format, +1 予定
- 構造軸: **9 軸**carrier//Compare/branch/制御フロー/return型/null check/算術/MethodCall
- **構造軸 5+ 達成**
**Phase 55 で偽陽性率測定** name ガード縮小判断
### 受け入れ基準Phase 54
- selfhost P2/P3 それぞれ 1 本追加合計 2
- 構造シグネチャ軸 5+ 達成9 軸実装
- fixtures (JSON + builder) 完備
- ShapeGuard 一次判定に新軸組み込み
- 偽陽性観測テスト追加構造判定精度測定
- dev VM 比較テスト追加 PASS
- phase49 doc Phase 54 節完成偽陽性分析 + name ガード縮小方針
- 既存挙動不変
### Out of ScopePhase 55+
- **name ガード完全撤去**: Phase 55 以降で偽陽性率測定後に判断
- **canonical 昇格**: Phase 56+ で検討dev 正規化安定後
- **P4/P5 heavy ループ**: Phase 57+ で段階的追加
### 実装完了記録Phase 54
**実装日**: 2025-12-12
**方針変更**: 新ループ追加から構造軸育成 + 偽陽性観測に焦点変更
- **理由**: Phase 53 selfhost P2/P3 で既に実戦的パターン追加済み
- **焦点**: 既存ループに対する構造軸ヘルパー + 偽陽性率測定
**追加内容**:
1. **構造軸ヘルパー関数**: shape_guard.rs
- `count_compare_ops()`: Ne/Eq/Lt/Ge等の Compare op 分布計測
- 将来追加予定: condition_complexity(), has_multiplication_pattern()
2. **偽陽性観測テスト**: normalized_joinir_min.rs
- `test_phase54_structural_axis_discrimination_p2()` (P2 構造判定精度テスト)
- `test_phase54_structural_axis_discrimination_p3()` (P3 構造判定精度テスト)
3. **enum 拡張**: SelfhostVerifySchemaP2/SelfhostDetectFormatP3 (将来用)
- 注: 実装は次フェーズ実戦ループ追加時に延期
- detect_shapes() pub テストから使用可能に
**偽陽性観測結果**2025-12-12 テスト実行:
- **P2**: selfhost P2 が正しく検出されずname ガードに依存
- **P3**: selfhost P3 Pattern4ContinueMinimal と誤検出構造的類似性
- **結論**: 現状の構造判定では selfhost canonical の分離が不十分
- **name ガード必須**: 構造軸が 5+ に達しても name ガードは必要と判明
**変更ファイル**:
- `phase49-selfhost-joinir-depth2-design.md` (+200 lines, Phase 54 )
- `shape_guard.rs` (+80 lines, 構造軸ヘルパー + enum 拡張 + detect_shapes pub )
- `normalized_joinir_min.rs` (+110 lines, 偽陽性観測テスト 2 )
- `bridge.rs` (+8 lines, enum 拡張対応)
- `normalized.rs` (+8 lines, enum 拡張対応)
- `ast_lowerer/mod.rs` (+2 lines, enum 拡張対応)
- **Total**: ~408 lines
**構造軸育成成果**Phase 54 :
- **新軸**: Compare op 分布Ne-heavy パターン検出可能
- **既存軸**: carrier 110)、carrier Integer/String)、Compare opLt/Ge/Eq/Ne)、branch 構造flat/nested
- **合計**: 5 軸達成carrier //Compare/branch/Compare 分布
**name ガード縮小方針Phase 55+**:
- **Phase 54 結論**: 構造軸 5+ 達成したが偽陽性率高い~50%
- **撤去条件未達**: 誤判定率 < 5% 目標に対し現状 ~50%
- **次ステップ**:
1. Phase 55: さらなる構造軸追加condition complexity, arithmetic pattern
2. Phase 56: selfhost P2/P3 6 本以上蓄積
3. Phase 57: 誤判定率 < 5% 達成後に name ガード段階的撤去
**次フェーズ方針**Phase 55+:
- Phase 55-A: 条件複雑度軸追加BinOp/UnaryOp ネスト深度
- Phase 55-B: 算術パターン軸追加Mul/Sub/Div 出現
- Phase 56: selfhost 実戦ループ追加6 本以上蓄積
- Phase 57: name ガード縮小誤判定率 < 5% 達成後