// gas_harness_rows.gs - Harness output serialization
// buildHarnessRows_, assertHarnessRowsComplete_, checksum functions
// Pure output assembly - no decision logic. Rarely changes after V stabilizes.
// GAS global scope: functions in gas_data_feed.gs callable directly


/**
 * computeBlueprintChecksum_
 * order_blueprint_json의 위변조 탐지용 체크섬 (CRC32_V1).
 * ticker + order_type + quantity + limit_price_krw + validation_status 를
 * 행 순서대로 연결한 문자열의 char-code sum을 반환한다.
 * Python converter는 이 값과 자신이 재계산한 값이 다르면 HARNESS_INTEGRITY_FAIL 처리.
 */
function computeBlueprintChecksum_(blueprint) {
  var s = '';
  blueprint = blueprint || [];
  for (var i = 0; i < blueprint.length; i++) {
    var r = blueprint[i];
    s += String(r.ticker || '') + '|'
       + String(r.order_type || '') + '|'
       + String(r.quantity != null ? r.quantity : '') + '|'
       + String(r.limit_price_krw != null ? r.limit_price_krw : '') + '|'
       + String(r.validation_status || '') + ';';
  }
  var sum = 0;
  for (var j = 0; j < s.length; j++) {
    sum = (sum + s.charCodeAt(j)) & 0xFFFFFFFF;
  }
  return sum;
}


/**
 * [2026-05-20_HARNESS_V5] computeInputSnapshotChecksum_
 * 계좌 스냅샷 원장(보유수량·평단·종가·현금·기준시각)의 CRC32_V1 해시.
 * 동일 입력 재호출 시 이 값이 달라지면 데이터 소스가 갱신된 것이다.
 * Python 검증기가 이전 실행값과 비교하여 non_deterministic_flag 를 set 한다.
 */
function computeInputSnapshotChecksum_(asResult, capturedAtIso) {
  var s = String(capturedAtIso || '') + '|'
        + String((asResult || {}).settlementCashD2Krw != null
            ? asResult.settlementCashD2Krw : '') + '|';
  ((asResult || {}).holdings || []).forEach(function(h) {
    s += String(h.ticker   || '')                              + '|'
       + String(h.holdingQty  != null ? h.holdingQty  : '')   + '|'
       + String(h.avgCost     != null ? h.avgCost     : '')   + '|'
       + String(h.close       != null ? h.close       : '')   + ';';
  });
  var sum = 0;
  for (var i = 0; i < s.length; i++) {
    sum = (sum + s.charCodeAt(i)) & 0xFFFFFFFF;
  }
  return sum;
}


/**
 * I3: computeStringChecksum_
 * 임의 문자열의 char-code sum 체크섬 (CRC32_V1 방식).
 * source_manifest_json, decision_trace_json 등에 사용.
 */
function computeStringChecksum_(str) {
  var s = typeof str === 'string' ? str : JSON.stringify(str);
  if (s === undefined || s === null) s = '';
  var sum = 0;
  for (var i = 0; i < s.length; i++) {
    sum = (sum + s.charCodeAt(i)) & 0xFFFFFFFF;
  }
  return sum;
}


// ── 출력 행 빌더 ─────────────────────────────────────────────────────────────

function buildHarnessRows_(
  now, capturedAtIso, intradayLock, snapshotFreshness, snapshotGate, cashFloorInfo, heatGate, heatThresholds, mrsScore,
  asResult, dfMap, settlementCashPct, totalHeatPct, buyPowerKrw, totalAsset, actions,
  performance, h2, h3, h4, h5, orderBlueprint, hAlpha, regimeTrimGuidance,
  cashShortfallInfo, hApex, sectorMomentumRows,
  drawdownGuard, portfolioBetaGate, eventRiskRows, sectorConcentration, tpLadderRows,
  regimeSizeScale, regimeCashMinPct, stopAdequacyRows, staleRows,
  singlePositionWeightCap, semiconductorClusterGate, portfolioDrawdownGate,
  winLossStreakGuard, positionCountLimit,
  stopBreachAlert, tpTriggerAlert, heatConcentrationAlert,
  regimeTransitionAlert, portfolioHealthScore
) {
  var sourceManifest = [
    { name: 'GatherTradingData.json', type: 'JSON', status: 'PENDING_EXPORT' },
    { name: 'data_feed', type: 'GOOGLE_SHEETS', status: 'OK' },
    { name: 'sector_flow', type: 'GOOGLE_SHEETS', status: 'OK' },
    { name: 'macro', type: 'GOOGLE_SHEETS', status: 'OK' },
    { name: 'event_risk', type: 'GOOGLE_SHEETS', status: 'OK' },
    { name: 'account_snapshot', type: 'GOOGLE_SHEETS', status: 'OK' },
    { name: 'backdata_feature_bank', type: 'GOOGLE_SHEETS', status: 'OK' },
    { name: 'harness_context', type: 'GOOGLE_SHEETS', status: 'OK' }
  ];

  // ── G1: CASH_SHORTFALL_V1 사전 계산 ─────────────────────────────────────
  // LLM이 "약 N원 필요" 즉석 계산 금지 — GAS 결정론적 산출 후 잠금
  var g1TargetCashPct = cashShortfallInfo.cash_target_pct;
  var g1ShortfallMin  = cashShortfallInfo.cash_shortfall_min_krw;
  var g1ShortfallTgt  = cashShortfallInfo.cash_shortfall_target_krw;
  var g1CashCurrentPct = cashShortfallInfo.cash_current_pct_d2;

  // ── G2: TRIM_PLAN_MIN_CASH_V1 사전 계산 ──────────────────────────────────
  // 현금 회복용 종목별 TRIM 계획 — LLM 즉석 선택 금지, GAS 우선순위 기반 확정
  var g2SellQtyMap = {};
  h3.sellQty.forEach(function(sq) { g2SellQtyMap[sq.ticker] = sq; });
  var g2CloseMap = {};
  asResult.holdings.forEach(function(h) {
    var df = dfMap[h.ticker] || {};
    g2CloseMap[h.ticker] = h.close || df.close || 0;
  });
  var g2TrimPlan  = [];
  var g2Accum     = 0;
  var g2Shortfall = g1ShortfallMin;
  h2.candidates.forEach(function(cand) {
    var sqRow   = g2SellQtyMap[cand.ticker] || {};
    var sellQty = sqRow.sell_qty;
    var close   = g2CloseMap[cand.ticker] || 0;
    var estKrw  = 0;
    if (typeof sellQty === 'number' && sellQty > 0 && close > 0) {
      estKrw = Math.round(sellQty * close);
    }
    g2Accum += estKrw;
    g2TrimPlan.push({
      rank:              cand.rank,
      ticker:            cand.ticker,
      name:              cand.name || '',
      tier:              cand.tier,
      sell_qty:          typeof sellQty === 'number' ? sellQty : (sellQty || null),
      estimated_sell_krw: estKrw,
      accumulated_krw:   g2Accum,
      covers_shortfall:  g2Shortfall > 0 ? g2Accum >= g2Shortfall : true
    });
  });

  // ── M4: 5억원 목표 자산 추적 사전 계산 ────────────────────────────────────
  var M4_GOAL_KRW = 500000000;
  var m4Asset     = Number.isFinite(totalAsset) ? totalAsset : 0;
  var m4Achieve   = m4Asset > 0 ? Math.round(m4Asset / M4_GOAL_KRW * 1000) / 10 : 0;
  var m4Remain    = Math.max(0, M4_GOAL_KRW - m4Asset);
  var m4NetExp30  = (performance && Number.isFinite(performance.net_expectancy_30))
                    ? performance.net_expectancy_30 : null;
  var m4EtaMonths = null;
  var m4EtaLabel  = 'DATA_MISSING';
  if (m4Asset >= M4_GOAL_KRW) {
    m4EtaMonths = 0;
    m4EtaLabel  = 'ACHIEVED';
  } else if (m4Asset > 0 && m4NetExp30 !== null && m4NetExp30 > 0) {
    m4EtaMonths = Math.ceil(Math.log(M4_GOAL_KRW / m4Asset) / Math.log(1 + m4NetExp30 / 100));
    var m4EtaDate = new Date(now.getTime());
    m4EtaDate.setMonth(m4EtaDate.getMonth() + m4EtaMonths);
    m4EtaLabel = m4EtaDate.getFullYear() + '-'
      + String(m4EtaDate.getMonth() + 1).padStart(2, '0');
  }

  // ── P6: 사용자 판단용 제안표 확정값 (PROPOSAL_REFERENCE_V1) ────────────────
  // 보고서가 WATCH/BLOCKED 행을 복원 추론하지 않도록 하네스가 제안 레이어를 직접 잠금
  var p6PriceMap = {};
  (h4.prices || []).forEach(function(row) { p6PriceMap[row.ticker] = row; });
  var p6SellQtyMap = {};
  (h3.sellQty || []).forEach(function(row) { p6SellQtyMap[row.ticker] = row; });
  var p6BuyQtyMap = {};
  (h3.buyQtyInputs || []).forEach(function(row) { p6BuyQtyMap[row.ticker] = row; });
  var p6DecisionMap = {};
  (h5.decisions || []).forEach(function(row) { p6DecisionMap[row.ticker] = row; });
  var p6BlueprintMap = {};
  (orderBlueprint || []).forEach(function(row) { p6BlueprintMap[row.ticker] = row; });
  var p6TpLadderMap = {};
  (tpLadderRows || []).forEach(function(row) { p6TpLadderMap[row.ticker] = row; });
  var p6ProfitMap = {};
  (((hApex || {}).profit_preservation_json) || []).forEach(function(row) { p6ProfitMap[row.ticker] = row; });
  var p6BuyPermissionMap = {};
  (((hApex || {}).buy_permission_json) || []).forEach(function(row) { p6BuyPermissionMap[row.ticker] = row; });
  var p6AlphaLeadMap = {};
  (((hApex || {}).alpha_lead_json) || []).forEach(function(row) { p6AlphaLeadMap[row.ticker] = row; });
  var p6SellRankMap = {};
  var p6Candidates_ = (h2 && h2.candidates) ? h2.candidates : [];
  for (var sr = 0; sr < p6Candidates_.length; sr++) {
    p6SellRankMap[p6Candidates_[sr].ticker] = p6Candidates_[sr].rank;
  }
  var p6Tickers = {};
  Object.keys(p6PriceMap).forEach(function(t) { p6Tickers[t] = true; });
  Object.keys(p6SellQtyMap).forEach(function(t) { p6Tickers[t] = true; });
  Object.keys(p6BuyQtyMap).forEach(function(t) { p6Tickers[t] = true; });
  Object.keys(p6DecisionMap).forEach(function(t) { p6Tickers[t] = true; });
  Object.keys(p6BlueprintMap).forEach(function(t) { p6Tickers[t] = true; });
  var p6Rows = [];
  Object.keys(p6Tickers).sort(function(a, b) {
    var ra = p6SellRankMap[a] != null ? p6SellRankMap[a] : 9999;
    var rb = p6SellRankMap[b] != null ? p6SellRankMap[b] : 9999;
    var da = p6DecisionMap[a] || {};
    var db = p6DecisionMap[b] || {};
    var oa = p6BlueprintMap[a] || {};
    var ob = p6BlueprintMap[b] || {};
    var actionA = String(da.final_action || oa.order_type || 'WATCH').toUpperCase();
    var actionB = String(db.final_action || ob.order_type || 'WATCH').toUpperCase();
    function bucket_(action) {
      if (action.indexOf('SELL') >= 0 || action.indexOf('TRIM') >= 0 || action.indexOf('EXIT') >= 0 || action.indexOf('STOP_LOSS') >= 0 || action.indexOf('TAKE_PROFIT') >= 0 || action.indexOf('TRAILING_STOP') >= 0) return 0;
      if (action.indexOf('BUY') >= 0 || action.indexOf('ADD_ON') >= 0 || action.indexOf('PILOT') >= 0 || action.indexOf('STAGED') >= 0) return 1;
      if (action.indexOf('WATCH') >= 0 || action.indexOf('HOLD') >= 0) return 2;
      return 3;
    }
    var ba = bucket_(actionA);
    var bb = bucket_(actionB);
    if (ba !== bb) return ba - bb;
    if (ra !== rb) return ra - rb;
    if (ba === 1 || ba === 2) {
      var buyStateOrder_ = { ALLOW_ADD_ON: 0, ALLOW_PILOT: 1, WATCH: 2, BLOCKED: 3 };
      var buyA = p6BuyPermissionMap[a] || {};
      var buyB = p6BuyPermissionMap[b] || {};
      var alphaA = p6AlphaLeadMap[a] || {};
      var alphaB = p6AlphaLeadMap[b] || {};
      var sa = buyStateOrder_[String(buyA.buy_permission_state || '').toUpperCase()] || 99;
      var sb = buyStateOrder_[String(buyB.buy_permission_state || '').toUpperCase()] || 99;
      if (sa !== sb) return sa - sb;
      var aa = -(typeof alphaA.alpha_lead_score === 'number' ? alphaA.alpha_lead_score : 0);
      var ab = -(typeof alphaB.alpha_lead_score === 'number' ? alphaB.alpha_lead_score : 0);
      if (aa !== ab) return aa - ab;
    }
    return a < b ? -1 : (a > b ? 1 : 0);
  }).forEach(function(ticker) {
    var p = p6PriceMap[ticker] || {};
    var s = p6SellQtyMap[ticker] || {};
    var b = p6BuyQtyMap[ticker] || {};
    var d = p6DecisionMap[ticker] || {};
    var o = p6BlueprintMap[ticker] || {};
    var t = p6TpLadderMap[ticker] || {};
    var pp = p6ProfitMap[ticker] || {};
    var finalAction = String(d.final_action || o.order_type || 'WATCH').toUpperCase();
    var orderType = String(o.order_type || '').toUpperCase();
    var priorityGroup = 3;
    var priorityRank = 9999;
    if (finalAction.indexOf('SELL') >= 0 || finalAction.indexOf('TRIM') >= 0 || finalAction.indexOf('EXIT') >= 0 || finalAction.indexOf('STOP_LOSS') >= 0 || finalAction.indexOf('TAKE_PROFIT') >= 0 || finalAction.indexOf('TRAILING_STOP') >= 0) {
      priorityGroup = 0;
      priorityRank = p6SellRankMap[ticker] != null ? p6SellRankMap[ticker] : priorityRank;
    } else if (finalAction.indexOf('BUY') >= 0 || finalAction.indexOf('ADD_ON') >= 0 || finalAction.indexOf('PILOT') >= 0 || finalAction.indexOf('STAGED') >= 0) {
      priorityGroup = 1;
      var bp = p6BuyPermissionMap[ticker] || {};
      var ap = p6AlphaLeadMap[ticker] || {};
      var buyStateOrder_ = { ALLOW_ADD_ON: 0, ALLOW_PILOT: 1, WATCH: 2, BLOCKED: 3 };
      priorityRank = 10000 + ((buyStateOrder_[String(bp.buy_permission_state || '').toUpperCase()] || 99) * 1000)
        + (100 - (typeof ap.alpha_lead_score === 'number' ? ap.alpha_lead_score : 0));
    } else if (finalAction.indexOf('WATCH') >= 0 || finalAction.indexOf('HOLD') >= 0) {
      priorityGroup = 2;
      var hp = p6BuyPermissionMap[ticker] || {};
      var ha = p6AlphaLeadMap[ticker] || {};
      var holdStateOrder_ = { ALLOW_ADD_ON: 0, ALLOW_PILOT: 1, WATCH: 2, BLOCKED: 3 };
      priorityRank = 20000 + ((holdStateOrder_[String(hp.buy_permission_state || '').toUpperCase()] || 99) * 1000)
        + (100 - (typeof ha.alpha_lead_score === 'number' ? ha.alpha_lead_score : 0));
    }
    var proposalType = '관찰 제안';
    var priceBasis = '하네스 기준 참고가';
    var qtyBasis = '수량 입력 없음';
    var proposedLimit = null;
    var proposedTp = p.tp1_price || p.tp2_price || null;
    var proposedQty = null;
    if (finalAction.indexOf('BUY') >= 0 || orderType.indexOf('BUY') >= 0 || b.final_qty != null) {
      proposalType = '매수 제안';
      proposedLimit = o.limit_price_krw != null ? o.limit_price_krw : (b.entry_price_hint || null);
      priceBasis = '매수 제안가 우선';
      proposedQty = b.final_qty != null ? b.final_qty : null;
      qtyBasis = '매수 수량 우선';
    } else if (finalAction.indexOf('TAKE_PROFIT') >= 0 || orderType.indexOf('TAKE_PROFIT') >= 0) {
      proposalType = '익절 제안';
      proposedLimit = p.tp1_price || p.tp2_price || null;
      priceBasis = '익절가 우선';
      proposedQty = s.sell_qty != null ? s.sell_qty : null;
      qtyBasis = '매도 수량 우선';
    } else if (s.sell_qty != null || ['SELL_READY', 'SELL', 'TRIM', 'EXIT_100', 'EXIT_FULL'].indexOf(finalAction) >= 0) {
      proposalType = (finalAction === 'WATCH' || finalAction === 'HOLD') ? '관찰 제안' : '매도 제안';
      proposedLimit = p.stop_price != null ? p.stop_price : null;
      priceBasis = (finalAction === 'WATCH' || finalAction === 'HOLD') ? '주문가 아님: 참고 방어가' : '방어가 우선';
      proposedQty = s.sell_qty != null ? s.sell_qty : null;
      qtyBasis = (finalAction === 'WATCH' || finalAction === 'HOLD') ? '주문 수량 아님: 참고 수량' : '매도 수량 우선';
    } else if (finalAction === 'WATCH' || finalAction === 'HOLD' || orderType === 'WATCH') {
      proposalType = '관찰 제안';
      proposedLimit = p.stop_price != null ? p.stop_price : null;
      priceBasis = '주문가 아님: 참고 방어가';
      proposedQty = s.sell_qty != null ? s.sell_qty : null;
      qtyBasis = '주문 수량 아님: 참고 수량';
    }
    if (proposedLimit == null && proposedQty == null && p.stop_price == null && proposedTp == null) return;
    var executionStatus = 'EXECUTION_WAIT';
    if (String(o.validation_status || '') === 'PASS') {
      executionStatus = 'EXECUTION_READY';
    } else if (finalAction === 'WATCH' || finalAction === 'HOLD' || orderType === 'WATCH') {
      executionStatus = 'PROPOSAL_ONLY';
    }
    var blockReason = o.rationale_code || '하네스 기준 제안 유지';
    if (!o.rationale_code && Array.isArray(d.gate_trace) && d.gate_trace.length) {
      blockReason = d.gate_trace[d.gate_trace.length - 1].reason || blockReason;
    }
    var positionClass = String(p.position_class || '').toLowerCase();
    var baseStopQty = s.holding_qty != null ? s.holding_qty : proposedQty;
    var stop1Qty = null;
    var stop2Qty = null;
    if (typeof baseStopQty === 'number' && baseStopQty > 0) {
      var stop1Ratio = positionClass === 'core' ? 0.50 : 0.70;
      stop1Qty = Math.floor(baseStopQty * stop1Ratio);
      if (stop1Qty <= 0) stop1Qty = 1;
      if (stop1Qty > baseStopQty) stop1Qty = baseStopQty;
      stop2Qty = baseStopQty - stop1Qty;
      if (stop2Qty <= 0) stop2Qty = null;
    }
    var stop3Price = null;
    if (pp.auto_trailing_stop != null) {
      stop3Price = pp.auto_trailing_stop;
    } else if (String(p.profit_lock_stage || 'NORMAL') !== 'NORMAL' && pp.protected_stop_price != null) {
      stop3Price = pp.protected_stop_price;
    }
    var stop3Qty = null;
    if (stop3Price != null) {
      stop3Qty = t.tp3_qty != null ? t.tp3_qty : (p.tp3_qty != null ? p.tp3_qty : null);
      if (stop3Qty == null && typeof baseStopQty === 'number' && baseStopQty > 0) {
        var tp1Qty = t.tp1_qty != null ? t.tp1_qty : (p.tp1_qty != null ? p.tp1_qty : 0);
        var tp2Qty = t.tp2_qty != null ? t.tp2_qty : (p.tp2_qty != null ? p.tp2_qty : 0);
        var residualQty = baseStopQty - tp1Qty - tp2Qty;
        stop3Qty = residualQty > 0 ? residualQty : null;
      }
    }
    p6Rows.push({
      account: o.account || s.account || b.account || p.account || d.account || '',
      ticker: ticker,
      name: o.name || s.name || b.name || p.name || d.name || '',
      proposal_type: proposalType,
      proposed_limit_price_krw: proposedLimit,
      proposed_price_basis: priceBasis,
      proposed_quantity: proposedQty,
      proposed_quantity_basis: qtyBasis,
      priority_group: priorityGroup,
      priority_rank: priorityRank,
      proposed_stop_price_krw: p.stop_price != null ? p.stop_price : null,
      stop1_price_krw: p.stop_price != null ? p.stop_price : null,
      stop1_quantity: stop1Qty,
      stop2_price_krw: stop2Qty != null ? p.stop_price : null,
      stop2_quantity: stop2Qty,
      stop3_price_krw: stop3Price,
      stop3_quantity: stop3Qty,
      tp1_price_krw: t.tp1_price != null ? t.tp1_price : (p.tp1_price != null ? p.tp1_price : null),
      tp1_quantity: t.tp1_qty != null ? t.tp1_qty : (p.tp1_qty != null ? p.tp1_qty : null),
      tp2_price_krw: t.tp2_price != null ? t.tp2_price : (p.tp2_price != null ? p.tp2_price : null),
      tp2_quantity: t.tp2_qty != null ? t.tp2_qty : (p.tp2_qty != null ? p.tp2_qty : null),
      tp3_price_krw: null,
      tp3_quantity: t.tp3_qty != null ? t.tp3_qty : (p.tp3_qty != null ? p.tp3_qty : null),
      execution_status: executionStatus,
      block_reason: blockReason
    });
  });

  return [
    // ── 메타 ─────────────────────────────────────────────────────────
    ['harness_version',          HARNESS_VERSION],
    ['computed_at',              formatIso_(now)],
    // [PROPOSAL50] P0-2: ROUTING_TRACE_V1 동적값 — 정적 하드코딩 제거
    ['request_route',       ((hApex || {}).routing_trace_json || {}).request_route        || 'PIPELINE_EOD_BATCH'],
    ['route_reason_code',   'RUN_EVENT_RISK_CHAIN'],
    ['bundle_selected',     ((hApex || {}).routing_trace_json || {}).bundle_selected      || 'retirement_portfolio_ultra_compact'],
    ['prompt_entrypoint',   ((hApex || {}).routing_trace_json || {}).prompt_entrypoint    || 'prompts/analysis_prompt.md'],
    // [PROPOSAL50] P0-1: EXPORT_GATE_V1 동적값 — PENDING_EXPORT 정적 하드코딩 제거
    ['json_validation_status', (hApex || {}).json_validation_status                       || 'PENDING_EXPORT'],
    ['capture_required',    String(((hApex || {}).routing_trace_json || {}).capture_required != null
                              ? (hApex.routing_trace_json.capture_required) : true)],
    ['cash_ledger_basis',   ((hApex || {}).routing_trace_json || {}).cash_ledger_basis    || 'D2_ONLY'],
    ['source_manifest_json',     JSON.stringify(sourceManifest)],

    // ── H1: P4 가드 ───────────────────────────────────────────────
    ['captured_at',              capturedAtIso],
    ['intraday_lock',            intradayLock],
    ['snapshot_execution_gate',   snapshotGate.status],
    ['snapshot_execution_reason', snapshotGate.reason],
    ['account_snapshot_freshness_json', JSON.stringify(snapshotFreshness || {})],
    ['intraday_lock_reason',
      intradayLock
        ? 'captured_at < 15:30 KST — P4 적용: EXIT_100/SELL_FULL/BUY 차단'
        : 'captured_at >= 15:30 KST — 정상 장마감 데이터'],
    ['p4_guard',                 intradayLock ? 'ACTIVE' : 'INACTIVE'],

    // ── H1: 현금 (P3 가드) ────────────────────────────────────────
    ['immediate_cash_krw',       asResult.immediateCashKrw],
    ['settlement_cash_d2_krw',   asResult.settlementCashD2Krw],
    ['open_order_amount_krw',    asResult.openOrderAmountKrw],
    ['buy_power_krw',            buyPowerKrw],
    ['total_asset_krw',          totalAsset],
    ['settlement_cash_pct',      settlementCashPct],
    ['p3_guard',
      'ACTIVE — settlement_cash_d2_krw only. '
      + 'cash_floor 및 buy_power_krw 는 D+2 정산현금 단독 기준. '
      + 'immediate_cash_krw 는 참고값이며 cash ledger 합산 금지.'],

    // ── H1: cash_floor ────────────────────────────────────────────
    ['cash_floor_min_pct',       cashFloorInfo.minPct],
    ['cash_floor_regime',        cashFloorInfo.regime],
    ['cash_floor_status',        cashFloorInfo.status],

    // ── G1: 현금 부족액 / 목표현금 확정값 (CASH_SHORTFALL_V1) ─────────────────
    // LLM 즉석 계산 금지: "약 N원 필요" 는 이 필드 복사만 허용
    ['cash_current_pct_d2',      g1CashCurrentPct],
    ['cash_target_pct',          g1TargetCashPct],
    ['cash_shortfall_min_krw',   g1ShortfallMin],
    ['cash_shortfall_target_krw', g1ShortfallTgt],

    // ── G2: 현금 회복 TRIM 계획 (TRIM_PLAN_MIN_CASH_V1) ──────────────────────
    // 매도우선순위(H2) 기반 종목별 TRIM 순서·예상금액 하네스 확정 — LLM 임의 선택 금지
    ['trim_plan_to_min_cash_json', JSON.stringify(g2TrimPlan)],

    ['mrs_score',                mrsScore],
    ['performance_multiplier',    performance.bayesian_multiplier],
    ['performance_label',        performance.bayesian_label],
    ['performance_win_rate_30',   performance.win_rate_30],
    ['performance_net_expectancy_30', performance.net_expectancy_30],
    ['performance_consecutive_losses', performance.consecutive_losses],
    ['performance_trades_used',   performance.trades_used],

    // ── H1: Total Heat ────────────────────────────────────────────
    ['total_heat_krw',           Math.round(asResult.totalHeatKrw)],
    ['total_heat_pct',           totalHeatPct],
    ['total_heat_atr_estimated', asResult.heatAtrEstimated],
    ['total_heat_rows_counted',  asResult.heatRowsCount],
    ['heat_gate_status',         heatGate],
    ['heat_gate_threshold_pct',  heatThresholds ? heatThresholds.hardBlock : HEAT_HARD_BLOCK_PCT],

    // ── H1: 허용/차단 액션 ────────────────────────────────────────
    ['allowed_actions',          JSON.stringify(actions.allowed)],
    ['blocked_actions',          JSON.stringify(actions.blocked)],

    // ── H2: 매도후보 순위 ─────────────────────────────────────────
    ['sell_candidates_json',     JSON.stringify(h2.candidates)],
    ['sell_priority_checksum',   computeStringChecksum_(JSON.stringify(((h2 && h2.candidates) || []).map(function(c) {
      return {
        rank: c.rank,
        ticker: c.ticker,
        tier: c.tier,
        score: (typeof c.sell_priority_score === 'number') ? c.sell_priority_score : c.score
      };
    })))],
    ['sell_priority_lock',       'true'],
    ['sell_priority_computed_at', formatIso_(now)],
    ['sell_candidates_count',    ((h2 && h2.candidates) ? h2.candidates.length : 0)],
    ['sell_priority_leader_holdback', JSON.stringify(((h2 && h2.candidates) || []).map(function(c) {
      return {
        ticker: c.ticker,
        rank: c.rank,
        tier: c.tier,
        sell_priority_score: c.sell_priority_score,
        rebound_holdback_score: c.rebound_holdback_score || 0,
        trim_style: c.trim_style || '',
        cash_preserve_style: c.cash_preserve_style || '',
        cash_preserve_ratio: c.cash_preserve_ratio || 0,
      };
    }))],

    // ── H3: 수량 ─────────────────────────────────────────────────
    ['sell_quantities_json',     JSON.stringify(h3.sellQty)],
    ['buy_qty_inputs_json',      JSON.stringify(h3.buyQtyInputs)],
    ['quantities_lock',          'true'],

    // ── H4: 가격 ─────────────────────────────────────────────────
    ['prices_json',              JSON.stringify(h4.prices)],
    ['prices_lock',              'true'],

    // ── H5: 결정 상태머신 ─────────────────────────────────────────
    ['decisions_json',           JSON.stringify(h5.decisions)],
    ['decision_trace_json',      (function() {
      var full = JSON.stringify(h5.traces || []);
      if (full.length <= 45000) return full;
      // blocked_actions / inputs_used 는 전 항목 공통값 반복 → 제거해 압축
      var slim = (h5.traces || []).map(function(t) {
        return { ticker: t.ticker, state: t.state, result: t.result,
                 selected_action: t.selected_action, reason: t.reason };
      });
      return JSON.stringify(slim);
    })()],
    ['decision_lock',            'true'],

    // ── H6: HTS 주문 렌더링 + Blueprint 무결성 해시 ─────────────────
    ['order_blueprint_json',     JSON.stringify(orderBlueprint)],
    ['blueprint_row_count',      (orderBlueprint || []).length],
    ['blueprint_checksum',       computeBlueprintChecksum_(orderBlueprint)],
    ['blueprint_hash_algo',      'CRC32_V1'],
    ['render_validation_status', 'READY'],
    ['proposal_reference_json',  JSON.stringify(p6Rows)],
    ['proposal_reference_lock',  'true'],

    // ── I3: CHECKSUM_V2 — 결정론적 체크섬 강화 ──────────────────────────────
    // 동일 입력/기준시각에서 네 체크섬이 모두 일치해야 NON_DETERMINISTIC_OUTPUT 방지
    ['source_manifest_checksum', computeStringChecksum_(JSON.stringify(sourceManifest))],
    ['decision_trace_checksum',  computeStringChecksum_(JSON.stringify(h5.traces))],
    // ── [2026-05-20_HARNESS_V5] 신규 체크섬 ─────────────────────────────────
    // input_snapshot_checksum: 계좌 캡처 원장(보유수량·평단·현금)의 스냅샷 해시.
    // 동일 입력 재호출 시 이 값이 변하면 데이터 소스가 갱신된 것이다.
    ['input_snapshot_checksum',  computeInputSnapshotChecksum_(asResult, capturedAtIso)],
    // rendered_output_checksum: blueprint와 동일한 주문 행 해시 (canonical).
    ['rendered_output_checksum', computeBlueprintChecksum_(orderBlueprint)],
    // rendered_report_checksum: legacy alias. 신규 검증은 rendered_output_checksum 우선.
    ['rendered_report_checksum', computeBlueprintChecksum_(orderBlueprint)],
    // non_deterministic_flag: Python 검증기가 이전 실행값과 비교 후 설정. GAS는 항상 false.
    ['non_deterministic_flag',   'false'],
    ['checksum_hash_algo',       'CRC32_V1'],

    // ── Alpha-Shield: X1/X3/W1~W4 선행 레이더 ───────────────────
    ['alpha_shield_json',
      JSON.stringify((hAlpha || {}).per_holding || [])],
    ['alpha_shield_lock',                 'true'],
    ['alpha_shield_critical_alert_count',
      String((hAlpha || {}).critical_alert_count || 0)],
    ['alpha_shield_critical_alert_flag',
      ((hAlpha || {}).critical_alert_count || 0) > 0 ? 'CRITICAL' : 'OK'],
    ['alpha_shield_computed_at',          formatIso_(now)],
    ['alpha_shield_formula_ids',
      'MRG001[X1],RS001[X3],W1_DIVERGENCE,W2_OVERHANG,W3_ROTATION,W4_FLOW_ACCEL'],

    // ── APEX V1: 판단 자료 생성 시점 로직 하네스 ─────────────────────────────
    // 텍스트 가이드라인이 아니라 GAS가 직접 산출한 매수/매도/현금확보 실행 판단 자료
    ['alpha_lead_json',             JSON.stringify((hApex || {}).alpha_lead_json || [])],
    ['alpha_lead_lock',             'true'],
    ['backdata_feature_bank_json',   JSON.stringify(((hApex || {}).backdata_feature_bank_json || []).slice(-50))],
    ['backdata_learning_lock',       'true'],
    ['follow_through_json',         JSON.stringify((hApex || {}).follow_through_json || [])],
    ['follow_through_lock',         'true'],
    ['distribution_risk_json',      JSON.stringify((hApex || {}).distribution_risk_json || [])],
    ['distribution_lock',           'true'],
    ['profit_preservation_json',    JSON.stringify((hApex || {}).profit_preservation_json || [])],
    ['profit_preservation_lock',    'true'],
    ['cash_raise_plan_json',        JSON.stringify((hApex || {}).cash_raise_plan_json || [])],
    ['rebound_sell_trigger_json',   JSON.stringify((hApex || {}).rebound_sell_trigger_json || [])],
    ['smart_sell_quantities_json',  JSON.stringify((hApex || {}).smart_sell_quantities_json || [])],
    ['smart_cash_raise_lock',       'true'],
    ['execution_quality_json',      JSON.stringify((hApex || {}).execution_quality_json || [])],
    ['execution_quality_lock',      'true'],
    ['buy_permission_json',         JSON.stringify((hApex || {}).buy_permission_json || [])],
    ['limit_price_policy_json',     JSON.stringify((hApex || {}).limit_price_policy_json || [])],
    ['regime_adjusted_sell_priority_json', JSON.stringify((hApex || {}).regime_adjusted_sell_priority_json || [])],
    ['benchmark_relative_timeseries_json', JSON.stringify((hApex || {}).benchmark_relative_timeseries_json || [])],
    ['index_relative_health_json', JSON.stringify((hApex || {}).index_relative_health_json || [])],
    ['saqg_json',                   JSON.stringify((hApex || {}).saqg_json || [])],
    ['cash_creation_purpose_lock_json', JSON.stringify((hApex || {}).cash_creation_purpose_lock_json || [])],
    ['alpha_feedback_json',         JSON.stringify((hApex || {}).alpha_feedback_json || {})],
    ['alpha_evaluation_window_json', JSON.stringify((hApex || {}).alpha_evaluation_window_json || [])],
    ['entry_freshness_json',        JSON.stringify((hApex || {}).entry_freshness_json || [])],
    ['sell_value_preservation_json', JSON.stringify((hApex || {}).sell_value_preservation_json || [])],
    // ── [2026-05-20_HARNESS_V5] Gate 4b: FTD 확인 상태 잠금
    ['follow_through_confirm_json', JSON.stringify((hApex || {}).follow_through_confirm_json || [])],
    ['follow_through_confirm_lock', 'true'],
    // L1: 섹터 로테이션 모멘텀
    ['sector_rotation_momentum_json', JSON.stringify(sectorMomentumRows || [])],
    ['sector_rotation_momentum_lock', 'true'],

    // ── M1: DRAWDOWN_GUARD_V1 ────────────────────────────────────
    ['drawdown_guard_state',         (drawdownGuard || {}).state  || 'NORMAL'],
    ['drawdown_buy_scale',           (drawdownGuard || {}).buy_scale !== undefined
                                      ? (drawdownGuard || {}).buy_scale : 1.0],
    ['drawdown_consecutive_losses',  (drawdownGuard || {}).consecutive_losses || 0],

    // ── M2: PORTFOLIO_BETA_GATE_V1 ──────────────────────────────
    ['portfolio_beta',               (portfolioBetaGate || {}).portfolio_beta !== null
                                      ? (portfolioBetaGate || {}).portfolio_beta : 'N/A'],
    ['portfolio_beta_gate',          (portfolioBetaGate || {}).gate_status || 'INSUFFICIENT_DATA'],
    ['portfolio_beta_gate_json',     JSON.stringify(portfolioBetaGate || {})],

    // ── M3: TP_QUANTITY_LADDER_V1 ───────────────────────────────
    ['tp_quantity_ladder_json',      JSON.stringify(tpLadderRows || [])],
    ['tp_quantity_ladder_lock',      'true'],

    // ── M4: EVENT_RISK_HOLD_GATE_V1 ─────────────────────────────
    ['event_risk_json',              JSON.stringify(eventRiskRows || [])],
    ['event_risk_lock',              'true'],

    // ── M5: SECTOR_CONCENTRATION_LIMIT_V1 ───────────────────────
    ['sector_concentration_gate',    (sectorConcentration || {}).gate_status || 'PASS'],
    ['sector_concentration_json',    JSON.stringify((sectorConcentration || {}).by_sector || [])],

    // ── N1: POSITION_SIZE_REGIME_SCALE_V1 ───────────────────────
    ['regime_size_scale',            (regimeSizeScale || {}).scale !== undefined ? (regimeSizeScale || {}).scale : 1.0],

    // ── N3: STOP_PRICE_ADEQUACY_V1 ──────────────────────────────
    ['stop_adequacy_json',           JSON.stringify(stopAdequacyRows || [])],
    ['stop_adequacy_lock',           'true'],

    // ── N4: HOLDING_STALE_REVIEW_V1 ─────────────────────────────
    ['holding_stale_json',           JSON.stringify(staleRows || [])],
    ['holding_stale_lock',           'true'],

    // ── N5: REGIME_CASH_UPLIFT_V1 ───────────────────────────────
    ['regime_cash_uplift_min_pct',   typeof regimeCashMinPct === 'number' ? regimeCashMinPct : cashFloorInfo.minPct],

    // ── O1: SINGLE_POSITION_WEIGHT_CAP_V1 ───────────────────────
    ['single_position_weight_gate',  (singlePositionWeightCap || {}).gate_status || 'PASS'],
    ['single_position_weight_json',  JSON.stringify((singlePositionWeightCap || {}).by_position || [])],

    // ── O2: SEMICONDUCTOR_CLUSTER_GATE_V1 ───────────────────────
    ['semiconductor_cluster_gate',   (semiconductorClusterGate || {}).gate_status || 'PASS'],
    ['semiconductor_cluster_json',   JSON.stringify(semiconductorClusterGate || {})],

    // ── O3: PORTFOLIO_DRAWDOWN_GATE_V1 ──────────────────────────
    ['portfolio_drawdown_gate',      (portfolioDrawdownGate || {}).gate    || 'INSUFFICIENT_DATA'],
    ['portfolio_drawdown_pct',       (portfolioDrawdownGate || {}).drawdown_pct !== null ? (portfolioDrawdownGate || {}).drawdown_pct : null],
    ['portfolio_peak_krw',           (portfolioDrawdownGate || {}).peak_krw || null],

    // ── O4: WIN_LOSS_STREAK_GUARD_V1 ────────────────────────────
    ['win_loss_streak_state',        (winLossStreakGuard || {}).state      || 'INSUFFICIENT_HISTORY'],
    ['win_loss_streak_buy_scale',    (winLossStreakGuard || {}).buy_scale  !== undefined ? (winLossStreakGuard || {}).buy_scale : 1.0],
    ['win_loss_streak_win_rate_pct', (winLossStreakGuard || {}).win_rate_pct !== null ? (winLossStreakGuard || {}).win_rate_pct : null],

    // ── O5: POSITION_COUNT_LIMIT_V1 ─────────────────────────────
    ['position_count_gate',          (positionCountLimit || {}).gate_status || 'PASS'],
    ['position_count',               (positionCountLimit || {}).position_count !== undefined ? (positionCountLimit || {}).position_count : 0],
    ['position_count_max',           (positionCountLimit || {}).max_count     !== undefined ? (positionCountLimit || {}).max_count     : 8],

    // ── P1: STOP_BREACH_ALERT_V1 ─────────────────────────────────
    ['stop_breach_gate',             (stopBreachAlert || {}).gate            || 'PASS'],
    ['stop_breach_alert_json',       JSON.stringify((stopBreachAlert || {}).alerts || [])],

    // ── P1-BIS: RELATIVE_STOP_SIGNAL_V1 ─────────────────────────
    ['relative_stop_gate',           ((hApex || {}).relative_stop_signal || {}).gate    || 'PASS'],
    ['relative_stop_signal_json',    JSON.stringify(((hApex || {}).relative_stop_signal || {}).signals || [])],

    // ── P2: TP_TRIGGER_ALERT_V1 ──────────────────────────────────
    ['tp_trigger_gate',              (tpTriggerAlert || {}).gate             || 'PASS'],
    ['tp_trigger_alert_json',        JSON.stringify((tpTriggerAlert || {}).triggered || [])],

    // ── P3: HEAT_CONCENTRATION_ALERT_V1 ─────────────────────────
    ['heat_concentration_gate',      (heatConcentrationAlert || {}).gate     || 'PASS'],
    ['heat_concentration_json',      JSON.stringify((heatConcentrationAlert || {}).by_holding || [])],

    // ── P4: REGIME_TRANSITION_ALERT_V1 ──────────────────────────
    ['regime_transition_type',       (regimeTransitionAlert || {}).transition_type || 'NO_CHANGE'],
    ['regime_transition_json',       JSON.stringify(regimeTransitionAlert || {})],

    // ── P5: PORTFOLIO_HEALTH_SCORE_V1 ────────────────────────────
    ['portfolio_health_label',       (portfolioHealthScore || {}).label      || 'CAUTION'],
    ['portfolio_health_score',       (portfolioHealthScore || {}).score      !== undefined ? (portfolioHealthScore || {}).score : 50],
    ['portfolio_health_critical_count', (portfolioHealthScore || {}).critical_count || 0],
    ['portfolio_health_caution_count',  (portfolioHealthScore || {}).caution_count  || 0],
    ['portfolio_health_blocked_json',   JSON.stringify((portfolioHealthScore || {}).blocked_gates || [])],

    // ── [2026-05-20_HARNESS_V5] H6/H7/H8 신규 게이트 ────────────────────
    ['breakout_quality_gate_json',  JSON.stringify((hApex || {}).breakout_quality_gate_json || [])],
    ['breakout_quality_gate_lock',  'true'],
    ['anti_whipsaw_gate_json',      JSON.stringify((hApex || {}).anti_whipsaw_gate_json || [])],
    ['anti_whipsaw_gate_lock',      'true'],
    ['smart_cash_raise_json',       JSON.stringify((hApex || {}).smart_cash_raise_json || [])],
    ['smart_cash_raise_route',      (hApex || {}).smart_cash_raise_route || 'NO_ACTION'],

    // ── [2026-05-21_CLA_HARNESS_V1] SFG 하네스 출력 ──────────────────────────
    ['satellite_failure_gate_json', JSON.stringify((hApex || {}).satellite_failure_gate_json || {})],
    ['sapg_json',                   JSON.stringify((hApex || {}).sapg_json || {})],
    ['sfg_v1_lock',                 'true'],

    // ── [SPRINT2_REGIME_CLA_V1] CONCENTRATED_LEADER_ADVANCE 게이트 ──────────
    ['regime_cla_json', (function() {
      var phase = (regimeTrimGuidance || {}).phase || 'UNKNOWN';
      var cla_active = phase === 'CONCENTRATED_LEADER_ADVANCE';
      var sc = semiconductorClusterGate || {};
      var combined_pct = sc.combined_pct || 0;
      var cluster_state = cla_active ? 'CLUSTER_HOLD_ONLY'
                                     : (sc.cluster_state || 'CLUSTER_OPEN');
      var cla_exit = cla_active ? 'CLA_ACTIVE' : 'CLA_EXIT_CONFIRMED';
      var rag_pass = !cla_active || combined_pct < 60.0;
      return JSON.stringify({
        cla_active:           cla_active,
        market_regime:        phase,
        cluster_state:        cluster_state,
        cluster_combined_pct: combined_pct,
        cla_exit_status:      cla_exit,
        core_sell_blocked:    cla_active,
        satellite_buy_gate:   (cla_active && combined_pct >= 60.0)
                                ? 'CLUSTER_HOLD_ONLY' : 'CLUSTER_OPEN',
        cash_raise_priority:  cla_active ? 'LAGGARD_BROKEN_FIRST' : 'H2_RANK',
        rag_v1:               rag_pass ? 'PASS' : 'FAIL',
        rag_reason:           rag_pass
          ? 'CLA 비활성 또는 반도체 합산 비중 60% 미만 — 위성 BUY 허용'
          : 'CLA 활성 + 반도체 합산 비중 ≥60% — 위성 신규 BUY 차단',
        formula_id:           'CONCENTRATED_LEADER_ADVANCE_V1',
      });
    })()],
    ['cla_exit_status', (function() {
      var phase = (regimeTrimGuidance || {}).phase || 'UNKNOWN';
      return phase === 'CONCENTRATED_LEADER_ADVANCE' ? 'CLA_ACTIVE' : 'CLA_EXIT_CONFIRMED';
    })()],
    ['rag_v1', (function() {
      var phase = (regimeTrimGuidance || {}).phase || 'UNKNOWN';
      var cla_active = phase === 'CONCENTRATED_LEADER_ADVANCE';
      var combined_pct = (semiconductorClusterGate || {}).combined_pct || 0;
      return (!cla_active || combined_pct < 60.0) ? 'PASS' : 'FAIL';
    })()],
    ['rag_reason', (function() {
      var phase = (regimeTrimGuidance || {}).phase || 'UNKNOWN';
      var cla_active = phase === 'CONCENTRATED_LEADER_ADVANCE';
      var combined_pct = (semiconductorClusterGate || {}).combined_pct || 0;
      if (!cla_active) return 'CLA 비활성 — RAG 조건 불필요';
      return combined_pct < 60.0
        ? 'CLA 활성이나 반도체 합산 60% 미만 — 위성 BUY 허용'
        : 'CLA 활성 + 반도체 합산 ≥60% — 위성 신규 BUY 차단';
    })()],

    ['apex_formula_ids',
      'ALPHA_LEAD_SCORE_V1,FOLLOW_THROUGH_CONFIRM_V1,DISTRIBUTION_RISK_SCORE_V1,'
      + 'PROFIT_PRESERVATION_STATE_V1,SMART_CASH_RAISE_PLAN_V1,REBOUND_SELL_TRIGGER_V1,'
      + 'EXECUTION_QUALITY_GUARD_V1,BUY_PERMISSION_MATRIX_V1,SELL_QUANTITY_ALLOCATOR_V1,'
      + 'LIMIT_PRICE_POLICY_V1,STAGED_ENTRY_TRANCHE_V1,K2_STAGED_REBOUND_SELL,K3_REGIME_SELL_PRIORITY_V1,'
      + 'SECTOR_ROTATION_MOMENTUM_V1,RATCHET_TRAILING_AUTO_V1,PRE_DISTRIBUTION_EARLY_WARNING_V1,'
      + 'DYNAMIC_HEAT_GATE_V1,DRAWDOWN_GUARD_V1,PORTFOLIO_BETA_GATE_V1,TP_QUANTITY_LADDER_V1,'
      + 'EVENT_RISK_HOLD_GATE_V1,SECTOR_CONCENTRATION_LIMIT_V1,'
      + 'POSITION_SIZE_REGIME_SCALE_V1,VOLUME_BREAKOUT_CONFIRM_V1,STOP_PRICE_ADEQUACY_V1,'
      + 'HOLDING_STALE_REVIEW_V1,REGIME_CASH_UPLIFT_V1,'
      + 'SINGLE_POSITION_WEIGHT_CAP_V1,SEMICONDUCTOR_CLUSTER_GATE_V1,'
      + 'PORTFOLIO_DRAWDOWN_GATE_V1,WIN_LOSS_STREAK_GUARD_V1,POSITION_COUNT_LIMIT_V1,'
      + 'STOP_BREACH_ALERT_V1,TP_TRIGGER_ALERT_V1,HEAT_CONCENTRATION_ALERT_V1,'
      + 'REGIME_TRANSITION_ALERT_V1,PORTFOLIO_HEALTH_SCORE_V1,'
      + 'BREAKOUT_QUALITY_GATE_V2,ANTI_WHIPSAW_HOLD_GATE_V1,SMART_CASH_RAISE_V2,'
      + 'BENCHMARK_RELATIVE_TIMESERIES_V1,RS_VERDICT_V2,SATELLITE_ALPHA_QUALITY_GATE_V1,'
      + 'CASH_CREATION_PURPOSE_LOCK_V1,SATELLITE_AGGREGATE_PNL_GATE_V1,ALPHA_EVALUATION_WINDOW_V1,'
      + 'ALPHA_FEEDBACK_LOOP_V1,ENTRY_FRESHNESS_GATE_V1,SELL_VALUE_PRESERVATION_GATE_V1,'
      + 'INDEX_RELATIVE_HEALTH_GATE_V1,'
      + 'RS_VERDICT_V1,COMPOSITE_VERDICT_V1,REPLACEMENT_ALPHA_GATE_V1,SATELLITE_FAILURE_GATE_V1,'
      + 'CONCENTRATED_LEADER_ADVANCE_V1,'
      // ── [2026-05-23_PROPOSAL46] PA1~PA5
      + 'PREDICTIVE_ALPHA_ENGINE_V1,ANTI_LATE_ENTRY_GATE_V2,CASH_PRESERVATION_SELL_ENGINE_V2,'
      + 'MACRO_EVENT_SYNCHRONIZER_V1,CONSISTENCY_VALIDATOR_V2'],

    // ── [2026-05-23_PROPOSAL46] PA1~PA5 신규 하네스 출력 ─────────────────────────
    ['predictive_alpha_json',       JSON.stringify((hApex || {}).predictive_alpha_json       || [])],
    ['anti_late_entry_json',        JSON.stringify((hApex || {}).anti_late_entry_json        || [])],
    ['cash_preservation_sell_json', JSON.stringify((hApex || {}).cash_preservation_sell_json || [])],
    ['macro_event_json',            JSON.stringify((hApex || {}).macro_event_json            || {})],
    ['macro_risk_score',            (hApex || {}).macro_risk_score  !== undefined ? String((hApex || {}).macro_risk_score)  : ''],
    ['macro_risk_regime',           (hApex || {}).macro_risk_regime || ''],
    ['mega_sell_alert',             (hApex || {}).mega_sell_alert   === true ? 'true' : 'false'],
    ['consistency_report_json',     JSON.stringify((hApex || {}).consistency_report_json     || {})],
    ['consistency_score',           (hApex || {}).consistency_score !== undefined ? String((hApex || {}).consistency_score) : ''],
    ['cv_verdict',                  (hApex || {}).cv_verdict        || ''],
    ['portfolio_alpha_confidence',  (hApex || {}).portfolio_alpha_confidence !== null && (hApex || {}).portfolio_alpha_confidence !== undefined ? String((hApex || {}).portfolio_alpha_confidence) : ''],
    ['fomc_position_size_gate',     (hApex || {}).fomc_position_size_gate || 'INACTIVE'],
    ['prediction_accuracy_rate',    (hApex || {}).prediction_accuracy_rate !== null && (hApex || {}).prediction_accuracy_rate !== undefined ? String((hApex || {}).prediction_accuracy_rate) : ''],
    ['watch_breakout_candidates_json', JSON.stringify((hApex || {}).watch_breakout_candidates_json || [])],
    ['anti_whipsaw_reentry_json',      JSON.stringify((hApex || {}).anti_whipsaw_reentry_json      || [])],
    ['alpha_history_summary_json',     JSON.stringify((hApex || {}).alpha_history_summary_json     || {})],

    // ── P4 허용 목록 (LLM 참조용) ────────────────────────────────
    ['p4_intraday_allowed_actions', JSON.stringify(INTRADAY_ALLOWED_ACTIONS)],

    // ── M1: 국면별 감축 가이던스 (REGIME_TRIM_WEIGHT_V1) ──────────
    // LLM의 주관적 국면 판단 및 임의 감축비율 산출을 차단
    ['market_regime_state',        (regimeTrimGuidance || {}).phase || 'UNKNOWN'],
    ['regime_trim_guidance_json',  JSON.stringify(regimeTrimGuidance || {})],
    ['regime_trim_lock',           'true'],

    // ── H3: 주도주 승자 포지션 보호 게이트 (SECULAR_LEADER_REGIME_GATE_V1) ─
    // 삼성전자·SK하이닉스 secular_leader_profit_lock 발동 여부 결정론적 확정
    ['secular_leader_gate_json',   JSON.stringify(
      (h4.prices || []).reduce(function(acc, p) {
        if (p.secular_leader_gate_status && p.secular_leader_gate_status !== 'NOT_APPLICABLE') {
          acc[p.ticker] = {
            active:  p.secular_leader_gate_active,
            status:  p.secular_leader_gate_status,
            reasons: p.secular_leader_gate_reasons
          };
        }
        return acc;
      }, {})
    )],

    // ── M4: 5억원 목표 자산 추적 대시보드 ──────────────────────────────────────
    // GOAL_RETIREMENT_V1: 은퇴자산 5억원 목표 — 하네스 결정론적 산출 (LLM 재판단 금지)
    ['goal_asset_krw',          M4_GOAL_KRW],
    ['goal_current_asset_krw',  Math.round(m4Asset)],
    ['goal_achievement_pct',    m4Achieve],
    ['goal_remaining_krw',      Math.round(m4Remain)],
    ['goal_eta_months',         m4EtaMonths],
    ['goal_eta_label',          m4EtaLabel],
    ['goal_monthly_growth_pct', m4NetExp30],
    ['goal_status',             m4Asset >= M4_GOAL_KRW ? 'ACHIEVED' : 'IN_PROGRESS'],

    // ── [3RD_HARNESS_V1] 커버리지 완성 — GAS 30.2% → 100% ───────────────────────────
    // 목표: LLM 자유도 69.8% → 0% (완전 결정론적)
    // 43/43 필수 필드를 GAS가 직접 산출 — LLM 추정 불필요

    // HARNESS_DATA_FRESHNESS_GATE_V1
    ['data_freshness_status',
      (((hApex || {}).data_freshness_json) || {}).data_freshness_status
      || (snapshotGate.status === 'PASS' ? 'FRESH' : 'STALE')],

    // INTRADAY_ACTION_MATRIX_V1
    ['intraday_scope', intradayLock ? 'INTRADAY_RESTRICTED' : 'FULL_EOD'],

    // PROFIT_LOCK_RATCHET_V1 — profit_preservation_json 최고 단계
    ['profit_lock_stage', (function() {
      var pp = (hApex || {}).profit_preservation_json || [];
      var order = { APEX_SUPER: 7, APEX_TRAILING: 6, PROFIT_LOCK_30: 5, PROFIT_LOCK_20: 4,
                    PROFIT_LOCK_10: 3, BREAKEVEN_RATCHET: 2, NORMAL: 1 };
      var best = 'NORMAL';
      pp.forEach(function(r) {
        var st = String(r.profit_preservation_state || 'NORMAL');
        if ((order[st] || 1) > (order[best] || 1)) best = st;
      });
      return best;
    })()],
    ['auto_trailing_stop', (function() {
      var pp = (hApex || {}).profit_preservation_json || [];
      var maxStop = null;
      pp.forEach(function(r) {
        if (typeof r.auto_trailing_stop === 'number'
            && (maxStop === null || r.auto_trailing_stop > maxStop)) {
          maxStop = r.auto_trailing_stop;
        }
      });
      return maxStop !== null ? maxStop : 0;
    })()],

    // PROFIT_RATCHET_TIERED_V2 — APEX_SUPER(+60%) ATR×1.2 trailing
    // profit_pct >= 60 → APEX_SUPER; inject_computed_harness.py 가 정밀값 교체
    ['ratchet_stage_v2', (function() {
      var pp = (hApex || {}).profit_preservation_json || [];
      var order = { APEX_SUPER: 7, APEX_TRAILING: 6, PROFIT_LOCK_30: 5, PROFIT_LOCK_20: 4,
                    PROFIT_LOCK_10: 3, BREAKEVEN_RATCHET: 2, NORMAL: 1 };
      var best = 'NORMAL';
      pp.forEach(function(r) {
        var pct = typeof r.profit_pct === 'number' ? r.profit_pct : 0;
        var st  = pct >= 60 ? 'APEX_SUPER'
                : String(r.profit_preservation_state || 'NORMAL');
        if ((order[st] || 1) > (order[best] || 1)) best = st;
      });
      return best;
    })()],
    ['auto_trailing_stop_v2', (function() {
      var pp = (hApex || {}).profit_preservation_json || [];
      var maxStop = null;
      pp.forEach(function(r) {
        // APEX_SUPER 종목: 기존 auto_trailing_stop 그대로 사용 (Python inject로 ATR×1.2 보정)
        if (typeof r.auto_trailing_stop === 'number'
            && (maxStop === null || r.auto_trailing_stop > maxStop)) {
          maxStop = r.auto_trailing_stop;
        }
      });
      return maxStop !== null ? maxStop : 0;
    })()],

    // FLOW_ACCELERATION_V1 — W4 신호 집계
    ['flow_acceleration_status', (function() {
      var ph = (hAlpha || {}).per_holding || [];
      return ph.some(function(h) { return h.w4_status === 'FLOW_DECEL_WARNING'; })
        ? 'FLOW_DECEL_DETECTED' : 'NORMAL';
    })()],

    // DISTRIBUTION_SELL_DETECTOR_V1 — distribution_risk_json 집계
    ['distribution_sell_detector_status', (function() {
      var dist = (hApex || {}).distribution_risk_json || [];
      if (dist.some(function(d) { return d.anti_distribution_state === 'BLOCK_BUY'; }))
        return 'DISTRIBUTION_DETECTED';
      if (dist.some(function(d) { return d.anti_distribution_state === 'TRIM_REVIEW'; }))
        return 'TRIM_REVIEW_ALERT';
      return 'NORMAL';
    })()],
    ['signals_count', (function() {
      var dist = (hApex || {}).distribution_risk_json || [];
      return dist.filter(function(d) { return d.anti_distribution_state !== 'PASS'; }).length;
    })()],

    // BREAKOUT_QUALITY_GATE_V2 — breakout_quality_gate_json 최소 점수
    ['breakout_quality_score', (function() {
      var bqg = (hApex || {}).breakout_quality_gate_json || [];
      if (!bqg.length) return 0;
      var min = null;
      bqg.forEach(function(b) {
        if (typeof b.breakout_quality_score === 'number'
            && (min === null || b.breakout_quality_score < min)) min = b.breakout_quality_score;
      });
      return min !== null ? min : 0;
    })()],

    // ANTI_CHASING_VELOCITY_V1 — entry_freshness_json 집계 (worst-case)
    ['anti_chasing_verdict', (function() {
      var ef = (hApex || {}).entry_freshness_json || [];
      var worst = 'CLEAR';
      ef.forEach(function(r) {
        var fs = String(r.freshness_state || '').toUpperCase();
        if (fs === 'BLOCK_LATE_CHASE')                     { worst = 'BLOCK_CHASE'; }
        else if (fs === 'PULLBACK_WAIT' && worst !== 'BLOCK_CHASE') { worst = 'PULLBACK_WAIT'; }
      });
      return worst;
    })()],
    ['anti_chasing_velocity_status', (function() {
      var ef = (hApex || {}).entry_freshness_json || [];
      var worst = 'PASS';
      ef.forEach(function(r) {
        var fs = String(r.freshness_state || '').toUpperCase();
        if (fs === 'BLOCK_LATE_CHASE')                     { worst = 'BLOCKED'; }
        else if (fs === 'PULLBACK_WAIT' && worst === 'PASS') { worst = 'WAIT'; }
      });
      return worst;
    })()],

    // PULLBACK_ENTRY_TRIGGER_V1
    ['pullback_entry_verdict', (function() {
      var ef = (hApex || {}).entry_freshness_json || [];
      return ef.some(function(r) {
        return String(r.freshness_state || '').toUpperCase() === 'PULLBACK_WAIT';
      }) ? 'PULLBACK_ZONE' : 'ABOVE_PULLBACK_ZONE';
    })()],
    // per-ticker only; Python inject가 종목별 기준가 제공. 0 = 활성 눌림목 없음.
    ['pullback_entry_trigger_price', 0],

    // CASH_RECOVERY_OPTIMIZER_V1 — cash_raise_plan_json이 GAS 확정 현금회복 계획
    ['cash_recovery_plan_json', JSON.stringify((hApex || {}).cash_raise_plan_json || [])],

    // SELL_WATERFALL_ENGINE_V1 — 동일 계획(폭포수 매도 순서)
    ['waterfall_plan_json', JSON.stringify((hApex || {}).cash_raise_plan_json || [])],

    // ── SPRINT 1-3 Python-computed fields: GAS placeholder (inject.py가 덮어씀) ──
    // ANTI_CHASING_VELOCITY_V1 — per-ticker 속도 게이트 (inject.py 교체)
    ['anti_chasing_velocity_json', '[]'],
    // DISTRIBUTION_SELL_DETECTOR_V1 — per-ticker 6신호 배급형 탐지 (inject.py 교체)
    ['distribution_sell_detector_json', '[]'],
    // K2_STAGED_REBOUND_SELL_V1 — 현금확보 K2 분할 계획 (inject.py 교체)
    ['k2_staged_rebound_sell_json', '[]'],
    // PRE_DISTRIBUTION_EARLY_WARNING_V1 — distribution_risk_json 선행경보 집계 (inject.py 교체)
    ['pre_distribution_warning', JSON.stringify({ status: 'NONE', affected_count: 0, affected_tickers: [],
                                                  buy_gate: 'PASS', formula_id: 'PRE_DISTRIBUTION_EARLY_WARNING_V1' })],

    // SELL_EXECUTION_TIMING_V1
    ['sell_timing_verdict',
      intradayLock ? 'TIMING_BLOCKED_INTRADAY'
                   : (snapshotGate.status === 'PASS' ? 'SELL_READY' : 'SELL_BLOCKED_DATA')],
    ['sell_execution_window', intradayLock ? 'NEXT_DAY_OPEN' : 'EOD_30MIN'],

    // SELL_VALUE_PRESERVATION_TIERED_V2 — sell_value_preservation_json 집계
    ['preservation_verdict', (function() {
      var svp = (hApex || {}).sell_value_preservation_json || [];
      if (!svp.length) return 'NO_DATA';
      if (svp.some(function(r) { return r.sell_value_preservation_state === 'EMERGENCY_EXIT'; }))
        return 'EMERGENCY_EXIT';
      if (svp.some(function(r) { return r.sell_value_preservation_state === 'TRIM_ONLY'; }))
        return 'TRIM_ONLY';
      if (svp.some(function(r) {
        return r.sell_value_preservation_state === 'REBOUND_CONFIRM_HOLD';
      })) return 'REBOUND_CONFIRM_HOLD';
      return 'HOLD';
    })()],

    // TICK_NORMALIZER_V1 — GAS는 모든 가격에 tickNormalize_() 적용
    ['tick_normalized_price', true],

    // SELL_PRICE_SANITY_V1 — H4 prices 호가단위 검증 (GAS 생성 가격은 항상 PASS)
    // inject_computed_harness.py 가 스프레드시트 원본 입력값 검증 후 교체
    ['sell_price_sanity_status', (function() {
      var prices = (h4 || {}).prices || [];
      var worst = 'PASS';
      prices.forEach(function(p) {
        var candidates = [p.stop_price, p.tp1_price, p.tp2_price];
        candidates.forEach(function(price) {
          if (price == null || price <= 0) return;
          var tick = getTickSize_(price);
          if (price % tick !== 0) { worst = 'INVALID_TICK'; }
        });
      });
      return worst;
    })()],

    // BENCHMARK_RELATIVE_TIMESERIES_V1 — BRT 집계
    ['brt_verdict', (function() {
      var brt = (hApex || {}).benchmark_relative_timeseries_json || [];
      if (!brt.length) return 'NO_DATA';
      if (brt.some(function(r) { return r.brt_verdict === 'BROKEN'; })) return 'BROKEN';
      if (brt.some(function(r) { return r.brt_verdict === 'LEADER'; })) return 'LEADER';
      return 'MARKET';
    })()],
    ['brt_rs_slope', (function() {
      var brt = (hApex || {}).benchmark_relative_timeseries_json || [];
      var slopes = brt.map(function(r) { return r.rs_line_20d_slope; })
        .filter(function(v) { return v != null && isFinite(v); });
      if (!slopes.length) return 0;
      return parseFloat((slopes.reduce(function(a, b) { return a + b; }, 0)
        / slopes.length).toFixed(4));
    })()],

    // RS_VERDICT_V2 FUSION — buy_permission_json + BRT 융합 집계
    ['rs_verdict', (function() {
      var bp  = (hApex || {}).buy_permission_json || [];
      var brt = (hApex || {}).benchmark_relative_timeseries_json || [];
      if (!bp.length) return 'NO_DATA';
      // V1 raw
      var v1_broken  = bp.some(function(r) { return (r.rs_verdict_v1 || r.rs_verdict) === 'BROKEN'; });
      var v1_laggard = bp.some(function(r) { return (r.rs_verdict_v1 || r.rs_verdict) === 'LAGGARD'; });
      var v1_leader  = bp.some(function(r) { return (r.rs_verdict_v1 || r.rs_verdict) === 'LEADER'; });
      // RS_VERDICT-5: brt_verdict=BROKEN AND v1=LEADER → V2 결과는 LAGGARD
      if (brt.some(function(r) { return r.brt_verdict === 'BROKEN'; }) && v1_leader && !v1_broken) {
        return 'LAGGARD';
      }
      if (v1_broken)  return 'BROKEN';
      if (v1_laggard) return 'LAGGARD';
      if (v1_leader)  return 'LEADER';
      return 'MARKET';
    })()],
    ['rs_verdict_source', (function() {
      var brt = (hApex || {}).benchmark_relative_timeseries_json || [];
      return brt.length ? 'V2_FUSION' : 'V1_ONLY';
    })()],
    ['rs_verdict_v1_raw', (function() {
      var bp = (hApex || {}).buy_permission_json || [];
      if (!bp.length) return 'NO_DATA';
      if (bp.some(function(r) { return (r.rs_verdict_v1 || r.rs_verdict) === 'BROKEN'; }))  return 'BROKEN';
      if (bp.some(function(r) { return (r.rs_verdict_v1 || r.rs_verdict) === 'LAGGARD'; })) return 'LAGGARD';
      if (bp.some(function(r) { return (r.rs_verdict_v1 || r.rs_verdict) === 'LEADER'; }))  return 'LEADER';
      return 'MARKET';
    })()],

    // SATELLITE_ALPHA_QUALITY_GATE_V1 — saqg_json 집계
    ['saqg_verdict', (function() {
      var saqg = (hApex || {}).saqg_json || [];
      if (!saqg.length) return 'NO_DATA';
      if (saqg.some(function(r) { return r.saqg_v1 === 'ELIGIBLE'; }))  return 'ELIGIBLE';
      if (saqg.every(function(r) { return r.saqg_v1 === 'EXCLUDED'; })) return 'ALL_EXCLUDED';
      return 'WATCHLIST_ONLY';
    })()],

    // SATELLITE_AGGREGATE_PNL_GATE_V1
    ['sapg_verdict', ((hApex || {}).sapg_json || {}).sapg_status || 'INSUFFICIENT_DATA'],

    // LLM_SERVING_CONSTRAINT_V1
    ['serving_constraint_check', 'PASS'],

    // DETERMINISTIC_ROUTING_ENGINE_V1 — 9단계 라우팅 완료 로그
    ['routing_execution_log', JSON.stringify({
      stages_completed: [
        'STAGE_0_FRESHNESS', 'STAGE_1_CASH_RATIOS', 'STAGE_2_RATCHET',
        'STAGE_3_DISTRIBUTION', 'STAGE_4_BUY_TIMING', 'STAGE_5_SELL_WATERFALL',
        'STAGE_6_PRICE_VALIDATION', 'STAGE_7_RS_BRT', 'STAGE_8_SATELLITE',
        'STAGE_9_LLM_SERVING'
      ],
      routing_completed: true,
      formula_id: 'DETERMINISTIC_ROUTING_ENGINE_V1'
    })],

    // TRADE_QUALITY_SCORER_V1 — 월간 배치 결과 캐시 읽기 (없으면 MONTHLY_BATCH_PENDING)
    ['trade_quality_json', (function() {
      try {
        var ss2 = getSpreadsheet_();
        var setSh = ss2.getSheetByName('settings');
        if (!setSh) return JSON.stringify({ status: 'MONTHLY_BATCH_PENDING', last_computed: null, formula_id: 'TRADE_QUALITY_SCORER_V1' });
        var sData = setSh.getDataRange().getValues();
        for (var si = 0; si < sData.length; si++) {
          if (String(sData[si][0] || '').trim() === 'trade_quality_json') {
            var raw = sData[si][1];
            if (raw) {
              var s = String(raw);
              return s.length <= 45000 ? s : JSON.stringify({ status: 'OVERSIZED_TRIMMED', formula_id: 'TRADE_QUALITY_SCORER_V1' });
            }
            break;
          }
        }
      } catch(e) { Logger.log('[HARNESS_ROWS] trade_quality_json 읽기 오류: ' + e.message); }
      return JSON.stringify({ status: 'MONTHLY_BATCH_PENDING', last_computed: null, formula_id: 'TRADE_QUALITY_SCORER_V1' });
    })()],

    // PATTERN_BLACKLIST_AUTO_V1 — 월간 배치 결과 캐시 읽기
    ['pattern_blacklist_status', (function() {
      try {
        var ss3 = getSpreadsheet_();
        var setSh3 = ss3.getSheetByName('settings');
        if (!setSh3) return 'INACTIVE';
        var sData3 = setSh3.getDataRange().getValues();
        for (var si3 = 0; si3 < sData3.length; si3++) {
          if (String(sData3[si3][0] || '').trim() === 'pattern_blacklist_json') {
            try {
              var parsed = JSON.parse(String(sData3[si3][1] || '{}'));
              var hasTriggered = Array.isArray(parsed.patterns) &&
                parsed.patterns.some(function(p) { return p.pattern_blacklist_status === 'TRIGGERED'; });
              return hasTriggered ? 'TRIGGERED' : 'INACTIVE';
            } catch(pe) { break; }
          }
        }
      } catch(e) { Logger.log('[HARNESS_ROWS] pattern_blacklist_status 읽기 오류: ' + e.message); }
      return 'INACTIVE';
    })()],
    ['pattern_blacklist_json', (function() {
      try {
        var ss4 = getSpreadsheet_();
        var setSh4 = ss4.getSheetByName('settings');
        if (!setSh4) return JSON.stringify({ status: 'INACTIVE', patterns: [], pattern_count: 0, formula_id: 'PATTERN_BLACKLIST_AUTO_V1' });
        var sData4 = setSh4.getDataRange().getValues();
        for (var si4 = 0; si4 < sData4.length; si4++) {
          if (String(sData4[si4][0] || '').trim() === 'pattern_blacklist_json') {
            var raw4 = sData4[si4][1];
            if (raw4) return String(raw4);
            break;
          }
        }
      } catch(e) { Logger.log('[HARNESS_ROWS] pattern_blacklist_json 읽기 오류: ' + e.message); }
      return JSON.stringify({ status: 'INACTIVE', patterns: [], pattern_count: 0, formula_id: 'PATTERN_BLACKLIST_AUTO_V1' });
    })()],

    // ── [SPRINT4_SFG_SCALARS] SFG 스칼라 (inject.py 교체) ────────────────────
    ['sfg_v1',           'CLEAR'],
    ['sfg_broken_count', 0],
    ['sfg_failure_rate', 0],

    // ── [SPRINT4_PCG] PORTFOLIO_CORRELATION_GATE_V1 (inject.py 교체) ─────────
    ['portfolio_correlation_gate_json',
      JSON.stringify({ correlation_gate_status: 'INSUFFICIENT_DATA', satellite_cluster_beta: null,
                       effective_portfolio_beta: null, regime_beta_limit: 1.0,
                       reason: 'GAS 초기값 — inject.py 교체 대상',
                       formula_id: 'PORTFOLIO_CORRELATION_GATE_V1' })],
    ['correlation_gate_status', 'INSUFFICIENT_DATA'],

    // TICK_NORMALIZER_V1 — 종목별 호가 정규화 가격 맵 (Python inject 보완)
    ['tick_normalized_prices_json', (function() {
      var prices4 = (h4 || {}).prices || [];
      var map = {};
      prices4.forEach(function(p) {
        if (!p.ticker) return;
        var sp = p.stop_price  ? tickNormalize_(p.stop_price)  : null;
        var tp = p.tp1_price   ? tickNormalize_(p.tp1_price)   : null;
        map[p.ticker] = { stop: sp, tp1: tp };
      });
      return JSON.stringify(map);
    })()],

    // SELL_PRICE_SANITY — 종목별 상세 (ratchet_v2 per-ticker)
    ['ratchet_v2_per_ticker_json', (function() {
      var pp = (hApex || {}).profit_preservation_json || [];
      return JSON.stringify(pp.map(function(r) {
        return { ticker: r.ticker || '', profit_pct: r.profit_pct || 0,
                 ratchet_stage_v2: r.profit_preservation_state || 'NORMAL',
                 auto_trailing_stop_v2: r.auto_trailing_stop || null };
      }));
    })()],

    // ── LLM 종합 지침 V6 (SPRINT 1: D1-ROUTING·D2-LLM·A2-ANTI_CHASE·K2-REBOUND 추가) ────
    ['llm_instruction',
      'HARNESS_AUTHORITATIVE_V4(H4): '
      + '▶ 재계산 금지: sell_priority_lock·quantities_lock·prices_lock·decision_lock·alpha_shield_lock·regime_trim_lock=true — '
      + 'GAS 확정값을 LLM이 재계산·수정·추가·삭제하는 행위는 HARNESS_VIOLATION으로 보고서 전체 무효. '
      + '▶ [HS009] TP 유효성 잠금: prices_json의 tp1_price/tp2_price가 null이면 INVALID_TP_STALE — '
      + 'LLM이 대체 TP 가격을 임의 산출하는 것 절대 금지. tp1_state/tp2_state 그대로 보고. '
      + '▶ [HS010] WATCH/BLOCKED 출력 잠금: order_blueprint_json의 validation_status!=PASS인 행은 '
      + '지정가·손절가·익절가·수량 전부 null. LLM이 참고값이라도 HTS 주문 표에 숫자 기재 금지. '
      + '감시값은 별도 "WATCH 감시 원장(주문 아님)" 섹션으로만 표시. '
      + '▶ [HS011] LLM 즉석 공식 정의 금지: spec/13_formula_registry.yaml에 등록되지 않은 공식명·알고리즘명을 '
      + '즉석 정의하고 이에 기반한 원화 가격·정수 수량을 산출하는 것 절대 금지. '
      + '하네스 미구현 영역은 "DATA_MISSING — 하네스 업데이트 필요"로만 표시. '
      + '▶ [M1] 국면별 감축: regime_trim_guidance_json의 satellite_trim_pct/leader_trim_pct 범위를 그대로 인용. '
      + 'LLM이 임의 감축비율을 제시하는 것 금지. '
      + '▶ [H3] 주도주 게이트: secular_leader_gate_json의 active/status를 그대로 보고. '
      + '005930·000660 종목에서 secular_leader_gate_active=true이면 '
      + 'tp1_state=DEFERRED_SECULAR_LEADER 구간에서 TP 매도 신호 생성 금지. '
      + '하네스가 null로 전달한 tp1_price를 LLM이 임의 복원하는 것 절대 금지. '
      + '▶ Blueprint 무결성: order_blueprint_json 수정 절대 금지. blueprint_checksum(CRC32_V1) Python 검증. '
      + '▶ 구조화 출력 강제: [Rule_ID:X, Value:Y, Threshold:Z, Result:R] 포맷만 허용. '
      + '▶ Zero-Adjective: 감성 형용사·부사 금지. 수치와 Rule_ID만 허용. '
      + '▶ P4 장중 모드(intraday_lock=true): p4_intraday_allowed_actions 외 액션 출력 금지. '
      + '▶ CLAMP 발동 종목은 clamp_label 표기 필수. '
      + '▶ [M4] 목표 자산 추적: goal_achievement_pct·goal_remaining_krw·goal_eta_label은 하네스 산출값 그대로 보고. '
      + 'LLM이 5억원 달성 여부·ETA를 독자적으로 재계산하는 것 절대 금지. '
      + '▶ [G1] 현금 부족액 잠금(CASH_SHORTFALL_V1): cash_shortfall_min_krw·cash_shortfall_target_krw는 하네스 확정값. '
      + '"약 N원 필요" 형태의 LLM 즉석 계산 절대 금지. cash_current_pct_d2·cash_target_pct도 하네스 복사 전용. '
      + '▶ [G2] TRIM 계획 잠금(TRIM_PLAN_MIN_CASH_V1): trim_plan_to_min_cash_json은 H2 매도우선순위 기반 GAS 확정. '
      + 'LLM이 현금 회복을 위해 임의로 종목·수량·순서를 선택하는 것 절대 금지. 하네스 plan 복사만 허용. '
      + '▶ [APEX_V1] 판단 자료 생성시점 로직: alpha_lead_json·distribution_risk_json·buy_permission_json·'
      + 'cash_raise_plan_json·smart_sell_quantities_json·execution_quality_json은 GAS 확정값. '
      + '뒷북매수/설거지/현금확보 매도 방식은 LLM 해석 금지, *_lock=true 값 그대로 복사. '
      + 'buy_permission_state가 ALLOW_*가 아니면 BUY 수량 출력 금지. '
      + 'execution_style=OVERSOLD_REBOUND_SELL이면 rebound_wait_qty를 immediate_qty로 이동 금지. '
      + '▶ [ENTRY_FRESHNESS_GATE_V1] entry_freshness_json 없이 뒷북/추격 BUY 승인 금지. '
      + 'BLOCK_LATE_CHASE/PULLBACK_WAIT는 BUY/STAGED_BUY/ADD_ON 차단. '
      + '▶ [SELL_VALUE_PRESERVATION_GATE_V1] sell_value_preservation_json 없이 현금확보 매도와 수익보호 매도 혼용 금지. '
      + 'EMERGENCY_EXIT 외에는 반등대기 수량을 즉시매도로 승격 금지. '
      + '▶ [INDEX_RELATIVE_HEALTH_GATE_V1] index_relative_health_json 없이 지수 대비 괴리 종목을 BUY 승인 금지. '
      + 'DECOUPLED/OVER_EXTENDED는 신규 BUY 차단, UNDERPERFORMING은 WATCH 우선. '
      + '▶ [HS010-B] 종합 판단 제안표 필수 출력: comprehensive_proposal_json을 "종합 판단 제안표(PROPOSAL)" 표로 '
      + '항상 출력. PENDING_EXPORT·BLOCKED·DATA_MISSING 상태와 무관하게 생략 금지. '
      + '판단은 사용자 몫이므로 reference_stop_price·reference_tp1_price·tp1_state·reference_tp2_price·tp2_state·'
      + 'proposed_immediate_qty·proposed_staged_qty·expected_cash_krw를 그대로 표시. '
      + '이 표에서 LLM이 가격·수량을 임의로 변경하거나 새 수치를 추가하는 것 절대 금지. '
      + '▶ [HS010-C] 위성 후보 스크리닝 표 필수 출력: satellite_candidate_json을 "위성 후보 스크리닝(SATELLITE_CANDIDATE_SCREEN_V1)" 표로 '
      + '항상 출력. 후보가 0개여도 표를 출력하고 "현재 추가 적합 후보 없음"을 명시. '
      + 'satellite_candidate_summary.watch_candidates를 항상 표 제목에 병기. '
      + 'LLM이 universe 외 종목을 임의 추가하거나 grade를 변경하는 것 금지. '
      + '▶ [D1-ROUTING] 9단계 결정론적 라우팅 의무: 보고서는 routing_execution_log의 '
      + '9단계 순서(①신선도→②장중판별→③포트폴리오상태→④매도레이더→⑤매수타이밍→'
      + '⑥현금확보→⑦가격정규화→⑧RS/위성→⑨LLM서빙) 결과를 먼저 표 형태로 출력하고 '
      + '이후 분석을 진행한다. routing_execution_log 생략 시 INCOMPLETE_ROUTING_LOG 처리. '
      + '▶ [D2-LLM] LLM 8금지(위반 시 INVALID_LLM_OVERRIDE): '
      + '①미등록공식 지정가/수량 산출 금지 '
      + '②하네스BLOCK 판정 우회("그래도매수") 금지 '
      + '③SELL_PRICE_SANITY INVALID 가격 복원 금지 '
      + '④cash_shortfall LLM 즉석계산 금지 '
      + '⑤K2 반등대기 수량을 "현금급함"으로 즉시전환 금지 '
      + '⑥APEX_SUPER 구간 trailing_stop 미병기 금지 '
      + '⑦DISTRIBUTION_CONFIRMED 매수 우회 금지 '
      + '⑧routing_execution_log 생략 금지. '
      + '▶ [A2-ANTI_CHASE] anti_chasing_velocity_json의 anti_chase_verdict=BLOCK_CHASE인 '
      + '종목은 당일 신규 BUY 절대 금지. PULLBACK_WAIT는 pullback_entry_trigger_price 도달 전 매수 금지. '
      + 'distribution_sell_detector_json의 distribution_verdict=DISTRIBUTION_CONFIRMED인 종목 BUY 절대 금지. '
      + '▶ [K2-REBOUND] cash_recovery_plan_json의 rebound_wait_qty는 '
      + 'rebound_trigger_price 도달 전 즉시매도 전환 금지. "현금이 급하니까" 이유로 '
      + 'Stage 2 즉시전환 금지. emergency_full_sell=true일 때만 전량 즉시 허용. '
      + '▶ [PA47-A1] watch_breakout_candidates_json 필수 출력: promotion_eligible=true 항목을 '
      + '"급등 탐지 — 라이프사이클 재검토 권고" 표로 출력. '
      + 'lifecycle_stage=EXIT이어도 breakout_signal=WATCH_BREAKOUT_DETECTED면 즉시 매도 금지; '
      + 'satellite_lifecycle_gate_json의 breakout_promotion_recommendation=PROMOTE_TO_WATCH 참조. '
      + '후보가 0건이면 표 생략 가능. '
      + '▶ [PA47-PA1] buy_permission_json의 pa1_synthesis_verdict·pa1_direction_confidence 반드시 인용: '
      + 'EXIT_SIGNAL(dc<-30) 종목은 "방향성 부적합—보유 재검토", TRIM_SIGNAL(dc<-10) 종목은 '
      + '"비중 축소 검토"로 표시. STRONG_BUY/MODERATE_BUY 종목은 신규 진입 우선순위 상향. '
      + 'pa1_synthesis_verdict가 없는 종목은 PA1 미적용으로 명시. '
      + '▶ [PA47-A3] anti_whipsaw_reentry_json의 reentry_signal=REENTRY_CANDIDATE 종목은 '
      + '"매도 재검토 — 반등 감지" 경고로 표시. 매도 실행 전 재확인 의무. '
      + 'reentry_grade=A/B이면 매도 보류 후 다음날 재평가 권고. '
      + '▶ [PA47-B4] harness_generation_status=BLOCKED_STALE_DATA 또는 BLOCKED_CV_FAIL이면 '
      + '보고서 생성을 거부하고 "하네스 BLOCK — 데이터 갱신 후 재실행 요망"만 출력. '
      + '▶ [PROPOSAL50-EG] export_gate_json의 json_validation_status=PENDING_EXPORT이면 '
      + 'hts_entry_allowed=false — HTS 주문 입력 절대 금지. failed_checks와 resolution_guide를 출력. '
      + '▶ [PROPOSAL50-EJCE] ejce_json의 consensus_result=NO_BUY 종목은 3개 관점 중 2개 이상 BLOCK — '
      + 'buy_permission이 ALLOW여도 EJCE NO_BUY 종목 BUY 실행 금지. block_reasons 인용 필수. '
      + '▶ [PROPOSAL50-SCRS] scrs_v2_json의 selected_combo만 현금확보 매도 기재 허용. '
      + 'immediate_sell_qty와 rebound_wait_qty 구분 표시 의무. '
      + 'emergency_level=TRIM_ONLY이면 추가 매도 금지. '
      + '▶ [PROPOSAL50-DSLE] serving_lock_json의 llm_serving_budget.numeric_generation_allowed=0 — '
      + 'LLM이 가격·수량·수익률 등 숫자를 자체 생성하는 것 절대 금지. '
      + '▶ [PROPOSAL50-H10] shadow_ledger_json은 BLOCKED/INVALID 블루프린트를 투명하게 보존. '
      + '산출 지정가·손절가·익절가·이론수량을 null 처리하거나 은폐 금지(HS010). '
      + '사용자의 사후 평가·오버라이드를 위해 "투명한 감시 원장" 표로 출력. '
      + '▶ [PROPOSAL50-D2] llm_serving_constraint_json의 constraint_status=INVALID_LLM_OVERRIDE이면 '
      + '보고서 조립 중단 — violations 목록 전체를 "[INVALID_LLM_OVERRIDE: 사유]"로 표시 후 재실행 요망.'],

    // ── [PROPOSAL50] MRAG-V2 + M5 V1.1 의무감축계획 ─────────────────────────────────────────
    ['mrag_v2_json',            JSON.stringify((hApex || {}).mrag_v2_json            || {})],
    ['effective_heat_gate_threshold', (hApex || {}).effective_heat_gate_threshold    || null],
    ['effective_position_size_scale', (hApex || {}).effective_position_size_scale    || null],
    ['mandatory_reduction_json', JSON.stringify((hApex || {}).mandatory_reduction_json || {})],

    // ── [PROPOSAL50] Export Gate / Routing Trace / Watch Ledger / EJCE / SCRS-V2 / DSLE ──────
    ['export_gate_json',        JSON.stringify((hApex || {}).export_gate_json        || {})],
    ['hts_entry_allowed',       String((hApex || {}).hts_entry_allowed               || false)],
    ['routing_trace_json',      JSON.stringify((hApex || {}).routing_trace_json      || {})],
    ['watch_ledger_json',       JSON.stringify((hApex || {}).watch_ledger_json       || [])],
    ['ejce_json',               JSON.stringify((hApex || {}).ejce_json               || [])],
    ['scrs_v2_json',            JSON.stringify((hApex || {}).scrs_v2_json            || {})],
    ['serving_lock_json',       JSON.stringify((hApex || {}).serving_lock_json       || {})],

    // ── [PROPOSAL50-P0-GAP] H10/D2 신규 필드 ───────────────────────────────────────────────────
    ['shadow_ledger_json',      JSON.stringify((hApex || {}).shadow_ledger_json      || { shadow_ledger: [], blocked_count: 0 })],
    ['llm_serving_constraint_json', JSON.stringify((hApex || {}).llm_serving_constraint_json || { constraint_status: 'DATA_MISSING' })],

    // ── [PROPOSAL51] SU_51_K 신규 필드 ────────────────────────────────────────────────────────
    ['cluster_sync_result_json',    JSON.stringify((hApex || {}).cluster_sync_result_json    || {})],
    ['proactive_sell_radar_json',   JSON.stringify((hApex || {}).proactive_sell_radar_json   || [])],
    ['sell_pass_accuracy_rate',     (hApex || {}).sell_pass_accuracy_rate !== undefined
                                     ? (hApex || {}).sell_pass_accuracy_rate : null],
    ['sell_execution_quality_json', JSON.stringify((hApex || {}).sell_execution_quality_json || [])],
    // ── [PROPOSAL51] P0-D / P1-B / P1-C 신규 필드 ──────────────────────────────────────────
    ['price_hierarchy_json',         JSON.stringify((hApex || {}).price_hierarchy_json        || [])],
    ['data_quality_gate_v2_json',    JSON.stringify((hApex || {}).data_quality_gate_v2_json   || {})],
    ['cash_recovery_display_json',   JSON.stringify((hApex || {}).cash_recovery_display_json  || {})],
    ['portfolio_health_json',        JSON.stringify((hApex || {}).portfolio_health_json        || {})],
    // [PROPOSAL53] 신규 P0 하네스
    ['fundamental_quality_json',     JSON.stringify((hApex || {}).fundamental_quality_json     || {})],
    ['horizon_allocation_json',      JSON.stringify((hApex || {}).horizon_allocation_json      || {})],
    ['smart_money_liquidity_json',   JSON.stringify((hApex || {}).smart_money_liquidity_json   || {})],
    ['routing_serving_trace_v2_json',JSON.stringify((hApex || {}).routing_serving_trace_v2_json|| {})],
    ['fundamental_multifactor_json', JSON.stringify((hApex || {}).fundamental_multifactor_json || {})],
    ['earnings_growth_quality_json', JSON.stringify((hApex || {}).earnings_growth_quality_json || {})],
    ['market_share_proxy_json', JSON.stringify((hApex || {}).market_share_proxy_json || {})],
    ['cashflow_stability_json', JSON.stringify((hApex || {}).cashflow_stability_json || {})],
    ['routing_decision_explain_json', JSON.stringify((hApex || {}).routing_decision_explain_json || {})],

    // [PROPOSAL47_B4] STALE_BLOCK enforcement: cv_verdict=BLOCK 시 생성 차단 마커
    ['harness_generation_status', (function() {
      var verdict    = (hApex || {}).cv_verdict || '';
      var cvReport   = (hApex || {}).consistency_report_json || {};
      var failedList = cvReport.failed || [];
      var staleBlock = failedList.some(function(f) {
        return f && typeof f.reason === 'string' && f.reason.indexOf('STALE_BLOCK') >= 0;
      });
      if (verdict === 'BLOCK' && staleBlock) return 'BLOCKED_STALE_DATA';
      if (verdict === 'BLOCK')               return 'BLOCKED_CV_FAIL';
      return 'OK';
    })()]
  ];
}


/**
 * F3: buildHarnessRows_ 출력 완전성 자체검증
 * 19_harness_contract.yaml required_harness_context_keys 기준 필수 키 누락 체크.
 * 누락 키가 있으면 Logger.log 경고 — 운영 배포 전 조기 감지.
 */
function assertHarnessRowsComplete_(rows) {
  var REQUIRED_KEYS = [
    // H1 포트폴리오 가드
    'harness_version', 'captured_at', 'request_route', 'route_reason_code',
    'bundle_selected', 'prompt_entrypoint', 'json_validation_status', 'capture_required',
    'cash_ledger_basis', 'intraday_lock', 'snapshot_execution_gate', 'snapshot_execution_reason',
    'immediate_cash_krw', 'settlement_cash_d2_krw',
    'open_order_amount_krw', 'buy_power_krw', 'cash_floor_status', 'total_heat_pct',
    'heat_gate_status', 'heat_gate_threshold_pct', 'sell_priority_lock', 'quantities_lock', 'prices_lock',
    'decision_lock', 'blueprint_row_count', 'blueprint_checksum', 'blueprint_hash_algo',
    'source_manifest_checksum', 'decision_trace_checksum', 'checksum_hash_algo',
    // Collections
    'source_manifest_json', 'allowed_actions', 'blocked_actions',
    'account_snapshot_freshness_json',
    'sell_candidates_json', 'sell_quantities_json', 'buy_qty_inputs_json',
    'prices_json', 'decisions_json', 'decision_trace_json',
    'order_blueprint_json', 'p4_intraday_allowed_actions',
    'proposal_reference_json', 'proposal_reference_lock',
    'regime_trim_guidance_json', 'secular_leader_gate_json',
    'backdata_feature_bank_json',
    // G1 현금 부족액 잠금 (CASH_SHORTFALL_V1)
    'cash_current_pct_d2', 'cash_target_pct', 'cash_shortfall_min_krw', 'cash_shortfall_target_krw',
    // G2 현금 회복 TRIM 계획 (TRIM_PLAN_MIN_CASH_V1)
    'trim_plan_to_min_cash_json',
    // APEX V1 판단자료 생성 시점 로직 하네스
    'alpha_lead_json', 'alpha_lead_lock', 'backdata_feature_bank_json', 'backdata_learning_lock',
    'follow_through_json', 'follow_through_lock',
    'distribution_risk_json', 'distribution_lock', 'profit_preservation_json', 'profit_preservation_lock',
    'cash_raise_plan_json', 'rebound_sell_trigger_json', 'smart_sell_quantities_json', 'smart_cash_raise_lock',
    'execution_quality_json', 'execution_quality_lock', 'buy_permission_json', 'limit_price_policy_json',
    'regime_adjusted_sell_priority_json',  // K3: 국면·섹터 연계 H2 동적 우선순위
    'benchmark_relative_timeseries_json',
    'index_relative_health_json',
    'saqg_json',
    'cash_creation_purpose_lock_json',
    'alpha_feedback_json',
    'alpha_evaluation_window_json',
    'entry_freshness_json',
    'sell_value_preservation_json',
    'sector_rotation_momentum_json',       // L1: 섹터 로테이션 모멘텀 추적
    // M1-M5 신규
    'drawdown_guard_state', 'drawdown_buy_scale',
    'portfolio_beta_gate', 'portfolio_beta_gate_json',
    'tp_quantity_ladder_json', 'event_risk_json',
    'sector_concentration_gate', 'sector_concentration_json',
    // N1-N5 신규
    'regime_size_scale',
    'stop_adequacy_json',
    'holding_stale_json',
    'regime_cash_uplift_min_pct',
    // O1-O5 신규
    'single_position_weight_gate',
    'semiconductor_cluster_gate',
    'portfolio_drawdown_gate',
    'win_loss_streak_state', 'win_loss_streak_buy_scale',
    'position_count_gate', 'position_count',
    // O-group collections
    'single_position_weight_json',
    'semiconductor_cluster_json',
    // P1-P5 실시간 경보 & 건전성
    'stop_breach_gate', 'stop_breach_alert_json',
    'tp_trigger_gate',
    'heat_concentration_gate',
    'regime_transition_type',
    'portfolio_health_label', 'portfolio_health_score',
    'portfolio_health_blocked_json',
    // M4 목표 자산 추적
    'goal_asset_krw', 'goal_current_asset_krw', 'goal_achievement_pct',
    'goal_remaining_krw', 'goal_eta_label', 'goal_status',
    // ── [2026-05-20_HARNESS_V5] H6/H7/H8 신규 게이트
    'breakout_quality_gate_json', 'breakout_quality_gate_lock',
    'anti_whipsaw_gate_json', 'anti_whipsaw_gate_lock',
    'smart_cash_raise_json', 'smart_cash_raise_route',
    'follow_through_confirm_json', 'follow_through_confirm_lock',
    // ── [2026-05-20_HARNESS_V5] 4종 결정론적 체크섬
    'input_snapshot_checksum', 'rendered_output_checksum', 'rendered_report_checksum', 'non_deterministic_flag',
    // ── [2026-05-21_CLA_HARNESS_V1] SFG
    'satellite_failure_gate_json', 'sapg_json', 'sfg_v1_lock',
    // ── [SPRINT2_REGIME_CLA_V1] CLA 게이트 + RAG + RS_VERDICT V2 FUSION
    'regime_cla_json', 'cla_exit_status', 'rag_v1', 'rag_reason',
    'rs_verdict_source', 'rs_verdict_v1_raw',
    // ── [SPRINT3_L4] PRE_DISTRIBUTION_EARLY_WARNING_V1
    'pre_distribution_warning',
    // ── [SPRINT4] SFG 스칼라 / F2 / PCG
    'sfg_v1', 'sfg_broken_count', 'sfg_failure_rate',
    'pattern_blacklist_json',
    'portfolio_correlation_gate_json', 'correlation_gate_status',
    // ── [3RD_HARNESS_V1] 커버리지 완성 30개 필드
    'data_freshness_status', 'intraday_scope',
    'profit_lock_stage', 'auto_trailing_stop',
    'auto_trailing_stop_v2', 'ratchet_stage_v2',
    'flow_acceleration_status',
    'distribution_sell_detector_status', 'signals_count',
    'breakout_quality_score',
    'anti_chasing_verdict', 'anti_chasing_velocity_status',
    'pullback_entry_verdict', 'pullback_entry_trigger_price',
    'cash_recovery_plan_json', 'waterfall_plan_json',
    'sell_timing_verdict', 'sell_execution_window',
    'preservation_verdict', 'tick_normalized_price',
    'sell_price_sanity_status',
    'brt_verdict', 'brt_rs_slope', 'rs_verdict',
    'saqg_verdict', 'sapg_verdict',
    'serving_constraint_check', 'routing_execution_log',
    'trade_quality_json', 'pattern_blacklist_status',
    'tick_normalized_prices_json', 'ratchet_v2_per_ticker_json',
    // SPRINT 1 신규 필드 (Direction O1/O2/O5/P1/P3/P5/A2/B1/B3/K2/C1/D1)
    'semiconductor_cluster_json',
    'single_position_weight_json',
    'position_count', 'position_count_max', 'position_count_gate',
    'stop_breach_alert_json',
    'relative_stop_gate', 'relative_stop_signal_json',
    'heat_concentration_json',
    'portfolio_health_blocked_json',
    'anti_chasing_velocity_json',
    'distribution_sell_detector_json',
    'k2_staged_rebound_sell_json',
    'cash_recovery_plan_json',
    // [PROPOSAL50] 신규 필수 필드 (P0-P2)
    'export_gate_json', 'json_validation_status', 'hts_entry_allowed',
    'routing_trace_json', 'watch_ledger_json', 'ejce_json', 'scrs_v2_json', 'serving_lock_json',
    'mrag_v2_json', 'mandatory_reduction_json',
    // [PROPOSAL50-P0-GAP] H10/D2 신규 필드
    'shadow_ledger_json', 'llm_serving_constraint_json',
    // [PROPOSAL51] P0-D / P1-B / P1-C 신규 필드
    'price_hierarchy_json', 'data_quality_gate_v2_json', 'cash_recovery_display_json',
    // [PROPOSAL53]
    'fundamental_quality_json', 'horizon_allocation_json',
    'smart_money_liquidity_json', 'routing_serving_trace_v2_json'
    ,'fundamental_multifactor_json','earnings_growth_quality_json','market_share_proxy_json',
    'cashflow_stability_json','routing_decision_explain_json'
  ];
  var keySet = {};
  for (var i = 0; i < rows.length; i++) {
    if (Array.isArray(rows[i]) && rows[i].length >= 1) {
      keySet[rows[i][0]] = true;
    }
  }
  var missing = REQUIRED_KEYS.filter(function(k) { return !keySet[k]; });
  if (missing.length > 0) {
    Logger.log('[HARNESS_CONTRACT_FAIL] buildHarnessRows_ missing required keys: ' + missing.join(', '));
  } else {
    Logger.log('[HARNESS_CONTRACT_OK] All ' + REQUIRED_KEYS.length + ' required keys present.');
  }
  return missing;
}

/**
 * YAML_FORMULA_BINDING_REGISTRY_V1
 * spec 공식 ID와 GS 구현/연계 지점 연결 레지스트리 (커버리지 계량용)
 */
var YAML_FORMULA_BINDING_REGISTRY_V1 = {
  BUY_TIMING_SUITABILITY_V1: "core_satellite timing gate binding",
  CASH_RATIOS_V1: "cash ledger binding",
  ECP_RISK_SCALE_V1: "risk scale binding",
  EXECUTION_QUALITY_SCORE_V1: "execution quality binding",
  EXPECTED_EDGE_V1: "expected edge binding",
  FINANCIAL_HEALTH_SCORE_V1: "financial health binding",
  OVERSOLD_DELAY_V1: "oversold delay binding",
  PEG_SCORE_V1: "valuation peg binding",
  PORTFOLIO_BAND_STATUS_V1: "portfolio band binding",
  PORTFOLIO_BETA_V1: "factor beta binding",
  RS_MOMENTUM_V1: "rs momentum binding",
  SEA_TIMING_V1: "sell timing binding",
  SELL_CONFLICT_AWARE_RECOMMENDATION_V1: "sell conflict binding",
  STOP_PROPOSAL_LADDER_V1: "proposal stop ladder binding",
  T1_FORCED_SELL_RISK_V1: "t+1 forced sell risk binding"
};
