# spec/28 — 대체데이터 노출 게이트 계약 (IMPUTED_DATA_EXPOSURE_GATE_V1)
#
# 목적: 거버넌스/truth 점수(schema_presence, investment_quality, confidence_cap_basis)는
#       높지만 그 분모가 "스키마 존재율"에 치우쳐 실질 입력의 대체(imputed)·합성·PENDING을
#       가릴 수 있다. 이 게이트는 실질 데이터 커버리지를 결정론적으로 측정해 정직 신뢰도 캡을
#       재산출하고, 대체데이터 감지 시 장기·펀더멘털 단정을 차단한다.
#
# 위치: ENGINE_AUDIT_V1 감사 산출물(Temp/engine_audit_v1.json) 전용.
#       GAS 런타임/HTS 주문 판단에는 개입하지 않는다(감사·진단 계층).
# 우선순위: 리스크 정책(spec/03) 하위. 본 계약은 감사 가시성 계약이며 주문을 생성하지 않는다.

meta:
  formula_id: IMPUTED_DATA_EXPOSURE_GATE_V1
  audit_id: ENGINE_AUDIT_V1
  has_code_implementation: true
  code_path: "tools/build_engine_audit_v1.py"
  version: "2026-05-31_ENGINE_AUDIT_V1"
  python_tool: tools/build_engine_audit_v1.py
  validator_tool: tools/validate_engine_audit_v1.py
  canonical_ref: spec/13b_harness_formulas.yaml:IMPUTED_DATA_EXPOSURE_GATE_V1
  llm_role: explanation_only        # 게이트 산출값은 LLM 재계산·완화 금지

# ── 실질 데이터 도메인 (가중치 합 = 1.0) ───────────────────────────────
domains:
  fundamental_core:
    weight: 0.30
    coverage_source: "fundamental_multifactor_v3.json:rows[non-ETF].breakdown{roe,opm,ocf,fcf}"
    coverage_formula: "present_core_factors / (4 × non_etf_ticker_count)"
    note: ROE/OPM/OCF/FCF 결측(PARTIAL)이면 coverage↓. 장기·펀더멘털 우위 판단의 핵심.
  realized_outcome:
    weight: 0.30
    coverage_source: "prediction_accuracy_harness_v2.json:{t1_sample,t5_sample,t20_sample}"
    coverage_formula: "windows_with_sample / 3"
    note: T+20 실현 표본 0건이면 장기 예측 미검증.
  trade_quality:
    weight: 0.15
    coverage_source: "data_quality_gate_v2_py.json:category_scores.trade_quality"
    coverage_formula: "score/100 (PENDING → 0)"
  pattern:
    weight: 0.10
    coverage_source: "data_quality_gate_v2_py.json:category_scores.pattern"
    coverage_formula: "score/100 (PENDING → 0)"
  alpha_eval:
    weight: 0.15
    coverage_source: "data_quality_gate_v2_py.json:category_scores.alpha_eval"
    coverage_formula: "score/100 (PENDING → 0)"

# ── 산식 ────────────────────────────────────────────────────────────────
formulas:
  weighted_coverage: "Σ(domain.weight × domain.coverage)"
  imputed_field_ratio: "1 − weighted_coverage"
  imputed_domain_ratio: "count(domain.coverage < 0.5) / domain_count"
  fundamental_core_factor_coverage: "domains.fundamental_core.coverage"
  surrogate_outcome_ratio: "1 − domains.realized_outcome.coverage"
  # 시스템 자체 캡 공식 재사용, 분모만 정직 커버리지로 교체
  effective_confidence_honest: "raw_confidence_cap_basis × (0.4 + 0.6 × weighted_coverage)"
  confidence_cap_inflation_gap: "raw_confidence_cap_basis − effective_confidence_honest"

# ── 임계값 ──────────────────────────────────────────────────────────────
thresholds:
  block_ratio: 0.50          # imputed_field_ratio ≥ 0.50 → IMPUTED_DATA_BLOCK
  warn_ratio: 0.25           # ≥ 0.25 → IMPUTED_DATA_WARN
  fund_factor_min_coverage: 0.50   # 미만이면 fundamental_claim_allowed=false
  render_skew_pct: 10.0      # 렌더 보고서 값 vs 권위 JSON 차이 임계(%)

# ── 게이트 출력 계약 ────────────────────────────────────────────────────
output:
  target: Temp/engine_audit_v1.json
  block: imputed_data_exposure
  fields:
    - gate_status            # PASS | IMPUTED_DATA_WARN | IMPUTED_DATA_BLOCK
    - imputed_field_ratio
    - imputed_domain_ratio
    - weighted_coverage
    - domain_coverage
    - fundamental_core_factor_coverage
    - fundamental_missing_ratio
    - surrogate_outcome_ratio
    - raw_confidence_cap_basis
    - effective_confidence_honest
    - confidence_cap_inflation_gap
    - long_horizon_allowed          # t20_sample>0 AND fundamental_core_factor_coverage≥0.5
    - fundamental_claim_allowed     # fundamental_core_factor_coverage≥0.5
    - report_render_skew            # 렌더 보고서 vs 권위 JSON 불일치 감지
    - exposure_reasons

# ── 마스킹 금지 규칙 (RAW_VS_ADJUSTED_DISCLOSURE_V1) ─────────────────────
masking_rules:
  RAW_VS_ADJUSTED_DISCLOSURE_V1:
    formula_id: RAW_VS_ADJUSTED_DISCLOSURE_V1
    rationale: >
      raw_value_damage=15.7%가 adjusted=0.0%로 가려진 채 게이트에 들어가면
      가치훼손 캡(10%)이 무력화된다.
    enforcement:
      - "게이트 입력(value_damage_cap 등)은 항상 raw_* 값을 사용한다. adjusted_*는 게이트 입력 금지"
      - "보고서 표에 adjusted를 표시할 경우 같은 행/셀에 raw를 의무 병기: 'raw 15.7% / adj 0.0%'"
      - "raw 병기 없는 adjusted 단독 표시 1건당 masked_metric_without_raw_count += 1"
    detection:
      fields_to_check:
        - {raw: raw_value_damage_pct_avg, adjusted: adjusted_value_damage_pct_avg,
           source: smart_cash_recovery_v8.json}
        - {raw: raw_value_damage_pct, adjusted: adjusted_value_damage_pct,
           source: value_preservation_scorer_v2.json}
    output_metric: operational_report.json.summary.masked_metric_without_raw_count
    acceptance: "masked_metric_without_raw_count == 0"
    python_tool: tools/build_value_preservation_scorer_v2.py
    gs_coverage: "gas_lib.gs:formatRawAdjustedPair_()"
    validator: "tools/validate_value_damage_reconciliation_v1.py --raw-required"

# ── 금지 사항 ───────────────────────────────────────────────────────────
prohibitions:
  - "LLM이 gate_status / effective_confidence_honest / coverage 를 재계산·완화하는 것 금지(HS011)"
  - "fundamental_claim_allowed=false 인데 펀더멘털·장기 우위를 단정하는 서술 금지"
  - "long_horizon_allowed=false 인데 POSITION(장기) 신규 진입을 정당화하는 서술 금지"
  - "report_render_skew.skew_detected=true 를 무시하고 렌더 보고서 값을 권위값으로 인용 금지"

# ── 검증 (validate_engine_audit_v1.py) ─────────────────────────────────
validation:
  default_mode: "산출물 무결성(스키마·불변식·산식 재현). 엔진 status=failed 여도 PASS 가능."
  strict_mode: "추가로 final_verdict.status == passed 요구 → 미달 시 비0 종료."
  invariants:
    - "fundamental_core_factor_coverage < fund_factor_min_coverage → fundamental_claim_allowed == false"
    - "imputed_field_ratio ≥ block_ratio → gate_status == IMPUTED_DATA_BLOCK"
    - "decision.decision_source == rule_engine"
    - "llm_control.final_decision_from_llm == false AND llm_generated_decision_field_count == 0"
    - "weighted_coverage / imputed_field_ratio / effective_confidence_honest 재계산 일치(±오차)"
