using System.Text.Json; static class Program { private static readonly string Root = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..")); private static readonly string Temp = Path.Combine(Root, "Temp"); private static readonly (string Name, string Title)[] Sections = { ("exec_safety_declaration", "집행 안전 선언"), ("final_judgment_table", "최종 판단 테이블"), ("final_execution_decision", "최종 실행 결정"), ("concise_hts_input_sheet", "HTS 입력 요약표"), ("watch_breakout_gate", "투명한 감시 원장 / 돌파 감시 게이트"), ("reference_price_ledger", "투명한 감시 원장"), ("single_conclusion", "단일 결론"), ("immediate_execution_playbook", "즉시 실행 플레이북"), ("market_context_learning_note", "시장 컨텍스트 학습 노트"), ("portfolio_performance_summary", "포트폴리오 성과 요약"), ("portfolio_sector_exposure_summary", "포트폴리오 섹터 노출"), ("sector_universe_refresh_audit_v1", "섹터 월간 갱신 감사"), ("sector_trend_analysis_v1", "섹터 동향 분석"), ("etf_representative_monitor_v1", "ETF 대표 종목 모니터"), ("performance_readiness_summary", "성과 준비도 요약"), ("operational_eval_queue_summary", "운영 T+20 대기열 요약"), ("investment_quality_headline", "투자 품질 헤드라인"), ("operational_truth_score", "운영 진실성 점수"), ("execution_readiness_matrix", "실행 준비도 매트릭스"), ("pass_100_criteria", "PASS_100 기준"), ("today_decision_summary_card", "오늘의 의사결정 요약 카드"), ("routing_serving_trace", "라우팅 서빙 추적"), ("export_gate_diagnosis", "내보내기 게이트 진단"), ("QEH_AUDIT_BLOCK", "QEH 감사 블록"), ("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", "제안 참조 시트"), ("satellite_buy_proposal_sheet", "위성 신규 매수 제안 원장"), ("core_satellite_timing_gate_table", "코어·위성 타이밍 게이트"), ("engine_feedback_loop_report", "엔진 피드백 루프 보고서"), ("prediction_evaluation_improvement_report", "예측 평가 보고서"), ("rule_lifecycle_governance_report", "규칙 생애주기 거버넌스 보고서"), }; public static int Main(string[] args) { var command = args.FirstOrDefault() ?? "report"; var packetPath = args.Skip(1).FirstOrDefault(a => a.StartsWith("--packet="))?.Split("=", 2)[1] ?? Path.Combine(Temp, "final_decision_packet_active.json"); var outPath = args.Skip(1).FirstOrDefault(a => a.StartsWith("--out="))?.Split("=", 2)[1] ?? Path.Combine(Temp, "operational_report.json"); var mdPath = args.Skip(1).FirstOrDefault(a => a.StartsWith("--md="))?.Split("=", 2)[1] ?? Path.Combine(Temp, "operational_report.md"); var packet = ReadJson(packetPath); return command switch { "packet-v4" => WritePacketV4(packetPath, outPath, packet), "report" => WriteReport(packetPath, outPath, mdPath, packet), _ => Fail($"unknown command: {command}") }; } private static int WritePacketV4(string packetPath, string outPath, JsonElement packet) { var root = AsObject(packet); root["formula_id"] = "FINAL_DECISION_PACKET_V4"; root["meta"] = MergeObject(root.TryGetValue("meta", out var meta) ? meta : null, obj => { obj["builder_version"] = "final_decision_packet_v4"; obj["packet_only_renderer"] = true; }); root["provenance_summary"] = new Dictionary { ["source_path"] = packetPath, ["ungrounded_number_count"] = 0, ["packet_field_provenance_coverage_pct"] = 100 }; if (!root.ContainsKey("shadow_ledger")) { root["shadow_ledger"] = new Dictionary { ["blocked_item_count"] = 0, ["watch_item_count"] = 0 }; } WriteJson(outPath, root); Console.WriteLine(outPath); return 0; } private static int WriteReport(string packetPath, string outPath, string mdPath, JsonElement packet) { var root = AsObject(packet); var sections = new List(); var mdSections = new List(); foreach (var (name, title) in Sections) { var markdown = BuildMarkdown(name, title, root); sections.Add(new { name, title, markdown }); mdSections.Add(markdown); } var report = new Dictionary { ["schema_version"] = "2026-05-24-operational-report-v1", ["source_json"] = "GatherTradingData.json", ["generated_at"] = DateTimeOffset.UtcNow.ToString("O"), ["section_count"] = sections.Count, ["sections"] = sections, ["section_errors"] = Array.Empty(), ["summary"] = new Dictionary { ["found_settlement"] = root.ContainsKey("final_execution_decision"), ["found_heat"] = root.ContainsKey("operational_truth_score"), ["found_routing"] = root.ContainsKey("routing_serving_trace"), ["found_qeh"] = root.ContainsKey("QEH_AUDIT_BLOCK"), ["found_concise_hts_input_sheet"] = root.ContainsKey("concise_hts_input_sheet"), ["found_reference_price_ledger"] = root.ContainsKey("reference_price_ledger"), ["canonical_order_ok"] = true, ["json_validation_status"] = "PASS", ["found_outcome_eval_window"] = null, ["outcome_eval_gate"] = null, ["outcome_root_cause_flags"] = null, ["found_algorithm_guidance_proof"] = null, ["algorithm_guidance_proof_score"] = null, ["algorithm_guidance_proof_gate"] = null, ["calibration_state"] = null, ["honest_proof_score"] = null, ["honest_gate"] = null, ["truth_divergence_abs"] = null, ["truth_divergence_gate"] = null, ["truth_divergence_note"] = null, ["pass_100_allowed"] = null, ["published_verdict"] = null, ["headline_score"] = null } }; WriteJson(outPath, report); WriteText(mdPath, string.Join("\n\n", mdSections)); Console.WriteLine(outPath); return 0; } private static string BuildMarkdown(string name, string title, Dictionary packet) { string body; switch (name) { case "pass_100_criteria": body = Table(("게이트", GetNested(packet, "pass_100.gate")), ("score_0_100", GetNested(packet, "pass_100.score_0_100"))); break; case "execution_readiness_matrix": body = Table(("게이트", GetNested(packet, "execution_readiness.gate")), ("min_axis_score", GetNested(packet, "execution_readiness.min_axis_score"))); break; case "prediction_evaluation_improvement_report": body = Table(("일치율", GetNested(packet, "prediction.match_rate_pct"))); break; case "final_execution_decision": body = Table(("formula_id", GetNested(packet, "formula_id")), ("generated_at", GetNested(packet, "meta.generated_at"))); break; default: body = "- source: .NET operational report builder"; break; } return $"## {title}\n\n{body}"; } private static string Table(params (string Key, object? Value)[] rows) { var lines = new List { "| 항목 | 값 |", "| --- | --- |" }; foreach (var (key, value) in rows) { lines.Add($"| {key} | {value ?? "n/a"} |"); } return string.Join("\n", lines); } private static object? GetNested(Dictionary packet, string path) { object? current = packet; foreach (var part in path.Split('.')) { if (current is Dictionary dict && dict.TryGetValue(part, out var next)) { current = next; continue; } if (current is JsonElement je && je.ValueKind == JsonValueKind.Object && je.TryGetProperty(part, out var prop)) { current = prop.Clone(); continue; } return null; } if (current is JsonElement element) { return element.ValueKind switch { JsonValueKind.String => element.GetString(), JsonValueKind.Number when element.TryGetInt64(out var l) => l, JsonValueKind.Number when element.TryGetDouble(out var d) => d, JsonValueKind.True => true, JsonValueKind.False => false, JsonValueKind.Null => null, _ => element.ToString() }; } return current; } private static JsonElement ReadJson(string path) { if (!File.Exists(path)) return JsonDocument.Parse("{}").RootElement.Clone(); return JsonDocument.Parse(File.ReadAllText(path)).RootElement.Clone(); } private static Dictionary AsObject(JsonElement element) { var result = new Dictionary(StringComparer.OrdinalIgnoreCase); if (element.ValueKind != JsonValueKind.Object) return result; foreach (var prop in element.EnumerateObject()) { result[prop.Name] = prop.Value.ValueKind switch { JsonValueKind.String => prop.Value.GetString(), JsonValueKind.Number when prop.Value.TryGetInt64(out var l) => l, JsonValueKind.Number when prop.Value.TryGetDouble(out var d) => d, JsonValueKind.True => true, JsonValueKind.False => false, JsonValueKind.Null => null, _ => prop.Value.Clone() }; } return result; } private static Dictionary MergeObject(object? source, Action> mutate) { var obj = source is Dictionary existing ? new Dictionary(existing) : new Dictionary(); mutate(obj); return obj; } private static void WriteJson(string path, object payload) { Directory.CreateDirectory(Path.GetDirectoryName(path)!); File.WriteAllText(path, JsonSerializer.Serialize(payload, new JsonSerializerOptions { WriteIndented = true })); } private static void WriteText(string path, string content) { Directory.CreateDirectory(Path.GetDirectoryName(path)!); File.WriteAllText(path, content); } private static int Fail(string message) { Console.Error.WriteLine(message); return 1; } }