// MirBuilderBox — Program(JSON v0) → MIR(JSON v0)
// Contract (Phase 20.34, staged):
// - I/F is stable; implementation starts as a delegate to Runner path.
// - Unsupported or unavailable paths must Fail‑Fast with stable tags (no silent fallback).
//
// API
// - emit_from_program_json_v0(program_json: String, opts: Map|Null) -> String|Null
//   Returns MIR(JSON v0) on success, or prints a tag and returns null on failure/skip.
//
// Toggles
// - HAKO_MIR_BUILDER_DELEGATE=1 — delegate to Runner (--program-json-to-mir)
// - HAKO_MIR_BUILDER_INTERNAL=0/1 — internal lowers gate（既定=1）
// - HAKO_MIR_BUILDER_REGISTRY=0/1 — pattern registry gate（既定=1）
// - HAKO_SELFHOST_NO_DELEGATE=1 — selfhost-first internal 専用プロファイル（delegate を完全に無効化）
//
// Phase 22.0: Hako‑first（registry）を既定ONにする。必要なら 0 を明示して無効化する。

using selfhost.shared.json.utils.json_frag as JsonFragBox
using "hako.mir.builder.internal.jsonfrag_normalizer" as NormBox
using "hako.mir.builder.internal.pattern_util" as PatternUtilBox
using lang.mir.builder.func_lowering as FuncLoweringBox

static box MirBuilderBox {
  method _log_builder_fail(reason) {
    local tag = "[builder/selfhost-first:unsupported:" + reason + "]"
    print(tag)
    if env.get("HAKO_SELFHOST_TRACE") == "1" {
      print("[builder/selfhost-first:trace] fail_reason=" + reason)
    }
  }
  // Helper to inject optional function defs + normalization
  method _norm_if_apply(m, func_defs_mir) {
    if m == null { return null }
    local result = FuncLoweringBox.inject_funcs(m, func_defs_mir)
    // methodization は既定ON。明示的に "0" が指定された場合のみ無効化。
    local methodize = env.get("HAKO_MIR_BUILDER_METHODIZE")
    if methodize == null || ("" + methodize) != "0" {
      result = FuncLoweringBox.methodize_calls_in_mir(result)
    }
    local nv = env.get("HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE")
    if nv != null && ("" + nv) == "1" { return NormBox.normalize_all(result) }
    return result
  }
  // Availability probe (for canaries)
  is_available() {
    // For now, availability means delegate toggle is present
    local t = env.get("HAKO_MIR_BUILDER_DELEGATE")
    if t == null { return 0 }
    if ("" + t) == "1" { return 1 } else { return 0 }
  }


  // Main entry
  method emit_from_program_json_v0(program_json, opts) {
    // Debug tag (dev toggle only)
    using "hako.mir.builder.internal.builder_config" as BuilderConfigBox
    if BuilderConfigBox.trace_enabled() == 1 {
      print("[mirbuilder/entry:build]")
    }

    if program_json == null {
      print("[mirbuilder/input/null] program_json is null")
      return null
    }
    // Minimal validation: must include version/kind
    local s = "" + program_json
    if !(s.contains("\"version\"")) || !(s.contains("\"kind\"")) {
      print("[mirbuilder/input/invalid] missing version/kind keys")
      return null
    }

    // Dev-toggle: extract and lower function definitions (defs)
    // Toggle: HAKO_MIR_BUILDER_FUNCS=1
    // Policy: delegate to FuncLoweringBox for lowering
    // Output: inject additional MIR functions to output JSON
    local func_defs_mir = ""
    {
      local funcs_toggle = env.get("HAKO_MIR_BUILDER_FUNCS")
      if funcs_toggle != null && ("" + funcs_toggle) == "1" {
        func_defs_mir = FuncLoweringBox.lower_func_defs(s, s)
      }
    }
    // Internal path（既定ON） — const(int)+ret, binop+ret ほか、registry 優先の lowering
    // Disable with: HAKO_MIR_BUILDER_INTERNAL=0
    {
      local internal = env.get("HAKO_MIR_BUILDER_INTERNAL")
      local internal_on = (internal == null) || (("" + internal) == "1")
      if internal_on == 1 {
        // Dev-only: force loop JSONFrag minimal output (pre-optimization bring-up)
        // Guard: HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG=1
        {
          local f = env.get("HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG")
          if f != null && ("" + f) == "1" {
            local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[" +
              "{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":0}},{\"op\":\"compare\",\"operation\":\"<\",\"lhs\":1,\"rhs\":2,\"dst\":3},{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
              "{\"id\":1,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}," +
              "{\"id\":2,\"instructions\":[{\"op\":\"ret\",\"value\":1}]}]}]}"
            return me._norm_if_apply(mir, func_defs_mir)
          }
        }
        // Optional: registry-driven lowering (scaffold). When HAKO_MIR_BUILDER_REGISTRY=1,
        // iterate PatternRegistryBox.candidates() and dispatch by name.
        // NOTE: using/alias は prelude で解決される（位置に依存しない）。
        local use_reg = env.get("HAKO_MIR_BUILDER_REGISTRY")
        local reg_on = (use_reg == null) || (("" + use_reg) == "1")
        if reg_on == 1 {
          // Optional: restrict registry candidates to a single name for bring-up
          local only = env.get("HAKO_MIR_BUILDER_REGISTRY_ONLY")
          // Fast path: registry-only=arraymap → 最小JsonFrag実装（heavy using を回避）
          if only != null && ("" + only) == "return.method.arraymap" {
            // Minimal inline lower (Return(Method Var recv, method, args...)) → mir_call(JSON文字列)
            // Scope: method in {get,set,push,length,size,len}（argsは最大2；Intのみ許容）
            local k_ret = JsonFragBox.index_of_from(s, "\"type\":\"Return\"", 0); if k_ret < 0 { return null }
            local k_m = JsonFragBox.index_of_from(s, "\"type\":\"Method\"", k_ret); if k_m < 0 { return null }
            local k_recv = JsonFragBox.index_of_from(s, "\"recv\":{\"type\":\"Var\"", k_m); if k_recv < 0 { return null }
            local k_mm = JsonFragBox.index_of_from(s, "\"method\":\"", k_m); if k_mm < 0 { return null }
            local method = JsonFragBox.read_string_after(s, k_mm + 9); if method == null { return null }
            // allow aliases
            if method == "len" { method = "length" }
            // build insts: receiver placeholder const 0 → r1
            local insts = "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":1,\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":0}}"
            // parse up to two Int args
            local args_text = ""; local next_id = 2; local first = 1
            local k_args = JsonFragBox.index_of_from(s, "\"args\":", k_m)
            if k_args >= 0 {
              // first Int
              local k_i1 = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", k_args)
              if k_i1 >= 0 {
                local kv1 = JsonFragBox.index_of_from(s, "\"value\":", k_i1); if kv1 >= 0 {
                  local v1 = JsonFragBox.read_int_after(s, kv1 + 8)
                  if v1 != null { insts = insts + ",{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + next_id + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + v1 + "}}"; args_text = args_text + ("" + next_id); next_id = next_id + 1; first = 0 }
                }
              }
              // second Int
              local k_i2 = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", k_args + 1)
              if k_i2 >= 0 {
                local kv2 = JsonFragBox.index_of_from(s, "\"value\":", k_i2); if kv2 >= 0 {
                  local v2 = JsonFragBox.read_int_after(s, kv2 + 8)
                  if v2 != null { insts = insts + ",{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + next_id + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + v2 + "}}"; if first==0 { args_text = args_text + "," } ; args_text = args_text + ("" + next_id) }
                }
              }
            }
            local mir = "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[{\"name\":\"main\",\"blocks\":[{\"id\":0,\"instructions\":[" +
                       insts + "," +
                       "{\\\"op\\\":\\\"mir_call\\\",\\\"dst\\\":4,\\\"mir_call\\\":{\\\"callee\\\":{\\\"type\\\":\\\"Method\\\",\\\"method\\\":\\\"" + method + "\\\",\\\"receiver\\\":1},\\\"args\\\":[" + args_text + "],\\\"effects\\\":[]}}," +
                       "{\\\"op\\\":\\\"ret\\\",\\\"value\\\":4}]}]}]}"
            print("[mirbuilder/registry:return.method.arraymap]")
            return me._norm_if_apply(mir, func_defs_mir)
          }
          // Registry list（汎用）
          using "hako.mir.builder.pattern_registry" as PatternRegistryBox
          // Lowers needed by registry dispatch（using は prelude で集約される）
          using "hako.mir.builder.internal.lower_if_compare" as LowerIfCompareBox
          using "hako.mir.builder.internal.lower_if_compare_fold_binints" as LowerIfCompareFoldBinIntsBox
          using "hako.mir.builder.internal.lower_if_compare_fold_varint" as LowerIfCompareFoldVarIntBox
          using "hako.mir.builder.internal.lower_if_compare_varint" as LowerIfCompareVarIntBox
          using "hako.mir.builder.internal.lower_if_compare_varvar" as LowerIfCompareVarVarBox
          using "hako.mir.builder.internal.lower_return_method_array_map" as LowerReturnMethodArrayMapBox
          using "hako.mir.builder.internal.lower_return_method_string_length" as LowerReturnMethodStringLengthBox
          using "hako.mir.builder.internal.lower_return_var_local" as LowerReturnVarLocalBox
          using "hako.mir.builder.internal.lower_return_string" as LowerReturnStringBox
          using "hako.mir.builder.internal.lower_return_float" as LowerReturnFloatBox
          using "hako.mir.builder.internal.lower_return_bool" as LowerReturnBoolBox
          using "hako.mir.builder.internal.lower.logical" as LowerReturnLogicalBox
          using "hako.mir.builder.internal.lower_return_binop_varint" as LowerReturnBinOpVarIntBox
          using "hako.mir.builder.internal.lower_return_binop_varvar" as LowerReturnBinOpVarVarBox
          using "hako.mir.builder.internal.lower_return_binop" as LowerReturnBinOpBox
          using "hako.mir.builder.internal.lower_return_int" as LowerReturnIntBox
          using "hako.mir.builder.internal.lower_return_loop_strlen_sum" as LowerReturnLoopStrlenSumBox
          // Registry list（通常）
          local names = PatternRegistryBox.candidates()
          if only != null { local arr = new ArrayBox(); arr.push("" + only); names = arr }
          local i = 0; local n = names.length()
          loop(i < n) {
            local nm = "" + names.get(i)
            if nm == "if.compare.intint" { local out = LowerIfCompareBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "if.compare.fold.binints" { local out = LowerIfCompareFoldBinIntsBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "if.compare.fold.varint" { local out = LowerIfCompareFoldVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "if.compare.varint" { local out = LowerIfCompareVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "if.compare.varvar" { local out = LowerIfCompareVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.method.arraymap" { local out = LowerReturnMethodArrayMapBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.method.string.length" { local out = LowerReturnMethodStringLengthBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.loop.strlen.sum" { local out = LowerReturnLoopStrlenSumBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.var.local" { local out = LowerReturnVarLocalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.string" { local out = LowerReturnStringBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.float" { local out = LowerReturnFloatBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.bool" { local out = LowerReturnBoolBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.logical" { local out = LowerReturnLogicalBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.binop.varint" { local out = LowerReturnBinOpVarIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.binop.varvar" { local out = LowerReturnBinOpVarVarBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.binop.intint" { local out = LowerReturnBinOpBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            if nm == "return.int" { local out = LowerReturnIntBox.try_lower(s); if out != null { if env.get("HAKO_MIR_BUILDER_DEBUG")=="1" || env.get("NYASH_CLI_VERBOSE")=="1" { print("[mirbuilder/registry:" + nm + "]") } return me._norm_if_apply(out, func_defs_mir) } }
            i = i + 1
          }
          // Fall-through to chain if none matched
        }
        // Boxified lowers via using+alias (prefer using over include; VM include unsupported)
        using "hako.mir.builder.internal.lower_if_then_else_following_return" as LowerIfThenElseFollowingReturnBox
        using "hako.mir.builder.internal.lower_if_nested" as LowerIfNestedBox
        using "hako.mir.builder.internal.lower_loop_sum_bc" as LowerLoopSumBcBox
        using "hako.mir.builder.internal.lower_newbox_constructor" as LowerNewboxConstructorBox
        using "hako.mir.builder.internal.lower_method_array_size" as LowerMethodArraySizeBox
        using "hako.mir.builder.internal.lower_method_array_push" as LowerMethodArrayPushBox
        using "hako.mir.builder.internal.lower_method_array_get_set" as LowerMethodArrayGetSetBox
        using "hako.mir.builder.internal.lower_method_map_size" as LowerMethodMapSizeBox
        using "hako.mir.builder.internal.lower_method_map_get_set" as LowerMethodMapGetSetBox
        using "hako.mir.builder.internal.lower_load_store_local" as LowerLoadStoreLocalBox
        using "hako.mir.builder.internal.lower_typeop_check" as LowerTypeOpCheckBox
        using "hako.mir.builder.internal.lower_typeop_cast" as LowerTypeOpCastBox
        using "hako.mir.builder.internal.lower_loop_count_param" as LowerLoopCountParamBox
        using "hako.mir.builder.internal.lower_loop_simple" as LowerLoopSimpleBox
        // Prefer New(Constructor) minimal first to avoid unresolved nested lowers in inline runs
        { local out_newc = LowerNewboxConstructorBox.try_lower(s); if out_newc != null { return me._norm_if_apply(out_newc, func_defs_mir) } }
        { local out_arr_size = LowerMethodArraySizeBox.try_lower(s); if out_arr_size != null { return me._norm_if_apply(out_arr_size, func_defs_mir) } }
        { local out_arr_push = LowerMethodArrayPushBox.try_lower(s); if out_arr_push != null { return me._norm_if_apply(out_arr_push, func_defs_mir) } }
        { local out_arr_gs = LowerMethodArrayGetSetBox.try_lower(s); if out_arr_gs != null { return me._norm_if_apply(out_arr_gs, func_defs_mir) } }
        { local out_map_size = LowerMethodMapSizeBox.try_lower(s); if out_map_size != null { return me._norm_if_apply(out_map_size, func_defs_mir) } }
        { local out_map_gs = LowerMethodMapGetSetBox.try_lower(s); if out_map_gs != null { return me._norm_if_apply(out_map_gs, func_defs_mir) } }
        { local out_ls = LowerLoadStoreLocalBox.try_lower(s); if out_ls != null { return me._norm_if_apply(out_ls, func_defs_mir) } }
        { local out_toc = LowerTypeOpCheckBox.try_lower(s); if out_toc != null { return me._norm_if_apply(out_toc, func_defs_mir) } }
        { local out_tca = LowerTypeOpCastBox.try_lower(s); if out_tca != null { return me._norm_if_apply(out_tca, func_defs_mir) } }
        // Loop lowers (sum_bc/continue/break normalization)
        // Allow skipping heavy loop lowers on plugin-less hosts: HAKO_MIR_BUILDER_SKIP_LOOPS=1
        {
          local skip_loops = env.get("HAKO_MIR_BUILDER_SKIP_LOOPS")
          local do_loops = (skip_loops == null) || (("" + skip_loops) != "1")
          if do_loops == 1 {
            { local out_loop2 = LowerLoopSumBcBox.try_lower(s); if out_loop2 != null { return me._norm_if_apply(out_loop2, func_defs_mir) } }
          }
        }
        { local out_if2b = LowerIfNestedBox.try_lower(s); if out_if2b != null { return me._norm_if_apply(out_if2b, func_defs_mir) } }
        { local out_if2 = LowerIfThenElseFollowingReturnBox.try_lower(s); if out_if2 != null { return me._norm_if_apply(out_if2, func_defs_mir) } }
        { local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { return me._norm_if_apply(out_if, func_defs_mir) } }
        { local out_ifb = LowerIfCompareFoldBinIntsBox.try_lower(s); if out_ifb != null { return me._norm_if_apply(out_ifb, func_defs_mir) } }
        { local out_ifbv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_ifbv != null { return me._norm_if_apply(out_ifbv, func_defs_mir) } }
        { local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { return me._norm_if_apply(out_ifvi, func_defs_mir) } }
        { local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { return me._norm_if_apply(out_ifvv, func_defs_mir) } }
        {
          local skip_loops2 = env.get("HAKO_MIR_BUILDER_SKIP_LOOPS")
          local do_loops2 = (skip_loops2 == null) || (("" + skip_loops2) != "1")
          if do_loops2 == 1 {
            { local out_loopp = LowerLoopCountParamBox.try_lower(s); if out_loopp != null { return me._norm_if_apply(out_loopp, func_defs_mir) } }
            { local out_loop = LowerLoopSimpleBox.try_lower(s); if out_loop != null { return me._norm_if_apply(out_loop, func_defs_mir) } }
          }
        }
        { local out_var = LowerReturnVarLocalBox.try_lower(s); if out_var != null { return me._norm_if_apply(out_var, func_defs_mir) } }
        { local out_str = LowerReturnStringBox.try_lower(s); if out_str != null { return me._norm_if_apply(out_str, func_defs_mir) } }
        { local out_f = LowerReturnFloatBox.try_lower(s); if out_f != null { return me._norm_if_apply(out_f, func_defs_mir) } }
        { local out_log = LowerReturnLogicalBox.try_lower(s); if out_log != null { return me._norm_if_apply(out_log, func_defs_mir) } }
        { local out_meth = LowerReturnMethodArrayMapBox.try_lower(s); if out_meth != null { return me._norm_if_apply(out_meth, func_defs_mir) } }
        { local out_meth_s = LowerReturnMethodStringLengthBox.try_lower(s); if out_meth_s != null { return me._norm_if_apply(out_meth_s, func_defs_mir) } }
        { local out_sum = LowerReturnLoopStrlenSumBox.try_lower(s); if out_sum != null { return me._norm_if_apply(out_sum, func_defs_mir) } }
        { local out_bool = LowerReturnBoolBox.try_lower(s); if out_bool != null { return me._norm_if_apply(out_bool, func_defs_mir) } }
        { local out_bvi = LowerReturnBinOpVarIntBox.try_lower(s); if out_bvi != null { return me._norm_if_apply(out_bvi, func_defs_mir) } }
        { local out_bvv = LowerReturnBinOpVarVarBox.try_lower(s); if out_bvv != null { return me._norm_if_apply(out_bvv, func_defs_mir) } }
        { local out_bin = LowerReturnBinOpBox.try_lower(s); if out_bin != null { return me._norm_if_apply(out_bin, func_defs_mir) } }
        {
          local out_int = LowerReturnIntBox.try_lower(s)
          if out_int != null { return me._norm_if_apply(out_int, func_defs_mir) }
        }
        // Find Return marker (or If)
        // Case (If with Compare + Return(Int)/Return(Int) in branches)
        {
          local k_if = JsonFragBox.index_of_from(s, "\"type\":\"If\"", 0)
          if k_if >= 0 {
            // cond: Compare with Int lhs/rhs
            local k_cmp = JsonFragBox.index_of_from(s, "\"type\":\"Compare\"", k_if)
            if k_cmp >= 0 {
              // op
              local k_op2 = JsonFragBox.index_of_from(s, "\"op\":", k_cmp)
              if k_op2 >= 0 {
                local op2 = JsonFragBox.read_string_after(s, k_op2 + 5)
                // support <,>,<=,>=,==,!=
                if !(op2 == "<" || op2 == ">" || op2 == "<=" || op2 == ">=" || op2 == "==" || op2 == "!=") {
                  print("[mirbuilder/internal/unsupported] compare op: " + op2)
                  return null
                }
                // lhs Int
                local lhs_val2 = null
                {
                  local klhs2 = JsonFragBox.index_of_from(s, "\"lhs\":{", k_cmp)
                  if klhs2 >= 0 {
                    local ti = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", klhs2)
                    if ti >= 0 {
                      local kv = JsonFragBox.index_of_from(s, "\"value\":", ti)
                      if kv >= 0 {
                        lhs_val2 = JsonFragBox.read_int_after(s, kv + 8)
                      }
                    }
                  }
                }
                // rhs Int
                local rhs_val2 = null
                {
                  local krhs2 = JsonFragBox.index_of_from(s, "\"rhs\":{", k_cmp)
                  if krhs2 >= 0 {
                    local ti = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", krhs2)
                    if ti >= 0 {
                      local kv = JsonFragBox.index_of_from(s, "\"value\":", ti)
                      if kv >= 0 {
                        rhs_val2 = JsonFragBox.read_int_after(s, kv + 8)
                      }
                    }
                  }
                }
                // then: Return(Int ...) — limit search to then array bounds
                local then_val = null
                local tb = PatternUtilBox.then_array_bounds(s, k_if)
                if tb != null {
                  local cp = tb.indexOf(":")
                  if cp >= 0 {
                    local lb_then = JsonFragBox._str_to_int(tb.substring(0, cp))
                    local rb_then = JsonFragBox._str_to_int(tb.substring(cp + 1, tb.length()))
                    local rt = JsonFragBox.index_of_from(s, "\"type\":\"Return\"", lb_then)
                    if rt >= 0 && rt < rb_then {
                      local ti = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", rt)
                      if ti >= 0 && ti < rb_then {
                        local kv = JsonFragBox.index_of_from(s, "\"value\":", ti)
                        if kv >= 0 && kv < rb_then { then_val = JsonFragBox.read_int_after(s, kv + 8) }
                      }
                    }
                  }
                }
                // else: Return(Int ...) — limit to else array bounds
                local else_val = null
                local eb = PatternUtilBox.else_array_bounds_after_then(s, k_if)
                if eb != null {
                  local cp2 = eb.indexOf(":")
                  if cp2 >= 0 {
                    local lb_else = JsonFragBox._str_to_int(eb.substring(0, cp2))
                    local rb_else = JsonFragBox._str_to_int(eb.substring(cp2 + 1, eb.length()))
                    local rt2 = JsonFragBox.index_of_from(s, "\"type\":\"Return\"", lb_else)
                    if rt2 >= 0 && rt2 < rb_else {
                      local ti2 = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", rt2)
                      if ti2 >= 0 && ti2 < rb_else {
                        local kv2 = JsonFragBox.index_of_from(s, "\"value\":", ti2)
                        if kv2 >= 0 && kv2 < rb_else { else_val = JsonFragBox.read_int_after(s, kv2 + 8) }
                      }
                    }
                  }
                }
                if lhs_val2 != null && rhs_val2 != null && then_val != null && else_val != null {
                  // MIR: bb0(const lhs, const rhs, compare op -> v3, branch v3 then bb1 else bb2), bb1(const then, ret), bb2(const else, ret)
                  local mir_if = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[" +
                    "{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_val2 + "}}," +
                    "{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val2 + "}}," +
                    "{\"op\":\"compare\",\"operation\":\"" + op2 + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
                    "{\"op\":\"branch\",\"cond\":3,\"then\":1,\"else\":2}]}," +
                    "{\"id\":1,\"instructions\":[{\"op\":\"const\",\"dst\":4,\"value\":{\"type\":\"i64\",\"value\":" + then_val + "}},{\"op\":\"ret\",\"value\":4}]}," +
                    "{\"id\":2,\"instructions\":[{\"op\":\"const\",\"dst\":5,\"value\":{\"type\":\"i64\",\"value\":" + else_val + "}},{\"op\":\"ret\",\"value\":5}]}]}]}"
                  return me._norm_if_apply(mir_if, func_defs_mir)
                }
              }
            }
          }
        }
        // NewBox(Constructor) minimal
        {
          local out_new = LowerNewboxConstructorBox.try_lower(s)
          if out_new != null { return me._norm_if_apply(out_new, func_defs_mir) }
        }
        // Fallback cases below: Return(Binary) and Return(Int)
        local k_ret = JsonFragBox.index_of_from(s, "\"type\":\"Return\"", 0)
        if k_ret >= 0 {
          // Case (Binary): {"type":"Binary","op":"+|-|*|/","lhs":{Int},"rhs":{Int}}
          local k_bin = JsonFragBox.index_of_from(s, "\"type\":\"Binary\"", k_ret)
          if k_bin >= 0 {
            // op
            local k_op = JsonFragBox.index_of_from(s, "\"op\":", k_bin)
            if k_op >= 0 {
              local op = JsonFragBox.read_string_after(s, k_op + 5)
              if !(op == "+" || op == "-" || op == "*" || op == "/") {
                print("[mirbuilder/internal/unsupported] binary op: " + op)
                return null
              }
              // lhs Int value
              local klhs = JsonFragBox.index_of_from(s, "\"lhs\":{", k_bin)
              local lhs_val = null
              if klhs >= 0 {
                local ti = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", klhs)
                if ti >= 0 {
                  local kv = JsonFragBox.index_of_from(s, "\"value\":", ti)
                  if kv >= 0 { lhs_val = JsonFragBox.read_int_after(s, kv + 8) }
                }
              }
              // rhs Int value
              local krhs = JsonFragBox.index_of_from(s, "\"rhs\":{", k_bin)
              local rhs_val = null
              if krhs >= 0 {
                local ti2 = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", krhs)
                if ti2 >= 0 {
                  local kv2 = JsonFragBox.index_of_from(s, "\"value\":", ti2)
                  if kv2 >= 0 { rhs_val = JsonFragBox.read_int_after(s, kv2 + 8) }
                }
              }
              if lhs_val != null && rhs_val != null {
                local mir_bin = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[" +
                  "{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + lhs_val + "}}," +
                  "{\"op\":\"const\",\"dst\":2,\"value\":{\"type\":\"i64\",\"value\":" + rhs_val + "}}," +
                  "{\"op\":\"binop\",\"operation\":\"" + op + "\",\"lhs\":1,\"rhs\":2,\"dst\":3}," +
                  "{\"op\":\"ret\",\"value\":3}]}]}]}"
                return me._norm_if_apply(mir_bin, func_defs_mir)
              }
            }
          }
          // Case (Int): Return(Int N) — ensure expr.type is Int (not Binary/Logical/etc)
          local k_expr = JsonFragBox.index_of_from(s, "\"expr\":{", k_ret)
          if k_expr >= 0 {
            // Quick check for Int near expr
            local kt = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", k_expr)
            if kt >= 0 && kt < k_expr + 64 {
              local kv = JsonFragBox.index_of_from(s, "\"value\":", kt)
              if kv >= 0 {
                local num = JsonFragBox.read_int_after(s, kv + 8)
                if num != null {
                  if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" { print("[mirbuilder/fallback:Return(Int) val=" + num + "]") }
                  local mir = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"locals\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":" + num + "}},{\"op\":\"ret\",\"value\":1}]}]}]}"
                  return me._norm_if_apply(mir, func_defs_mir)
                }
              }
            } else {
              if env.get("HAKO_MIR_BUILDER_DEBUG") == "1" || env.get("NYASH_CLI_VERBOSE") == "1" {
                print("[mirbuilder/fallback:Return(Int) skip - expr.type not Int]")
              }
            }
          }
        }
        // Unsupported internal shape → try defs fallback (dev: HAKO_MIR_BUILDER_FUNCS=1)
        print("[mirbuilder/internal/unsupported] only Return(Int|Binary(Int,Int)) supported in internal mode")
        if func_defs_mir != null && func_defs_mir != "" {
          me._log_builder_fail("defs_only")
        } else {
          me._log_builder_fail("no_match")
        }
        return null
      }
    }
    // Selfhost-first profile: when explicitly disabled, do not delegate even if
    // HAKO_MIR_BUILDER_DELEGATE=1 が立っていても env.mirbuilder.emit には逃げない。
    {
      local no_delegate = env.get("HAKO_SELFHOST_NO_DELEGATE")
      if no_delegate != null && ("" + no_delegate) == "1" {
        if env.get("HAKO_SELFHOST_TRACE") == "1" {
          print("[mirbuilder/delegate/disabled] HAKO_SELFHOST_NO_DELEGATE=1; internal-only mode")
        }
        return null
      }
    }
    // Delegate-first policy (Phase 20.34 Milestone A)
    local d = env.get("HAKO_MIR_BUILDER_DELEGATE")
    if d != null && ("" + d) == "1" {
      // Call host provider via extern: env.mirbuilder.emit(program_json)
      local args = new ArrayBox(); args.push(program_json)
      local ret = hostbridge.extern_invoke("env.mirbuilder", "emit", args)
      return me._norm_if_apply(ret, func_defs_mir)
    }
    // Provider not wired → Fail‑Fast tag
    print("[mirbuilder/delegate/missing] no provider; enable HAKO_MIR_BUILDER_DELEGATE=1")
    return null
  }
}
