meta: title: "은퇴자산포트폴리오 — 의사결정 상태 머신" parent_file: "RetirementAssetPortfolio.yaml" version: "2026-05-15-F3_decision_flow" language: "ko-KR" timezone: "Asia/Seoul" purpose: > LLM이 투자 판단을 임의 순서로 수행하지 않도록 상태 머신으로 절차를 고정한다. 각 상태는 통과 조건, 실패 시 행동, 참조 파일을 가진다. decision_flow: initial_state: "MODEL_GOVERNANCE_GATE" terminal_states: ["FINAL_DECISION", "INSUFFICIENT_DATA", "BLOCKED"] deterministic_execution_control: purpose: "텍스트 해석 차이로 매번 다른 결론이 나오는 것을 줄이기 위한 결정 추적·동률 처리·first-match 규칙." rule: "모든 상태는 ordered_checks를 위에서 아래로 평가하고, 첫 번째 BLOCK/PASS/SELECT 결과를 decision_trace에 기록한다." no_freeform_override: - "상태 머신 밖의 산문 판단으로 final_action, grade, quantity, sell_priority를 변경 금지" - "동일 입력·동일 기준시각이면 동일 decision_trace와 동일 final_action이 나와야 한다." - "규칙 충돌 시 자연어로 절충하지 말고 tie_breaker 또는 INSUFFICIENT_DATA/BLOCKED로 종료한다." trace_required_fields: - "state" - "check_id" - "rule_ref" - "inputs_used" - "result" - "selected_action" - "blocked_actions" - "missing_inputs" - "tie_breaker_applied" tie_breaker_order: 1: "source_of_truth_order 상위 파일" 2: "risk hard stop 또는 master_prohibitions" 3: "데이터 완성도 OK > PARTIAL > DATA_MISSING" 4: "계산 가능한 공식 ID가 있는 규칙" 5: "보수적 행동: BLOCKED/INSUFFICIENT_DATA/WATCH" null_propagation_rule: "필수 입력이 null이면 해당 계산은 null로 유지하고 prohibited_calculations에 사유를 남긴다. null을 0으로 대체 금지." output_requirement: "OUTPUT_VALIDATION에서 decision_trace 누락 시 schema_validation_status=FAIL." states: MODEL_GOVERNANCE_GATE: # [governance/todo/v8_9_p1_adoption_plan.yaml P1-5] purpose: > 매 의사결정 사이클 시작 전 kill switch(data_quarantine_rate, implementation_shortfall, T5_hit_rate, calibration_error, drawdown)를 평가해 execution_mode를 확정한다. 이후 모든 단계는 이 execution_mode를 기준으로 동작한다. required_refs: - "spec/formulas/domains/governance.yaml:MODEL_GOVERNANCE_KILL_SWITCH_V1" - "tools/build_model_governance_kill_switch_v1.py" required_inputs: ["data_quarantine_rate_pct", "implementation_shortfall_ratio", "t5_hit_rate_pct", "t5_sample_count", "calibration_error", "account_mdd_pct"] computed_outputs: ["execution_mode", "kill_switch_triggered", "kill_switch_reason_codes"] pass_condition: "execution_mode 확정(kill switch 평가 완료 또는 DATA_MISSING)" fail_state: "INSUFFICIENT_DATA" INPUT_VALIDATION: purpose: "요청, 기준일, 계좌, 보유수량, 가격/수급/ATR 입력 존재 여부 확인" required_refs: - "spec/01_objective_profile.yaml:input_required" - "spec/12_field_dictionary.yaml:field_dictionary" - "spec/02_data_contract.yaml:quant_feed_contract" required_inputs: ["request_scope", "analysis_date", "account_scope"] computed_outputs: ["normalized_request", "field_alias_resolution_log"] pass_condition: "minimum request context exists" fail_state: "INSUFFICIENT_DATA" DATA_COMPLETENESS_CHECK: purpose: "데이터 상태 OK/PARTIAL/DATA_MISSING/DATA_CONFLICT/DATA_STALE 판정" required_refs: - "spec/02_data_contract.yaml:data_rule" - "spec/02_data_contract.yaml:quant_feed_contract.data_completeness_gate" - "spec/12_field_dictionary.yaml:field_dictionary.policy" required_inputs: ["normalized_request", "raw_data_sources"] computed_outputs: ["data_completeness_matrix", "missing_fields", "field_unit_conflicts"] pass_condition: "data_completeness_matrix produced" fail_state: "INSUFFICIENT_DATA" STATE_VECTOR_CONSTRUCTION: # [governance/todo/v8_9_p3_adoption_plan.yaml P3-F] purpose: > holdings, cash, tax_lots, sector_graph, factor_exposures, macro_regime_probabilities를 단일 state_vector로 통합한다. cash_ladder 구성 시 WEEKLY_LEGACY_TRANSFER_PLAN_V1을 통해 미확정 레거시 이전계획을 deployable_cash에서 제외한다. required_refs: - "spec/formulas/domains/portfolio.yaml:STATE_VECTOR_CONSTRUCTOR_V1" - "spec/formulas/domains/cash.yaml:WEEKLY_LEGACY_TRANSFER_PLAN_V1" - "tools/build_state_vector_constructor_v1.py" - "tools/build_weekly_legacy_transfer_plan_v1.py" required_inputs: ["data_completeness_matrix", "account_snapshot"] computed_outputs: ["state_vector", "state_vector_completeness_pct", "missing_components", "deployable_cash_contribution_krw"] pass_condition: "state_vector 산출(결측 component 포함 가능)" fail_state: "INSUFFICIENT_DATA" HARD_FILTER_CHECK: purpose: "하드 필터를 점수보다 먼저 적용" required_refs: - "spec/08_scoring_rules.yaml:hard_filters" - "spec/risk/aggregate_risk.yaml:risk_control.aggregate_risk_cap" - "spec/risk/circuit_breakers.yaml:risk_control.weekly_circuit_breaker" - "spec/13_formula_registry.yaml:formula_registry.formulas.TOTAL_HEAT_V1" required_inputs: ["data_completeness_matrix", "account_snapshot", "total_asset"] computed_outputs: ["hard_filter_results", "total_heat_pct", "blocked_actions"] pass_condition: "no blocking hard filter for requested action" fail_state: "BLOCKED" MARKET_REGIME_CHECK: purpose: "Risk-On/Neutral/Risk-Off 및 현금 목표 확인" required_refs: - "spec/risk/market_risk_cash.yaml:risk_control.market_risk_score_based_cash" - "spec/strategy/entry_core.yaml:entry_timing_guardrails.regime_based_entry" - "spec/13_formula_registry.yaml:formula_registry.formulas.MARKET_RISK_SCORE_V1" - "spec/13_formula_registry.yaml:formula_registry.formulas.TARGET_CASH_PCT_V1" required_inputs: ["vix_close", "kospi_close", "kospi_ma20", "usd_krw", "usd_jpy_2d_change_pct", "credit_stress_status"] computed_outputs: ["market_risk_score", "target_cash_pct", "market_regime_state"] pass_condition: "market regime classified or marked UNKNOWN" fail_state: "INSUFFICIENT_DATA" STRATEGY_SCORING: purpose: > 섹터/종목/수급/유동성/실적/밸류 점수 산출. GOLDEN_CROSS_SIGNAL_V1, STRONG_CLOSE_SIGNAL_V1, VOLATILITY_EXPANSION_BREAKOUT_V1, FIFTY_TWO_WEEK_HIGH_TRIGGER_V1, CONSECUTIVE_STREAK_V1, TREND_FILTER_GATE_V1은 component_scores의 보조신호로만 포함되며, 단독으로 BUY를 허가하지 않는다(BREAKOUT_QUALITY_GATE_V2/FOLLOW_THROUGH_DAY_CONFIRM_V1/ANTI_LATE_ENTRY_GATE_V2 체인 우회 금지). required_refs: - "spec/08_scoring_rules.yaml:strategy_score" - "spec/strategy/sector_model.yaml:sector_model" - "spec/13_formula_registry.yaml:formula_registry.formulas.FLOW_CREDIT_V1" - "spec/formulas/domains/entry.yaml:GOLDEN_CROSS_SIGNAL_V1" - "spec/formulas/domains/entry.yaml:STRONG_CLOSE_SIGNAL_V1" - "spec/formulas/domains/entry.yaml:VOLATILITY_EXPANSION_BREAKOUT_V1" - "spec/formulas/domains/entry.yaml:FIFTY_TWO_WEEK_HIGH_TRIGGER_V1" - "spec/formulas/domains/entry.yaml:CONSECUTIVE_STREAK_V1" - "spec/formulas/domains/entry.yaml:TREND_FILTER_GATE_V1" required_inputs: ["close_price", "ma20", "flow_ok", "flow_rows", "frg_5d_sh", "inst_5d_sh", "avg_trade_value_5d"] computed_outputs: ["flow_credit", "strategy_score", "component_scores", "grade_candidate", "golden_cross_today", "strong_close", "volatility_expansion_breakout", "fifty_two_week_high_breakout", "up_streak", "down_streak", "trend_filter_pass"] pass_condition: "strategy_score calculated or missing fields listed" fail_state: "INSUFFICIENT_DATA" PORTFOLIO_CONSTRAINT_CHECK: purpose: "계좌·비중·중복노출·현금·납입한도 확인" required_refs: - "spec/10_portfolio_rules.yaml" - "spec/risk/portfolio_exposure.yaml:portfolio_exposure_framework" required_inputs: ["target_cash_pct", "available_cash", "total_asset", "current_exposures", "account_scope"] computed_outputs: ["portfolio_fit_score", "cash_floor_status", "exposure_limit_amounts"] pass_condition: "portfolio constraints pass or downgrade action selected" fail_state: "BLOCKED" cash_shortfall_resolution: # [2026-05-19_HARNESS_AUDIT_V1] H2 rule_id: "CSR001" purpose: > 현금 < target_cash_pct 확인 시 BUY를 결정론적으로 차단하고 SELL_PRIORITY 엔진으로 TRIM 수량을 자동 배정한다. LLM 임의 배정 금지. trigger_condition: "CASH_RATIOS_V1.current_cash_pct < TARGET_CASH_PCT_V1.target_cash_pct" steps: step_1_block_buy: action: "모든 BUY 신호 → IGNORE 전환. 이유 불문, LLM 재해석 금지." output_key: "buy_gate_status = CASH_BLOCKED" step_2_calc_shortfall: formula: "cash_shortfall_krw = max(0, (target_cash_pct/100 - current_cash_pct/100) × total_asset)" source: "CASH_RATIOS_V1 결과 기반 — LLM 직접 계산 금지" output_key: "cash_shortfall_krw" step_3_assign_trim: source: "sell_priority_engine (spec/risk/portfolio_exposure.yaml)" algorithm: > SELL_PRIORITY 1순위 종목부터 계단식 TRIM 수량 배정. 잔여 부족액이 남으면 2순위로 이동하며 반복. formula: "trim_qty = min(floor(remaining_shortfall_krw / current_price), holding_quantity)" cascade: "remaining_shortfall_krw -= trim_qty × current_price → 다음 순위" output_tag: "[CASH_RAISE_AUTO: {종목} {qty}주 TRIM → 예상확보 {proceeds}원]" step_4_output_required: fields: - "cash_shortfall_krw (부족액)" - "trim_assignments: [{종목, qty, expected_proceeds}] (배정 목록)" - "post_trim_cash_pct (TRIM 후 예상 현금비중)" enforcement: - "수동 배정·서사적 배정 금지 — 공식 결과만 기재" - "trim_assignments 없이 현금 부족 해소 주장 금지" - "QEH_AUDIT_BLOCK.SELL_PRIORITY_V1 행에 배정 결과 요약 필수" - "1순위 소진 전 2순위 배정 금지 (sell_priority_engine 순서 준수)" SECTOR_EXPOSURE_REVIEW: # [governance/todo/v8_9_p1_adoption_plan.yaml P1-5] purpose: > 섹터를 canonical ID로 분류하고 ETF lookthrough·factor beta residualization을 적용해 실질노출을 산출하며, 리더 생명주기(CAPTAIN~DISTRIBUTION_RISK)를 평가한다. required_refs: - "spec/formulas/domains/sector.yaml:SECTOR_EXPOSURE_GRAPH_V1" - "spec/formulas/domains/sector.yaml:LEADER_LIFECYCLE_GATE_V1" - "tools/build_sector_exposure_graph_v1.py" required_inputs: ["exposure_limit_amounts", "current_exposures", "etf_constituents_json"] computed_outputs: ["sector_family_total_pct", "lookthrough_etf_weight_pct", "leader_role"] pass_condition: "섹터 노출 산출 또는 ETF_BUY_BLOCKED/DATA_MISSING 명시" fail_state: "INSUFFICIENT_DATA" POSITION_SIZING: purpose: "ATR20·현금·목표비중·유동성으로 정수 수량 산출" required_refs: - "spec/05_position_sizing.yaml:position_sizing" - "spec/13_formula_registry.yaml:formula_registry.formulas.RISK_BUDGET_CASCADE_V1" - "spec/13_formula_registry.yaml:formula_registry.formulas.POSITION_SIZE_V1" required_inputs: ["total_asset", "atr20", "entry_price", "available_cash", "exposure_limit_amounts", "bayesian_confidence_multiplier"] computed_outputs: ["final_risk_budget", "atr_quantity", "cash_limit_quantity", "target_weight_limit_quantity", "sector_limit_quantity", "liquidity_limit_quantity", "final_quantity"] pass_condition: "integer quantity calculated, or NO_QUANTITY reason emitted" fail_state: "INSUFFICIENT_DATA" EXIT_POLICY_CHECK: purpose: > 손절/익절/trailing/보유주 점검 규칙 적용. 돌파 매수로 진입한 포지션에는 BREAKOUT_FAILURE_STOP_V1을 적용해 전고점 재이탈을 ANTI_WHIPSAW_GATE_V1과 별도로 포착한다. required_refs: - "spec/exit/stop_loss.yaml:stop_loss" - "spec/exit/take_profit.yaml:take_profit" - "spec/exit/position_review.yaml:position_review_cycle" - "spec/13_formula_registry.yaml:formula_registry.formulas.EXPECTED_EDGE_V1" - "spec/formulas/domains/exit.yaml:BREAKOUT_FAILURE_STOP_V1" required_inputs: ["entry_price", "stop_price", "target_price", "final_quantity"] computed_outputs: ["expected_edge", "stop_order", "take_profit_order", "invalidation_conditions", "breakout_failure"] pass_condition: "exit or hold policy evaluated" fail_state: "INSUFFICIENT_DATA" proactive_exit_radar_check: # [2026-05-19_PROACTIVE_RADAR_V1] purpose: > 보유 포지션 분석 시 항상 실행. 가격이 멀쩡할 때도 수급·유동성·섹터 신호로 선제 매도 적기를 감지한다. '매도가 예술이다' 원칙 구현. required_refs: - "spec/exit/proactive_exit_radar.yaml" - "spec/13_formula_registry.yaml:DIVERGENCE_SCORE_V1" - "spec/13_formula_registry.yaml:OVERHANG_PRESSURE_V1" - "spec/13_formula_registry.yaml:SECTOR_ROTATION_RADAR_V1" required_inputs: - "frg_5d_sh, inst_5d_sh, flow_credit (W1)" - "frg_5d_sh, frg_20d_sh, volume, avg_volume_5d (W2)" - "sector_flow.SmartMoney_5D_Norm_Score, sector_flow.Rank (W3)" computed_outputs: - "divergence_score + W1_status" - "overhang_score + W2_status" - "W3_status" - "radar_composite_status (PASS/CAUTION/ALERT/CRITICAL_ALERT)" - "PROACTIVE_RADAR_BLOCK (보고서 출력용)" hard_rules: - "ALERT 이상 발화 시 sell_priority_engine 실행 필수" - "CRITICAL_ALERT 시 코어 포지션 포함 전면 재검토 강제" - "LLM이 레이더 결과를 완화하는 서사 출력 금지 (Section B 해설만 허용)" - "RADAR_MISSING(데이터 부족) 시 soft-block: 보유 포지션 수동 점검 권고 문구 출력" PORTFOLIO_TRANSITION_REVIEW: # [governance/todo/v8_9_p0_adoption_plan.yaml P0-1.6, v8_9_p2_adoption_plan.yaml P2-E] purpose: > 개별 종목 단위 결정을 포트폴리오 전체 전환 효용으로 재평가한다. EXIT_POLICY_CHECK에서 나온 sell 후보들을 단일 전환 패킷으로 비교해 selected_transition_set 또는 NO_TRADE를 결정론적으로 확정한다. CE70/CVaR95 분포는 WALK_FORWARD_BOOTSTRAP_V1으로 생성하고 SCENARIO_SHOCK_MATRIX_V1의 5개 스트레스 시나리오를 반영하며, candidate 1건이 아니라 TRANSITION_SET_ENUMERATOR_V1으로 조합 단위 hard_constraint(MRC/CVaR95 포함)를 재검증한다. 실제 실행은 REBALANCE_CADENCE_GATE_V1의 rebalance_execution_allowed=true일 때만 허용된다. required_refs: - "spec/formulas/domains/portfolio.yaml:PORTFOLIO_TRANSITION_UTILITY_V1" - "spec/formulas/domains/portfolio.yaml:TRANSITION_SET_ENUMERATOR_V1" - "spec/formulas/domains/portfolio.yaml:REBALANCE_CADENCE_GATE_V1" - "spec/formulas/domains/simulation.yaml:SCENARIO_SHOCK_MATRIX_V1" - "spec/formulas/domains/simulation.yaml:WALK_FORWARD_BOOTSTRAP_V1" - "tools/build_portfolio_transition_optimizer_v1.py" - "tools/build_transition_set_enumerator_v1.py" - "tools/build_scenario_shock_matrix_v1.py" - "tools/build_walk_forward_bootstrap_v1.py" - "tools/build_rebalance_cadence_gate_v1.py" required_inputs: ["stop_order", "take_profit_order", "sell_waterfall_rows", "cash_repair_benefit_krw", "rebalance_execution_allowed"] computed_outputs: ["transition_utility_krw", "acceptance_margin_krw", "selected_transition", "portfolio_transition_final_action", "selected_transition_set", "scenario_results", "post_trade_mrc", "post_trade_cvar95_krw"] pass_condition: "selected_transition_set 결정(rebalance_execution_allowed=true 필요) 또는 default_action=NO_TRADE 명시" fail_state: "INSUFFICIENT_DATA" EXECUTION_CAPACITY_CHECK: # [governance/todo/v8_9_p1_adoption_plan.yaml P1-5, v8_9_p2_adoption_plan.yaml P2-E] purpose: > selected_transition_set의 주문금액이 실제 체결 가능 용량(20일 평균거래대금, 당일 거래대금, 호가창 깊이)을 초과하지 않는지 확인하고, broker_microstructure_packet 결측 시 차단한다. 용량이 확인되면 EXECUTION_PLAN_COMPILER_V1으로 30/30/40 LIMIT_SPLIT 슬라이스를 컴파일한다. required_refs: - "spec/formulas/domains/execution.yaml:EXECUTION_CAPACITY_LADDER_V1" - "spec/formulas/domains/execution.yaml:EXECUTION_PLAN_COMPILER_V1" - "tools/build_execution_capacity_ladder_v1.py" - "tools/build_execution_plan_compiler_v1.py" required_inputs: ["selected_transition_set", "avg_trade_value_20d_krw", "intraday_trade_value_krw", "orderbook_top3_depth_krw", "spread_bps"] computed_outputs: ["order_capacity_krw", "execution_plan_status", "compiled_slices"] pass_condition: "order_capacity_krw 산출 또는 EXECUTION_PLAN_BLOCKED 명시" fail_state: "INSUFFICIENT_DATA" OUTPUT_VALIDATION: purpose: "JSON Schema와 HTS 표 필드 검증" required_refs: - "schemas/output_schema.json" - "spec/07_output_schema.yaml" required_inputs: ["final_action", "orders", "scores", "position_sizing", "triggered_rules", "missing_data"] computed_outputs: ["schema_validation_status", "prohibited_calculations"] pass_condition: "all required output fields populated or prohibited_calculations filled" fail_state: "INSUFFICIENT_DATA" FINAL_DECISION: purpose: "BUY/HOLD/SELL/TRIM/ROTATE/AVOID/WATCH/INSUFFICIENT_DATA 중 하나로 결론" required_refs: - "spec/07_output_schema.yaml:recommendation_grade" - "spec/formulas/domains/governance.yaml:IMMUTABLE_DECISION_LEDGER_V1" output_required: - "final_action" - "grade" - "orders or prohibited_calculations" - "rules_used" - "ledger_append_status" post_state_action: "tools/build_immutable_decision_ledger_v1.py 호출 — decision_id 재기록 시도는 DUPLICATE_DECISION_ID로 거부." INSUFFICIENT_DATA: purpose: "데이터 부족으로 산출 불가. 다음 확인 출처를 제시." output_required: - "missing_fields" - "prohibited_calculations" - "next_source_to_check" - "ledger_append_status" post_state_action: "INSUFFICIENT_DATA로 종료해도 IMMUTABLE_DECISION_LEDGER_V1에 기록한다(결정 없음도 기록 대상)." BLOCKED: purpose: "하드 필터 또는 리스크 정책으로 행동 차단." output_required: - "triggered_rules" - "blocked_action" - "allowed_alternative" - "ledger_append_status" post_state_action: "BLOCKED로 종료해도 IMMUTABLE_DECISION_LEDGER_V1에 기록한다." transitions: - from: "MODEL_GOVERNANCE_GATE" to: "INPUT_VALIDATION" condition: "execution_mode 확정(kill switch 평가 완료 또는 DATA_MISSING)" - from: "MODEL_GOVERNANCE_GATE" to: "INSUFFICIENT_DATA" condition: "kill switch 평가에 필요한 모든 지표가 결측" - from: "INPUT_VALIDATION" to: "DATA_COMPLETENESS_CHECK" condition: "minimum request context exists" - from: "INPUT_VALIDATION" to: "INSUFFICIENT_DATA" condition: "account/request context missing and cannot be inferred" - from: "DATA_COMPLETENESS_CHECK" to: "STATE_VECTOR_CONSTRUCTION" condition: "data_completeness_matrix produced" - from: "DATA_COMPLETENESS_CHECK" to: "INSUFFICIENT_DATA" condition: "matrix cannot be produced" - from: "STATE_VECTOR_CONSTRUCTION" to: "HARD_FILTER_CHECK" condition: "state_vector 산출(결측 component 포함 가능)" - from: "HARD_FILTER_CHECK" to: "BLOCKED" condition: "blocking hard_filter failed" - from: "HARD_FILTER_CHECK" to: "MARKET_REGIME_CHECK" condition: "no blocking hard_filter failed" - from: "MARKET_REGIME_CHECK" to: "STRATEGY_SCORING" condition: "regime classified or UNKNOWN with caution" - from: "STRATEGY_SCORING" to: "PORTFOLIO_CONSTRAINT_CHECK" condition: "strategy score calculated or downgrade reason emitted" - from: "PORTFOLIO_CONSTRAINT_CHECK" to: "BLOCKED" condition: "cash_floor, duplicate exposure, account limit, or Total_Heat blocks requested action" - from: "PORTFOLIO_CONSTRAINT_CHECK" to: "SECTOR_EXPOSURE_REVIEW" condition: "requested action requires quantity or affects sector exposure" - from: "PORTFOLIO_CONSTRAINT_CHECK" to: "EXIT_POLICY_CHECK" condition: "requested action is hold/trim/sell review" - from: "SECTOR_EXPOSURE_REVIEW" to: "POSITION_SIZING" condition: "섹터 노출 산출 또는 ETF_BUY_BLOCKED/DATA_MISSING 명시" - from: "SECTOR_EXPOSURE_REVIEW" to: "INSUFFICIENT_DATA" condition: "sector exposure 계산에 필요한 입력 누락" - from: "POSITION_SIZING" to: "EXIT_POLICY_CHECK" condition: "quantity calculated or NO_QUANTITY reason emitted" - from: "EXIT_POLICY_CHECK" to: "PORTFOLIO_TRANSITION_REVIEW" condition: "order/hold/watch decision prepared" - from: "PORTFOLIO_TRANSITION_REVIEW" to: "EXECUTION_CAPACITY_CHECK" condition: "selected_transition 결정 또는 NO_TRADE 명시" - from: "PORTFOLIO_TRANSITION_REVIEW" to: "INSUFFICIENT_DATA" condition: "transition utility 계산에 필요한 입력 누락" - from: "EXECUTION_CAPACITY_CHECK" to: "OUTPUT_VALIDATION" condition: "order_capacity_krw 산출 또는 EXECUTION_PLAN_BLOCKED 명시 (NO_TRADE인 경우 즉시 통과)" - from: "EXECUTION_CAPACITY_CHECK" to: "INSUFFICIENT_DATA" condition: "broker_microstructure_packet 필드 누락" - from: "OUTPUT_VALIDATION" to: "FINAL_DECISION" condition: "schema and required output fields valid" - from: "OUTPUT_VALIDATION" to: "INSUFFICIENT_DATA" condition: "required output fields missing without prohibited_calculations reason" global_prohibitions: - "HARD_FILTER_CHECK 이전에 BUY 결론 출력 금지" - "POSITION_SIZING 이전에 정수 주문수량 출력 금지" - "OUTPUT_VALIDATION 실패 상태에서 즉시 실행 플레이북 출력 금지" - "BLOCKED 상태를 WATCH로 미화 금지. 차단 사유를 명시한다."