Files
hakorune/docs/development/current/main/phases/phase-121

Phase 121: StepTree→Normalized Shadow Lowering (if-only, dev-only)

Status: COMPLETE Date: 2025-12-18 Scope: if-only patterns (no loops)

Overview

Phase 121 establishes a minimal shadow lowering route from StepTree (structure SSOT) to Normalized JoinIR form, with parity verification against the existing router for if-only patterns.

Objectives

  • Create dev-only shadow lowering path: StepTree → JoinModule (Normalized)
  • Verify parity with existing router (exit contracts + writes)
  • Maintain default behavior (dev-only, no production impact)
  • Establish foundation for future Normalized migration

Implementation

P0: Design Documentation

Commit: 8d930d2dc - "docs: Phase 121 StepTree→Normalized shadow plan"

Added comprehensive design section to docs/development/current/main/design/control-tree.md:

  • Input SSOT: StepTree + StepTreeContract
  • Output: JoinModule (Normalized dialect)
  • Execution conditions: dev-only, strict fail-fast
  • Prohibitions: fallback, env direct reads, hardcoding

P1: Module Structure

Commit: 1e5432f61 - "feat(control_tree): add StepTree→Normalized shadow lowerer (if-only, dev-only)"

Created modular structure in src/mir/control_tree/normalized_shadow/:

mod.rs: Module interface and exports

contracts.rs: Capability checking

  • UnsupportedCapability enum (Loop/Break/Continue/Other)
  • CapabilityCheckResult type
  • check_if_only() function

builder.rs: Shadow lowering

  • StepTreeNormalizedShadowLowererBox (Box-First principle)
  • try_lower_if_only() - Returns Result<Option<(JoinModule, JoinFragmentMeta)>, String>
    • Ok(None): Out of scope
    • Ok(Some(...)): Success
    • Err(...): Internal error
  • get_status_string() - Dev logging

parity.rs: Comparison & verification

  • MismatchKind enum (ExitMismatch/WritesMismatch/UnsupportedKind)
  • ShadowParityResult type
  • compare_exit_contracts(), compare_writes_contracts(), check_full_parity()

Tests: 12 unit tests (all passing)

P2: Dev-Only Wiring

Commit: 89b868703 - "feat(joinir/dev): wire Phase 121 StepTree shadow lowering (strict fail-fast)"

Integrated into src/mir/builder/calls/lowering.rs:

  • Wired into existing lower_function_body() after capability guard
  • Only runs when joinir_dev_enabled() returns true
  • Strict mode fail-fast with freeze_with_hint (hint required)
  • Dev logging: [trace:dev] phase121/shadow: ...

Behavior:

  • Default: No impact (dev-only gate)
  • Dev mode: Shadow lowering attempted, status logged
  • Strict mode: Fail-fast on if-only mismatch

P3: Parity SSOT

Included in P1 (parity.rs):

  • Minimal comparison: exits + writes contracts
  • No value comparison (too fragile)
  • BTreeSet deterministic ordering
  • Clear mismatch classification

P4: Smoke Tests

Commit: 0892df6df - "test(joinir): Phase 121 shadow parity smokes (VM + LLVM EXE)"

Created smoke tests in tools/smokes/v2/profiles/integration/apps/:

  • phase121_shadow_if_only_vm.sh (3/3 tests PASS)
  • phase121_shadow_if_only_llvm_exe.sh (created, LLVM harness config needed)

Test fixtures (existing):

  1. phase103_if_only_merge_min.hako - Basic if merge (output: 2)
  2. phase114_if_only_return_then_post_min.hako - Return + post (output: 7\n2)
  3. phase117_if_only_nested_if_call_merge_min.hako - Nested if (output: 2\n3\n4)

Test conditions:

  • HAKO_JOINIR_STRICT=1 (strict mode)
  • NYASH_JOINIR_DEV=1 (dev mode)
  • NYASH_DISABLE_PLUGINS=1 (VM required)

P5: Documentation

This document + updates to:

  • docs/development/current/main/design/control-tree.md (Phase 121 design)
  • docs/development/current/main/10-Now.md (updated)
  • docs/development/current/main/01-JoinIR-Selfhost-INDEX.md (updated)
  • docs/development/current/main/30-Backlog.md (updated)

Verification

Build

cargo build --lib
cargo test --lib normalized_shadow
# 12 tests passed

Smoke Tests

bash tools/smokes/v2/profiles/integration/apps/phase121_shadow_if_only_vm.sh
# [PASS] phase121_shadow_if_only_vm: All tests passed

Manual Verification

NYASH_JOINIR_DEV=1 ./target/release/hakorune apps/tests/phase103_if_only_merge_min.hako 2>&1 \
  | grep "phase121/shadow"
# [trace:dev] phase121/shadow: shadow_lowered=true ...

Design Highlights

Box-First Principle

  • Single responsibility modules (contracts/builder/parity)
  • Clear boundaries (capability check → lowering → parity)
  • Testable units (12 unit tests)

Fail-Fast Enforcement

  • No fallback on error (dev log or strict freeze)
  • Explicit unsupported reasons (Loop/Break/Continue)
  • Mandatory hint on freeze (freeze_with_hint requires non-empty hint)

SSOT Discipline

  • No AST re-analysis (contract-only decisions)
  • No env direct reads (all through config::env/*)
  • No hardcoding (no fixture name branching)

Next Steps (Future Phases)

Phase 121 is complete. Future work:

  1. Actual lowering - Currently returns empty JoinModule, implement real conversion
  2. Loop support - Extend beyond if-only scope
  3. Value parity - Compare generated values (post-stabilization)
  4. LLVM harness - Complete LLVM smoke test configuration
  5. Production migration - Gradually enable for production paths

Known Limitations

  • Stub implementation: Returns empty JoinModule (contract check only)
  • If-only scope: Loops/breaks/continues rejected
  • No value comparison: Only contracts compared (exits/writes)
  • LLVM test incomplete: Harness configuration needed (VM tests validate core)

Files Modified

New files (6):

  • src/mir/control_tree/normalized_shadow/mod.rs
  • src/mir/control_tree/normalized_shadow/contracts.rs
  • src/mir/control_tree/normalized_shadow/builder.rs
  • src/mir/control_tree/normalized_shadow/parity.rs
  • tools/smokes/v2/profiles/integration/apps/phase121_shadow_if_only_vm.sh
  • tools/smokes/v2/profiles/integration/apps/phase121_shadow_if_only_llvm_exe.sh

Modified files (2):

  • src/mir/control_tree/mod.rs (normalized_shadow module export)
  • src/mir/builder/calls/lowering.rs (dev-only wiring)
  • docs/development/current/main/design/control-tree.md (Phase 121 design)

Commits

  1. 8d930d2dc - docs: Phase 121 StepTree→Normalized shadow plan
  2. 1e5432f61 - feat(control_tree): add StepTree→Normalized shadow lowerer (if-only, dev-only)
  3. 89b868703 - feat(joinir/dev): wire Phase 121 StepTree shadow lowering (strict fail-fast)
  4. 0892df6df - test(joinir): Phase 121 shadow parity smokes (VM + LLVM EXE)

Total: +793 lines, 4 commits, 8 files