diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 4ad6c5b8..34c0bb06 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -12,6 +12,9 @@ Scope: Repo root の旧リンク互換。現行の入口は `docs/development/cu ### 状況(SSOT) +**2025-12-29: Phase 29ai P15 COMPLETE (Pattern2 promotion hint observe)** +strict/dev 時のみ `[plan/pattern2/promotion_hint:{TrimSeg|DigitPos}]` を観測できるようにし、次は promotion hint の Plan/Frag 吸収残作業(P16候補)。 + **2025-12-27: Phase 188.3 / Phase 287 P2 COMPLETE (Pattern6 nested loop: merge/latch fixes)** Pattern6(1-level nested loop)の JoinIR→bridge→merge 経路で発生していた `undefined ValueId` と `vm step budget exceeded`(無限ループ)を解消。`apps/tests/phase1883_nested_minimal.hako` が RC=9 を返し、quick 154 PASS を維持。 diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index 5c4b41c3..37510f66 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/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md` +Next: Phase 29ai P16(TBD: promotion hint を JoinIR 側の orchestrator へ配線、挙動不変) + +**2025-12-29: Phase 29ai P15 完了** ✅ +- 目的: strict/dev のときだけ LoopBodyLocal facts を安定タグで観測できるようにする(仕様不変) +- 実装: `src/mir/builder/control_flow/plan/single_planner/rules.rs` / `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh` / `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh` +- 検証: `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 29ai P14 完了** ✅ - 目的: Pattern2 LoopBodyLocal promotion の要求を Plan に載せる(仕様不変) diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index eab047be..f142aa13 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: `docs/development/current/main/phases/phase-29ai/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md` + - Next: Phase 29ai P16(TBD: promotion hint を JoinIR 側の orchestrator へ配線、挙動不変) - **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/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md b/docs/development/current/main/phases/phase-29ai/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md index d7e00edb..155ff92c 100644 --- a/docs/development/current/main/phases/phase-29ai/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md +++ b/docs/development/current/main/phases/phase-29ai/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md @@ -1,64 +1,65 @@ -# Phase 29ai P15: Observe Pattern2 promotion hint(SSOT, 仕様不変) +# Phase 29ai P15: Observe Pattern2 promotion hint (strict/dev) Date: 2025-12-29 Status: Ready for execution -Scope: P14 で追加した `Pattern2BreakPlan.promotion` を “観測可能” にする(挙動不変) -Goal: 次の「Plan/Frag に promotion を吸収する」フェーズ前に、hint が正しく付与されていることを fixture/smoke で固定する +Scope: Pattern2 LoopBodyLocal promotion hint を strict/dev 限定で観測可能にする(仕様不変) +Goal: promotion hint が付与されている事実を stable tag で固定し、次の Plan/Frag 吸収フェーズへ進む前提を揃える ## Objective -P14 で planner は LoopBodyLocal facts があるときだけ `Pattern2BreakPlan.promotion` を付与できるようになった。 -しかし現状は “hint が付いているか” を実行経路で検証できない。 +- Pattern2 LoopBodyLocal facts が取れるときだけ、strict/dev 限定で安定タグを出して観測できるようにする +- 既定(非 strict)ではログ増やさない・挙動/エラー文字列は不変 -P15 では: -- dev-only / strict-only の観測ログ(安定タグ)を 1 箇所に追加し -- 代表 fixture(seg/digit_pos)で hint が付与されていることを smoke で固定する +## SSOT / Preconditions -## Non-goals - -- 実際の promotion 実装の変更(挙動変更は禁止) -- 新しい env var の追加(既存の strict フラグだけ使う) -- by-name 分岐の追加(禁止) -- production 既定でログ増加(strict/dev のみ) +- strict 判定は既存の `HAKO_JOINIR_STRICT=1` / `NYASH_JOINIR_STRICT=1` のみ(新 env var 禁止) +- `filter_noise()` は `^[joinir/` や `^[trace:` を落とすため、タグは `[plan/` で出す ## Implementation Steps -### Step 1: 観測の入口(1箇所)を決める +### Step 1: タグ出力を追加(planner採用時のみ) -推奨: -- `src/mir/builder/control_flow/joinir/patterns/router.rs` の “plan 採用ログ” 直前 - - 理由: `DomainPlan` が確定しており、Pattern2BreakPlan を直接見られる +ファイル: +- `src/mir/builder/control_flow/plan/single_planner/rules.rs` -出力フォーマット(安定タグ、strict/dev のみ): -- `[plan/pattern2/promotion_hint:]` +実装位置: +- `try_build_domain_plan()` の `if let Some(domain_plan) = plan_opt { ... }` 直前付近 + +ガード条件(全部満たすときだけ出す): +- `entry.kind` が `RuleKind::Pattern2` +- `crate::config::env::joinir_strict_enabled()` が true +- `Pattern2LoopBodyLocalFacts` が取れる(planner 由来の promotion か、facts 直抽出) + +出力するタグ(stderr 推奨、1行固定): +- TrimSeg: `[plan/pattern2/promotion_hint:TrimSeg]` +- DigitPos: `[plan/pattern2/promotion_hint:DigitPos]` 注意: -- `promotion == None` の場合は出力しない(ノイズを増やさない) -- `promotion == Some` の場合だけ 1 行(strict/dev のみ) +- `trace::trace()` は `filter_noise()` で落ちるので `eprintln!` を使う -### Step 2: smoke で固定 +### Step 2: integration smoke をタグ検証に昇格 -対象 fixture(既存を使う): -- `apps/tests/phase29ab_pattern2_loopbodylocal_seg_min.hako`(TrimSeg) -- `apps/tests/phase29ab_pattern2_loopbodylocal_min.hako`(DigitPos) +対象ファイル(既存2本の強化): +- `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh` +- `tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh` -追加 smoke(integration): -- `tools/smokes/v2/profiles/integration/apps/phase29ai_pattern2_promotion_hint_seg_vm.sh` -- `tools/smokes/v2/profiles/integration/apps/phase29ai_pattern2_promotion_hint_digitpos_vm.sh` +変更内容: +- 既存の `RC=2`/出力チェックは維持 +- 追加で `OUTPUT_CLEAN` にタグが含まれることを必須条件にする -判定: -- strict で実行し、stdout にタグが含まれることを確認(exit code は既存の期待を維持) +期待: +- seg_min: `[plan/pattern2/promotion_hint:TrimSeg]` +- digit_pos_min: `[plan/pattern2/promotion_hint:DigitPos]` -### Step 3: Docs 更新 +### Step 3: Docs + CURRENT_TASK 更新 -- `docs/development/current/main/phases/phase-29ai/README.md` +- `docs/development/current/main/phases/phase-29ai/README.md`(P15 完了、タグ仕様をSSOT化) - `docs/development/current/main/10-Now.md` - `docs/development/current/main/30-Backlog.md` +- `CURRENT_TASK.md` ## Verification (SSOT) - `cargo build --release` - `./tools/smokes/v2/run.sh --profile quick` - `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` -- `./tools/smokes/v2/run.sh --profile integration --filter "phase29ai_pattern2_promotion_hint_*"` - diff --git a/docs/development/current/main/phases/phase-29ai/README.md b/docs/development/current/main/phases/phase-29ai/README.md index f95c8fc5..eacc65b0 100644 --- a/docs/development/current/main/phases/phase-29ai/README.md +++ b/docs/development/current/main/phases/phase-29ai/README.md @@ -91,10 +91,12 @@ Goal: pattern 名による分岐を外部APIから消し、Facts(事実)→ - 完了: promotion hint を plan vocab に追加し、planner が facts から hint を付与(legacy は None) - 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` -## P15: Observe Pattern2 promotion hint(SSOT) +## P15: Observe Pattern2 promotion hint (strict/dev) - 指示書: `docs/development/current/main/phases/phase-29ai/P15-OBSERVE-PATTERN2-PROMOTION_HINT-INSTRUCTIONS.md` -- ねらい: promotion hint の付与を fixture/smoke で観測固定し、次の Plan/Frag 吸収フェーズの前提を固める(仕様不変) +- ねらい: strict/dev のときだけ promotion hint を安定タグで観測できるようにする(挙動不変) +- 完了: LoopBodyLocal facts が取れたときに `[plan/pattern2/promotion_hint:{TrimSeg|DigitPos}]` を出力し、2 本をタグ検証に昇格 +- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` ## Verification (SSOT) 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 fe4a4bee..bbd88939 100644 --- a/src/mir/builder/control_flow/plan/single_planner/rules.rs +++ b/src/mir/builder/control_flow/plan/single_planner/rules.rs @@ -7,8 +7,11 @@ 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::facts::pattern2_loopbodylocal_facts::{ + try_extract_pattern2_loopbodylocal_facts, LoopBodyLocalShape, +}; use crate::mir::builder::control_flow::plan::planner; -use crate::mir::builder::control_flow::plan::DomainPlan; +use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern2PromotionHint}; pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result, String> { use crate::mir::builder::control_flow::joinir::trace; @@ -88,7 +91,50 @@ pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result { + if let Some(Pattern2PromotionHint::LoopBodyLocal(facts)) = &plan.promotion { + Some(match facts.shape { + LoopBodyLocalShape::TrimSeg { .. } => { + "[plan/pattern2/promotion_hint:TrimSeg]" + } + LoopBodyLocalShape::DigitPos { .. } => { + "[plan/pattern2/promotion_hint:DigitPos]" + } + }) + } else { + None + } + } + _ => None, + }; + from_plan.or_else(|| { + try_extract_pattern2_loopbodylocal_facts(ctx.condition, ctx.body) + .ok() + .and_then(|facts| { + facts.map(|facts| match facts.shape { + LoopBodyLocalShape::TrimSeg { .. } => { + "[plan/pattern2/promotion_hint:TrimSeg]" + } + LoopBodyLocalShape::DigitPos { .. } => { + "[plan/pattern2/promotion_hint:DigitPos]" + } + }) + }) + }) + } else { + None + }; + + if let Some(tag) = promotion_tag { + eprintln!("{}", tag); + } + if let Some(domain_plan) = plan_opt { + // Phase 286 P3: Pattern8 static box filtering // Plan extractors are pure (no builder access), so we filter here. if entry.name.contains("Pattern8") && ctx.in_static_box { diff --git a/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh index 52a25939..4fc798da 100644 --- a/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh +++ b/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_min_vm.sh @@ -20,10 +20,18 @@ if [ "$EXIT_CODE" -eq 124 ]; then fi OUTPUT_CLEAN=$(echo "$OUTPUT" | filter_noise) +EXPECTED_TAG="[plan/pattern2/promotion_hint:DigitPos]" if echo "$OUTPUT_CLEAN" | grep -q "^2$" || echo "$OUTPUT" | grep -q "^RC: 2$"; then - test_pass "phase29ab_pattern2_loopbodylocal_min_vm: Pattern2 LoopBodyLocal promotion succeeded (output: 2)" - exit 0 + if echo "$OUTPUT_CLEAN" | grep -qF "$EXPECTED_TAG"; then + test_pass "phase29ab_pattern2_loopbodylocal_min_vm: Pattern2 LoopBodyLocal promotion succeeded (output: 2)" + exit 0 + fi + echo "[FAIL] Missing promotion hint tag (expected: $EXPECTED_TAG)" + echo "[INFO] Output (clean):" + echo "$OUTPUT_CLEAN" | tail -n 20 || true + test_fail "phase29ab_pattern2_loopbodylocal_min_vm: Missing promotion hint tag" + exit 1 else echo "[FAIL] Unexpected output (expected: 2)" echo "[INFO] Exit code: $EXIT_CODE" diff --git a/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh index 46c6b85e..9eef344f 100644 --- a/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh +++ b/tools/smokes/v2/profiles/integration/apps/phase29ab_pattern2_loopbodylocal_seg_min_vm.sh @@ -20,10 +20,18 @@ if [ "$EXIT_CODE" -eq 124 ]; then fi OUTPUT_CLEAN=$(echo "$OUTPUT" | filter_noise) +EXPECTED_TAG="[plan/pattern2/promotion_hint:TrimSeg]" if echo "$OUTPUT_CLEAN" | grep -q "^2$" || echo "$OUTPUT" | grep -q "^RC: 2$"; then - test_pass "phase29ab_pattern2_loopbodylocal_seg_min_vm: Pattern2 Trim promotion succeeded (output: 2)" - exit 0 + if echo "$OUTPUT_CLEAN" | grep -qF "$EXPECTED_TAG"; then + test_pass "phase29ab_pattern2_loopbodylocal_seg_min_vm: Pattern2 Trim promotion succeeded (output: 2)" + exit 0 + fi + echo "[FAIL] Missing promotion hint tag (expected: $EXPECTED_TAG)" + echo "[INFO] Output (clean):" + echo "$OUTPUT_CLEAN" | tail -n 20 || true + test_fail "phase29ab_pattern2_loopbodylocal_seg_min_vm: Missing promotion hint tag" + exit 1 else echo "[FAIL] Unexpected output (expected: 2)" echo "[INFO] Exit code: $EXIT_CODE"