Files
hakorune/docs/development/current/main/phase80-bindingid-p3p4-plan.md
nyash-codex 9e32807a96 refactor(joinir): Phase 82-83 - Debug flag SSOT + Fallback verification
Phase 82: Centralized JoinIR debug flag reading
- Added is_joinir_debug() SSOT function in joinir_flags.rs
- Replaced 16 direct env::var() calls across 8 files
- Updated docs to recommend HAKO_JOINIR_DEBUG (NYASH_ deprecated)
- Backward compat: Both env vars work

Phase 83: Verified promoted carrier fallback behavior
- Confirmed NO fallback to name-based lookup for DigitPos/Trim
- Documented fallback expectations in Phase 80/81 docs
- Added verification commands and expected output

Changes:
- src/config/env/joinir_flags.rs: +187 lines (new SSOT module)
- 8 files: env var reads → is_joinir_debug() calls
- 3 docs: HAKO_JOINIR_DEBUG examples + fallback sections
- 1 summary doc: phase82-83-debug-flag-ssot-summary.md

Tests: 970/970 lib PASS, 58/58 normalized_dev PASS
Impact: Dev-only (zero production changes)
2025-12-13 19:01:14 +09:00

284 lines
10 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 80: BindingId P3/P4 Expansion Plan
**Status**: Completed (commit `84129a7e`)
**Created**: 2025-12-13
**Progress**:
- ✅ Task 80-0: Current Status Verification (complete)
- ✅ Task 80-A: SSOT Design Doc (complete)
- ✅ Task 80-B: Pattern3 BindingId Wiring (dev-only)
- ✅ Task 80-C: Pattern4 BindingId Wiring (dev-only)
- ✅ Task 80-D: E2E Tests (P3/P4, dev-only)
---
## Task 80-0: Current Status Verification
### Test Results
#### 1. Full lib test suite
```bash
cargo test --release --lib
```
**Result**: ✅ **PASS** - 970 passed; 0 failed; 56 ignored
**Interpretation**: All production code is stable, Phase 79 changes are production-safe.
---
#### 2. normalized_dev tests
```bash
NYASH_JOINIR_NORMALIZED_DEV_RUN=1 RUST_TEST_THREADS=1 cargo test --features normalized_dev --test normalized_joinir_min -- --nocapture
```
**Result**: ✅ **PASS**Phase 79 の “AST直組み E2E” は Normalized SSOT から外し、別途 Phase 80-D で整理予定)
---
#### 3. Quick smoke tests
```bash
tools/smokes/v2/run.sh --profile quick --filter "json_pp_vm"
```
**Result**: ✅ **PASS** - Representative smoke test passes
**Details**:
- `json_pp_vm`: ✅ PASS (.005s)
- `json_lint_vm`: ❌ FAIL (pre-existing, unrelated to Phase 79/80)
- Error: "normalized Pattern2 verifier: function 'main' does not end with tail call/if"
- Also fails on clean Phase 79 commit `b7f78825`
- Classification: **Out of scope** for Phase 80
**Interpretation**: Core VM functionality stable, Phase 79/80 changes don't break smoke tests
---
## Decision: Defer Pattern2 E2EDigitPos/Trimuntil ExitLine contract is stabilized
Phase 80 の主目的は Pattern3/4 の BindingId 配線なので、Pattern2 の E2E は Phase 81+ に回し、
さらに “promoted carriersDigitPos/Trimを含む Pattern2 の ExitLine 接続” が安定してから固定する。
## Task 80-0: Summary
**Status**: ✅ **COMPLETE**
**Key Findings**:
1.**Production code stable** - 970/970 lib tests PASS
2.**Phase 79 changes production-safe** - No regressions in lib tests
3.**Core VM functionality stable** - Smoke tests pass
4. ⚠️ **Pattern2DigitPos/TrimE2E は保留** - promoted carriers を含む ExitLine 契約の安定化後に固定する
5.**Pre-existing smoke test failure** - `json_lint_vm` fails (unrelated)
**Classification**:
- **Phase 80 blockers**: NONE
- **Phase 80 out-of-scope**:
- Pattern2DigitPos/Trimpromoted carriers の ExitLine 契約安定化Phase 81+
- json_lint_vm smoke test failure (pre-existing Pattern2 verifier issue)
**Decision**: ✅ **Proceed to Task 80-A**
**Justification**:
- Production code is stable and safe
- Dev-only test failures are **isolated** and **documented**
- Phase 80-B/C を先に進め、E2E80-Dは Pattern2 契約の整備後に固定する
---
## Next Steps
✅ Task 80-0 complete → Proceed to Task 80-A (Phase 80 SSOT design doc)
---
## Task 80-A: SSOT Design Doc
**Goal**: Document Pattern3/4 BindingId wiring strategy BEFORE implementation
**Status**: ✅ **COMPLETE**
### Phase 80 Scope
**In Scope**:
1. Pattern3 (if-sum) BindingId registration at ValueId determination point
2. Pattern4 (continue/Trim) BindingId registration at ValueId determination point
3. E2E verification tests (2 tests: P3 + P4)
4. Fallback detection capability (existing `[binding_pilot/fallback]` tags)
**Out of Scope** (deferred to Phase 81+):
- Name fallback removal (dual-path stays)
- BindingId mandatory enforcement
- Pattern1 Minimal (already has structural detection, doesn't use carriers)
- by-name rule branching (prohibited by invariant)
---
### Design Principles (unchanged from Phase 74-79)
1. **Dev-only**: All code `#[cfg(feature = "normalized_dev")]` or `#[cfg(debug_assertions)]`
2. **Dual-path maintained**: BindingId priority + name fallback (no removal yet)
3. **Structural detection only**: NO by-name rule branching
4. **Fail-Fast**: Explicit errors with tags, no silent fallbacks
5. **Zero production impact**: All changes gated, 970/970 lib tests must PASS
---
### Pattern3 (if-sum) BindingId Wiring Strategy
**Entry point**: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
**Key function**: `lower_pattern3_if_sum()`
**ConditionEnv creation**: `ConditionEnvBuilder` 生成直後(`lower_if_sum_pattern()` に渡す前)
**BindingId registration points**:
1. **Loop var registration**dev-only:
```rust
#[cfg(feature = "normalized_dev")]
if let Some(loop_var_bid) = builder.binding_map.get(&loop_var_name).copied() {
cond_env.register_loop_var_binding(loop_var_bid, loop_var_join_id);
eprintln!("[phase80/p3] Registered loop var '{}' BindingId({}) -> ValueId({})",
loop_var_name, loop_var_bid.0, loop_var_join_id.0);
}
```
2. **Condition bindings registration**dev-only:
```rust
#[cfg(feature = "normalized_dev")]
for binding in &inline_boundary.condition_bindings {
if let Some(bid) = builder.binding_map.get(&binding.name).copied() {
cond_env.register_condition_binding(bid, binding.join_value);
eprintln!(
"[phase80/p3] Registered condition binding '{}' BindingId({}) -> ValueId({})",
binding.name, bid.0, binding.join_value.0
);
}
}
```
**Timing**: BEFORE condition lowering
**Note (重要)**:
- Pattern3/4 は lowering の後段ExitMeta / merge 側)で carrier join_id が確定するため、Phase 80 では **carriers の BindingId 登録はしない**。
- ここでの目的は「条件 loweringlookup_with_binding」の経路を先に BindingId 化して、shadowing の破綻を防ぐこと。
---
### Pattern4 (continue/Trim) BindingId Wiring Strategy
**Entry point**: `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
**Key function**: `cf_loop_pattern4_with_continue()`
**ConditionEnv creation**: `lower_loop_with_continue_minimal()` 内
**BindingId registration points**:
1. **Caller side**dev-only: `binding_map` を lowerer に渡す
2. **Lowerer side**dev-only: join_id が確定したタイミングで
- loop var / condition bindings / carriers を BindingId 登録する
- ログは `[phase80/p4]` タグで可視化する
**Special note**: Trim patterns (skip_whitespace) use promoted carriers, BindingId comes from `promoted_bindings` map via CarrierBindingAssigner
**Timing**: ValueId allocation の直後condition lowering の前)
---
### Fallback Detection Mechanism
**Existing infrastructure** (no changes needed):
- `ConditionEnv::lookup_with_binding()` - tries BindingId first, logs `[binding_pilot/hit]`
- `ConditionEnv::lookup_or_fallback()` - fallback to name, logs `[binding_pilot/fallback]`
**Detection strategy**:
1. Run tests with `HAKO_JOINIR_DEBUG=1` (or legacy `NYASH_JOINIR_DEBUG=1`)
2. Check for `[binding_pilot/hit]` tags (BindingId path success)
3. Check for NO `[binding_pilot/fallback]` tags (name fallback NOT used)
4. If fallback occurs → test fails with diagnostic
**Note (Phase 82)**: Both `HAKO_JOINIR_DEBUG` and `NYASH_JOINIR_DEBUG` are supported.
Recommended: Use `HAKO_JOINIR_DEBUG=1` (NYASH_ variant is deprecated but still works)
**Acceptance criteria** (Task 80-D):
- P3 test: BindingId hit, NO fallback
- P4 test: BindingId hit, NO fallback
**Fallback Behavior Documentation (Phase 83)**:
**Expected**: Promoted carriers should ALWAYS use BindingId path, never fallback
- DigitPos carriers (`is_digit_pos`): ✅ BindingId hit only
- Trim carriers (`is_ch_match`): ✅ BindingId hit only
- Loop variables in P3/P4: ✅ BindingId hit only
**Verification**:
```bash
# Phase 80 tests verify BindingId resolution works (no runtime errors)
cargo test --features normalized_dev --test normalized_joinir_min test_phase80_p3_bindingid_lookup_works
cargo test --features normalized_dev --test normalized_joinir_min test_phase80_p4_bindingid_lookup_works
# Phase 81 tests verify ExitLine contract (promoted carriers handled correctly)
cargo test --features normalized_dev --test normalized_joinir_min test_phase81_digitpos_exitline_contract
cargo test --features normalized_dev --test normalized_joinir_min test_phase81_trim_exitline_contract
```
**Debug Tags** (dev-only, during MIR compilation):
- `[binding_pilot/hit]`: BindingId lookup succeeded ✅ (expected)
- `[binding_pilot/fallback]`: Name-based fallback occurred ❌ (should NOT appear for promoted carriers)
- `[binding_pilot/legacy]`: No BindingId provided, using name (legacy code paths only)
**Status (Phase 83)**: All Phase 80/81 tests PASS, indicating NO fallback to name-based lookup for promoted carriers.
---
### Implementation Order
1. ✅ Task 80-A: Design doc (this section)
2. ✅ Task 80-B: Pattern3 wiringloop var + condition bindings
3. ✅ Task 80-C: Pattern4 wiringlowerer 側で登録)
4. ✅ Task 80-D: E2E testsP3 1本 / P4 1本, `tests/normalized_joinir_min.rs`
---
### Success Metrics
- [x] P3: 条件 lowering が BindingId 経路で解決できるE2Eテストで固定
- [x] P4: 条件 lowering が BindingId 経路で解決できるE2Eテストで固定
- [x] Fallback 検知(`[binding_pilot/fallback]` をテストで検知できる)
- [x] `cargo test --release --lib` が PASSproduction 影響なし)
- [x] 追加コードは dev-only`normalized_dev`)に閉じている
---
## Goal (unchanged from Phase 80 spec)
- Pattern3 (if-sum) の条件 lowering で lookup_with_binding() が効く
- Pattern4 (continue/skip_ws) の条件 lowering で lookup_with_binding() が効く
- Fallback 監視: name fallback に落ちたら分かる仕掛け(既存ログタグ活用)
## Non-Goal
- Name fallback の撤去はまだPhase 81+ で対応)
- BindingId 完全義務化はまだdual-path 維持)
## Invariant
- by-name ルール分岐禁止structural detection のみ)
- binding_id_map 登録は「ValueId が確定した時点」のみ
- promoted が絡む場合は CarrierVar.binding_id / promoted_bindings を経由
## Implementation Tasks (pending Task 80-0 completion)
1. Pattern3 (if-sum) BindingId 登録配線 ✅
2. Pattern4 (continue) BindingId 登録配線 ✅
3. E2E tests (P3 1本 / P4 1本) ✅
## Success Metrics
- P3/P4 代表ケースで lookup_with_binding() 経路がヒット(ログ or テスト)
- Fallback 検知可能(既存 `[binding_pilot/fallback]` タグ活用)
- `cargo test --release --lib` PASS退行なし