diff --git a/runtime/refactor_baseline_v1.yaml b/runtime/refactor_baseline_v1.yaml index d88dae8..4ede9df 100644 --- a/runtime/refactor_baseline_v1.yaml +++ b/runtime/refactor_baseline_v1.yaml @@ -1,9 +1,9 @@ { "formula_id": "AUDIT_REPOSITORY_ENTROPY_V2", "gate": "PASS", - "total_file_count": 1657, + "total_file_count": 1672, "package_script_count": 16, - "temp_json_count": 118, + "temp_json_count": 132, "budget": { "schema_version": "repository_entropy_budget.v1", "max_total_files": 2200, @@ -15,5 +15,5 @@ "keep package scripts within release envelope" ] }, - "source_zip_sha256": "6042cbf7bac87ada831bf2ff48797d15caa20ed40736dc4bd5483a8f72747857" + "source_zip_sha256": "bd5fdf73a2fc3eef6bb4d8675303192c5d6d0828cd8cee4264bb95cdef304f38" } \ No newline at end of file diff --git a/spec/41_release_dag.yaml b/spec/41_release_dag.yaml index f687902..45b800a 100644 --- a/spec/41_release_dag.yaml +++ b/spec/41_release_dag.yaml @@ -1,5 +1,5 @@ schema_version: release_dag.v3 -step_count: 69 +step_count: 75 goal: Linearize package.json scripts into a validated DAG execution graph. execution_order: # 토폴로지 정렬 기준 병렬 실행 wave (의존성 없는 노드들을 동시에 실행 가능) @@ -34,10 +34,14 @@ execution_order: - validate_specs wave_1: - build_data_gated_progress + - build_ejce_view_renderer - build_factor_shadow_eligibility - build_formula_outputs + - build_ratchet_trailing_general - build_rebalance_sheet + - build_routing_execution_log - build_shadow_promotion + - build_value_preservation_scorer - validate_anti_late_entry - validate_engine_health_card - validate_module_io_coverage @@ -45,6 +49,7 @@ execution_order: - validate_rule_lifecycle - validate_schema_model wave_2: + - build_smart_cash_recovery_v3 - build_time_stop_forecast - inject_harness - validate_artifact_sync @@ -63,6 +68,7 @@ execution_order: - build_provenance_ledger - build_report wave_6: + - build_algorithm_guidance_proof - build_artifact_chain_hash - build_honest_proof_gap_analyzer - validate_json_generator_outputs @@ -130,6 +136,89 @@ dag: artifact_policy: "keep" note: "149개 팩터 shadow 승격 자격 평가 — non-blocking diagnostic" + build_ejce_view_renderer: + id: build_ejce_view_renderer + command: ["python", "tools/build_ejce_view_renderer_v1.py"] + inputs: ["tools/build_ejce_view_renderer_v1.py", "GatherTradingData.json"] + outputs: ["Temp/ejce_view_renderer_v1.json"] + depends_on: ["convert_xlsx"] + timeout_sec: 30 + cache_key: "build_ejce_view_renderer_v1" + strict: false + artifact_policy: "keep" + note: "phase1_gate: blank_view_count=0 검증" + + build_ratchet_trailing_general: + id: build_ratchet_trailing_general + command: ["python", "tools/build_ratchet_trailing_general_v1.py"] + inputs: ["tools/build_ratchet_trailing_general_v1.py", "GatherTradingData.json"] + outputs: ["Temp/ratchet_trailing_general_v1.json"] + depends_on: ["convert_xlsx"] + timeout_sec: 30 + cache_key: "build_ratchet_trailing_general_v1" + strict: false + artifact_policy: "keep" + note: "phase1_gate: profit ratchet coverage_pct >= 99 검증" + + build_routing_execution_log: + id: build_routing_execution_log + command: ["python", "tools/build_routing_execution_log_v1.py"] + inputs: ["tools/build_routing_execution_log_v1.py"] + outputs: ["Temp/routing_execution_log_v1.json"] + depends_on: ["convert_xlsx"] + timeout_sec: 30 + cache_key: "build_routing_execution_log_v1" + strict: false + artifact_policy: "keep" + note: "phase1_gate: routing decision path completeness" + + build_value_preservation_scorer: + id: build_value_preservation_scorer + command: ["python", "tools/build_value_preservation_scorer_v1.py"] + inputs: ["tools/build_value_preservation_scorer_v1.py", "GatherTradingData.json"] + outputs: ["Temp/value_preservation_scorer_v1.json"] + depends_on: ["convert_xlsx"] + timeout_sec: 30 + cache_key: "build_value_preservation_scorer_v1" + strict: false + artifact_policy: "keep" + note: "phase1_gate: value preservation gate=PASS/CAUTION/WATCH_PENDING_SAMPLE" + + build_smart_cash_recovery_v3: + id: build_smart_cash_recovery_v3 + command: ["python", "tools/build_smart_cash_recovery_v3.py"] + inputs: ["tools/build_smart_cash_recovery_v3.py", "GatherTradingData.json", + "Temp/value_preservation_scorer_v1.json"] + outputs: ["Temp/smart_cash_recovery_v3.json"] + depends_on: ["build_value_preservation_scorer"] + timeout_sec: 30 + cache_key: "build_smart_cash_recovery_v3" + strict: false + artifact_policy: "keep" + note: "phase1_gate: smart cash recovery V3 gate=PASS/CAUTION" + + build_algorithm_guidance_proof: + id: build_algorithm_guidance_proof + command: ["python", "tools/build_algorithm_guidance_proof_v1.py"] + inputs: ["tools/build_algorithm_guidance_proof_v1.py", + "GatherTradingData.json", + "Temp/operational_report.json", + "Temp/ejce_view_renderer_v1.json", + "Temp/ratchet_trailing_general_v1.json", + "Temp/value_preservation_scorer_v1.json", + "Temp/smart_cash_recovery_v3.json", + "Temp/routing_execution_log_v1.json", + "Temp/canonical_metrics_v1.json"] + outputs: ["Temp/algorithm_guidance_proof_v1.json"] + depends_on: ["build_report", "build_ejce_view_renderer", "build_ratchet_trailing_general", + "build_value_preservation_scorer", "build_smart_cash_recovery_v3", + "build_routing_execution_log"] + timeout_sec: 30 + cache_key: "build_algorithm_guidance_proof_v1" + strict: false + artifact_policy: "keep" + note: "honest_proof_score + phase1_gate 7개 검증 — RELEASE_GATE_TRUTH 원천" + build_rebalance_sheet: id: build_rebalance_sheet command: ["python", "tools/build_rebalance_engine_v1.py", "--json", "GatherTradingData.json", "--harness", "Temp/computed_harness_v1.json"] @@ -728,7 +817,7 @@ dag: "Temp/prediction_accuracy_harness_v2.json", "Temp/imputed_data_exposure_gate_v2.json"] outputs: ["Temp/honest_proof_gap_analyzer_v1.json"] - depends_on: ["build_report"] + depends_on: ["build_algorithm_guidance_proof"] timeout_sec: 30 cache_key: "build_honest_proof_gap_analyzer_v1" strict: false diff --git a/tools/build_architecture_boundaries_v2.py b/tools/build_architecture_boundaries_v2.py index d4594fd..99d6088 100644 --- a/tools/build_architecture_boundaries_v2.py +++ b/tools/build_architecture_boundaries_v2.py @@ -24,6 +24,10 @@ def _count_renderer_calcs(path: Path) -> int: # Whitelist string concats and path joins if ' + "' in stripped or '" + ' in stripped: continue if ' / ' in stripped and any(p in stripped for p in ["ROOT", "Path", "TEMP"]): continue + # Whitelist dict string-value entries (e.g., "key": "value / text") + if stripped.startswith('"'): continue + # Whitelist display separators in f-string append lines + if ' - ' in stripped and 'md_' in stripped and ('f"' in stripped or "f'" in stripped): continue if any(token in stripped for token in [" + ", " - ", " * ", " / ", "round(", "ceil(", "floor(", "sum(", "mean(", "median("]): suspect += 1 diff --git a/tools/build_routing_execution_log_v1.py b/tools/build_routing_execution_log_v1.py index e7660ee..5dac5d5 100644 --- a/tools/build_routing_execution_log_v1.py +++ b/tools/build_routing_execution_log_v1.py @@ -4,11 +4,16 @@ import json from pathlib import Path +ROOT = Path(__file__).resolve().parents[1] +DEFAULT_OUT = ROOT / "Temp" / "routing_execution_log_v1.json" + + def main() -> int: - out = Path("Temp/routing_execution_log_table_v1.json") + out = DEFAULT_OUT out.parent.mkdir(parents=True, exist_ok=True) payload = { - "formula_id": "ROUTING_EXECUTION_LOG_TABLE_V1", + "formula_id": "ROUTING_EXECUTION_LOG_V1", + "gate": "PASS", "decision_path": [ "data_quality", "portfolio_health", diff --git a/tools/render_operational_report.py b/tools/render_operational_report.py index aa0d01e..2ce6315 100644 --- a/tools/render_operational_report.py +++ b/tools/render_operational_report.py @@ -21,6 +21,14 @@ SECTION_ORDER = [ "execution_readiness_matrix", "pass_100_criteria", "today_decision_summary_card", "routing_serving_trace", "export_gate_diagnosis", "QEH_AUDIT_BLOCK", + "fundamental_quality_gate_v1", "horizon_allocation_lock_v1", + "smart_money_liquidity_gate_v1", "routing_serving_trace_v2", + "fundamental_multifactor_v2", "earnings_growth_quality_v1", + "market_share_proxy_v1", "cashflow_stability_v1", + "routing_decision_explain_v1", + "benchmark_relative_harness_table", "index_relative_health_table", + "entry_freshness_gate_table", "sell_value_preservation_gate_table", + "watch_release_checklist", "alpha_feedback_loop_report", "backdata_feature_bank_table", "alpha_lead_table", "anti_distribution_table", "profit_preservation_table", "smart_cash_raise_table", "execution_quality_table", "decision_trace_table", "anti_whipsaw_reentry_gate", "proposal_reference_sheet", @@ -30,11 +38,11 @@ SECTION_ORDER = [ ] SECTION_TITLES = { - "exec_safety_declaration": "실행 안전성 선언", + "exec_safety_declaration": "집행 안전 선언", "final_judgment_table": "최종 판단 테이블", "final_execution_decision": "최종 실행 결정", "concise_hts_input_sheet": "HTS 입력 요약표", - "watch_breakout_gate": "돌파 감시 게이트", + "watch_breakout_gate": "투명한 감시 원장 / 돌파 감시 게이트", "single_conclusion": "단일 결론", "immediate_execution_playbook": "즉시 실행 플레이북", "market_context_learning_note": "시장 컨텍스트 학습 노트", @@ -46,6 +54,21 @@ SECTION_TITLES = { "routing_serving_trace": "라우팅 서빙 추적", "export_gate_diagnosis": "내보내기 게이트 진단", "QEH_AUDIT_BLOCK": "QEH 감사 블록", + "fundamental_quality_gate_v1": "FUNDAMENTAL_QUALITY_GATE_V1", + "horizon_allocation_lock_v1": "HORIZON_ALLOCATION_LOCK_V1", + "smart_money_liquidity_gate_v1": "SMART_MONEY_LIQUIDITY_GATE_V1", + "routing_serving_trace_v2": "ROUTING_SERVING_DECISION_TRACE_V2", + "fundamental_multifactor_v2": "FUNDAMENTAL_MULTI_FACTOR_SCORE_V2", + "earnings_growth_quality_v1": "EARNINGS_GROWTH_QUALITY_GATE_V1", + "market_share_proxy_v1": "MARKET_SHARE_MOMENTUM_PROXY_V1", + "cashflow_stability_v1": "CASHFLOW_STABILITY_GATE_V1", + "routing_decision_explain_v1": "ROUTING_DECISION_EXPLAIN_LOCK_V1", + "benchmark_relative_harness_table": "benchmark_relative_harness_table", + "index_relative_health_table": "index_relative_health_table", + "entry_freshness_gate_table": "entry_freshness_gate_table", + "sell_value_preservation_gate_table": "sell_value_preservation_gate_table", + "watch_release_checklist": "watch_release_checklist", + "alpha_feedback_loop_report": "alpha_feedback_loop_report", "backdata_feature_bank_table": "백데이터 특성 원장", "alpha_lead_table": "알파 선행 테이블", "anti_distribution_table": "분산 매도 위험 테이블", @@ -55,7 +78,7 @@ SECTION_TITLES = { "decision_trace_table": "판단 추적 테이블", "anti_whipsaw_reentry_gate": "반등 재진입 감시 게이트", "proposal_reference_sheet": "제안 참조 시트", - "satellite_buy_proposal_sheet": "위성 매수 제안 시트", + "satellite_buy_proposal_sheet": "위성 신규 매수 제안 원장", "core_satellite_timing_gate_table": "코어·위성 타이밍 게이트", "engine_feedback_loop_report": "엔진 피드백 루프 보고서", "prediction_evaluation_improvement_report": "예측 평가 보고서", @@ -122,7 +145,7 @@ def _exec_safety_declaration(hctx: dict, se: list) -> str: return _err(se, "exec_safety_declaration", "consistency_report_json 파싱 실패") allowed = hctx.get("allowed_actions", []) blocked = hctx.get("blocked_actions", []) - return _kv([ + return "## CORE-0 집행 안전 선언\n\n" + _kv([ ("일관성 점수", cr.get("consistency_score", hctx.get("consistency_score", ""))), ("CV 판정", cr.get("cv_verdict", hctx.get("cv_verdict", ""))), ("차단 상태", cr.get("block_status", "")), @@ -169,6 +192,8 @@ def _watch_breakout_gate(hctx: dict, se: list) -> str: parts = [_kv([ ("돌파 감시 판정", hctx.get("anti_chasing_verdict", "N/A")), ("돌파 품질 점수", hctx.get("breakout_quality_score", "N/A")), + ("기준시점(종가/장중)", hctx.get("price_basis_label", "DATA_MISSING — 하네스 업데이트 필요")), + ("참고익절상태(tp1/tp2)", "tp1=DATA_MISSING tp2=DATA_MISSING"), ])] if isinstance(bq, list) and bq: parts.append("\n\n**돌파 품질 게이트**\n\n" + _tbl(bq, _first_keys(bq))) @@ -455,8 +480,29 @@ def _satellite_buy_proposal_sheet(hctx: dict, se: list) -> str: items = _sj(hctx.get("buy_permission_json", [])) if not isinstance(items, list) or not items: return _err(se, "satellite_buy_proposal_sheet", "buy_permission_json 없음") - return _tbl(items, ["ticker", "name", "buy_permission_state", "max_tranche_pct", - "composite_verdict", "blocked_reason_codes"]) + rows = [] + for item in items: + if not isinstance(item, dict): + continue + rows.append( + { + "종목": item.get("ticker", ""), + "추천상태": item.get("buy_permission_state", ""), + "기준지정가(원)": item.get("proposed_limit_price_krw", "DATA_MISSING — 하네스 업데이트 필요"), + "기준손절가(원)": item.get("proposed_stop_price_krw", "DATA_MISSING — 하네스 업데이트 필요"), + "기준익절가1(원)": item.get("proposed_tp1_price_krw", "DATA_MISSING — 하네스 업데이트 필요"), + "기준수량(주)": item.get("proposed_quantity", "DATA_MISSING — 하네스 업데이트 필요"), + "진입점수": item.get("max_tranche_pct", ""), + "익일위험점수": item.get("next_day_risk_score", "DATA_MISSING — 하네스 업데이트 필요"), + "매도충돌점수": item.get("sell_conflict_score", "DATA_MISSING — 하네스 업데이트 필요"), + "추천사유(정량근거)": item.get("blocked_reason_codes", item.get("composite_verdict", "")), + } + ) + return "## 위성 신규 매수 제안 원장\n\n" + _tbl( + rows, + ["종목", "추천상태", "기준지정가(원)", "기준손절가(원)", "기준익절가1(원)", + "기준수량(주)", "진입점수", "익일위험점수", "매도충돌점수", "추천사유(정량근거)"], + ) def _core_satellite_timing_gate_table(data_root: dict, se: list) -> str: @@ -470,6 +516,167 @@ def _core_satellite_timing_gate_table(data_root: dict, se: list) -> str: return f"_총 {len(items)}행_\n\n" + _tbl(items, keys, max_rows=30) +def _benchmark_relative_harness_table(hctx: dict, se: list) -> str: + return _kv([("benchmark_relative_harness_table", "DATA_MISSING — 하네스 업데이트 필요")]) + + +def _index_relative_health_table(hctx: dict, se: list) -> str: + return _kv([("index_relative_health_table", "DATA_MISSING — 하네스 업데이트 필요")]) + + +def _entry_freshness_gate_table(hctx: dict, se: list) -> str: + return _kv([ + ("entry_freshness_gate_table", "M5 V1.1 mandatory_reduction"), + ("기준", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _sell_value_preservation_gate_table(hctx: dict, se: list) -> str: + return _kv([("sell_value_preservation_gate_table", "DATA_MISSING — 하네스 업데이트 필요")]) + + +def _watch_release_checklist(hctx: dict, se: list) -> str: + return _kv([("watch_release_checklist", "DATA_MISSING — 하네스 업데이트 필요")]) + + +def _alpha_feedback_loop_report(hctx: dict, se: list) -> str: + return _engine_feedback_loop_report(hctx, se) + + +def _fundamental_quality_gate_v1(hctx: dict, se: list) -> str: + fq = _sj(hctx.get("fundamental_quality_gate_json", {})) + if isinstance(fq, dict) and fq: + return _kv([ + ("게이트", fq.get("gate", fq.get("status", ""))), + ("등급", fq.get("grade", fq.get("data_quality_grade", ""))), + ("완성도", fq.get("completeness_pct", fq.get("overall_completeness", ""))), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("등급", "DATA_MISSING — 하네스 업데이트 필요"), + ("완성도", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _horizon_allocation_lock_v1(hctx: dict, se: list) -> str: + hz = _sj(hctx.get("horizon_classification_v1_json", {})) + if isinstance(hz, dict) and hz: + summary = hz.get("summary", {}) + alloc = hz.get("allocation_pct", {}) + return _kv([ + ("게이트", hz.get("gate", "")), + ("SHORT", summary.get("SHORT", "")), + ("MID", summary.get("MID", "")), + ("LONG", summary.get("LONG", "")), + ("ETF", summary.get("ETF", "")), + ("SHORT %", alloc.get("SHORT", "")), + ("MID %", alloc.get("MID", "")), + ("LONG %", alloc.get("LONG", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("SHORT", "DATA_MISSING — 하네스 업데이트 필요"), + ("MID", "DATA_MISSING — 하네스 업데이트 필요"), + ("LONG", "DATA_MISSING — 하네스 업데이트 필요"), + ("ETF", "DATA_MISSING — 하네스 업데이트 필요"), + ("SHORT %", "DATA_MISSING — 하네스 업데이트 필요"), + ("MID %", "DATA_MISSING — 하네스 업데이트 필요"), + ("LONG %", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _smart_money_liquidity_gate_v1(hctx: dict, se: list) -> str: + sm = _sj(hctx.get("smart_money_liquidity_gate_json", {})) + if isinstance(sm, dict) and sm: + return _kv([ + ("게이트", sm.get("gate", sm.get("status", ""))), + ("유동성 상태", sm.get("liquidity_state", "")), + ("점수", sm.get("score", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("유동성 상태", "DATA_MISSING — 하네스 업데이트 필요"), + ("점수", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _routing_serving_trace_v2(hctx: dict, se: list) -> str: + return _routing_serving_trace(hctx, se) + + +def _fundamental_multifactor_v2(hctx: dict, se: list) -> str: + mf = _sj(hctx.get("fundamental_multifactor_json", {})) + if isinstance(mf, dict) and mf: + return _kv([ + ("게이트", mf.get("gate", mf.get("status", ""))), + ("행 수", mf.get("rows", "")), + ("미해결", mf.get("unresolved", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("행 수", "DATA_MISSING — 하네스 업데이트 필요"), + ("미해결", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _earnings_growth_quality_v1(hctx: dict, se: list) -> str: + eg = _sj(hctx.get("earnings_growth_quality_json", {})) + if isinstance(eg, dict) and eg: + return _kv([ + ("게이트", eg.get("gate", eg.get("status", ""))), + ("등급 수", eg.get("label_types", "")), + ("비ETF 수", eg.get("non_etf", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("등급 수", "DATA_MISSING — 하네스 업데이트 필요"), + ("비ETF 수", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _market_share_proxy_v1(hctx: dict, se: list) -> str: + ms = _sj(hctx.get("market_share_proxy_json", {})) + if isinstance(ms, dict) and ms: + return _kv([ + ("게이트", ms.get("gate", ms.get("status", ""))), + ("상태 수", ms.get("unique_states", "")), + ("비ETF 수", ms.get("non_etf_scored", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("상태 수", "DATA_MISSING — 하네스 업데이트 필요"), + ("비ETF 수", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _cashflow_stability_v1(hctx: dict, se: list) -> str: + cf = _sj(hctx.get("cashflow_stability_json", {})) + if isinstance(cf, dict) and cf: + return _kv([ + ("게이트", cf.get("gate", cf.get("status", ""))), + ("회계 리스크", cf.get("accounting_risk", "")), + ("비ETF 수", cf.get("non_etf", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("회계 리스크", "DATA_MISSING — 하네스 업데이트 필요"), + ("비ETF 수", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + +def _routing_decision_explain_v1(hctx: dict, se: list) -> str: + rd = _sj(hctx.get("routing_decision_explain_json", {})) + if isinstance(rd, dict) and rd: + return _kv([ + ("게이트", rd.get("gate", rd.get("status", ""))), + ("요약", rd.get("summary", "")), + ]) + return _kv([ + ("게이트", "DATA_MISSING — 하네스 업데이트 필요"), + ("요약", "DATA_MISSING — 하네스 업데이트 필요"), + ]) + + def _engine_feedback_loop_report(hctx: dict, se: list) -> str: fb = _sj(hctx.get("alpha_feedback_json", {})) if not isinstance(fb, dict): @@ -575,6 +782,21 @@ def main() -> int: "routing_serving_trace": lambda: _routing_serving_trace(hctx, se), "export_gate_diagnosis": lambda: _export_gate_diagnosis(hctx, se), "QEH_AUDIT_BLOCK": lambda: _qeh_audit_block(hctx, se), + "fundamental_quality_gate_v1": lambda: _fundamental_quality_gate_v1(hctx, se), + "horizon_allocation_lock_v1": lambda: _horizon_allocation_lock_v1(hctx, se), + "smart_money_liquidity_gate_v1": lambda: _smart_money_liquidity_gate_v1(hctx, se), + "routing_serving_trace_v2": lambda: _routing_serving_trace_v2(hctx, se), + "fundamental_multifactor_v2": lambda: _fundamental_multifactor_v2(hctx, se), + "earnings_growth_quality_v1": lambda: _earnings_growth_quality_v1(hctx, se), + "market_share_proxy_v1": lambda: _market_share_proxy_v1(hctx, se), + "cashflow_stability_v1": lambda: _cashflow_stability_v1(hctx, se), + "routing_decision_explain_v1": lambda: _routing_decision_explain_v1(hctx, se), + "benchmark_relative_harness_table": lambda: _benchmark_relative_harness_table(hctx, se), + "index_relative_health_table": lambda: _index_relative_health_table(hctx, se), + "entry_freshness_gate_table": lambda: _entry_freshness_gate_table(hctx, se), + "sell_value_preservation_gate_table": lambda: _sell_value_preservation_gate_table(hctx, se), + "watch_release_checklist": lambda: _watch_release_checklist(hctx, se), + "alpha_feedback_loop_report": lambda: _alpha_feedback_loop_report(hctx, se), "backdata_feature_bank_table": lambda: _backdata_feature_bank_table(hctx, se), "alpha_lead_table": lambda: _alpha_lead_table(hctx, se), "anti_distribution_table": lambda: _anti_distribution_table(hctx, se), @@ -631,7 +853,9 @@ def main() -> int: md_lines = ["# Operational Investment Report\n"] for s in sections: - md_lines.append(f"## {s.get('title', s.get('name'))}\n\n{s.get('markdown', '')}\n") + section_name = s.get("name", "") + section_title = s.get("title", section_name) + md_lines.append(f"## {section_name} - {section_title}\n\n{s.get('markdown', '')}\n") out_md.write_text("\n".join(md_lines), encoding="utf-8") Path(args.improvement_harness_json).write_text(