From 82b0a87599c8d8790100b9af801b04fa16bef9e8 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Mon, 29 Dec 2025 08:55:17 +0900 Subject: [PATCH] phase29ai(p8): try planner before legacy pattern6 --- docs/development/current/main/10-Now.md | 7 +- docs/development/current/main/30-Backlog.md | 2 +- ...ER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md | 112 ++++++++++++++++++ .../current/main/phases/phase-29ai/README.md | 5 + .../control_flow/plan/single_planner/rules.rs | 20 +++- 5 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 docs/development/current/main/phases/phase-29ai/P9-PLANNER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index 36dddd62..64bdd513 100644 --- a/docs/development/current/main/10-Now.md +++ b/docs/development/current/main/10-Now.md @@ -2,7 +2,12 @@ ## Current Focus: Phase 29ai(Plan/Frag single-planner) -Next: `docs/development/current/main/phases/phase-29ai/P8-WIRE-PLANNER-INTO-SINGLE_PLANNER-PATTERN6-INSTRUCTIONS.md` +Next: `docs/development/current/main/phases/phase-29ai/P9-PLANNER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md` + +**2025-12-29: Phase 29ai P8 完了** ✅ +- 目的: Facts→Planner を実行経路へ 1 歩だけ接続し、Pattern6(scan-with-init)の subset から吸収を開始(仕様不変) +- 実装: `src/mir/builder/control_flow/plan/single_planner/rules.rs`(Pattern6 rule の先頭で planner を試す) +- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` PASS **2025-12-29: Phase 29ag P1 完了** ✅ - 目的: coordinator の ValueId(idx) 前提を撤去し、boundary.join_inputs を SSOT 化(仕様不変) diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index 02a736b3..b88ae616 100644 --- a/docs/development/current/main/30-Backlog.md +++ b/docs/development/current/main/30-Backlog.md @@ -19,7 +19,7 @@ Related: - **Phase 29ai(candidate): Plan/Frag single-planner(Facts SSOT)** - 入口: `docs/development/current/main/phases/phase-29ai/README.md` - - Next: P8(Wire planner into single_planner: Pattern6 subset) + - Next: P9(Planner: Pattern7 split-scan subset) - **Phase 29ae P1(✅ COMPLETE): JoinIR Regression Pack (SSOT固定)** - 入口: `docs/development/current/main/phases/phase-29ae/README.md` diff --git a/docs/development/current/main/phases/phase-29ai/P9-PLANNER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md b/docs/development/current/main/phases/phase-29ai/P9-PLANNER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md new file mode 100644 index 00000000..8d8c3aeb --- /dev/null +++ b/docs/development/current/main/phases/phase-29ai/P9-PLANNER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md @@ -0,0 +1,112 @@ +# Phase 29ai P9: Planner support + wiring(Pattern7 split-scan subset) + +Date: 2025-12-29 +Status: Ready for execution +Scope: Facts→Planner→DomainPlan を Pattern7(SplitScan)へ拡張し、single_planner で planner-first を開始する +Goal: 「pattern名で入口分岐」ではなく「Facts→Plan」の 1 本導線に寄せつつ、既定挙動は不変のまま前進する + +## Objective + +Pattern7(split-scan)の最小ケースについて、Facts が `Ok(Some(...))` を返し、planner が `Ok(Some(DomainPlan::SplitScan))` +まで到達できるようにする。そのうえで `single_planner` の Pattern7 rule でも planner-first を開始し、legacy へは `Ok(None)` +時のみフォールバックする。 + +## Non-goals(この P9 ではやらない) + +- 仕様変更(挙動変更・ログ増加・エラーメッセージ変更) +- Pattern2 などの大物の移植(Phase 29ai P10+ へ) +- Freeze を「実行経路で出す」こと(この P9 は `Ok(None)` 保守で進める) +- plan/extractors の削除(legacy の受け口は残す) + +## Target Fixture(SSOT) + +以下の既存 fixture/smoke を “SplitScan subset” の SSOT として使う: + +- Fixture: `apps/tests/phase29ab_pattern7_splitscan_ok_min.hako` +- Smoke: `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern7_splitscan_ok_min_vm.sh` +- Regression pack: `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` + +## Implementation Steps(Critical Order) + +### Step 1: Facts の最小対応(SplitScanFacts) + +ファイル: +- `src/mir/builder/control_flow/plan/facts/loop_facts.rs` +- `src/mir/builder/control_flow/plan/facts/scan_shapes.rs`(必要なら) + +やること: +- `LoopFacts` に `split_scan: Option` を追加。 +- `try_build_loop_facts()` は Pattern6 と同様に “保守的に Some を返す” 方針で、SplitScan 最小形だけ `Some` を返す。 +- 最小形の抽出は **AST の変数名が確定できる場合のみ**(推測や固定名は禁止)。 + +最低限抽出したい変数(`SplitScanPlan` に必要): +- `s_var`, `sep_var`, `result_var`, `i_var`, `start_var` + +注意: +- 抽出が不完全なら `Ok(None)`(Freeze は出さない)。 +- 既存 Pattern7 extractor(`plan/extractors/pattern7_split_scan.rs`)の形と齟齬が出そうなら、まずはその extractor が要求する + 最小セットに合わせる(この P9 では新たな shape 語彙を増やしすぎない)。 + +### Step 2: Planner の候補生成(DomainPlan::SplitScan) + +ファイル: +- `src/mir/builder/control_flow/plan/planner/build.rs` + +やること: +- `facts.facts.split_scan` が `Some` のときに `DomainPlan::SplitScan(SplitScanPlan { ... })` を CandidateSet に push する。 +- 0/1/2+ の境界は CandidateSet の finalize に委譲し、planner 側は “候補を積むだけ” に徹する。 + +注意: +- P9 時点では `SplitScanFacts` は “最小 OK fixture のみ Some” に抑えて、誤マッチで 2+ にならないようにする。 + +### Step 3: unit test で Facts/Planner の境界を固定 + +ファイル: +- `src/mir/builder/control_flow/plan/facts/loop_facts.rs`(`#[cfg(test)]`) +- (必要なら)`src/mir/builder/control_flow/plan/planner/build.rs`(unit test) + +やること: +- Facts: “OK minimal っぽい AST” で `Ok(Some(...))` を返すテスト。 +- Facts: “明らかに違う AST” で `Ok(None)` を返すテスト。 +- Planner: Facts→Planner で `Ok(Some(DomainPlan::SplitScan))` になるテスト(直で `build_plan_from_facts` を呼ぶ形式で OK)。 + +### Step 4: single_planner の Pattern7 も planner-first に + +ファイル: +- `src/mir/builder/control_flow/plan/single_planner/rules.rs` + +やること: +- Pattern6 と同様に、Pattern7 rule の先頭で `planner::build_plan(ctx.condition, ctx.body)` を試す。 +- `Ok(Some(plan))` なら採用。 +- `Ok(None)` なら `legacy_rules::pattern7::extract(ctx)` にフォールバック。 +- planner の `Ok(None)` では新規ログは出さない(観測差分を抑える)。 + +### Step 5: Docs / Tracking 更新(SSOT) + +ファイル: +- `docs/development/current/main/phases/phase-29ai/README.md` +- `docs/development/current/main/10-Now.md` +- `docs/development/current/main/30-Backlog.md` + +やること: +- P9 完了の記録(目的/影響/検証コマンド)。 +- Next を P10(例: Pattern2 extractor → Facts/Planner への段階移植)に向けて 1 行だけ書く。 + +## Verification Checklist(Acceptance) + +- `cargo build --release` +- `./tools/smokes/v2/run.sh --profile quick` +- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` + +期待: +- 0 errors +- quick 154/154 PASS +- regression pack PASS +- 既定挙動は不変(planner が `Ok(None)` を返す領域は legacy 経路へ落ちる) + +## Risk Notes + +- **誤マッチ**: Facts は “最小形だけ Some” を徹底し、疑わしい場合は `Ok(None)` に倒す。 +- **観測差分**: planner `Ok(None)` でログを出さない。採用時も既存の pattern 名ログを維持する。 +- **Freeze**: P9 は Freeze を実行経路に出さない。Freeze taxonomy の投入は P10+ で段階的に行う。 + diff --git a/docs/development/current/main/phases/phase-29ai/README.md b/docs/development/current/main/phases/phase-29ai/README.md index e6ffe132..1973a260 100644 --- a/docs/development/current/main/phases/phase-29ai/README.md +++ b/docs/development/current/main/phases/phase-29ai/README.md @@ -49,6 +49,11 @@ Goal: pattern 名による分岐を外部APIから消し、Facts(事実)→ - 指示書: `docs/development/current/main/phases/phase-29ai/P8-WIRE-PLANNER-INTO-SINGLE_PLANNER-PATTERN6-INSTRUCTIONS.md` - ねらい: Facts→Planner を実行経路へ1歩だけ接続し、Pattern6最小ケースから吸収を開始(仕様不変) +## P9: Planner support + wiring(Pattern7 split-scan subset) + +- 指示書: `docs/development/current/main/phases/phase-29ai/P9-PLANNER-PATTERN7-SPLITSCAN-WIRE-INSTRUCTIONS.md` +- ねらい: Pattern7(split-scan)の最小ケースを Facts→Planner で `Ok(Some(DomainPlan::SplitScan))` まで到達させ、single_planner で planner-first を開始(仕様不変を維持しつつ段階吸収) + ## Verification (SSOT) - `cargo build --release` diff --git a/src/mir/builder/control_flow/plan/single_planner/rules.rs b/src/mir/builder/control_flow/plan/single_planner/rules.rs index 56e65d68..d6b0e8cd 100644 --- a/src/mir/builder/control_flow/plan/single_planner/rules.rs +++ b/src/mir/builder/control_flow/plan/single_planner/rules.rs @@ -7,6 +7,7 @@ use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternCont use crate::mir::loop_pattern_detection::LoopPatternKind; use super::legacy_rules; +use crate::mir::builder::control_flow::plan::planner; use crate::mir::builder::control_flow::plan::DomainPlan; pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result, String> { @@ -62,11 +63,18 @@ pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result extract(ctx), - RuleKind::Pattern6 => legacy_rules::pattern6::extract(ctx), - RuleKind::Pattern7 => legacy_rules::pattern7::extract(ctx), - }?; + let (plan_opt, log_none) = match entry.kind { + RuleKind::Simple(extract) => (extract(ctx)?, true), + RuleKind::Pattern6 => { + let from_planner = planner::build_plan(ctx.condition, ctx.body) + .map_err(|freeze| freeze.to_string())?; + match from_planner { + Some(plan) => (Some(plan), false), + None => (legacy_rules::pattern6::extract(ctx)?, true), + } + } + RuleKind::Pattern7 => (legacy_rules::pattern7::extract(ctx)?, true), + }; if let Some(domain_plan) = plan_opt { // Phase 286 P3: Pattern8 static box filtering @@ -87,7 +95,7 @@ pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result